手势图形密码控件

手势图形密码控件

手势图形密码控件_第1张图片
手势图形密码控件_第2张图片

手势图形密码控件
public class GestureView extends View {

    /**
     * 默认格子数
     */
    private static final int POINT_COUNT = 9;

    /**
     * 默认中心点的半径
     */
    private static final int POINT_RADIUS = 2;

    /**
     * 默认外圈半径
     */
    private static final int CIRCLE_RADIUS = 25;

    /**
     * 默认最少连接的点数,否则提示错误
     */
    private static final int MIN_POINT_COUNT = 4;
    private static final int DEF_LINE_WIDTH = 4;
    private static final int DEF_CIRCLE_BORDER_WIDTH = 1;

    /**
     * 中心点颜色
     */
    private int pointColor;

    /**
     * 中心点选择态颜色
     */
    private int pointSelectColor;
    private int pointNormalSelectedColor;
    private int pointErrorSelectedColor;

    /**
     * 圆圈的颜色
     */
    private int circleColor;

    /**
     * 圆圈的选择态颜色
     */
    private int circleSelectedColor;
    private int circleNormalSelectedColor;
    private int circleErrorSelectedColor;

    /**
     * 条目外圆框的颜色
     */
    private int circleBorderColor;
    private float circleBorderWidth;

    /**
     * 条目外圆框的选择态颜色
     */
    private int circleBorderSelectedColor;
    private int circleBorderNormalSelectedColor;
    private int circleBorderErrorSelectedColor;

    private Paint mPaint;
    private Paint mLinePaint;

    private Map pointMap = new HashMap<>(10);
    /**
     * 记录选择过的点下标
     */
    private ArrayList selectList = new ArrayList<>();
    private GestureListener listener;
    private int pointNum;
    private int linkMinNum;
    private float lineWidth;
    private int lineColor;
    private int lineErrorColor;
    private float pointRadius;
    private float circleRadius;
    private Path linePath;

    public void setListener(GestureListener listener) {
        this.listener = listener;
    }

    public GestureView(Context context) {
        this(context, null);
    }

    public GestureView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initAttr(context, attrs);
        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setStyle(Paint.Style.FILL);

