美文网首页
Android自定义控件之九宫格

Android自定义控件之九宫格

作者: xiaolei123 | 来源:发表于2017-11-17 15:40 被阅读111次

    在网上找了好多九宫格控件,但是都不尽人意,所以自己写了一个,写个博客记录一下

    LockView.java

    package com.tianjs.tianjinsuop2p.widgets;
    
    import android.content.Context;
    import android.content.res.TypedArray;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.support.annotation.ColorInt;
    import android.util.AttributeSet;
    import android.util.DisplayMetrics;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.WindowManager;
    
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * 九宫格控件
     * Created by xiaolei on 2017/3/16.
     */
    
    public class LockView extends View
    {
        private int pointCount = 3;
        private Cell cells[][];
        private Paint mPaint;
        private int ScreenWidth;//屏幕宽度
        private int ScreenHeight;//屏幕高度
        private int RADIUS;//半径
        private int OFFSET;//点与点之间的距离
        private int startX, startY;
        private int lastTouchX = 0;
        private int lastTouchY = 0;
        private List<Cell> selectCells = new ArrayList<>();
        
        private int normalCricleColor = Color.WHITE;//正常的宫格的颜色
        private int selectCricleColor = Color.RED;//选中的宫格的颜色
        
        private int normalCriclePointColor = Color.WHITE;//正常的宫格内点的颜色
        private int selectCriclePointColor = Color.RED;//选中的宫格内点的颜色
        
        private int lineColor = Color.RED;//线的颜色
        private int topLineColor = Color.RED;//线的顶端的颜色
        private int circleStrokeWidth = 5;//圆的环的宽度
        private int lineStrokeWidth = 4;//线的宽度
        
        private boolean showInLastCircle = true;//画线的时候,是否在最后的那个圈内显示
        private int minSeleted = 4;//最小选中数量
        private OnSelectListener onSelectListener;
        
        public LockView(Context context)
        {
            this(context, null);
        }
    
        public LockView(Context context, AttributeSet attrs)
        {
            this(context, attrs, 0);
        }
    
        public LockView(Context context, AttributeSet attrs, int defStyleAttr)
        {
            super(context, attrs, defStyleAttr);
            TypedArray array = getContext().obtainStyledAttributes(attrs, R.styleable.LockView);
    
            pointCount = array.getInt(R.styleable.LockView_pointCount,3);
            showInLastCircle = array.getBoolean(R.styleable.LockView_showInLastCircle,true);
            lineStrokeWidth = array.getInt(R.styleable.LockView_lineStrokeWidth,4);
            circleStrokeWidth = array.getInt(R.styleable.LockView_circleStrokeWidth,4);
            
            topLineColor = array.getColor(R.styleable.LockView_topLineColor,Color.RED);
            lineColor = array.getColor(R.styleable.LockView_lineColor,Color.RED);
            selectCriclePointColor = array.getColor(R.styleable.LockView_selectCriclePointColor,Color.RED);
            
            normalCriclePointColor = array.getColor(R.styleable.LockView_normalCriclePointColor,Color.WHITE);
            normalCricleColor = array.getColor(R.styleable.LockView_normalCricleColor,Color.WHITE);
            selectCricleColor = array.getColor(R.styleable.LockView_selectCricleColor,Color.RED);
            
            array.recycle();
            init(context);
        }
    
        private void init(Context context)
        {
            cells = new Cell[pointCount][pointCount];
            mPaint = new Paint();
            mPaint.setStrokeWidth(5);
            mPaint.setAntiAlias(true);
            mPaint.setColor(Color.GRAY);
            mPaint.setStyle(Paint.Style.FILL);
            mPaint.setStyle(Paint.Style.STROKE);
            WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
            DisplayMetrics dm = new DisplayMetrics();
            manager.getDefaultDisplay().getMetrics(dm);
            ScreenWidth = dm.widthPixels;
            ScreenHeight = dm.heightPixels;
            Log.e("LockView", "ScreenWidth:" + ScreenWidth + " ScreenHeight:" + ScreenHeight);
        }
        
        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
        {
            // 父容器传过来的宽度方向上的模式
            int widthMode = MeasureSpec.getMode(widthMeasureSpec);
            // 父容器传过来的高度方向上的模式
            int heightMode = MeasureSpec.getMode(heightMeasureSpec);
    
            // 父容器传过来的宽度的值
            int width = MeasureSpec.getSize(widthMeasureSpec) - getPaddingLeft()
                    - getPaddingRight();
            // 父容器传过来的高度的值
            int height = MeasureSpec.getSize(heightMeasureSpec) - getPaddingLeft()
                    - getPaddingRight();
            Log.e("LockView","onMeasure(width:"+width+",height:"+height+")");
    
            //计算半径
            RADIUS = Math.min(width, height) / ((pointCount * 2) + (pointCount + 1));
            OFFSET = RADIUS;
            //开始计算左上角的X,Y轴
            if (ScreenWidth < ScreenHeight)//竖屏
            {
                Log.d("LockView", "竖屏");
                startX = RADIUS * 2;
                startY = RADIUS * 2;
            } else//横屏
            {
                Log.d("LockView", "横屏");
                startX = (width - height)/2 + (RADIUS * 2);
                startY = RADIUS * 2;
            }
            initCells();
            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        }
        
        private void initCells()//计算每个点的位置
        {
            for (int i = 0; i < cells.length; i++)
            {
                for (int j = 0; j < cells[i].length; j++)
                {
                    if(cells[i][j] == null)
                    {
                        cells[i][j] = new Cell();
                    }
                    cells[i][j].x = (((j + 1) * (3 * RADIUS)) - RADIUS) + (startX - RADIUS * 2);
                    cells[i][j].y = (((i + 1) * (3 * RADIUS)) - RADIUS);
                }
            }
        }
        
        @Override
        public boolean onTouchEvent(MotionEvent event)
        {
            switch (event.getAction())
            {
                case MotionEvent.ACTION_DOWN:
                {
                    selectCells.clear();
                    Cell selectCell = inWhichCircle((int) event.getX(), (int) event.getY());
                    if(selectCell != null && !selectCells.contains(selectCell))
                    {
                        selectCells.add(selectCell);
                        this.postInvalidate();
                    }
                    break;
                }
                case MotionEvent.ACTION_MOVE:
                {
                    Cell selectCell = inWhichCircle((int) event.getX(), (int) event.getY());
                    if(selectCell != null && !selectCells.contains(selectCell))
                    {
                        selectCells.add(selectCell);
                    }
                    lastTouchX = (int) event.getX();
                    lastTouchY = (int) event.getY();
                    this.postInvalidate();
                    break;
                }
                case MotionEvent.ACTION_UP:
                {
                    lastTouchX = 0;
                    lastTouchY = 0;
                    if(selectCells.size() < minSeleted)//抬起手的时候,判断选中的点的数量是否小小于最小选中数
                    {
                        selectCells.clear();
                    }else if(onSelectListener != null)
                    {
                        List<Cell> list = new ArrayList<>();
                        list.addAll(selectCells);
                        onSelectListener.onSelected(list);
                    }
                    this.postInvalidate();
                    break;   
                }
            }
            return true;
        }
    
        /**
         * 判断某个触摸点,是否在某个宫格内
         * @param x
         * @param y
         * @return
         */
        private Cell inWhichCircle(int x, int y)
        {
            Cell result = null;
            for (int i = 0; i < cells.length; i++)
            {
                for (int j = 0; j < cells[i].length; j++)
                {
                    //选择画笔&&画圆  
                    Cell cell = cells[i][j];
                    if (isInCell(cell,x,y))
                    {
                        return cell;
                    }
                }
            }
            return result;
        }
    
        /**
         * 这个点是否在这个宫格里
         * @param cell
         * @param x
         * @param y
         * @return
         */
        private boolean isInCell(Cell cell,int x,int y)
        {
            return (Math.abs(x - cell.x) < RADIUS) && Math.abs(y - cell.y) < RADIUS;
        }
        
        @Override
        protected void onDraw(Canvas canvas)
        {
            super.onDraw(canvas);
            drawCell(canvas);
            drawLine(canvas);
        }
    
        /**
         * 画线
         * @param canvas
         */
        private void drawLine(Canvas canvas)
        {
            mPaint.setColor(lineColor);
            mPaint.setStrokeWidth(lineStrokeWidth);
            Cell lastCell = null;
            for (Cell cell:selectCells)
            {
                if(lastCell != null)
                {
                    canvas.drawLine(cell.x, cell.y, lastCell.x, lastCell.y, mPaint);
                }
                lastCell = cell;
            }
            if(showInLastCircle)
            {
                if((lastTouchX != 0 || lastTouchY != 0)
                        && lastCell != null)
                {
                    canvas.drawLine(lastCell.x, lastCell.y,lastTouchX,lastTouchY, mPaint);
                    mPaint.setColor(topLineColor);
                    //线的顶端,画一个圆角的视觉效果,我喜欢
                    canvas.drawCircle(lastTouchX, lastTouchY,0.8f, mPaint);
                }
            }else 
            {
                if((lastTouchX != 0 || lastTouchY != 0)
                        && lastCell != null
                        && !isInCell(lastCell,lastTouchX,lastTouchY))
                {
                    canvas.drawLine(lastCell.x, lastCell.y,lastTouchX,lastTouchY, mPaint);
                    mPaint.setColor(topLineColor);
                    //线的顶端,画一个圆角的视觉效果,我喜欢
                    canvas.drawCircle(lastTouchX, lastTouchY,0.8f, mPaint);
                }
            }
            
        }
        
        /**
         * 画出圆
         * @param canvas
         */
        private void drawCell(Canvas canvas)
        {
            for (int i = 0; i < cells.length; i++)
            {
                for (int j = 0; j < cells[i].length; j++)
                {
                    //选择画笔&&画圆  
                    Cell cell = cells[i][j];
                    if (selectCells.contains(cell))
                    {
                        mPaint.setColor(selectCricleColor);
                        mPaint.setStrokeWidth(circleStrokeWidth);
                        canvas.drawCircle(cell.x, cell.y, RADIUS, mPaint);
                        mPaint.setColor(selectCriclePointColor);
                        canvas.drawCircle(cell.x, cell.y, circleStrokeWidth/2, mPaint);
                    } else
                    {
                        mPaint.setColor(normalCricleColor);
                        mPaint.setStrokeWidth(circleStrokeWidth);
                        canvas.drawCircle(cell.x, cell.y, RADIUS, mPaint);
                        mPaint.setColor(normalCriclePointColor);
                        canvas.drawCircle(cell.x, cell.y, circleStrokeWidth/2, mPaint);
                    }
                    
                }
            }
        }
    
        public static class Cell
        {
            public int x;
            public int y;
        }
    
        /**
         * 设置宫格数量 X * X
         * @param pointCount
         */
        public void setPointCount(int pointCount)
        {
            this.pointCount = pointCount;
        }
    
        /**
         * 设置普通宫格颜色
         * @param normalCricleColor
         */
        public void setNormalCricleColor(@ColorInt int normalCricleColor)
        {
            this.normalCricleColor = normalCricleColor;
        }
    
        /**
         * 设置选中宫格颜色
         * @param selectCricleColor
         */
        public void setSelectCricleColor(@ColorInt int selectCricleColor)
        {
            this.selectCricleColor = selectCricleColor;
        }
    
        /**
         * 设置普通宫格内点的颜色
         * @param normalCriclePointColor
         */
        public void setNormalCriclePointColor(@ColorInt int normalCriclePointColor)
        {
            this.normalCriclePointColor = normalCriclePointColor;
        }
    
        /**
         * 设置选中宫格内点的颜色
         * @param selectCriclePointColor
         */
        public void setSelectCriclePointColor(@ColorInt int selectCriclePointColor)
        {
            this.selectCriclePointColor = selectCriclePointColor;
        }
    
        /**
         * 设置线条颜色
         * @param lineColor
         */
        public void setLineColor(@ColorInt int lineColor)
        {
            this.lineColor = lineColor;
        }
    
        /**
         * 设置线条顶端颜色
         * @param topLineColor
         */
        public void setTopLineColor(@ColorInt int topLineColor)
        {
            this.topLineColor = topLineColor;
        }
        
        /**
         * 设置宫格环宽度
         * @param circleStrokeWidth
         */
        public void setCircleStrokeWidth(int circleStrokeWidth)
        {
            this.circleStrokeWidth = circleStrokeWidth;
        }
    
        /**
         * 设置线条宽度
         * @param lineStrokeWidth
         */
        public void setLineStrokeWidth(int lineStrokeWidth)
        {
            this.lineStrokeWidth = lineStrokeWidth;
        }
        
        /**
         * 设置线条是否在最后一个宫格内显示
         * @param showInLastCircle
         */
        public void setShowInLastCircle(boolean showInLastCircle)
        {
            this.showInLastCircle = showInLastCircle;
        }
        
        /**
         * 设置最小选中数量
         * @param minSeleted
         */
        public void setMinSeleted(int minSeleted)
        {
            this.minSeleted = minSeleted;
        }
    
        public void setOnSelectListener(OnSelectListener onSelectListener)
        {
            this.onSelectListener = onSelectListener;
        }
    
        /**
         * 选中完成监听事件
         */
        public static interface OnSelectListener 
        {
            void onSelected(List<Cell> list);
        }
    }
    
    

    style.xml

    <declare-styleable name="LockView">
            <!-- 九宫格数量 -->
            <attr name="pointCount" format="integer"/>
            <!--是否在最后一个宫格显示连线-->
            <attr name="showInLastCircle" format="boolean"/>
            <!--线的宽度-->
            <attr name="lineStrokeWidth" format="integer"/>
            <!--圆形环的的宽度-->
            <attr name="circleStrokeWidth" format="integer"/>
            <!--连线顶端颜色-->
            <attr name="topLineColor" format="color"/>
            <!--连线的颜色-->
            <attr name="lineColor" format="color"/>
            <!--选中的宫格内点的颜色-->
            <attr name="selectCriclePointColor" format="color"/>
            <!--普通宫格内点的颜色-->
            <attr name="normalCriclePointColor" format="color"/>
            <!--普通宫格环的颜色-->
            <attr name="normalCricleColor" format="color"/>
            <!--选中宫格环的颜色-->
            <attr name="selectCricleColor" format="color"/>
        </declare-styleable>
    

    上图:

    普通的效果

    输入图片说明

    修改的自己颜色

    输入图片说明

    十六宫格

    输入图片说明

    自定义颜色

    输入图片说明

    相关文章

      网友评论

          本文标题:Android自定义控件之九宫格

          本文链接:https://www.haomeiwen.com/subject/heyjvxtx.html