自定義手勢鍵盤
手勢鍵盤有三種狀態,初始狀態、點擊狀態和錯誤狀態,分別以下列三個圖片顯示。
2. 數據類CircleArea
CircleArea
類用來記錄手勢鍵盤的信息。
static class CircleArea {
float x, y; // 圓心X
float radius; // 圓半徑
int mValue; // 值
public CircleArea(float x, float y, float radius, int value) {
this.x = x;
this.y = y;
this.radius = radius;
this.mValue = value;
}
public CircleArea moveTo(float dx, float dy, int value) {
return new CircleArea(x + dx, y + dy, radius, value);
}
// 該點是否在圓內
public boolean contain(float x, float y) {
return Math.sqrt(Math.pow(this.x - x, 2) + Math.pow(this.y - y, 2)) < radius;
}
public int getValue() {
return mValue;
}
}
3. 手勢鍵盤GestureView
-
初始化,在構造函數裏面初始化圖片和畫筆,在
onMeasure(int, int)
計算手勢點的位置。public class GestureView extends View { private static final int INVALID_COUNT = 4; enum Status { Normal, Error } private Bitmap mNormalBmp, mClickBmp, mErrorBmp; private int mNormalColor, mErrorColor; private Paint mBitmapPaint, mLinePaint; private List<CircleArea> mTotalCircles = new ArrayList<>(); private List<CircleArea> mLinkCircle = new ArrayList<>(); private PointF mCurrentPoint = null; private Status mStatus = Status.Normal; public GestureView(Context context) { this(context, null); } public GestureView(Context context, @Nullable AttributeSet attrs) { super(context, attrs); mNormalBmp = BitmapFactory.decodeResource(getResources(), R.drawable.gesture_circle_normal); mClickBmp = BitmapFactory.decodeResource(getResources(), R.drawable.gesture_circle_click); mErrorBmp = BitmapFactory.decodeResource(getResources(), R.drawable.gesture_circle_error); mNormalColor = getResources().getColor(R.color.gesture_normal_line); mErrorColor = getResources().getColor(R.color.gesture_error_line); mBitmapPaint = new Paint(); mBitmapPaint.setFilterBitmap(true); mLinePaint = new Paint(); mLinePaint.setStrokeWidth(10); mLinePaint.setAntiAlias(true); } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); int measuredWidth = getMeasuredWidth(); int measuredHeight = getMeasuredHeight(); int squareWidth = Math.min(measuredWidth, measuredHeight); // 界面被3等分,每個手勢佔1/6 float radius = squareWidth / 12.0f; float startX = (measuredWidth - squareWidth) / 2.0f; float startY = (measuredHeight - squareWidth) / 2.0f; mTotalCircles.clear(); CircleArea circleArea = new CircleArea(startX + radius*2, startY + radius*2, radius, 1); mTotalCircles.add(circleArea); mTotalCircles.add(circleArea.moveTo(radius*4, 0, 2)); mTotalCircles.add(circleArea.moveTo(radius*8, 0, 3)); mTotalCircles.add(circleArea.moveTo(0, radius*4, 4)); mTotalCircles.add(circleArea.moveTo(radius*4, radius*4, 5)); mTotalCircles.add(circleArea.moveTo(radius*8, radius*4, 6)); mTotalCircles.add(circleArea.moveTo(0, radius*8, 7)); mTotalCircles.add(circleArea.moveTo(radius*4, radius*8, 8)); mTotalCircles.add(circleArea.moveTo(radius*8, radius*8, 9)); int width = (int)(radius * 2); mNormalBmp = Bitmap.createScaledBitmap(mNormalBmp, width, width, false); mClickBmp = Bitmap.createScaledBitmap(mClickBmp, width, width, false); mErrorBmp = Bitmap.createScaledBitmap(mErrorBmp, width, width, false); } }
-
onTouchEvent(MotionEvent)
方法捕捉手勢@Override public boolean onTouchEvent(MotionEvent event) { // 只有在正常狀態下,觸摸才生效 if (mStatus == Status.Normal) { switch (event.getAction()) { case MotionEvent.ACTION_DOWN: case MotionEvent.ACTION_MOVE: // 記錄手勢 moveTo(event.getX(), event.getY()); break; case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: moveTo(event.getX(), event.getY()); // 如果小於有效數量 if (mLinkCircle.size() < INVALID_COUNT) { Toast.makeText(getContext(), "InvalidCount " + mLinkCircle.size(), Toast.LENGTH_LONG).show(); // 錯誤狀態,界面不可點 mStatus = Status.Error; // 2秒後清空 postDelayed(new Runnable() { @Override public void run() { clearCircle(); postInvalidate(); } }, 2000); } else { Toast.makeText(getContext(), "Success " + getLinkText(), Toast.LENGTH_LONG).show(); clearCircle(); } break; } postInvalidate(); } return true; } private void moveTo(float x, float y) { // 當前觸摸點 mCurrentPoint = new PointF(x, y); // 如果不在選擇列表中,加入選擇列表 for (CircleArea circle : mTotalCircles) { if (circle.contain(x, y) && !mLinkCircle.contains(circle)) { if (!mLinkCircle.contains(circle)) { mLinkCircle.add(circle); } return; } } } private String getLinkText() { String text = ""; for(CircleArea circle : mLinkCircle) { text += circle.getValue(); } return text; } private void clearCircle() { mLinkCircle.clear(); mCurrentPoint = null; mStatus = Status.Normal; }
-
onDraw(Canvas)
方法繪製手勢@Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); canvas.drawColor(getResources().getColor(R.color.black)); // 如果選擇列表不爲空 if (mLinkCircle.size() > 0) { // 設置連接線的顏色 if (mStatus == Status.Normal) { mLinePaint.setColor(mNormalColor); } else { mLinePaint.setColor(mErrorColor); } // 手勢鍵盤之間的連接線 CircleArea lastCircle = mLinkCircle.get(0); for (int i = 1; i < mLinkCircle.size(); i++) { CircleArea circle = mLinkCircle.get(i); canvas.drawLine(lastCircle.x, lastCircle.y, circle.x, circle.y, mLinePaint); lastCircle = circle; } // 手勢鍵盤和當前點之間的連接線 if (mCurrentPoint != null && mStatus == Status.Normal) { canvas.drawLine(lastCircle.x, lastCircle.y, mCurrentPoint.x, mCurrentPoint.y, mLinePaint); } } for (CircleArea circle : mTotalCircles) { drawCircle(canvas, circle); } } private void drawCircle(Canvas canvas, CircleArea circle) { Bitmap circleBitmap = mNormalBmp; if (mLinkCircle.contains(circle)) { if (mStatus == Status.Normal) { circleBitmap = mClickBmp; } else { circleBitmap = mErrorBmp; } } // 不同狀態下繪製不同的圖片。 canvas.drawBitmap(circleBitmap, circle.x - circle.radius, circle.y - circle.radius, mBitmapPaint); }
4. 效果如下