Android 自定义View之手势解锁控件

前言: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());
}
	  }

步骤四:处理按下和移动 抬起事件

 @Override
public 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;
}

步骤五:记录和保存

@Override
protected 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,结束 代码已经上传到 我的资源

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值