android 自定义View开发实战(一) CustomTitleView

本文介绍如何自定义一个可点击的TextView,包括定义属性、创建类、重写关键方法及实现点击事件,最后给出示例代码。

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

本文主要是自定义一个可点击的TextView,并无多大的实际用处,主要是理清自定义View的步骤和思路。为后面自定义复杂的View做一些准备,不足之处还请大家多多指正,有抄袭的地方就当我抄袭吧。

一般来说,自定义View的步骤一般如下:

1.   定义View的xml属性

自定义View的属性,首先在res/values/下建立一个attrs.xml(其他文件名也可以), 在里面定义我们的属性和声明我们的整个样式。

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="CustomTitleView">
        <attr name="titleText" format="string"/>
        <attr name="titleColor" format="color"/>
        <attr name="titleTextSize" format="dimension"/>
    </declare-styleable>

</resources>

我们定义了文本内容titleText,字体颜色titleColor,字体大小titleTextSize这3个属性,format是值该属性的取值类型:

一共有:string,color,demension,integer,enum,reference,float,boolean,fraction,flag这几种类型,相关的含义如下:

string:字符串类型值

color:颜色值

demension: 尺寸值

integer: 整型值

float: 浮点数值

enum: 枚举值

reference:引用值,引用某一资源

boolean:布尔值

fraction:百分数值

flag: 位或运算

2.   定义CustomTitleView类

在构造方法中获取属性,这里主要是我们定义的上面三种属性。

    //文本
    private String mTitleText;
    //文本颜色
    private int mTitleColor;
    //文本字体
    private int mTitleTextSize;

    //绘制时控制文本的范围
    private Rect mRect;
    //画笔
    private Paint mPaint;
    public CustomTitleView(Context context){
        this(context,null);
    }

    public CustomTitleView(Context context, AttributeSet attrs){
        this(context,attrs,0);
    }

    /**
     * 获取自定义的样式属性,一般来说该构造方法是必须要有的
     * @param context
     * @param attrs
     * @param defStyle
     */
    public CustomTitleView(Context context,AttributeSet attrs,int defStyle){
        super(context,attrs,defStyle);
        init(context,attrs);
    }

    private void init(Context context,AttributeSet attrs){
        /**
         * 获取自定义的样式属性(从xml文件)
         */
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.CustomTitleView);

        mTitleText = typedArray.getString(R.styleable.CustomTitleView_titleText);

        //默认为黑色
        mTitleColor = typedArray.getColor(R.styleable.CustomTitleView_titleColor, Color.BLACK);

        //默认设置16sp,TypeValue可以把sp转化为px
        mTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.CustomTitleView_titleTextSize,
                (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));

        Log.d(TAG,"mTitleText:" + mTitleText);
        Log.d(TAG,"mTitleColor:" + mTitleColor);
        Log.d(TAG,"mTitleSize:" + mTitleTextSize);

        typedArray.recycle();

        //初始化绘笔
        mPaint = new Paint();
        //设置抗锯齿
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(mTitleTextSize);
        mPaint.setColor(mTitleColor);

        //获得绘制文本的宽和高
        mRect = new Rect();
        mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mRect);
    }
可以看到,我们使用了TypeArray这个类,TypedArray可以很方便的便于我们去获取定义在xml文件中的属性。


3.   重写onMeasure()和onDraw()方法

onMeasure()方法主要处理wrap_content时的问题,以及处理好padding。

