美文网首页Android专题AndroidAndroid View讲解
android 图形验证码(顺序点选文字)

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

作者: android_haihong | 来源:发表于2019-03-30 15:22 被阅读68次

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

    上效果:


    video2gif_20190330_150424.gif

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

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

    <?xml version="1.0" encoding="utf-8"?>
    <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/v_main"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/transparent"
        android:gravity="center"
        android:orientation="vertical">
    
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="380dp"
            android:layout_alignParentStart="true"
            android:layout_centerVertical="true"
            android:layout_marginLeft="2dp"
            android:layout_marginRight="2dp"
            android:clickable="true">
    
            <LinearLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:layout_marginLeft="14dp"
                android:layout_marginRight="14dp"
                android:layout_marginTop="16dp"
                android:background="@mipmap/validation_bk"
                android:focusable="true"
                android:focusableInTouchMode="true"
                android:gravity="center_horizontal"
                android:orientation="vertical">
    
                <LinearLayout
                    android:id="@+id/ly"
                    android:layout_width="match_parent"
                    android:layout_height="26dp"
                    android:focusable="true"
                    android:focusableInTouchMode="true"
                    android:gravity="center_horizontal"
                    android:orientation="vertical">
    
                    <TextView
                        android:id="@+id/tv_title"
                        android:layout_width="wrap_content"
                        android:layout_height="match_parent"
                        android:layout_gravity="center"
                        android:gravity="center"
                        android:text="图形验证码"
                        android:textColor="@color/yellow"
                        android:textSize="13dp" />
    
    
                    <TextView
                        android:id="@+id/tv_button"
                        android:layout_width="100dp"
                        android:layout_height="36dp"
                        android:layout_centerHorizontal="true"
                        android:layout_marginTop="20dp"
                        android:background="@mipmap/wallet_btn"
                        android:gravity="center"
                        android:text="提交"
                        android:textColor="@color/white"
                        android:textSize="13dp"
                        android:visibility="gone" />
                </LinearLayout>
    
                <LinearLayout
                    android:layout_width="match_parent"
                    android:layout_height="match_parent"
                    android:layout_below="@+id/ly"
                    android:clickable="true"
                    android:gravity="center"
                    android:layout_gravity="center"
                    android:orientation="vertical">
    
                    <TextView
                        android:id="@+id/text_hint"
                        android:layout_width="match_parent"
                        android:layout_height="36dp"
                        android:layout_gravity="center"
                        android:gravity="center"
                        android:layout_marginBottom="12dp"
                        android:text="一      叶      知      秋"
                        android:textColor="@color/white"
                        android:textSize="18dp" />
    
                    <TextView
                        android:layout_width="match_parent"
                        android:layout_height="wrap_content"
                        android:gravity="center"
                        android:text="请按以上文字顺序依次点击图片上的文字"
                        android:textColor="#89aad9"
                        android:textSize="12dp" />
    
                    <com.haihong.codeselection.PictureTagLayout
                        android:id="@+id/img_select"
                        android:layout_width="600px"
                        android:layout_height="300px"
                        android:layout_marginTop="20dp"
                        android:background="@mipmap/image_code"
                        android:scaleType="centerCrop" />
    
                    <TextView
                        android:id="@+id/tv_changeone"
                        android:layout_width="100dp"
                        android:layout_height="36dp"
                        android:layout_gravity="center"
                        android:layout_marginTop="20dp"
                        android:background="@mipmap/wallet_btn"
                        android:gravity="center"
                        android:text="换一张"
                        android:textColor="@color/white"
                        android:textSize="13dp" />
                </LinearLayout>
            </LinearLayout>
    
            <ImageView
                android:id="@+id/img_close"
                android:layout_width="34dp"
                android:layout_height="34dp"
                android:layout_alignParentRight="true"
                android:scaleType="centerCrop"
                android:src="@mipmap/close" />
        </RelativeLayout>
    
    
    </RelativeLayout>
    
    /*
     *容器
     * */
    @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 图形验证码(顺序点选文字)

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