Android-自定义View

一、为什么要自定义View

  1. Android原生控件,不能直接满足具体业务场景的特殊交互方式;
  2. 封装代码,方便项目适配与复用;

二、自定义View方式

  1. 组合控件
  2. 继承控件
  3. 自绘控件

三、自定义View实现

(一)组合控件

绘制一个CustomViewByGroup视图,解析xml布局,其中xml布局集成LinearLayout & TextView等相关Android原生控件。

public class CustomViewByGroup extends LinearLayout {
    private TextView mTxt_title;
    private Context mContext;
    public CustomViewByGroup(Context context) {
        super(context);
    }

    public CustomViewByGroup(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);
        initData();
    }

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

    public CustomViewByGroup(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    private void initView(Context context) {
        mContext = context;
        View view = LayoutInflater.from(mContext).inflate(R.layout.widget_by_group,this);
        mTxt_title = view.findViewById(R.id.txt_title);
    }

    private void initData(){
        mTxt_title.setText(mContext.getString(R.string.customview_by_group_title));
    }
}
(二)继承控件

绘制一个CustomViewByExtend视图,继承Android原生控件TextView。

public class CustomViewByExtend extends androidx.appcompat.widget.AppCompatTextView {
    private Context mContext;

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

    public CustomViewByExtend(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
        initData();
    }

    public CustomViewByExtend(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void initView(Context context) {
        mContext = context;
    }

    private void initData(){
        int padding = (int)mContext.getResources().getDimension(R.dimen.content_title_padding);
        this.setPadding(padding,padding,padding,padding);
        this.setText(mContext.getString(R.string.customview_by_extend_title));
        float textSize = (float) (mContext.getResources().getDimension(R.dimen.content_title_size));
        this.setTextSize(px2dip(mContext,textSize));
    }

    // 根据手机的分辨率从 px(像素) 的单位 转成为 dp
    public static int px2dip(Context context, float pxValue) {
        // 获取当前手机的像素密度(1个dp对应几个px)
        float scale = context.getResources().getDisplayMetrics().density;
        return (int) (pxValue / scale + 0.5f); // 四舍五入取整
    }
}
(三)自绘控件

绘制一个CustomViewByDraw视图,继承View,重写onDraw方法。

public class CustomViewByDraw extends View {
    // 定义画笔
    private Paint mPaint;
    // 用于获取文字的宽和高
    private Rect mBounds;
    // 计数值,每点击一次本控件,其值增加1
    private Context mContext;

    /**
     * Java代码引用时,调用此构造函数
     * @param context
     */
    public CustomViewByDraw(Context context) {
        super(context);
    }

    /**
     * XML布局文件引用,调用此构造函数
     * @param context
     * @param attrs
     */
    public CustomViewByDraw(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        initView(context);
        initData();
    }

    /**
     * 涉及主题变更时,调用此构造函数
     * @param context
     * @param attrs
     * @param defStyleAttr
     */
    public CustomViewByDraw(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    /**
     * 涉及主题变更时,调用此构造函数
     * @param context
     * @param attrs
     * @param defStyleAttr
     * @param defStyleRes
     */
    public CustomViewByDraw(Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
    }

    private void initView(Context context) {
        mContext = context;
        // 初始化画笔、Rect
        mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
        mBounds = new Rect();
    }

    private void initData(){

    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        int view_height = 80;

        // 绘制一个填充色为蓝色的矩形
        mPaint.setColor(Color.BLUE);
        canvas.drawRect(0, 0, getWidth(), view_height, mPaint);

        // 绘制字符串
        mPaint.setColor(Color.YELLOW);
        mPaint.setTextSize(45);

        // 获取文字的宽和高
        String text = String.valueOf(mContext.getString(R.string.customview_by_draw_title));
        mPaint.getTextBounds(text, 0, text.length(), mBounds);
        float textHeight = mBounds.height();

        canvas.drawText(text, 12, view_height/2 + textHeight / 2, mPaint);
    }
}

四、View相关问题分析

1. 四个构造函数参数代表什么?

参数 描述
Context 上下文
AttributeSet attrs 从xml中定义的各种参数,如:textColor
int defStyleAttr 主题中优先级最高的属性,格式为reference(参考某一资源ID),只要在主题中对这个属性赋值,该View就会自动应用这个属性的值,如:textViewStyle
int defStyleRes 样式资源文件,如:R.style.GreenTextStyle

2. 四个构造函数的引用时机?

构造函数 引用时机
CustomViewByExtend(Context context) Java代码引用时,调用此构造函数
CustomViewByExtend(Context context, AttributeSet attrs) XML布局文件引用,调用此构造函数
CustomViewByExtend(Context context, AttributeSet attrs,
int defStyleAttr)
涉及主题等相关资源变更时,可主动调用此构造函数
CustomViewByExtend(Context context, @Nullable AttributeSet attrs,
int defStyleAttr, int defStyleRes)
涉及主题等相关资源变更时,可主动调用此构造函数

3. View资源属性赋值优先级?

  • 优先级:Xml直接定义 > xml中style引用 > defStyleAttr > defStyleRes > theme直接定义
  • 分析过程:目前是通过控制变量法,改变如上不同的属性,观察textview的字体颜色,可以看到属性赋值的优先级。详细可参考Demo代码(CustomViewActivity类),如下附上部分代码:
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="@dimen/content_tip_size"
        android:paddingLeft="@dimen/content_title_padding"
        android:text="优先级:xml直接定义 > xml的style定义 > theme直接定义"/>

    <!-- 字体为theme定义颜色 -->
    <com.lzq.mycustomdemo.widget.CustomViewByExtend
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <!-- 字体为style定义颜色 -->
    <com.lzq.mycustomdemo.widget.CustomViewByExtend
        style="@style/RedTextStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />
    <!-- 字体为xml布局定义颜色 -->
    <com.lzq.mycustomdemo.widget.CustomViewByExtend
        style="@style/RedTextStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="@android:color/holo_orange_light"/>

    <View
        android:layout_width="match_parent"
        android:layout_height="2dp"
        android:layout_margin="5dp"
        android:layerType="software"
        android:background="@drawable/dotted_line_shap"/>
    
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textSize="@dimen/content_tip_size"
        android:paddingLeft="@dimen/content_title_padding"
        android:text="优先级: defStyleAttr > defStyleRes > theme直接定义"/>

    <!-- defStyleAttr和defStyleRes都设置为0,字体为主题默认颜色 -->
    <com.lzq.mycustomdemo.example.customview.CustomViewBlank
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/content_title_padding"
        android:textSize="@dimen/content_title_size"
        android:text="@string/customview_by_extend_title"/>

    <!-- defStyleAttr为0 && defStyleRes设置为绿颜色,字体为绿颜色 -->
    <com.lzq.mycustomdemo.example.customview.CustomViewGreen
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="@dimen/content_title_padding"
        android:textSize="@dimen/content_title_size"
        android:text="@string/customview_by_extend_title"/>

Demo测试结果如下:

五、代码仓库地址

Demo地址:  https://gitee.com/linzhiqin/custom-demo

六、参考博客

https://blog.csdn.net/iamchb/article/details/79879995

posted @ 2022-08-02 16:19  林奋斗同学  阅读(168)  评论(0编辑  收藏  举报