博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
36、自定义控件详解(一)-- 自定义属性
阅读量:6494 次
发布时间:2019-06-24

本文共 10414 字,大约阅读时间需要 34 分钟。

一、自定义控件分类

1.1、原生控件拓展

修改原有控件我们只需要创建一个类继承该View(ViewGroup),再原有的逻辑上添加自己的实现即可。

a)文本框(TextView)默认是无法获取焦点的,想让它获取焦点,我们可以通过自定义控件,并重写isFocused来解决。

public class FocuseTextView extends TextView {    public FocuseTextView(Context context) {        this(context,null);    }    public FocuseTextView(Context context, AttributeSet attrs) {        super(context, attrs);    }    @Override    @ExportedProperty(category = "focus")    public boolean isFocused() {        //欺骗系统,误以为textview得到了焦点        return true;    }}

b)绘制一个带矩形边框的TextView

public class MyTetView extends TextView {    private Paint mPaint1;    private Paint mpaint2;    public MyTetView(Context context) {        this(context,null);    }    public MyTetView(Context context, AttributeSet attrs) {        super(context, attrs);        initPaint();    }    private void initPaint() {        mPaint1 = new Paint();        mPaint1.setStyle(Paint.Style.FILL);        mPaint1.setColor(getResources().getColor(android.R.color.holo_blue_light));                mpaint2 = new Paint();        mpaint2.setStyle(Paint.Style.FILL);        mpaint2.setColor(Color.YELLOW);    }        @Override    protected void onDraw(Canvas canvas) {        // 绘制外层矩形        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint1);        // 绘制内层矩形        canvas.drawRect(10, 10, getMeasuredWidth() - 10, getMeasuredHeight() - 10, mpaint2);        canvas.save();        super.onDraw(canvas);        canvas.restore();    }}

1.2、自定义组合控件

(1) 组合原生控件

在自定义组合控件时,我们并不需要自己去绘制视图上显示的内容,而只是用系统原生的控件组合即可。

a) 我们首先制定好需要组合的控件,用它来达到我们想要的效果:

b) 在values目录下创建attrs.xml文件,并定义好属性:

c) 创建自定义控件类继承自ViewGroup,并实现带attrs的构造函数,再使用TypeArray来获取属性:

public class TextViewCheckBox extends RelativeLayout {    private TextView mTvTitle,mTvContent;    private CheckBox mCbClick;    private String mTitle,mContentOn,mContentOff;        public TextViewCheckBox(Context context) {        this(context,null);    }    public TextViewCheckBox(Context context, AttributeSet attrs) {        super(context, attrs);        // 初始化布局和控件        View view = View.inflate(context, R.layout.ui_text_checkbox, this);        mTvTitle = (TextView) view.findViewById(R.id.tv_title);        mTvContent = (TextView) view.findViewById(R.id.tv_content);        mCbClick = (CheckBox) view.findViewById(R.id.cb_click);                // 将attrs.xml中定义的所有属性的值存储到TypeArray中        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.combinationView);        mTitle = array.getString(R.styleable.combinationView_title);        mContentOn = array.getString(R.styleable.combinationView_content_on);        mContentOff = array.getString(R.styleable.combinationView_content_off);        array.recycle();                // 初始化子控件描述和状态        if(mTitle != null){            mTvTitle.setText(mTitle);        }                if(mContentOff != null){            mTvContent.setText(mContentOff);        }    }}

d) 暴露方法给调用者来设置描述和状态:

/**判断是否被选中*/public boolean isChecked(){    return mCbClick.isChecked();}/**设置选中的状态*/public void setChecked(boolean isChecked){    mCbClick.setChecked(isChecked);    if(isChecked){        mTvContent.setText(mContentOn);    }else{        mTvContent.setText(mContentOff);    }}

e) 在布局中引用该控件,引入名称空间,并设置自定义的属性。

注意:在使用自定义控件时需要引入名称空间:xmlns:review="http://schemas.android.com/apk/res/cn.legend.review"

