博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
android自定义属性
阅读量:6071 次
发布时间:2019-06-20

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

hot3.png

                                 android自定义属性

1、引言

对于自定义属性,大家肯定都不陌生,遵循以下几步,就可以实现:

  1. 自定义一个CustomView(extends View )类
  2. 编写values/attrs.xml,在其中编写styleable和item等标签元素
  3. 在布局文件中CustomView使用自定义的属性(注意namespace)
  4. 在CustomView的构造方法中通过TypedArray获取

那么,我有几个问题:

  • 以上步骤是如何奏效的?
  • styleable 的含义是什么?可以不写嘛?我自定义属性,我声明属性就好了,为什么一定要写个styleable呢?
  • 如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?
  • 构造方法中的有个参数叫做AttributeSet 
    (eg: MyTextView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的数组,那么我能不能通过它去获取我的自定义属性呢?
  • TypedArray是什么鬼?从哪冒出来的,就要我去使用?

2、常见的例子

接下来通过例子来回答上述问题,问题的回答顺序不定~~大家先看一个常见的例子,即上述几个步骤的代码化。

  • 自定义属性的声明文件attrs.xml
  • 自定义View类
package qu.com.handlerthread;import android.content.Context;import android.content.res.TypedArray;import android.util.AttributeSet;import android.util.Log;import android.view.View;/** * Created by quguangle on 2016/11/25. */public class MyTextView extends View {    private static final String TAG = MyTextView.class.getSimpleName();    public MyTextView(Context context, AttributeSet attrs) {        super(context, attrs);        TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.Test);        String text = typedArray.getString(R.styleable.Test_text);        int textAttr = typedArray.getInteger(R.styleable.Test_textAttr,-1);        Log.e(TAG,"text:"+text+"-----"+"textAttr:"+textAttr);        typedArray.recycle();    }    public MyTextView(Context context) {        super(context);    }}
  • 布局文件中使用

运行结果后,打印记录为:

195017_3O46_2832222.png

应该都不意外吧,注意下,我的styleable的name写的是test,所以说这里并不要求一定是自定义View的名字。

3、AttributeSet与TypedArray

下面考虑:

构造方法中的有个参数叫做AttributeSet(eg: MyTextView(Context context, AttributeSet attrs) )这个参数看名字就知道包含的是参数的集合,那么我能不能通过它去获取我的自定义属性呢?

首先AttributeSet中的确保存的是该View声明的所有的属性,并且外面的确可以通过它去获取(自定义的)属性,怎么做呢? 

其实看下AttributeSet的方法就明白了,下面看代码。

