Android开发技巧——自定义控件之自定义属性

掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码

上一篇讲了如何通过xml把几个控件组织起来,并继承某个ViewGroup子类,把它们封装起来使用。这是我们接触到的最简单的一种自定制控件了。但许多时候,我们还需要在布局文件中使用它们的时候,能通过属性传入一些值,来影响最终的显示结果。

我们在做项目中经常会遇到的一个情况:一张图片加一个文本的组合。比如充值账户成功之后显示的一个界面,上面是一个表示成功的图标,下面是对应的说明文字:“充值成功”。或者是在登录界面的账户输入框中,输入框的左边需要显示一个表示账户的图标。如下所示:

图标文字组合的笨重实现

一开始对于这样的情况,我们可能会采用ImageViewTextView的方式。后来通过lint工具的提示,或者是其他的方式,你可能会知道TextView的几个属性drawableLeftdrawableRightdrawableTop以及drawableBottom可以做到。但是使用的时候,你会发现这几个属性设置进去的图片,是按其本身大小来显示的。
好像也没关系,让设计师切好图就是了。但是心里却是没底的。因为Android手机万万种,你公司的测试机却只有那两三个,也许换上某个大屏低分辨率的千元机,图标就被撑大了。所以你还是希望能设定图片的大小。

使用一个TextView实现

设定一个TextView的drawable大小,可以通过在java代码中调用drawable的setBounds(int left, int top, int right, int bottom)方法设置它的界限,然后调用setCompoundDrawables(Drawable left, Drawable top, Drawable right, Drawable bottom)方法把我们的drawable对象设置进去。
但是每次调用都要设置未免太麻烦,所以我们可以继承TextView写一个DrawableTextView,重写setCompoundDrawablesWithIntrinsicBounds(Drawable left, Drawable top, Drawable right, Drawable bottom)方法;或者是setCompoundDrawablesRelativeWithIntrinsicBounds(Drawable start, Drawable top, Drawable end, Drawable bottom),如果你是通过drawableStartdrawableEnd属性来设置图片的话。通过在这里对drawable进行setBounds(),也一样能达到上面的效果。
现在我们用一个TextView实现了上面TextView+ImageView的效果,达到了我们的目的:优化布局性能,简化布局代码。
但是对于这样的控件,我们可能在项目的多个地方用到,或者在其他项目也会用到。所以希望能把它做到更通用。这时我们就不能写死drawable的宽和高,而是希望能够在外部xml使用它时进行控制,也就是——自定义属性。

自定义属性

首先在res/values/attrs.xml文件中(如果没有请创建)定义一个declare-styleable,然后在里面定义两个属性,分别表示drawable的宽和高:

<declare-styleable name="DrawableTextView"><attr name="drawableHeight" format="dimension"/><attr name="drawableWidth" format="dimension"/>
</declare-styleable>

其中declare-styleable的名字name通常写为我们的控件的类名,这样在写布局代码中,Android Studio就可以给我们对应的属性提示了。
attr中定义的是我们的属性,name是属性的名字,format是属性值的格式,这里我们定义为dimension

属性是定义好了,也可以布局代码中使用了。但是我们还需要在我们的自定义的DrawableTextView中读取属性。首先重写构造方法:

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

然后在里面调用我们自定义的applyAttributes()方法,在里面我们会读取到属性。我们需要在我们的类中声明两个属性:

    private int mDrawableWidth;private int mDrawableHeight;

然后在applyAttributes()方法中,通过TypedArray获取属性值:

        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.DrawableTextView);mDrawableWidth = typedArray.getDimensionPixelSize(R.styleable.DrawableTextView_drawableWidth, 0);mDrawableHeight = typedArray.getDimensionPixelSize(R.styleable.DrawableTextView_drawableHeight, 0);typedArray.recycle();