        mLinePaint = new Paint();
        mLinePaint.setStyle(Paint.Style.STROKE);
        mLinePaint.setStrokeWidth(lineWidth);
        mLinePaint.setPathEffect(new CornerPathEffect(5));
        linePath = new Path();
        setSelectedColor(false);
    }

    private void initAttr(Context context, AttributeSet attrs) {
        TypedArray attr = context.obtainStyledAttributes(attrs, R.styleable.GestureView);
        pointNum = attr.getInt(R.styleable.GestureView_item_num, POINT_COUNT);
        linkMinNum = attr.getInt(R.styleable.GestureView_link_min_num, MIN_POINT_COUNT);
        circleColor = attr.getColor(R.styleable.GestureView_circle_Color, ResourcesUtils.getColor(R.color.gesture_circle_color));
        circleNormalSelectedColor = attr.getColor(R.styleable.GestureView_circle_selected_Color, ResourcesUtils.getColor(R.color.gesture_circle_select_color));
        circleErrorSelectedColor = attr.getColor(R.styleable.GestureView_circle_error_Color, ResourcesUtils.getColor(R.color.gesture_error_circle_color));
        pointColor = attr.getColor(R.styleable.GestureView_point_color, ResourcesUtils.getColor(R.color.gesture_point_color));
        pointNormalSelectedColor = attr.getColor(R.styleable.GestureView_point_selected_color, ResourcesUtils.getColor(R.color.gesture_point_select_color));
        pointErrorSelectedColor = attr.getColor(R.styleable.GestureView_point_error_color, ResourcesUtils.getColor(R.color.gesture_error_point_select_color));
        pointRadius = attr.getDimension(R.styleable.GestureView_point_radius, BaseUtils.dp2px(POINT_RADIUS));
        lineWidth = attr.getDimension(R.styleable.GestureView_line_width, DEF_LINE_WIDTH);
        lineColor = attr.getColor(R.styleable.GestureView_line_color, ResourcesUtils.getColor(R.color.gesture_point_select_color));
        lineErrorColor = attr.getColor(R.styleable.GestureView_line_error_color, ResourcesUtils.getColor(R.color.gesture_error_point_select_color));
        circleRadius = attr.getDimension(R.styleable.GestureView_circle_radius, BaseUtils.dp2px(CIRCLE_RADIUS));
        circleBorderColor = attr.getColor(R.styleable.GestureView_circle_border_Color, ResourcesUtils.getColor(R.color.gesture_circle_color));
        circleBorderNormalSelectedColor = attr.getColor(R.styleable.GestureView_circle_border_selected_Color, ResourcesUtils.getColor(R.color.gesture_point_select_color));
        circleBorderErrorSelectedColor = attr.getColor(R.styleable.GestureView_circle_border_error_Color, ResourcesUtils.getColor(R.color.gesture_error_point_select_color));
        circleBorderWidth = attr.getDimension(R.styleable.GestureView_circle_border_width, DEF_CIRCLE_BORDER_WIDTH);
        attr.recycle();
    }

    public void setItemCount(@IntRange(from = 1, to = 25) int count) {
        pointNum = count;
        initPointMap();
        requestLayout();
        invalidate();
    }

    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        initPointMap();
    }

    /**
     * 保存每个点对应的坐标到Map中
     */
    private void initPointMap() {
        pointMap.clear();
        int measuredWidth = getMeasuredWidth();
        int measuredHeight = getMeasuredHeight();
        int width = Math.min(measuredWidth, measuredHeight);
        if (width == 0) {
            return;
        }

        int columns = (int) Math.round(Math.sqrt(pointNum));//列columns
        int rows = (pointNum > (columns * columns)) ? columns + 1 : columns;//rows

        float columnW = width * 1.0f / columns;//列宽
        float rowH = width * 1.0f / (rows);//行高

        int offset = measuredWidth - measuredHeight;
        float abs = Math.abs(offset / 2f);
        float offsetX = (offset > 0) ? abs : 0;//控件宽度大于高度 则锚点需水平居中
        float offsetY = (offset < 0) ? abs : 0;//控件高度大于宽度 则锚点需垂直居中

        for (int index = 0; index < pointNum; index++) {
            float[] location = new float[2];
            if (columns > 0) {
                int rowLocation = (index) / columns + 1;//当前锚点所在行位置
                int columnLocation = (index) % columns + 1;//当前锚点所在列位置

                if (rowLocation == rows && (pointNum % columns) != 0) {//最后一行 && 锚点总数不平方数
                    columnW = width * 1.0f / (pointNum % columns);
                }

                location[0] = (columnLocation - 0.5f) * (columnW) + offsetX;
                location[1] = (rowLocation - 0.5f) * (rowH) + offsetY;
            }
            pointMap.put(index + 1, location);
        }
    }

    /**
     * 设置点,圈,和连接线的颜色
     */
    private void setSelectedColor(boolean isError) {
        pointSelectColor = isError ? pointErrorSelectedColor : pointNormalSelectedColor;
        circleSelectedColor = isError ? circleErrorSelectedColor : circleNormalSelectedColor;
        circleBorderSelectedColor = isError ? circleBorderErrorSelectedColor : circleBorderNormalSelectedColor;
        if (mLinePaint != null) {
            mLinePaint.setColor(isError ? lineErrorColor : lineColor);
        }
    }

    /**
     * 恢复初始未选择的状态
     */
    public void reset() {
        try {
            resetImpl();
            invalidate();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private void resetImpl() {
        setSelectedColor(false);
        lastPosition[0] = 0;
        lastPosition[1] = 0;
        selectList.clear();
    }

    /**
     * 显示错误,比如连结的点过少
     */
    public void showError() {
        setSelectedColor(true);
        invalidate();
    }

    private boolean isStart = false;
    private float currentX = 0f;
    private float currentY = 0f;
    private float[] lastPosition = new float[2];

    @SuppressLint("ClickableViewAccessibility")
    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (isStart) {
                    isStart = false;
                    if (getSelectPointCount() < linkMinNum) {
                        showError();
                        if (null != listener) {
                            listener.onError();
                        }
                    } else {
                        if (null != listener) {
                            listener.onFinish(selectList);
                        }
                    }
                    postDelayed(this::reset, 300);
                }
                break;
            case MotionEvent.ACTION_DOWN:
                isStart = true;
                if (null != listener) {
                    listener.onDrawStart();
                }
                resetImpl();
            case MotionEvent.ACTION_MOVE:
                currentX = event.getX();
                currentY = event.getY();
                addSelectPoint(currentX, currentY);
                invalidate();
                break;
            default:
                break;
        }
        return true;
    }

    /**
     * 获取当前触摸的点
     */
    private void addSelectPoint(float touchX, float touchY) {
        for (Map.Entry entry : pointMap.entrySet()) {
            float[] value = entry.getValue();
            if (value == null) {
                return;
            }
            if (lastPositionNoValue()) {
                if (isInBounds(touchX, touchY, value)) {
                    addSelected(entry, value);
                }
            } else if (SutherlandUtils.isInBounds(lastPosition[0], lastPosition[1], touchX, touchY, value, (int) circleRadius)) {
                addSelected(entry, value);
            }
        }
    }

    private boolean lastPositionNoValue() {
        return lastPosition[0] == 0 && lastPosition[1] == 0;
    }

    private void addSelected(Map.Entry entry, float[] value) {
        int selectIndex = entry.getKey();
        if (selectIndex > 0 && !selectList.contains(selectIndex)) {
            lastPosition[0] = value[0];
            lastPosition[1] = value[1];
            selectList.add(selectIndex);
        }
    }

    private boolean isInBounds(float x, float y, float[] location) {
        return isInBounds(x, location[0]) && isInBounds(y, location[1]);
    }

    private boolean isInBounds(float touch, float location) {
        return touch > location - circleRadius && touch < location + circleRadius;
    }

    /**
     * 获取已经选择的点数量
     */
    private int getSelectPointCount() {
        return (null == selectList || selectList.isEmpty()) ? 0 : selectList.size();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        //画连线
        linePath.reset();
        int size = selectList.size();
        if (size > 0 && !lastPositionNoValue()) {
            for (int i = 0; i < size; i++) {
                float[] point = pointMap.get(selectList.get(i));
                if (point == null) {
                    return;
                }
                if (i == 0) {
                    linePath.moveTo(point[0], point[1]);
                } else {
                    linePath.lineTo(point[0], point[1]);
                }
            }
            linePath.lineTo(currentX, currentY);
            canvas.drawPath(linePath, mLinePaint);
        }

        for (int i = 1; i <= pointNum; i++) {
            float[] location = pointMap.get(i);
            if (null == location) {
                return;
            }
            if (selectList.contains(i)) {//是否选中
                drawItem(canvas, location, pointSelectColor, circleSelectedColor, circleBorderSelectedColor);
                if (null != listener) {
                    listener.onDraw(i);
                }
            } else {
                drawItem(canvas, location, pointColor, circleColor, circleBorderColor);
            }
        }
    }

    private void drawItem(Canvas canvas, float[] location, @ColorInt int pointColor, @ColorInt int circleColor, @ColorInt int borderColor) {
        if (circleColor != 0) {
            //画圆圈
            mPaint.setColor(circleColor);
            canvas.drawCircle(location[0], location[1], circleRadius, mPaint);
        }

        if (borderColor != 0 && circleBorderWidth != 0) {
            //画圆圈边框
            mPaint.setColor(borderColor);
            mPaint.setStrokeWidth(circleBorderWidth);
            mPaint.setStyle(Paint.Style.STROKE);
            canvas.drawCircle(location[0], location[1], circleRadius - circleBorderWidth / 2, mPaint);
            mPaint.setStyle(Paint.Style.FILL);
        }

        mPaint.setColor(pointColor);
        //画未选择的点
        canvas.drawCircle(location[0], location[1], pointRadius, mPaint);
    }
}

Cohen-Sutherland算法实现求线段是否过区间

attr属性

    
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
        
    

color

   #FFD6D4
    #ff0000
    #000000
    #16BC83
    #00000000
    #3316BC83

你可能感兴趣的:(自定义控件,Android,手势图形密码,自定义控件)