自定义View入门篇(1)

View 是 Android 中各种组件的基类,我们经常用到的 TextView,ImageView,Button 等都是继承自View。

当我们想使用的 View 在 Android 库中无法找到时候,我们就需要自己写一个类继承 View,重写里面的方法,自己绘制出想要的View。

(1)自定义 View 第一步首先是创建一个类并继承 View,

    public MyTextView001(Context context) {
        super(context);
    }

    public MyTextView001(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView001(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

一般我们会重写上面的三个构造方法,第一个构造方法用于在代码中创建此 View 时使用

    MyTextView001 view001=new MyTextView001(this);

第二个和第三个构造函数都是用于在布局中使用此 View 时调用,不同在于方法三在有自定义属性时使用。

(2)第二步是要重写View中的两个重要方法,

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
    }

onMeasure 方法适用于自定义 View 的测量,onDraw 方法适用于自定义 View 的绘制。在 onMeasure 方法中可以通过

         int width_mode = MeasureSpec.getMode(widthMeasureSpec);
         int height_mode = MeasureSpec.getMode(heightMeasureSpec);

获取当前 View 的宽高模式,宽高模式具体有 UNSPECIFIED, EXACTLY, AT_MOST 三种:
UNSPECIFIED :为最不常用的一种,具体含义为尽可能的大,如果看过 ListView 源码的同学可以在其中找到此模式的使用;
EXACTLY :则是在布局中指定了确切的值或者是 match_parent, fill_parent;
AT_MOST :在布局中指定了 wrap_content。

onDraw 方法则是用于View的绘制过程,可以使用 canvas.drawCircle(),canvas.drawText() 等方法进行绘制。

(3)大概讲了下基本方法及构造函数,更多的内容让我们在实战自定义 TextView 中去学习吧。
首先在 values 文件夹下创建 attrs 文件,

    
        
        
        
    

在其中定义自定义属性,declare-styleable 的 name 属性使用自定义 View 的类名,attr 标签则是自定义属性的具体属性。我们这里设置了三个属性,分别是内容 text01,内容的颜色 text_color01,以及字体大小 text_size01。

新建好属性资源文件后,我们需要在自定义 View 中去获取这些属性,并创建画笔对象,

     TypedArray typedArray=context.obtainStyledAttributes(attrs, R.styleable.MyTextView001);
      mTextSize = (int) typedArray.getDimension(R.styleable.MyTextView001_text_size01,12);
      mText = typedArray.getString(R.styleable.MyTextView001_text01);
      mText_color = typedArray.getColor(R.styleable.MyTextView001_text_color01, Color.BLACK);

        mPaint = new Paint();
        mPaint.setTextSize(mTextSize);
        mPaint.setColor(mText_color);
        //抗锯齿
        mPaint.setAntiAlias(true);

接着我们需要在 onMeasure 方法中进行 View 的测量,

        //获取宽高模式
        int width_mode = MeasureSpec.getMode(widthMeasureSpec);
        int height_mode = MeasureSpec.getMode(heightMeasureSpec);
        //获取宽度
        int width = MeasureSpec.getSize(widthMeasureSpec);
        if (width_mode == MeasureSpec.AT_MOST) {
            //如果使用 wrap_content 模式,则重新计算宽度
            Rect rect = new Rect();
            mPaint.getTextBounds(mText, 0, mText.length(), rect);
            width = rect.width();

        }
        //获取高度
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (height_mode == MeasureSpec.AT_MOST) {
            //如果使用 wrap_content 模式,则重新计算高度
            Rect rect = new Rect();
            mPaint.getTextBounds(mText, 0, mText.length(), rect);
            height = rect.height();
        }
        //将最后的宽高赋值
        setMeasuredDimension(width, height);

接着在 onDraw 方法中进行绘制,绘制之前先讲一个概念,


文字绘制

baseline 是基线,在 Android 中绘制文本都是从 baseline 处开始的,从 baseline 往上至至文本最高处的距离称之为 ascent (上坡度),baseline 至文本最低处的距离称之为 descent (下坡度)。top 和 bottom 是绘制文本时在最外层留出的一些内边距。baseline 是基线,baseline 以上是负值,baseline 以下是正值,因此 ascent 和 top 都是负值,descent 和 bottom 都是正值。文本的实际高度应该就是 descent-asscent, 但是一般都是以 top-bottom 作为文本的高度。

        Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt();
        //具体计算 baseLine
        int dy = (fontMetricsInt.bottom - fontMetricsInt.top) / 2 - fontMetricsInt.bottom;
        canvas.drawText(mText1, 0, getHeight() / 2 + dy, mPaint);
    

onDraw 方法也完成后,就在布局中使用这个自定义 View

 

然后看一下效果
效果图

但是我们发现一个问题,就是内容贴边太近了,接下来我们来个她设置个 padding 值。将布局中代码修改如下:


将 onMeasure 方法中计算宽高的方法修改如下:

width = rect.width()+getPaddingLeft()+getPaddingRight();

height = rect.height()+getPaddingTop()+getPaddingBottom();

最后修改 onDraw 方法中的绘制部分:

canvas.drawText(mText, getPaddingLeft(), getHeight() / 2 + dy, mPaint);

修改完成后,我们再来看下效果:
效果图

这样看起来就舒服多啦!

自定义 View 的入门篇就讲到这里了,源码可见https://github.com/keven0632/MyView201805

你可能感兴趣的:(自定义View入门篇(1))