Android自定义View---验证码

之前学自定义View的时候,都是看一些博客文章,然后下载相关demo去学习。但是时间久了,再重新提起自定View,还是很模糊,想写个自定View一时还不知从哪下手。实在惭愧。
现在想在这里记录一下每一次学习自定View的过程。方便以后查看,反思和总结。算是一种积累吧,不然过段时间又忘了,又得去找资源。而通过记录,可以回翻,温故而知新,说不定能纠正自己以前的错误理解呢。
嗯,那,就开始了。
这篇主要是记录一下:看完这篇文章后(鸿洋大神写的)https://blog.csdn.net/lmj623565791/article/details/24252901,觉得例子虽然不难,但是对整个自定义过程写得很清楚。特此记录。

首先先了解一下自定义View的步骤吧:
1.自定义View的属性
2.在View的构造方法中获得我们自定义的属性
3.重写onMesure(此步骤不一定是必须的,但大多数情况下还是需要重写的)
4.重写onDraw

1.自定义View的属性

在res/values/下新建attrs.xml,在里面定义属性声明样式



 
    
    
    
 
    
        
        
        
    
 

属性取值类型有以下几种:string,color,demension,integer,enum,reference,float,boolean,fraction,flag

接着在布局中声明我们的自定义View


 
    
 

一定要引入 xmlns:custom="http://schemas.android.com/apk/res/com.example.customview01"我们的命名空间,后面的包路径指的是项目的package

2.在View的构造方法中,获得我们的自定义的样式和属性

/**
     * 文本
     */
    private String mTitleText;
    /**
     * 文本的颜色
     */
    private int mTitleTextColor;
    /**
     * 文本的大小
     */
    private int mTitleTextSize;
 
    /**
     * 绘制时控制文本绘制的范围
     */
    private Rect mBound;
    private Paint mPaint;
 
    public CustomTitleView(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }
 
    public CustomTitleView(Context context)
    {
        this(context, null);
    }
 
    /**
     * 获得我自定义的样式属性
     * 
     * @param context
     * @param attrs
     * @param defStyle
     */
    public CustomTitleView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        /**
         * 获得我们所定义的自定义样式属性
         */
        TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomTitleView, defStyle, 0);
        int n = a.getIndexCount();
        for (int i = 0; i < n; i++)
        {
            int attr = a.getIndex(i);
            switch (attr)
            {
            case R.styleable.CustomTitleView_titleText:
                mTitleText = a.getString(attr);
                break;
            case R.styleable.CustomTitleView_titleTextColor:
                // 默认颜色设置为黑色
                mTitleTextColor = a.getColor(attr, Color.BLACK);
                break;
            case R.styleable.CustomTitleView_titleTextSize:
                // 默认设置为16sp,TypeValue也可以把sp转化为px
                mTitleTextSize = a.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                        TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                break;
 
            }
 
        }
        a.recycle();
 
        /**
         * 获得绘制文本的宽和高
         */
        mPaint = new Paint();
        mPaint.setTextSize(mTitleTextSize);
        // mPaint.setColor(mTitleTextColor);
        mBound = new Rect();
        mPaint.getTextBounds(mTitleText, 0, mTitleText.length(), mBound);
 
    }

我们重写了3个构造方法,默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造调用我们的三个参数的构造,我们在三个参数的构造中获得自定义属性。

3.重写onDraw,onMeasure调用系统提供的

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
 
    @Override
    protected void onDraw(Canvas canvas)
    {
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);
 
        mPaint.setColor(mTitleTextColor);
        canvas.drawText(mTitleText, getWidth() / 2 - mBound.width() / 2, getHeight() / 2 + mBound.height() / 2, mPaint);
    }

效果如下:


Android自定义View---验证码_第1张图片
image.png

但现在有一个问题,当我们把布局文件中该View的宽高改为wrap_content的时候,效果如下(像系统控件TextView和Button等,即使宽高设置wrap_content,但是我们会发现它们也是有一个默认的大小的):

Android自定义View---验证码_第2张图片
image.png

系统帮我们测量的高度和宽度都是MATCH_PARNET,当我们设置明确的宽度和高度时,系统帮我们测量的结果就是我们设置的结果,当我们设置为WRAP_CONTENT,或者MATCH_PARENT系统帮我们测量的结果就是MATCH_PARENT的长度。

所以我们需要重写onMeasure方法,来处理WRAP_CONTENT的情况

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
    {
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        int width;
        int height ;
        if (widthMode == MeasureSpec.EXACTLY)
        {
            width = widthSize;
        } else
        {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
            float textWidth = mBounds.width();
            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
            width = desired;
        }
 
        if (heightMode == MeasureSpec.EXACTLY)
        {
            height = heightSize;
        } else
        {
            mPaint.setTextSize(mTitleTextSize);
            mPaint.getTextBounds(mTitle, 0, mTitle.length(), mBounds);
            float textHeight = mBounds.height();
            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = desired;
        }
        
        
 
        setMeasuredDimension(width, height);
    }

修改后效果:


Android自定义View---验证码_第3张图片
image.png

4.为View添加点击事件

在构造方法中添加:

this.setOnClickListener(new OnClickListener()
        {
 
            @Override
            public void onClick(View v)
            {
                mTitleText = randomText();
                postInvalidate();
            }
 
        });

随机生成四个数:

private String randomText()
    {
        Random random = new Random();
        Set set = new HashSet();
        while (set.size() < 4)
        {
            int randomInt = random.nextInt(10);
            set.add(randomInt);
        }
        StringBuffer sb = new StringBuffer();
        for (Integer i : set)
        {
            sb.append("" + i);
        }
 
        return sb.toString();
    }

好了,记录到此结束。自己的思路果然清晰了很多~~
再次提供原文链接:
Android 自定义View (一) - CSDN博客 https://blog.csdn.net/lmj623565791/article/details/24252901
送上源码:https://github.com/huangyula/CustomView.git

你可能感兴趣的:(Android自定义View---验证码)