Android 自定义View之手势解锁控件
16lz
2021-01-23
前言:Android有很多原生控件供开发者使用,但是原生控件使用起来也有局限性,这个时候呢Android也有给开发者提供一些方式来根据需求进行自定义,今天介绍自定义控件之手势解锁控件,效果如图
九宫格手势解锁控件是目前用得最广泛的控件,先介绍一下实现思路;九宫格是基于容器来的 所以我们可以在自定义的时候继承与ViewGroup来实现,然后再ViewGroup里面构建出连线时需要的点,最后再构建需要连接的线和保存选中的点和线的走向。
接下来看看实现:
步骤一:定义一个类继承与RelativeLayout
步骤二:定义出画笔和点的坐标、颜色、以及存储和答案
步骤三:测量控件的宽高和通过计算出的间距添加到布局中
private void setLockViewParams(LockViewFactory lockViewFactory) {if (mILockViews.size() > 0) { return;}if (mLockViewMargin == 0) { mLockViewMargin = (DisplayUtils.dp2px(getContext(), 250) - mLockViewWidth * mDotCount) / (mDotCount - 1);}for (int i = 0; i < mDotCount * mDotCount; i++) { ILockView iLockView = lockViewFactory.newLockView(); iLockView.getView().setId(i + 1); mILockViews.add(iLockView); RelativeLayout.LayoutParams lockerParams = new LayoutParams(mLockViewWidth, mLockViewWidth); //不是每行的第一个,则设置位置为前一个的右边 if (i % mDotCount != 0) { lockerParams.addRule(RelativeLayout.RIGHT_OF, mILockViews.get(i - 1).getView().getId()); } //从第二行开始,设置为上一行同一位置View的下面 if (i > mDotCount - 1) { lockerParams.addRule(RelativeLayout.BELOW, mILockViews.get(i - mDotCount).getView().getId()); } //设置右下左上的边距 int rightMargin = 0; int bottomMargin = 0; int leftMargin = 0; int topMargin = 0; //每个View都有右外边距和底外边距 第一行的有上外边距 第一列的有左外边距 if (i >= mDotCount) {//非第一行 topMargin = mLockViewMargin; } if (i % mDotCount != 0) {//非第一列 leftMargin = mLockViewMargin; } lockerParams.setMargins(leftMargin, topMargin, rightMargin, bottomMargin); mILockViews.get(i).onNoFinger(); mILockViews.get(i).getView().setLayoutParams(lockerParams); addView(mILockViews.get(i).getView());} }
步骤四:处理按下和移动 抬起事件
@Overridepublic boolean onTouchEvent(MotionEvent event) {if (mTouchable) { int action = event.getAction(); int x = (int) event.getX(); int y = (int) event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: handleDownEvent(x, y); break; case MotionEvent.ACTION_MOVE: handleMoveEvent(x, y); break; case MotionEvent.ACTION_UP: handleUpEvent(); break; } invalidate(); return true;} else { return false;} }
按下时需要复位View,处理移动事件
private void handleMoveEvent(int x, int y) {mPaint.setColor(mFingerTouchColor);ILockView lockView = getLockViewByPoint(x, y);if (lockView != null) { int childId = lockView.getView().getId(); if (!mChooseList.contains(childId)) { mChooseList.add(childId); lockView.onFingerTouch(); //手势解锁监听 if (mOnLockVerifyListener != null) { mOnLockVerifyListener.onGestureSelected(childId); } mLastPathX = lockView.getView().getLeft() / 2 + lockView.getView().getRight() / 2; mLastPathY = lockView.getView().getTop() / 2 + lockView.getView().getBottom() / 2; if (mChooseList.size() == 1) { mPath.moveTo(mLastPathX, mLastPathY); } else { mPath.lineTo(mLastPathX, mLastPathY); } }}//指引线终点坐标mLineX = x;mLineY = y;}
处理抬起事件:
private void handleUpEvent() {if (mCurrentMode == RESET_MODE) { handleResetMode();} else { handleVerifyMode();}//将指引线的终点坐标设置为最后一个Path的原点,即取消指引线mLineX = mLastPathX;mLineY = mLastPathY;}
步骤五:记录和保存
@Overrideprotected Parcelable onSaveInstanceState() {Parcelable superState = super.onSaveInstanceState();SavedState ss = new SavedState(superState);ss.tryTimes = mTryTimes;return ss; }
步骤六:校验
/** * 检查答案是否正确 */private boolean checkAnswer() { if (mAnswerList.size() != mChooseList.size()) { return false; } for (int i = 0; i < mAnswerList.size(); i++) { if (mAnswerList.get(i) != mChooseList.get(i) - 1) { return false; } }return true; } /** * 切换LockView是否匹配状态 */ private void toggleLockViewMatchedState(boolean isMatched) {if (isMatched) { mPaint.setColor(mFingerUpMatchedColor);} else { mPaint.setColor(mFingerUpUnmatchedColor);}for (ILockView iLockView : mILockViews) { if (mChooseList.contains(iLockView.getView().getId())) { if (!isMatched) { iLockView.onFingerUpUnmatched(); } else { iLockView.onFingerUpMatched(); } }} }
OK,结束 代码已经上传到 我的资源
更多相关文章
- Android重要控件概览(中)
- Android常用布局、控件以及Android存储方式
- android xml控件属性
- Android控件之EditText(输入文本框控件)
- Android--DatePickerDialog日期控件
- android > 控件 > RadioGroup
- Android Studio 基础控件使用
- android时间控件DigitalClock的使用
- android Switch控件