onDraw(Canvascanvas)方法需要注意,我们这个自定义的View到底要做成什么样子,就在里面调用画笔绘制成什么样子。

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int width;
        int height;
        
        //得到宽和高的测量模式和规格
        int widthMode = MeasureSpec.getMode(widthMeasureSpec);
        int widthSize = MeasureSpec.getSize(widthMeasureSpec);
        int heightMode = MeasureSpec.getMode(heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);

        mPaint.setTextSize(mTitleTextSize);
        mPaint.getTextBounds(mTitleText,0,mTitleText.length(),mRect);

        Log.d(TAG,"mRect.width()" + mRect.width());

        if (widthMode == MeasureSpec.EXACTLY){
            width = widthSize;
        } else {
            //加上左右两边的padding区域和字符的宽度
            width = getPaddingLeft() + mRect.width() + getPaddingRight();
        }

        if (heightMode == MeasureSpec.EXACTLY){
            height = heightSize;
        } else {
            //加上上下两边的padding区域和字符的高度
            height = getPaddingTop() + mRect.height() + getPaddingBottom();
        }

        //设置测量尺寸
        setMeasuredDimension(width,height);
    }

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

        //画一个矩形框,长宽为测量后的宽高
        mPaint.setColor(Color.YELLOW);
        canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),mPaint);

        mPaint.setColor(mTitleColor);
        //画在正中间
        canvas.drawText(mTitleText,getMeasuredWidth()/2 - mRect.width()/2
                ,getMeasuredHeight()/2 + mRect.height()/2,mPaint);
    }

4.   布局中使用的自定义View

注意,这里需要引入我们自己的命名空间才可以使用我们自定义的View属性。一般命令空间为

xmlns:xxx="http://schemas.android.com/apk/res/自己的包名 "

xmlns:custom="http://schemas.android.com/apk/res/com.customview"
……
<com.customview.ui.CustomTitleView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:id="@+id/custom_title_view"
        android:paddingLeft="10px"
        android:paddingRight="10px"
        android:paddingTop="10px"
        android:paddingBottom="10px"
        custom:titleText="hello"
        custom:titleColor="@color/colorAccent"
        custom:titleTextSize="20sp"/>
到这里,我们自定义的View已经可以用了,比如我们就可以显示出来了。基本上算是比较完善了。

5.   完善CustomTitleView

为什么要继续完善了,其实发现我们自定义的CustomTitleView和普通的TextView差不多,我们可以给它增加点击事件,或者增加滑动事件。

         想要增加点击事件,需要CustomTitleView实现View.OnClickListener接口,如果是想要监听滑动需要实现View.OnScrollChangeListener或其他滑动监听接口。这里以点击为例。

1.      实现View.OnClickListener接口,构造函数里面设置监听器。

public class CustomTitleView extends View implements View.OnClickListener{
……
/**
     * 获取自定义的样式属性,一般来说是必须要有的
     * @param context
     * @param attrs
     * @param defStyle
     */
    public CustomTitleView(Context context,AttributeSet attrs,int defStyle){
        super(context,attrs,defStyle);
        init(context,attrs);

        //设置监听器
        this.setOnClickListener(this);
}


2.      定义内部接口OnClickListener,由外部实现其方法

public interface OnClickListener{
        void onClick(View v);
}


3.      在CustomTitleView定义自定义监听器成员变量

    //监听器
    private OnClickListener mListener;

4.      重写View.OnClickListener的onClick()方法

在该方法中我们需要调用自定义监听器中的方法,并处理好后续逻辑

@Override
    public void onClick(View v) {
        mListener.onClick(v);
        //重新布局
        requestLayout();
        //该View重绘
        postInvalidate();
    }

当然还有其他的设置监听器等其他的后续操作,这里就不列出来了。现在我们的CustomTitleView已经可以相应我们的点击事件了。在Activity的onCreate()中写如下代码即可。

final CustomTitleView titleView = (CustomTitleView) findViewById(R.id.custom_title_view);
        titleView.setOnClickListener(new CustomTitleView.OnClickListener() {
            @Override
            public void onClick(View v) {
                titleView.setTitleText(titleView.randomText());
            }
        });

注意:

  •  attrs.xml里面的declare-styleable以及item,android会根据其在R.java中生成一些常量方便我们使用(aapt干的),本质上,我们可以不声明declare-styleable仅仅声明所需的属性即可。
  • 我们在自定义View的时候,可以使用系统已经定义的属性

最后效果图:


完整的源代码见github:点击打开链接






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值