Android自定义控件实现九宫格解锁
(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun)
关于九宫格解锁,我看了不少博客,但是都感觉很复杂,可能我的功夫还不到,所以很多东西我不了解,但是我还是打算写一个自己的九宫格。我相信我的九宫格大家都能很快的理解,当然如果需要实现更复杂的功能,需要大家自己接着往深了挖掘。
代码文件??????
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); ? ? } }
代码总是最直接的引导,我看博客最喜欢的是研究代码,当然如果代码中有一些讲解就更好了。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持程序员之家。
相关文章
Android Studio 合并module到统一文件夹的方法
这篇文章主要介绍了Android Studio 合并module到统一文件夹的方法,补充介绍了android studio关于同名资源文件的合并技巧,需要的朋友可以参考下2018-04-04
最新评论