使用自定義 View 繪製一個懸浮式可拖拽按鈕
最近公司因為業務要求需要實現一個可以拖拽的懸浮按鈕,Android 官方提供了 FloatingActionButton 但是並不支援定製。於是我打算採用自定義 View 的方法來實現。Android 官方文件告訴我們,使用自定義控制元件需要以下的步驟。(根據你的需要,某些步驟可以省略)
- 建立 View
- 處理 View 的佈局
- 繪製 View
- 與使用者進行互動
- 優化已定義的 View
下面我分別對每一步進行介紹。
建立 View(繼承 View)
在第三步 onDraw 方法中開始繪製之前,你應該讓畫筆 Paint 物件的資訊初始化完畢。這是因為 View 的重新繪製是比較頻繁的,這就可能多次呼叫 onDraw,所以初始化的程式碼不應該放在 onDraw 方法裡。
public class FloatDragView extends View {//繼承 View public FloatDragView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); //初始化畫筆 mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG); mTextPaint.setColor(Color.parseColor("#000000")); mTextPaint.setTextSize(context.getResources().getDimensionPixelSize(R.dimen.medium_text_size)); mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG); bitmap = BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher_round); } } 複製程式碼
處理 View 的佈局
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(mRadius * 2, mRadius * 2); } 複製程式碼
繪製 View
一旦自定義控制元件被建立並且測量程式碼寫好之後,接下來你就可以實現 onDraw()來繪製 View 了,onDraw 方法包含了一個 Canvas 叫做畫布的引數,onDraw()簡單來說就兩點:1、Canvas 決定要去畫什麼;2、Paint 決定怎麼畫。比如,Canvas 提供了畫線方法,Paint 就來決定線的顏色。Canvas 提供了畫矩形,Paint 又可以決定讓矩形是空心還是實心。程式碼如下:
protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawBitmap(bitmap, 0, 0, mBitmapPaint); float textWidth = mTextPaint.measureText(mText, 0, mText.length()); Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics(); canvas.drawText(mText, 0, mText.length(), mRadius - textWidth / 2, mRadius + -(fontMetrics.ascent + fontMetrics.descent) / 2, mTextPaint); } 複製程式碼
與使用者互動
本文要實現的是一個可拖拽可點選的按鈕。拖拽事件程式碼如下:
public boolean onTouchEvent(MotionEvent event) { float x = event.getRawX(); float y = event.getRawY() - getStatusBarHeight(getContext()); switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mTouchX = event.getX(); mTouchY = event.getY(); mStartX = x; mStartY = y; break; case MotionEvent.ACTION_MOVE: if (mOnScrollListener != null) { mOnScrollListener.onScroll((int) (x - mTouchX), (int) (y - mTouchY)); } break; case MotionEvent.ACTION_UP: mTouchX = mTouchY = 0; if (Math.abs(x - mStartX) < 5 && Math.abs(y - mStartY) < 5) { if (mOnClickListener != null) { mOnClickListener.onClick(); } } break; } return true; } public void setOnScrollListener(OnScrollListener onScrollListener) { mOnScrollListener = onScrollListener; } public interface OnScrollListener { void onScroll(int x, int y); } public void setOnClickListener(OnClickListener onClickListener) { mOnClickListener = onClickListener; } public interface OnClickListener { void onClick(); } /** * 滑動監聽,動態改變按鈕和列表的位置 */ @Override public void onScroll(int x, int y) { mFdvParams.x = x; mFdvParams.y = y; mWindowManager.updateViewLayout(mFloatDragView, mFdvParams); if (mIsSpinnerShow) { mRvParams.x = mFdvParams.x; mRvParams.y = mFdvParams.y + mFloatDragView.getHeight(); mWindowManager.updateViewLayout(mSpinnerRv, mRvParams); } } //點選事件忽略 複製程式碼
那麼我們需要將自定義 View 顯示在螢幕上(注意:顯示懸浮按鈕還需要申請許可權<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
):
public void showFloatDragView() { mContext = BaseApplication.getInstance(); mWindowManager = (WindowManager) mContext.getSystemService(WINDOW_SERVICE); mFloatDragView = new FloatDragView(mContext); mFloatDragView.setOnClickListener(this); mFloatDragView.setOnScrollListener(this); mFloatDragView.setText(mUrlArr[0]); mFdvParams = new WindowManager.LayoutParams(); mFdvParams.type = WindowManager.LayoutParams.TYPE_PHONE;//級別 mFdvParams.format = PixelFormat.TRANSPARENT;//背景透明 mFdvParams.gravity = Gravity.LEFT | Gravity.TOP;//位置 mFdvParams.flags = WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE; mFdvParams.width = WindowManager.LayoutParams.WRAP_CONTENT;//寬高 mFdvParams.height = WindowManager.LayoutParams.WRAP_CONTENT; mWindowManager.addView(mFloatDragView, mFdvParams); } 複製程式碼
這樣我們的懸浮按鈕就顯示在螢幕上了。
優化自定義的 View
- 去除無用程式碼
- 在 onDraw()方法中不應該有會導致垃圾回收的程式碼
- 儘可能少讓 onDraw()方法呼叫