Android自定义控件实现九宫格解锁
关于九宫格解锁,我看了不少博客,但是都感觉很复杂,可能我的功夫还不到,所以很多东西我不了解,但是我还是打算写一个自己的九宫格。我相信我的九宫格大家都能很快的理解,当然如果需要实现更复杂的功能,需要大家自己接着往深了挖掘。
代码文件
NineGroupView:为九宫格空间组
ToggleView:九宫格中的子View,也就是我们看到的圆形按钮,我自己定义的ToggleView可能不好看,当然大家可以自己定义更加好看的ToggleView。
MarkBean:记录ToggleView的索引(ChildIndex)以及是否选中的状态
PositionUtils:工具类,包含规划九个ToggleView的中心点位置,判断当前触摸点是否属于ToggleView中等方法。
NineActivity:测试页面。
布局规划图
public class PositionUtils { /** * 判断触摸的点是否属于View中的一点 * * @param point 触摸的点 * @param position 目标对象圆形坐标 * @param outR 目标对象的外半径 * @return */ public static boolean IsIn(Point point, Point position, int outR) { int touchX = point.x; int touchY = point.y; int cx = position.x; int cy = position.y; int distance = (int) Math.sqrt(Math.pow((touchX - cx), 2) + Math.pow((touchY - cy), 2)); if (distance <= outR) { return true; } else { return false; } } /** * 规划 child 的中心位置 * * @param width * @param height * @return */ public static List<Point> getNinePoints(int width, int height) { List<Point> points = new ArrayList<>(); for (int i = 1; i <= 3; i++) { for (int j = 1; j <= 3; j++) { points.add(getPoint(width, height, 0.25f * j, 0.2f * i + 0.1f)); } } return points; } /** * 获取 * * @param width 父控件的宽 * @param height 父控件的高 * @param x 横轴方向比例 * @param y 纵轴方向的比例 * @return */ private static Point getPoint(int width, int height, float x, float y) { Point point = new Point(); point.x = (int) (width * x); point.y = (int) (height * y); return point; } }
public class ToggleView extends View { private Paint inPaint; private Paint outPaint; private int outColor; private int inColor; private int outR; private int inR; private boolean isChecked; public int getOutColor() { return outColor; } public void setOutColor(int outColor) { this.outColor = outColor; } public int getInColor() { return inColor; } public void setInColor(int inColor) { this.inColor = inColor; } public int getOutR() { return outR; } public void setOutR(int outR) { this.outR = outR; } public int getInR() { return inR; } public void setInR(int inR) { this.inR = inR; } public boolean isChecked() { return isChecked; } public void setChecked(boolean checked) { isChecked = checked; } public ToggleView(Context context) { this(context, null); } public ToggleView(Context context, @Nullable AttributeSet attrs) { this(context, attrs, 0); } public ToggleView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(context, attrs, defStyleAttr); } /** * 初始化 */ private void init(Context context, AttributeSet attrs, int defStyleAttr) { TypedArray array = context.getTheme().obtainStyledAttributes(attrs, R.styleable.ToggleView, defStyleAttr, 0); int indexCount = array.getIndexCount(); for (int i = 0; i < indexCount; i++) { int attr = array.getIndex(i); switch (attr) { case R.styleable.ToggleView_InCircleR_T: inR = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(Dimension.DP, 10, getResources().getDisplayMetrics())); break; case R.styleable.ToggleView_OutCircleR_T: outR = array.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(Dimension.DP, 50, getResources().getDisplayMetrics())); break; case R.styleable.ToggleView_InCircleColor_T: inColor = array.getColor(attr, 0xff00ffff); break; case R.styleable.ToggleView_OutCircleColor_T: outColor = array.getColor(attr, 0xff888888); break; } } inPaint = new Paint(); inPaint.setStyle(Paint.Style.FILL_AND_STROKE); inPaint.setColor(inColor); inPaint.setAntiAlias(true); outPaint = new Paint(); outPaint.setAntiAlias(true); outPaint.setStrokeWidth(5); outPaint.setStyle(Paint.Style.STROKE); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int cx = getWidth() / 2; int cy = getHeight() / 2; outPaint.setStyle(Paint.Style.FILL); outPaint.setColor(Color.WHITE); canvas.drawCircle(cx, cy, outR, outPaint); outPaint.setStyle(Paint.Style.STROKE); outPaint.setColor(outColor); canvas.drawCircle(cx, cy, outR, outPaint); canvas.drawCircle(cx, cy, inR, inPaint); } }
public class NineGroupView extends ViewGroup { private OnFinishListener mListener; public interface OnFinishListener { public void onFinish(List<Integer> positionSet); } public void setOnFinishListener(OnFinishListener listener) { this.mListener = listener; } private Paint paint; private Path path; private TreeMap<Integer, Boolean> checkedMap; private List<Integer> checkedINdexSet; //用于记录被选中的序号排列。 private List<Point> positionList; private List<Point> childSize = new ArrayList<>(); public NineGroupView(Context context) { this(context, null); } public NineGroupView(Context context, AttributeSet attrs) { this(context, attrs, 0); } public NineGroupView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); init(); } public void reset() { for (int i = 0; i < 9; i++) { checkedMap.put(i, false); } checkedINdexSet.clear(); IsDownIn = false; IsUp = false; path.reset(); prePoint = new Point(-1, -1); currentPoint = new Point(-1, -1); invalidate(); } private void init() { checkedMap = new TreeMap<>(); for (int i = 0; i < 9; i++) { checkedMap.put(i, false); } checkedINdexSet = new ArrayList<>(); positionList = new ArrayList<>(); path = new Path(); paint = new Paint(); paint.setStrokeWidth(10); paint.setAntiAlias(true); paint.setColor(Color.RED); paint.setStyle(Paint.Style.STROKE); //如果该方法在此不调用的话,那么onDraw()方法将不被调用,那么就无法完成连接线的绘制 setWillNotDraw(false); } @Override protected void onLayout(boolean b, int left, int top, int right, int bottom) { int height = getMeasuredHeight(); int width = getMeasuredWidth(); positionList = PositionUtils.getNinePoints(width, height); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); Point size = childSize.get(i); Point position = positionList.get(i); int cLeft = position.x - size.x; int cTop = position.y - size.y; int cRight = position.x + size.x; int cBottom = position.y + size.y; child.layout(cLeft, cTop, cRight, cBottom); } } @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { View child = getChildAt(i); measureChild(child, widthMeasureSpec, heightMeasureSpec); Point point = new Point(); point.x = child.getMeasuredWidth(); point.y = child.getMeasuredHeight(); childSize.add(point); } setMeasuredDimension(widthMeasureSpec, heightMeasureSpec); } private boolean IsDownIn = false; private boolean IsUp = false; private Point prePoint = new Point(-1, -1); private Point currentPoint = new Point(-1, -1); @Override public boolean onTouchEvent(MotionEvent event) { int action = event.getAction(); int currentX = (int) event.getX(); int currentY = (int) event.getY(); switch (action) { case MotionEvent.ACTION_DOWN: { if (IsUp) { return true; } MarkBean bean = isInToggle(new Point(currentX, currentY)); if (bean != null) { IsDownIn = true; prePoint = positionList.get(bean.getIndex()); path.moveTo(prePoint.x, prePoint.y); invalidate(); } } break; case MotionEvent.ACTION_UP: IsUp = true; if (IsDownIn) { currentPoint = prePoint; IsDownIn = false; invalidate(); if (mListener != null) { mListener.onFinish(checkedINdexSet); reset(); } } break; case MotionEvent.ACTION_MOVE: { if (IsDownIn) { if (!IsUp) { MarkBean bean = isInToggle(new Point(currentX, currentY)); if (bean != null) { int index = bean.getIndex(); currentPoint = positionList.get(index); path.lineTo(currentPoint.x, currentPoint.y); invalidate(); prePoint = currentPoint; } else { currentPoint = new Point(currentX, currentY); invalidate(); } } } else { if (!IsUp) { MarkBean bean = isInToggle(new Point(currentX, currentY)); if (bean != null) { Point position = positionList.get(bean.getIndex()); prePoint = position; path.moveTo(position.x, position.y); IsDownIn = true; invalidate(); } } } } break; case MotionEvent.ACTION_CANCEL: break; } return true; } private MarkBean isInToggle(Point point) { MarkBean bean = new MarkBean(); int childCount = getChildCount(); for (int i = 0; i < childCount; i++) { Point position = positionList.get(i); ToggleView child = (ToggleView) getChildAt(i); if (PositionUtils.IsIn(point, position, child.getOutR())) { if (!checkedMap.get(i)) { checkedMap.put(i, true); checkedINdexSet.add(i); bean.setIndex(i); bean.setCheck(true); return bean; } } } return null; } @Override protected void onDraw(Canvas canvas) { canvas.drawPath(path, paint); if (prePoint.x != -1 && prePoint.y != -1 && currentPoint.x != -1 && currentPoint.y != -1) { canvas.drawLine(prePoint.x, prePoint.y, currentPoint.x, currentPoint.y, paint); } super.onDraw(canvas); } }
代码总是最直接的引导,我看博客最喜欢的是研究代码,当然如果代码中有一些讲解就更好了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。
赞 (0)