public MyTextView(Context context, AttributeSet attrs) {        super(context, attrs);        int count = attrs.getAttributeCount();        for (int i = 0; i < count; i++) {            String attrName = attrs.getAttributeName(i);            String attrVal = attrs.getAttributeValue(i);            Log.e(TAG, "attrName = " + attrName + " , attrVal = " + attrVal);        }        // ==>use typedarray ...    }

打印结果:

200242_xmHf_2832222.png

结合上面的布局文件,你发现了什么? 

我擦,果然很神奇,真的获得所有的属性,恩,没错,通过AttributeSet可以获得布局文件中定义的所有属性的key和value(还有一些方法,自己去尝试),那么是不是说TypedArray这个鬼可以抛弃了呢?答案是:NO!

现在关注下一个问题:

TypedArray是什么鬼?从哪冒出来的,就要我去使用?

我们简单修改下,布局文件中的MyTextView的属性。

现在再次运行的结果是:

201040_wPlX_2832222.png

发现了什么?通过AttributeSet获取的值,如果是引用都变成了@+数字的字符串。你说,这玩意你能看懂么?那么你看看最后一行使用TypedArray获取的值,是不是瞬间明白了什么。

TypedArray其实是用来简化我们的工作的,比如上例,如果布局中的属性的值是引用类型(比如:@dimen/dp100),如果使用AttributeSet去获得最终的像素值,那么需要第一步拿到id,第二步再去解析id。而TypedArray正是帮我们简化了这个过程。

贴一下:如果通过AttributeSet获取最终的像素值的过程:

int widthDimensionId =  attrs.getAttributeResourceValue(0, -1);        Log.e(TAG, "layout_width= "+getResources().getDimension(widthDimensionId));

ok,现在别人问你TypedArray存在的意义,你就可以告诉他了。

4、declare-styleable

我们已经解决了两个问题,接下来,我们看看布局文件,我们有一个属性叫做:zhy:text。 

总所周知,系统提供了一个属性叫做::text,那么我觉得直接使用android:text更nice,这样的话,考虑问题:

如果系统中已经有了语义比较明确的属性,我可以直接使用嘛?

答案是可以的,怎么做呢? 

直接在attrs.xml中使用android:text属性。

注意,这里我们是使用已经定义好的属性,不需要去添加format属性(注意声明和使用的区别,差别就是有没有format)。 

然后在类中这么获取:ta.getString(R.styleable.test_android_text);布局文件中直接android:text="@string/hello_world"即可。

这里提一下,系统中定义的属性,其实和我们自定义属性的方式类似,你可以在sdk/platforms/android-xx/data/res/values该目录下看到系统中定义的属性。然后你可以在系统提供的View(eg:TextView)的构造方法中发现TypedArray获取属性的代码(自己去看一下)。

ok,接下来,我在想,既然declare-styleable这个标签的name都能随便写,这么随意的话,那么考虑问题:

styleable 的含义是什么?可以不写嘛?我自定义属性,我声明属性就好了,为什么一定要写个styleable呢?

其实的确是可以不写的,怎么做呢?

  • 首先删除declare-styleable的标签

那么现在的attrs.xml为:

MyTextView实现

** * Created by quguangle on 2016/11/25. */public class MyTextView extends View {    private static final String TAG = MyTextView.class.getSimpleName();    private static final int[] mAttr = { R.attr.textAttr,R.attr.Mytext};    private static final int ATTR_ANDROID_TEXT = 0;    private static final int ATTR_TESTATTR = 1;    public MyTextView(Context context, AttributeSet attrs) {        super(context, attrs);        // ==>use typedarray        TypedArray ta = context.obtainStyledAttributes(attrs, mAttr);        String text = ta.getString(ATTR_ANDROID_TEXT);        int textAttr = ta.getInteger(ATTR_TESTATTR, -1);        //输出 text = Hello world! , textAttr = 520        Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);        ta.recycle();    }}

貌似多了些代码,可以看到我们声明了一个int数组,数组中的元素就是我们想要获取的attr的id。并且我们根据元素的在数组中的位置,定义了一些整形的常量代表其下标,然后通过TypedArray进行获取。 

可以看到,我们原本的:

R.styleable.test => mAttrR.styleable.test_text => ATTR_ANDROID_TEXT(0)R.styleable.test_testAttr => ATTR_TESTATTR(1)

打印结果: 

11-25 13:37:16.086 29383-29383/qu.com.handlerthread E/MyTextView: text = 250 , textAttr = -1

那么其实呢?android在其内部也会这么做,按照传统的写法,它会在 R.java生成如下代码:

public static final class attr {    public static final int testAttr=0x7f0100a9;    }public static final class styleable {     public static final int test_android_text = 0;     public static final int test_testAttr = 1;      public static final int[] test = {            0x0101014f, 0x7f0100a9        };    }

ok,根据上述你应该发现了什么。styleale的出现系统可以为我们完成很多常量(int[]数组,下标常量)等的编写,简化我们的开发工作(想想如果一堆属性,自己编写常量,你得写成什么样的代码)。那么大家肯定还知道declare-styleable的name属性,一般情况下写的都是我们自定义View的类名。主要为了直观的表达,该declare-styleable的属性,都是改View所用的。

ok,现在5个问题,回答了4个,第一个问题:

自定义属性的几个步骤是如何奏效的?

恩,上述以及基本涵盖了这个问题的答案,大家自己总结,所以:略。

总结下今天的博客。

  • attrs.xml里面的declare-styleable以及item,android会根据其在R.java中生成一些常量方便我们使用(aapt干的),本质上,我们可以不声明declare-styleable仅仅声明所需的属性即可。
  • 我们在View的构造方法中,可以通过AttributeSet去获得自定义属性的值,但是比较麻烦,而TypedArray可以很方便的便于我们去获取。
  • 我们在自定义View的时候,可以使用系统已经定义的属性。

转载于:https://my.oschina.net/quguangle/blog/794526

你可能感兴趣的文章
JSP中报错only a type can be imported: XXX resolves to package
查看>>
hbase源码带注释版本,放在这里,方便大家下载吧
查看>>
freemarker 模板生成 文件
查看>>
Express使用手记:核心入门
查看>>
工具03:PuTTY常用配置
查看>>
基于资源编排一键交付应用之基于WaitCondition的通知机制
查看>>
smarty的插件功能是smarty模板的精华
查看>>
xen虚拟化实战系列(四)之xen虚拟机扩展磁盘空间一法
查看>>
【转载】条款29: 避免返回内部数据的句柄
查看>>
Razor Templating Engine
查看>>
How do I avoid capturing self in blocks when implementing an API?
查看>>
Spring中bean的scope
查看>>
遇到过有人在文件服务器上新建共享文件夹,自己无法COPY文件,别人无法看到的情况么??...
查看>>
iOS开发系列—Objective-C之Foundation框架
查看>>
LLDB TO GDB COMMAND MAP LLDB TO GDB COMMAND MAP[转]
查看>>
js修改后没反应-看看是不是取的缓存
查看>>
将图库的图片剪切并保存
查看>>
Bootstrap
查看>>
HTTP请求(GET与POST区别)和响应
查看>>
深入理解Linux内存管理机制(一)
查看>>