前言: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,结束 代码已经上传到 我的资源