自定义控件那些事儿 ----- 一

一、自定义控件的实现思路

一个自定义控件的实质就是一个View,一个View的展示主要包含以下关键流程:

(1)View属性的初始化;

(2)View位置大小的量测 ,主要在onMeasure()中实现;

【(3)View内部的布局关系展示,主要在onLayout()中实现;

(4)View的绘制,即是最后的展示效果;

【(5)View交互事件的处理,实现View与目标对象的沟通。】

其中3,5小点不是每个View都必须要自行实现的。

---------------------------------------------------------站在巨人的肩膀上,能够看的更远。


二、例举实现自定义控件

公共使用变量声明:

   /**
     * 显示文字
     */
    private String textAttr;
    /**
     * 字体颜色
     */
    private int textColorAttr;
    /**
     * 字体大小
     */
    private int textSizeAttr;

    /**
     * 绘制工具--笔/绘制区域
     */
    private Paint paint;
    private Rect rect;
    private String[] strings = new String[]{
            "我就是我,不一样的烟火!",
            "谁的青春不迷茫,谁的未来是肯定。",
            "对自己好一点,和自己多一点的孤独相处。",
            "明天会更好!",
            "将来的你一定感谢现在拼命的自己。"
    };


1,控件的属性声明及设置,res/values下新建attrs.xml:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--声明属性的名称及取值类型-->
    <attr name="TextColor" format="color" />
    <attr name="TextSize" format="dimension" />
    <attr name="TextContent" format="string" />
    <attr name="image" format="reference" />
    <attr name="imageScaleType">
        <enum name="fillXY" value="0" />
        <enum name="center" value="1" />
    </attr>

    <!--声明该自定义控件所拥有的属性-->
    <declare-styleable name="CustomControlTextView">
        <attr name="TextColor" />
        <attr name="TextSize" />
        <attr name="TextContent" />
    </declare-styleable>

</resources>

2,自定义控件的属性值获取:

    public CustomControlTextView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        /**
         * 获取自定义属性
         * 【
         *      预留:
         *      1,属性的类型,及值类型;
         *      2,属性的获取方式
         *      3,属性默认值设置
         * 】
         *
         */
        TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CustomControlTextView, defStyleAttr, 0);
        int count = typedArray.getIndexCount();

        for (int i = 0; i < count; i++) {
            int attr = typedArray.getIndex(i);
            switch (attr) {
                case R.styleable.CustomControlTextView_TextColor:
                    textColorAttr = typedArray.getColor(attr, Color.BLACK);//默认字体颜色为黑色
                    break;
                case R.styleable.CustomControlTextView_TextContent:
                    textAttr = typedArray.getString(attr);
                    break;
                case R.styleable.CustomControlTextView_TextSize://像素转换细节注意
                    textSizeAttr = typedArray.getDimensionPixelSize(attr, (int) TypedValue.applyDimension(
                            TypedValue.COMPLEX_UNIT_SP, 16, getResources().getDisplayMetrics()));
                    break;
            }
        }

        typedArray.recycle();

        /**
         * 设置基本属性
         */
        paint = new Paint();
        paint.setColor(textColorAttr);
        paint.setTextSize(textSizeAttr);

        rect = new Rect();
        paint.getTextBounds(textAttr, 0, textAttr.length(), rect);

        this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int index = (int) (Math.random() * 4);
                textAttr = strings[index];
                postInvalidate();
            }
        });
    }
细节点:其他类型构造方法,采用调用本身的方法,使得所有该View被使用时,都通过该定义的方法初始化。如实笨一点方法,可以将其他类型构造方法下需要初始化的分别实现也行。

    public CustomControlTextView(Context context) {
//        super(context);
        this(context, null);
    }

    public CustomControlTextView(Context context, @Nullable AttributeSet attrs) {
//        super(context, attrs);
        /**
         * 重写构造方法,都走第三构造方法
         */
        this(context, attrs, 0);
    }


3,重新量测布局

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        /**
         * 【
         *      1,测量模式,测量的计算方式深度挖掘
         *      2,文字绘制的详细优化【padding等一系列的重算】
         * 】
         */
        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 {
            paint.setTextSize(textSizeAttr);
            paint.getTextBounds(textAttr, 0, textAttr.length(), rect);
            float textWidth = rect.width();
            int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight());
            width = desired;
        }

        if (heightMode == MeasureSpec.EXACTLY) {
            height = heightSize;
        } else {
            paint.setTextSize(textSizeAttr);
            paint.getTextBounds(textAttr, 0, textAttr.length(), rect);
            float textHeight = rect.height();
            int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom());
            height = desired;
        }

        setMeasuredDimension(width, height);
//        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }


使用定义控件:在主Activity的布局文件中添加控件,即可显示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:custom="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="com.future.customcontrolsfirstdemo.MainActivity">


    <com.future.customcontrolsfirstdemo.view.CustomControlTextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerInParent="true"
        custom:TextColor="#f07829"
        custom:TextContent="第一个自定义控件"
        custom:TextSize="16sp" />

</RelativeLayout>

注意补充自定义控件的命名空间(现在AndroidStudio会自动提醒,多好!)


在不重写量测方法的时候,除了写死控件的大小,控件都是占据整个屏幕的。


经过重新计算控件的大小,展示效果如图:



4,绘制显示

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

        paint.setColor(Color.GREEN);
        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), paint);

        paint.setColor(textColorAttr);
        canvas.drawText(textAttr, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + rect.height() / 2, paint);

    }

5,实现控件的可交互性

在构造方法的后面实现了该控件的点击事件监听器。可也会发现,没有完整的调整整个View,它的交互事件给人最后的结果并不如意:



交互实践的代码:【注意一下她被添加的位置啦】

   this.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                int index = (int) (Math.random() * 4);
                textAttr = strings[index];
                postInvalidate();
            }
        });

芝麻开门


如果每个人的内心,都像是锁了很多秘密的仓库。那么如果你够幸运的话,在你一生当中,你会碰到几个握有可以打开你内心仓库的钥匙的人。但很多人终其一生,内心的仓库却始终未曾被开启。
------------------------------------------------ 第一次亲密接触



评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

壹叁零壹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值