Android 图形验证码(顺序点选文字)

之前公司接到的一个图形验证码的需求,需求是那种按顺序点选验证码的效果,网上查了下资料,没发现对应的需求,然后就自己研究做了一个出来,一方面分享给大家,一方面给自己做个笔记.

上效果:


video2gif_20190330_150424.gif

因为这里没有走网络耗时操作,所以你们可能看不到第四个点击的效果,因为点击第四个的时候我直接回调吐司坐标了.

实现原理其实挺简单的:
首先我们得弄一个图片的容器(PictureTagLayout),在这个容器内,可以动态添加view
然后弄一个点击效果的view(PictureTagView)
直接上代码:
<1>首先是布局的:
我这是模拟登陆时的一个验证效果,所以我做个了一个自定义dialog,这个就是dialog的布局代码
假如我们的布局当中图片的宽高用的单位不是px,而是dp,那么就得考虑dp和px的转换了,大家不用担心说坐标不精确的问题,肯定是有个误差范围的




    

        

            

                


                
            

            

                

                

                

                
            
        

        
    



/*
 *容器
 * */
@SuppressLint("NewApi")
public class PictureTagLayout extends RelativeLayout implements OnTouchListener, View.OnClickListener {
    int startX;
    int startY;
    int startTouchViewLeft = 0;
    int startTouchViewTop = 0;
    private View touchView, clickView;
    private Context context;
    private int height;
    private int width;

    float xDown, yDown, xUp;
    private int mNum;
    private String user_position = "";

    public PictureTagLayout(Context context) {
        super(context, null);
    }

    public PictureTagLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        this.context = context;

        init();
    }

    private void init() {
        this.setOnTouchListener(this);
    }

    //用作更新图片时操作
    public void setImage(Context context, int num) {
        this.context = context;
        this.mNum = num;
        user_position = "";
    }

    //开始的位置小于结束的位置 向左滑动
    @Override
    public boolean onTouch(View v, MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                xDown = event.getX();
                yDown = event.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                break;
            case MotionEvent.ACTION_UP:

                touchView = null;
                if (clickView != null) {
                    clickView = null;
                }
                startX = (int) event.getX();
                startY = (int) event.getY();
                if (hasView(startX, startY)) {//如果点击位置已经有了
                    startTouchViewLeft = touchView.getLeft();
                    startTouchViewTop = touchView.getTop();
                } else {
                    showPopup();
                }
                Log.i("******点击的位置--x-", startX + "*----y" + startY);
                break;

        }
        return true;
    }

    public void showPopup() {
        mNum++;
        addData(mNum);
    }

    @Override
    public void onClick(View view) {
        switch (view.getId()) {
        }
    }


    public void addData(int num) {
        if (hasView(startX, startY)) {//有view 就不添加到集合里
            startTouchViewLeft = touchView.getLeft();
            startTouchViewTop = touchView.getTop();
        } else {
            if (num < 5) {
                Log.i("addItem   点击的位置--x-", startX + "*----y" + startY);
                addItem(startX, startY, num + "");

                //完成的时候 返给主界面回调
                if (num == 4) {
                    //咱们再布局中的PictureTagLayout宽高为
                    //android:layout_width="600px"
                    //android:layout_height="300px"
                    //为了精确的拿到正确的坐标:
                    //第一个600为图片本身像素宽,第二个600为布局中设置的像素宽
                    //第一个300为图片本身像素宽,第二个300为布局中设置的像素宽
                    //有人会问为什么需要600/600呢不是刚好就是1吗?  其实这里只是为了表达,
                    //假如你布局当中设置的宽不是600的话,那就需要这么设置了,不然无法得到精确的坐标与后台匹配
                    user_position += startX * 600 / 600 + "," + startY * 300 / 300;

                    //所以你们可能看不到第四个点击的效果
                    EventBus.getDefault().postSticky(user_position);
                    mNum = 0;
                    user_position = "";

                } else {
                    user_position += startX * 300 / 600 + "," + startY * 200 / 400 + "|";
                }

            }
        }
    }

    private void addItem(int x, int y, String share) {
        View view = null;
        LayoutParams params = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);

        //第一次点击添加标签是  PictureTagView.Direction.Measure 让TagView自己测量方向
        view = new PictureTagView(getContext(), PictureTagView.Direction.Right, share);
        view.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
                LinearLayout.LayoutParams.WRAP_CONTENT));
        int w = MeasureSpec.makeMeasureSpec(0,
                MeasureSpec.UNSPECIFIED);
        int h = View.MeasureSpec.makeMeasureSpec(0,
                View.MeasureSpec.UNSPECIFIED);
        view.measure(w, h);
        height = view.getMeasuredHeight();
        width = view.getMeasuredWidth();
        //标签在右面 点击位置 x-标签宽度   右边的标签并不是以圆点开始的  而是以左边的wei
        params.leftMargin = x - width + 10;
        params.topMargin = y - height / 2;

        //上下位置在视图内
        if (params.topMargin <= 0) {
            params.topMargin = 0;
        } else if ((params.topMargin + height) > getHeight()) {
            params.topMargin = getHeight() - height;
        }
        if (params.leftMargin <= 0) {
            params.leftMargin = 0;
        }
        if (params.rightMargin >= getWidth()) {
            params.rightMargin = getWidth();
        }
        this.addView(view, params);
    }

    private boolean hasView(int x, int y) {
        //循环获取子view,判断xy是否在子view上,即判断是否按住了子view
        for (int index = 0; index < this.getChildCount(); index++) {
            View view = this.getChildAt(index);
            int left = (int) view.getX();
            int top = (int) view.getY();
            int right = view.getRight();
            int bottom = view.getBottom();
//          Toast.makeText(context, "已经有的---"+((PictureTagView)view).getShare()+"-x-"+left+"--y--"+top, Toast.LENGTH_SHORT).show();
            Rect rect = new Rect(left, top, right, bottom);
            boolean contains = rect.contains(x, y);
            //如果是与子view重叠则返回真,表示已经有了view不需要添加新view了
            if (contains) {
                touchView = view;
                touchView.bringToFront();
                return true;
            }

        }

        touchView = null;
        return false;
    }

}

这个是点击后样式的一个view,这个样式可以自定义

/**
 *点击样式
 * */
public class PictureTagView extends RelativeLayout implements OnEditorActionListener {

    private Context context;
    private View view;
    private TextView tvNum;

    public enum Direction {Left, Right, Measure}

    private Direction direction;
    private InputMethodManager imm;
    private String type;
    private String share;

    public PictureTagView(Context context) {
        super(context);

    }

    public PictureTagView(Context context, Direction direction, String share) {
        super(context);
        this.context = context;
        this.direction = direction;
        this.share = share;
        initViews();
        init();
    }

    /**
     * 初始化视图
     **/
    protected void initViews() {
        view = LayoutInflater.from(context).inflate(R.layout.picturetagview, this, true);

        tvNum = (TextView) view.findViewById(R.id.tvNum);
        tvNum.setText(share);
    }

    /**
     * 初始化
     **/
    protected void init() {
        imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
    }

    @Override
    public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
        return true;
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        super.onLayout(changed, l, t, r, b);
    }

}

有什么可以改进的,请多指教
具体的话直接看demo吧:
github代码:
(https://github.com/a824676719/CodeSelection)

你可能感兴趣的:(Android 图形验证码(顺序点选文字))