前言
最近公司因為業務要求需要實現一個可以拖拽的懸浮按鈕,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()方法呼叫