如果想让控件响应事件的话,则直接重写事件即可,如果用到了wrap_content或match_parent则需要进行测量等操作。

1.3、自定义属性种类

1. reference:参考某一资源ID。

(1)属性定义:        
(2)属性使用:

2. color:颜色值

(1)属性定义:        
(2)属性使用:

3. boolean:布尔值

1)属性定义:        
(2)属性使用:

4. dimension:尺寸值

(1)属性定义:        
(2)属性使用:

5. float:浮点值

(1)属性定义:        
(2)属性使用:

6. integer:整型值

(1)属性定义:        
(2)属性使用:

7. string:字符串

(1)属性定义:        
(2)属性使用:

8. fraction:百分数

(1)属性定义:        
(2)属性使用:

9. enum:枚举值

(1)属性定义:        
(2)属性使用:

10. 位或运算

(1)属性定义:        
(2)属性使用:

注意:属性定义时可以指定多种类型值,使用“|”进行隔离多种类型。

(1)属性定义:        
(2)属性使用:

1.4、自定义属性用法

在我们自定义的View中有需要自定义的属性,则需要在values下建立attrs.xml,在其中定义你的属性。

a)在res/values文件下定义一个attrs.xml文件,代码如下:

b)在布局xml中如下使用该属性:

2、两种获取方式

我们可以在自定义View的构造函数中获取属性的值,有以下两种方式:

第一种获取方式:

TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.ToolBar);int buttonNum = array.getInt(R.styleable.ToolBar_buttonNum, 5);int itemBg = array.getResourceId(R.styleable.ToolBar_itemBackground, -1);array.recycle();

第二种获取方式:

TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.ToolBar);int count = array.getIndexCount();for (int i = 0; i < count; i++) {    int attr = array.getIndex(i);    switch (attr) {    case R.styleable.ToolBar_buttonNum:        int buttonNum = array.getInt(attr, 5);        break;    case R.styleable.ToolBar_itemBackground:        int itemBg = array.getResourceId(attr, -1);        break;    }}

3、使用要点

属性类型:string、integer、dimension、reference、color、enum。

下面我看下使用自定义属性需要注意的一些地方,首先看下attrs.xml文件前面几种声明方式都是一致的,例如:

只有enum是不同的,用法举例:

如果该属性可同时传两种不同的属性,则可以用“|”分割开即可。让我们再看看布局xml中需要注意的事项,在自定义组件的构造函数中使用:

TypedArray array = context.obtainStyledAttributes(attrs,R.styleable.ToolBar);

获得对属性集的引用,然后就可以用“array”的各种方法来获取相应的属性值。这里需要注意的是,如果使用的方法和获取值的类型不对的话,则会返回默认值。

因此,如果一个属性是带两个及以上不用类型的属性,需要做多次判断,直到读取完毕后才能判断应该赋予何值。

1.5、自定义View演示

编写简单验证码程序:

a) 自定义View的属性,首先在res/value/下建立attrs.xml,里面定义我们的属性和声明我们的整个样式

b) 我们定义字体、字体颜色、字体大小等3个属性,foemat是指该属性的取值类型。

然后在布局文件中声明我们自定义的View,注意:自定义属性需要引入名称空间

c) 由于自定义属性,我们的View必须实现带attr的构造方法:

public class CustomTitleView extends View {    private String mCustomText;    private int mCustomTextColor;    private int mCustomTextSize;    private Rect mRect;    private Paint mPaint;    public CustomTitleView(Context context) {        this(context, null);    }    public CustomTitleView(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.CustomTitleView);        mCustomText = array.getString(R.styleable.CustomTitleView_titleText);        mCustomTextColor = array.getColor(R.styleable.CustomTitleView_titleTextColor, Color.BLACK);        mCustomTextSize = (int) array.getDimension(R.styleable.CustomTitleView_titleTextSize, 16);        array.recycle();                initPaint();    }    private void initPaint() {        mPaint = new Paint();        mPaint.setTextSize(mCustomTextSize);        mPaint.setColor(mCustomTextColor);                // 文字的骨骼        mRect = new Rect();        mPaint.getTextBounds(mCustomText, 0, mCustomText.length(), mRect);    }        @Override    protected void onDraw(Canvas canvas) {        super.onDraw(canvas);        // 画背景        mPaint.setColor(Color.YELLOW);        canvas.drawRect(0, 0, getMeasuredWidth(), getMeasuredHeight(), mPaint);                mPaint.setColor(mCustomTextColor);        canvas.drawText(mCustomText, getWidth() / 2 - mRect.width() /2, getHeight() / 2 + mRect.height() / 2, mPaint);    }}

运行效果:

d) 当我们将布局文件中的宽和高为明确的值时,系统测量的结果就是我们设置的结果,如果wrap_content或match_parent时,

