Android进阶之自定义View(1)实现可换行的TextView

本文介绍了如何自定义一个名为MyTextView的Android组件,该组件能够实现TextView的分行显示和自定义属性处理。通过重写构造方法、初始化操作、OnDraw方法以及onMeasure方法,详细讲解了自定义过程。文章还提出了在中英文混排、最后一行显示和textSize转换为sp单位等方面存在的问题,并给出了相关参考资料。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

         今天来一起学习一下最简单的自定义view,自己动手写一个MyTextView,当然不会像系统的TextView那么复杂,只是实现一下TextView的简单功能,包括分行显示及自定义属性的处理,主要目的是介绍自定义view的实现的基本思路和需要掌握的一些基础知识。

《一》先展示一下实现的最终效果



《二》实现步骤分析


1、创建MyTextView extends View,重写构造方法。一般是重写前三个构造方法,让前两个构造方法最终调用三个参数的构造方法,然后在第三个构造方法中进行一些初始化操作。
2、在构造方法中进行一些初始化操作,如初始化画笔及获取自定义属性等。

如何自定义属性?
(1)在values下创建attrs.xml.

//定义你的view可以在布局文件中配置的自定义属性
  <declare-styleable name="MyTextViewApprence">
        <attr name="textColor" format="color"></attr>
        <attr name="textSize" format="dimension"></attr>
        <attr name="text" format="string"></attr>
        <attr name="showMode">
            <enum name="left" value="0" />
            <enum name="center" value="1" />
        </attr>
    </declare-styleable>


(2)获取自定义属性
 

        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.MyTextViewApprence, defStyleAttr, 0);
        mText = typedArray.getString(R.styleable.MyTextViewApprence_text);
        mTextColor = typedArray.getColor(R.styleable.MyTextViewApprence_textColor, Color.BLACK);
        mTextSize = (int) typedArray.getDimension(R.styleable.MyTextViewApprence_textSize, 15);
        showMode = typedArray.getInt(R.styleable.MyTextViewApprence_showMode, 0);
        typedArray.recycle();

3、重写OnDraw()方法,在onDraw()中使用carvas绘制文字,x,y为绘制的起点。
需要注意两点:
(1)这里的x,y不是指的左上顶点,而是左下顶点。
(2)drawText绘制文字时,是有规则的,这个规则就是基线!详细可阅读drawText()详解


 

  //绘制每行文字的建议高度为:
        Paint.FontMetrics fm = mPaint.getFontMetrics();
        drawTextHeight = (int) (fm.descent - fm.ascent);

绘制文字的方法:

canvas.drawText(@NonNull String text, float x, float y, @NonNull Paint paint)


4、到第三步,其实就可以绘制出文字了,但是会发现一个问题,无论在布局文件中声明控件的宽高是wrap_content和match_parent,效果都是铺满了整个屏幕,这个时候,我们就需要重写onMesure()方法来测量控件的实际大小了,分析View的源码:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
    }
 
public static int getDefaultSize(int size, int measureSpec) {
        int result = size;
        int specMode = MeasureSpec.getMode(measureSpec);
        int specSize = MeasureSpec.getSize(measureSpec);
 
        switch (specMode) {
        case MeasureSpec.UNSPECIFIED:
            result = size;
            break;
        case MeasureSpec.AT_MOST:
        case MeasureSpec.EXACTLY:
            result = specSize;
            break;
        }
        return result;
    }
/**MeasureSpec 封装了父控件对其孩子的布局要求
有大小和模式两种,而模式则有三种模式
public static class MeasureSpec {
        private static final int MODE_SHIFT = 30;
        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
        //父控件不强加任何约束给子控件,它可以为它逍遥的任何大小
        public static final int UNSPECIFIED = 0 << MODE_SHIFT; //0
        //父控件给子控件一个精确的值
        public static final int EXACTLY     = 1 << MODE_SHIFT; //1073741824
    //父控件给子控件竟可能最大的值
        public static final int AT_MOST     = 2 << MODE_SHIFT; //-2147483648
        //设定尺寸和模式创建的统一约束规范
        public static int makeMeasureSpec(in
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值