这里说明一下,attrs是我们在xml中定义的相关联的属性集。通过调用context.obtainStyledAttributes(attrs, R.styleable.DrawableButton);,我们能够接收到在Context所对应的theme中我们的样式属性,也就是这里的属性值我们也可以通过在theme中来设置。关于通过在theme中指定属性值,后续的博客会再详细说明,这里暂略不谈。第二个参数传的是我们所定义的declare-styleable
得到TypedArray对象之后,通过它里面的getXXX方法我们可以得到各个属性的值,比如通过getDimensionPixelSize(int index, int defValue)方法得到属性尺寸对应的像素大小。第一个参数是属性的索引,第二个是默认值。在获取完属性之后,必须调用typedArray.recycle()对它进行回收,以便后面的调用者可以复用。因为Resources对象中会缓存一些TypedArray的数据,TypedArray使用的时候会从中获取,以优化内存利用。

拿到属性值并赋给我们的成员变量之后,我们就可以在后面的方法进行设置了。由于本篇主要讲自定义属性,关于该项目的后续实现,可以参考我在Github上的项目 DrawableWidget。

上面的例子中,关于在attrs定义属性其实是存在着问题的。因为我们直接就定义了drawableWidthdrawableHeight属性,如果我有一个项目使用到它,并且同时使用了另外一个库,那个库也定义了这两个属性的话,就会产生冲突了。所以要强调一点:对于自定义属性,必须加上我们自己的前缀。比如把属性定义为nbDrawableWidth等等。(我那个项目在写的时候还不成熟,而且自己有些懒,没遇到问题总是不想改,暂且留作反例吧。)

共用属性

还有一种情况。我们在布局里会经常遇到这样一种情况:

在布局里面的每一个条目,会用分割线分开。而这些条件还会分成几组,组里面的分割线是有左边距的。这样的情况很好实现,重写这个layout,在onDraw里面画线就可以了。我们把右边的每一个item看成是:上面一条满的分割线或者是不带分割线,下面是一条带边距的分割线,或者是满的分割线。这样我们就可以定义一个pwBorder属性,里面定义<flag name="TOP" value="1"/>``<flag name="BOTTOM" value="2"/>表示标记位。然后在代码里获取pwBorder的值,与1做&操作,大于0就表示要画上面的分割线,否则不画,再和2做&操作,大于0就表示画完整的分割线,否则画带边距的分割线。如果需要左分割线或右分割线时,还可以再定义值为4或者8的标志位。
在xml引用它时,属性是这样的:

app:pwBorder="TOP|BOTTOM"

但是,可能除了这个RelativeLayout需要这种属性之外,我们在写的其他控件,可能也要包括这个属性以及其他属性。这时我们可以把attr的定义抽出来,写在declare-styleable节点外面。如下:

    <!--带边框的Layout属性--><attr name="pwBorder"><flag name="TOP" value="1"/><flag name="BOTTOM" value="2"/></attr><attr name="pwBorderWidth" format="dimension"/><attr name="pwBorderColor" format="color"/>

然后再这样使用:

    <declare-styleable name="BorderLinearLayout"><attr name="pwBorder"/><attr name="pwBorderWidth"/><attr name="pwBorderColor"/></declare-styleable><declare-styleable name="TextFieldView"><attr name="pwBorder"/><attr name="pwBorderWidth"/><attr name="pwBorderColor"/><attr name="pwLabel"/><attr name="pwValue" format="string"/><attr name="pwIcon" format="reference"/></declare-styleable>

我建议使用这种方式来定义属性。因为这样当我们的属性与其他库的属性正好重名时,如果定义的format相同,也是可以被合并,并且通过编译的。

本文原创,转载请注明出处:http://blog.csdn.net/maosidiaoxian/article/details/50009725

