Android 深入理解Android中的自定义属性

转载请标明出处:
http://blog.csdn.net/lmj623565791/article/details/45022631
本文出自:【张鸿洋的博客】

1、引言

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

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

ps:如果你对上述几个步骤不熟悉,建议先熟悉下,再继续~

那么,我有几个问题:

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

恩,针对这几个问题,大家可以考虑下,如何回答呢?还是说:老子会背上述4个步骤就够了~~

2、常见的例子

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

  • 自定义属性的声明文件
    <?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="test">
        <attr name="text" format="string" />
        <attr name="testAttr" format="integer" />
    </declare-styleable>

</resources>
  • 自定义View类
package com.example.test;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class MyTextView extends View {

    private static final String TAG = MyTextView.class.getSimpleName();

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);

        TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.test);

        String text = ta.getString(R.styleable.test_testAttr);
        int textAttr = ta.getInteger(R.styleable.test_text, -1);

        Log.e(TAG, "text = " + text + " , textAttr = " + textAttr);

        ta.recycle();
    }

}
  • 布局文件中使用
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:zhy="http://schemas.android.com/apk/res/com.example.test"
    android:layout_width="match_parent"
    android:layout_height="match_parent" >

    <com.example.test.MyTextView
        android:layout_width="100dp"
        android:layout_height="200dp"
        zhy:testAttr="520"
        zhy:text="helloworld" />

</RelativeLayout>

ok,大家花3s扫一下,运行结果为:

 MyTextView: text = helloworld , textAttr = 520

应该都不意外吧,注意下,我的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 ...

    }

输出:

MyTextView(4136): attrName = layout_width , attrVal = 100.0dip
MyTextView(4136): attrName = layout_height , attrVal = 200.0dip
MyTextView(4136): attrName = text , attrVal = helloworld
MyTextView(4136): attrName = testAttr , attrVal = 520

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

现在关注下一个问题:

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

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

<com.example.test.MyTextView
        android:layout_width="@dimen/dp100"
        android:layout_height="@dimen/dp200"
        zhy:testAttr="520"
        zhy:text="@string/hello_world" />

现在再次运行的结果是:

MyTextView(4692): attrName = layout_width , attrVal = @2131165234
MyTextView(4692): attrName = layout_height , attrVal = @2131165235
MyTextView(4692): attrName = text , attrVal = @2131361809
MyTextView(4692): attrName = testAttr , attrVal = 520
>>use typedarray
MyTextView(4692): text = Hello world! , textAttr = 520

发现了什么?通过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
总所周知,系统提供了一个属性叫做:android:text,那么我觉得直接使用android:text更nice,这样的话,考虑问题:

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

答案是可以的,怎么做呢?
直接在attrs.xml中使用android:text属性。

    <declare-styleable name="test">
        <attr name="android:text" />
        <attr name="testAttr" format="integer" />
    </declare-styleable>

注意,这里我们是使用已经定义好的属性,不需要去添加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为:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <attr name="testAttr" format="integer" />
</resources>

哟西,so清爽~
* MyTextView实现

package com.example.test;

import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;

public class MyTextView extends View {

    private static final String TAG = MyTextView.class.getSimpleName();

