Android自定义控件实现九宫格解锁

 更新时间:2022年06月28日 11:49:40   作者:冷薄荷  
这篇文章主要为大家详细介绍了Android自定义控件实现九宫格解锁,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云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 SDCard存储的问题

    关于Android SDCard存储的问题

    本篇文章小编为大家介绍,关于Android SDCard存储的问题。需要的朋友参考下
    2013-04-04
  • android 实现控件左右或上下抖动教程

    android 实现控件左右或上下抖动教程

    这篇文章主要介绍了android 实现控件左右或上下抖动教程,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-03-03
  • 36个Android开发常用经典代码大全

    36个Android开发常用经典代码大全

    本篇文章主要介绍了36个Android开发常用经典代码片段,都是实用的代码段,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2016-11-11
  • Android ScrollView取消惯性滚动的方法

    Android ScrollView取消惯性滚动的方法

    下面小编就为大家带来一篇Android ScrollView取消惯性滚动的方法。小编觉得挺不错的,现在就分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-04-04
  • Android 6.0开发实现关机菜单添加重启按钮的方法

    Android 6.0开发实现关机菜单添加重启按钮的方法

    这篇文章主要介绍了Android 6.0开发实现关机菜单添加重启按钮的方法,涉及Android6.0针对相关源码的修改与功能添加操作技巧,需要的朋友可以参考下
    2017-09-09
  • Android原生定位服务LocationManager

    Android原生定位服务LocationManager

    这篇文章主要为大家介绍了Android原生定位服务LocationManager实现示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-08-08
  • Android获取手机SIM卡运营商信息的方法

    Android获取手机SIM卡运营商信息的方法

    这篇文章主要介绍了Android获取手机SIM卡运营商信息的方法,可获得手机的型号、运营商信息及系统版本等,需要的朋友可以参考下
    2014-09-09
  • Android开发flow常见API的使用示例详解

    Android开发flow常见API的使用示例详解

    这篇文章主要为大家介绍了Android开发flow常见API的使用示例详解,希望能够帮助大家更好的掌握flow使用,熟练的应用于各种场景,祝大家多多进步,早日升职加薪
    2022-08-08
  • Android Studio轻松构建自定义模板的步骤记录

    Android Studio轻松构建自定义模板的步骤记录

    这篇文章主要给大家介绍了关于Android Studio轻松构建自定义模板的相关资料,文中通过示例代码以及图文介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2018-10-10
  • Android Studio 合并module到统一文件夹的方法

    Android Studio 合并module到统一文件夹的方法

    这篇文章主要介绍了Android Studio 合并module到统一文件夹的方法,补充介绍了android studio关于同名资源文件的合并技巧,需要的朋友可以参考下
    2018-04-04

最新评论

?


http://www.vxiaotou.com