系统默认会让宽和高和窗体同宽高,因为系统根本不知道我们的控件的具体大小,我们可以通过重写onMeasure方法

重写之前先了解MeasureSpec的specMode有三种类型:

  • EXACTLY:一般是设置了明确的值或者是MATCH_PARENT
  • AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT
  • UNSPECIFIED:表示子布局想要多大就多大,很少使用

e) 测量是比较复杂的,我们来测量下该控件:

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    int widthMode = MeasureSpec.getMode(widthMeasureSpec);    int widthSize = MeasureSpec.getSize(widthMeasureSpec);        int heightMode = MeasureSpec.getMode(heightMeasureSpec);    int heightSize = MeasureSpec.getSize(heightMeasureSpec);        int width,height;        if(widthMode == MeasureSpec.EXACTLY){
// 明确的值或match_parent width = widthSize; }else{ mPaint.setTextSize(mCustomTextSize); mPaint.getTextBounds(mCustomText, 0, mCustomText.length(), mRect); int textWidth = mRect.width(); // 期望的值,左右边距加上文本的宽度 width = getPaddingLeft() + textWidth + getPaddingRight(); } if(heightMode == MeasureSpec.EXACTLY){ height = heightSize; }else{ mPaint.setTextSize(mCustomTextSize); mPaint.getTextBounds(mCustomText, 0, mCustomText.length(), mRect); int textHeight = mRect.height(); // 期望的值,上下边距加上文本的高度 height = getPaddingTop() + textHeight + getPaddingBottom(); } setMeasuredDimension(width, height);}

由于我们让宽度和高度分别是左边距 + 右边距 + 文本宽度 和 上边距 + 下边距 + 文本高度,所以我们布局必须加入padding值:

e) 接下来给该控件加入事件

this.setOnClickListener(new OnClickListener() {    @Override    public void onClick(View v) {        // 生成四位随机数        Random random = new Random();        Set
set = new HashSet
(); while(set.size() < 4){ int randomInt = random.nextInt(10); set.add(randomInt); } StringBuffer sb = new StringBuffer(); for (Integer integer : set) { sb.append("" + integer); } mCustomText = sb.toString(); // 重绘 postInvalidate(); }});

##############################################待续

转载于:https://www.cnblogs.com/pengjingya/p/5510217.html

你可能感兴趣的文章
overflow清除浮动的原理
查看>>
Spring Boot 使用parent方式引用时 获取值属性方式默认@
查看>>
Elasticsearch之中文分词器插件es-ik(博主推荐)
查看>>
解决maven下载jar慢的问题(如何更换Maven下载源)
查看>>
linux安装gitLab
查看>>
concurrent包的实现示意图
查看>>
golang os.Args
查看>>
Linux常用命令
查看>>
【重磅】云栖社区2017年度内容特辑
查看>>
Java WEB开发时struts标签 显示set内容
查看>>
spring-data-elasticsearch 概述及入门(二)
查看>>
Solr启动和结束命令
查看>>
1.12 xshell密钥认证
查看>>
3.2 用户组管理
查看>>
awk
查看>>
AliOS Things SMP系统及其在esp32上实现示例
查看>>
VMware虚拟机出现“需要整合虚拟机磁盘”的解决方法
查看>>
ibatis 动态查询
查看>>
汇编语言之实验一
查看>>
观影识人生
查看>>