Android开发技巧——自定义控件之自定义属性相关推荐

  1. Android开发技巧——自定义控件之组合控件

    Android开发技巧--自定义控件之组合控件 我准备在接下来一段时间,写一系列有关Android自定义控件的博客,包括如何进行各种自定义,并分享一下我所知道的其中的技巧,注意点等. 还是那句老话,尽 ...

  2. Android开发技巧——大图裁剪

    本篇内容是接上篇<Android开发技巧--定制仿微信图片裁剪控件> 的,先简单介绍对上篇所封装的裁剪控件的使用,再详细说明如何使用它进行大图裁剪,包括对旋转图片的裁剪. 裁剪控件的简单使 ...

  3. 一些很不错的Android开发技巧

    一些很不错的Android开发技巧,这个项目翻译自 android-tips-tricks 去掉了一些我认为不重要的,对我使用过的东东做了评价,同时翻译了一些自己没有注意到的知识点的文章. ❤️ st ...

  4. android开发技巧杂谈

    android开发技巧一 android的一些常用包是发布在国外的,所以一些包,我们下载不下来,我们可以使用阿里云的镜像地址(maven { url 'https://maven.aliyun.com ...

  5. Android开发之自定义控件——直尺

    Android开发之自定义控件--直尺 本人的第一篇技术博客,希望能够在分享的路上走的更远. 先上效果图: 涉及到的知识点: - Paint类 - Cancas类 - Path类 * 由于功能简单,此 ...

  6. 社区说|常用 Android 开发技巧

    活动时间 4月7日(本周四) 20:00-21:00 活动日程 20:00-20:45 主题分享 常用 Android 开发技巧 李老师的开发技巧私房菜,一定有你没吃过的菜! 重构技巧 常用插件 阅读 ...

  7. 移动周刊第 182 期:谈 Android 开发技巧、 iOS 系统框架实践

    写在前面 移动周刊第 182 期如约而至.如果你有好的文章以及优化建议,请发送邮件至mobilehub@csdn.net,在技术探索的道路上我们共同进步. YouTube 推出 VR 视频和 360 ...

  8. Android开发技巧!Android开发大佬的百度,美团,快手等大厂Offer收割之旅,附超全教程文档

    想要成为一名优秀的Android开发,你需要一份完备的知识体系,在这里,让我们一起成长为自己所想的那样. 本文参考了目前大部分 Android 应用启动优化的方案,将大家的方案做一个汇总,如果你有这方 ...

  9. Android开发技巧:我的菜单我做主

    本文截选自<Android开发权威指南> Android SDK本身提供了一种默认创建菜单的机制.但通过这种机制创建的菜单虽然从功能上很完备,但在界面效果上实在是有点"土&quo ...

最新文章

  1. Cell子刊:特异靶向病原菌致病力的植物天然产物的作用机制
  2. Android跳转intent简单教程
  3. Visual Studio 2013开发 mini-filter driver step by step 应用层与内核通讯(8)
  4. linux终端中如何对目录压缩,软网推荐:Linux中使用命令行查看压缩文档
  5. ASP.Net Core MVC 发生二次请求
  6. 深度学习:正则化方法
  7. 渗透测试工具——BurpSuite
  8. html中让页面点击向左滑动,HTML5页面点击和左右滑动页面滚动详解
  9. magick++配置
  10. 如何使用 VNC 远程访问树莓派
  11. 华泰证券 python 自动交易_tushare开源股票交易接口基于python实现技术
  12. 1688API详情接口调用展示
  13. C++ 内存管理 —— 第一講:C++ 內存構件
  14. JAVA的安装与卸载
  15. 史蒂夫·乔布斯逝世 盘点生平经历
  16. jzxx1783小高考
  17. 西门子触摸屏通讯连接及下装注意事项
  18. was loaded over HTTPS, but requested an insecure错误解决
  19. iOS接入开屏广告教程 : 以腾讯优量汇为案例(适配iOS15)
  20. idea使用jdk17时MyBatisPlus报module java.base does not “opens java.lang.reflect“ to unnamed module

热门文章

  1. Delphi DLL制作和加载 Static, Dynamic, Delayed 以及 Shared-Memory Manager
  2. 在Latex使用條列式清單itemize , enumerate , description
  3. 为了你,我一定要写诗
  4. Linux+apache+svn
  5. JVM调优: 转载JVM调优总结
  6. Python + OpenCV 太好玩了,可惜你可能还不会
  7. kvm服务器中心管理,IP KVM如何在公共场所数据中心合理应用
  8. centos php7.0 mysql_CentOS 7.3 下 安装LNMP(Nginx1.10+MySQL5.7+PHP7.0.20)
  9. mysql xtrabackup 主从_使用 Xtrabackup 在线对MySQL做主从复制
  10. SpringBoot启动流程是怎样的