    private static final int[] mAttr = { android.R.attr.text, R.attr.testAttr };
    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 => mAttr
R.styleable.test_text => ATTR_ANDROID_TEXT(0)
R.styleable.test_testAttr => ATTR_TESTATTR(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所用的。

其实了解该原理是有用的,详见:Android 自定义控件 优雅实现元素间的分割线

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

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

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

总结下今天的博客。

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

近期的更新计划:自定义View的一些细节相关的Blog(重点会在交互上),Android最佳实践相关的文章,framework相关的一些文章,敬请期待。

群号:423372824

微信公众号:hongyangAndroid
(欢迎关注,第一时间推送博文信息)

  • 272
    点赞
  • 370
    收藏
    觉得还不错? 一键收藏
  • 112
    评论
Android开发艺术探索》是一本Android进阶类书籍,采用理论、源码和实践相结合的方式来阐述高水准的Android应用开发要点。《Android开发艺术探索》从三个方面来组织内容。第一,介绍Android开发者不容易掌握的一些知识点;第二,结合Android源代码和应用层开发过程,融会贯通,介绍一些比较深入的知识点;第三,介绍一些核心技术和Android的性能优化思想。 《Android开发艺术探索》侧重于Android知识的体系化和系统工作机制的分析,通过《Android开发艺术探索》的学习可以极大地提高开发者的Android技术水平,从而更加高效地成为高级开发者。而对于高级开发者来说,仍然可以从《Android开发艺术探索》的知识体系获益。 全书目录 ------------------------------------------------------------------- 第1章 Activity的生命周期和启动模式 / 1   1.1 Activity的生命周期全面分析 / 1   1.1.1 典型情况下的生命周期分析 / 2   1.1.2 异常情况下的生命周期分析 / 8   1.2 Activity的启动模式 / 16   1.2.1 Activity的LaunchMode / 16   1.2.2 Activity的Flags / 27   1.3 IntentFilter的匹配规则 / 28   第2章 IPC机制 / 35   2.1 Android IPC简介 / 35   2.2 Android的多进程模式 / 36   2.2.1 开启多进程模式 / 36   2.2.2 多进程模式的运行机制 / 39   2.3 IPC基础概念介绍 / 42   2.3.1 Serializable接口 / 42   2.3.2 Parcelable接口 / 45   2.3.3 Binder / 47   2.4 Android的IPC方式 / 61   2.4.1 使用Bundle / 61   2.4.2 使用文件共享 / 62   2.4.3 使用Messenger / 65   2.4.4 使用AIDL / 71   2.4.5 使用ContentProvider / 91   2.4.6 使用Socket / 103   2.5 Binder连接池 / 112   2.6 选用合适的IPC方式 / 121   第3章 View的事件体系 / 122   3.1 View基础知识 / 122   3.1.1 什么是View / 123   3.1.2 View的位置参数 / 123   3.1.3 MotionEvent和TouchSlop / 125   3.1.4 VelocityTracker、GestureDetector和Scroller / 126   3.2 View的滑动 / 129   3.2.1 使用scrollTo/scrollBy / 129   3.2.2 使用动画 / 131   3.2.3 改变布局参数 / 133   3.2.4 各种滑动方式的对比 / 133   3.3 弹性滑动 / 135   3.3.1 使用Scroller / 136   3.3.2 通过动画 / 138   3.3.3 使用延时策略 / 139   3.4 View的事件分发机制 / 140   3.4.1 点击事件的传递规则 / 140   3.4.2 事件分发的源码解析 / 144   3.5 View的滑动冲突 / 154   3.5.1 常见的滑动冲突场景 / 155   3.5.2 滑动冲突的处理规则 / 156   3.5.3 滑动冲突的解决方式 / 157   第4章 View的工作原理 / 174   4.1 初识ViewRoot和DecorView / 174   4.2 理解MeasureSpec / 177   4.2.1 MeasureSpec / 177   4.2.2 MeasureSpec和LayoutParams的对应关系 / 178   4.3 View的工作流程 / 183   4.3.1 measure过程 / 183   4.3.2 layout过程 / 193   4.3.3 draw过程 / 197   4.4 自定义View / 199   4.4.1 自定义View的分类 / 200   4.4.2 自定义View须知 / 201   4.4.3 自定义View示例 / 202   4.4.4 自定义View的思想 / 217   第5章 理解RemoteViews / 218   5.1 RemoteViews的应用 / 218   5.1.1 RemoteViews在通知栏上的应用 / 219   5.1.2 RemoteViews在桌面小部件上的应用 / 221   5.1.3 PendingIntent概述 / 228   5.2 RemoteViews的内部机制 / 230   5.3 RemoteViews的意义 / 239   第6章 Android的Drawable / 243   6.1 Drawable简介 / 243   6.2 Drawable的分类 / 244   6.2.1 BitmapDrawable / 244   6.2.2 ShapeDrawable / 247   6.2.3 LayerDrawable / 251   6.2.4 StateListDrawable / 253   6.2.5 LevelListDrawable / 255   6.2.6 TransitionDrawable / 256   6.2.7 InsetDrawable / 257   6.2.8 ScaleDrawable / 258   6.2.9 ClipDrawable / 260   6.3 自定义Drawable / 262   第7章 Android动画深入分析 / 265   7.1 View动画 / 265   7.1.1 View动画的种类 / 265   7.1.2 自定义View动画 / 270   7.1.3 帧动画 / 272   7.2 View动画的特殊使用场景 / 273   7.2.1 LayoutAnimation / 273   7.2.2 Activity的切换效果 / 275   7.3 属性动画 / 276   7.3.1 使用属性动画 / 276   7.3.2 理解插值器和估值器 / 280   7.3.3 属性动画的监听器 / 282   7.3.4 对任意属性做动画 / 282   7.3.5 属性动画的工作原理 / 288   7.4 使用动画的注意事项 / 292   第8章 理解Window和WindowManager / 294   8.1 Window和WindowManager / 294   8.2 Window的内部机制 / 297   8.2.1 Window的添加过程 / 298   8.2.2 Window的删除过程 / 301   8.2.3 Window的更新过程 / 303   8.3 Window的创建过程 / 304   8.3.1 Activity的Window创建过程 / 304   8.3.2 Dialog的Window创建过程 / 308   8.3.3 Toast的Window创建过程 / 311   第9章 四大组件的工作过程 / 316   9.1 四大组件的运行状态 / 316   9.2 Activity的工作过程 / 318   9.3 Service的工作过程 / 336   9.3.1 Service的启动过程 / 336   9.3.2 Service的绑定过程 / 344   9.4 BroadcastReceiver的工作过程 / 352   9.4.1 广播的注册过程 / 353   9.4.2 广播的发送和接收过程 / 356   9.5 ContentProvider的工作过程 / 362   第10章 Android的消息机制 / 372   10.1 Android的消息机制概述 / 373   10.2 Android的消息机制分析 / 375   10.2.1 ThreadLocal的工作原理 / 375   10.2.2 消息队列的工作原理 / 380   10.2.3 Looper的工作原理 / 383   10.2.4 Handler的工作原理 / 385   10.3 主线程的消息循环 / 389   第11章 Android的线程和线程池 / 391   11.1 主线程和子线程 / 392   11.2 Android的线程形态 / 392   11.2.1 AsyncTask / 392   11.2.2 AsyncTask的工作原理 / 395   11.2.3 HandlerThread / 402   11.2.4 IntentService / 403   11.3 Android的线程池 / 406   11.3.1 ThreadPoolExecutor / 407   11.3.2 线程池的分类 / 410   第12章 Bitmap的加载和Cache / 413   12.1 Bitmap的高效加载 / 414   12.2 Android的缓存策略 / 417   12.2.1 LruCache / 418   12.2.2 DiskLruCache / 419   12.2.3 ImageLoader的实现 / 424   12.3 ImageLoader的使用 / 441   12.3.1 照片墙效果 / 441   12.3.2 优化列表的卡顿现象 / 446   第13章 综合技术 / 448   13.1 使用CrashHandler来获取应用的crash信息 / 449   13.2 使用multidex来解决方法数越界 / 455   13.3 Android的动态加载技术 / 463   13.4 反编译初步 / 469   13.4.1 使用dex2jar和jd-gui反编译apk / 470   13.4.2 使用apktool对apk进行二次打包 / 470   第14章 JNI和NDK编程 / 473   14.1 JNI的开发流程 / 474   14.2 NDK的开发流程 / 478   14.3 JNI的数据类型和类型签名 / 484   14.4 JNI调用Java方法的流程 / 486   第15章 Android性能优化 / 489   15.1 Android的性能优化方法 / 490   15.1.1 布局优化 / 490   15.1.2 绘制优化 / 493   15.1.3 内存泄露优化 / 493   15.1.4 响应速度优化和ANR日志分析 / 496   15.1.5 ListView和Bitmap优化 / 501   15.1.6 线程优化 / 501   15.1.7 一些性能优化建议 / 501   15.2 内存泄露分析之MAT工具 / 502   15.3 提高程序的可维护性 / 506

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 112
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值