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

简介: Android开发技巧——自定义控件之自定义属性掌握自定义控件是很重要的,因为通过自定义控件,能够:解决UI问题,优化布局性能,简化布局代码。上一篇讲了如何通过xml把几个控件组织起来,并继承某个ViewGroup子类,把它们封装起来使用。

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

目录
相关文章
|
3天前
|
开发框架 前端开发 Android开发
安卓与iOS开发中的跨平台策略
在移动应用开发的战场上,安卓和iOS两大阵营各据一方。随着技术的演进,跨平台开发框架成为开发者的新宠,旨在实现一次编码、多平台部署的梦想。本文将探讨跨平台开发的优势与挑战,并分享实用的开发技巧,帮助开发者在安卓和iOS的世界中游刃有余。
|
8天前
|
搜索推荐 Android开发 开发者
探索安卓开发中的自定义视图:打造个性化UI组件
【10月更文挑战第39天】在安卓开发的世界中,自定义视图是实现独特界面设计的关键。本文将引导你理解自定义视图的概念、创建流程,以及如何通过它们增强应用的用户体验。我们将从基础出发,逐步深入,最终让你能够自信地设计和实现专属的UI组件。
|
10天前
|
Android开发 Swift iOS开发
探索安卓与iOS开发的差异和挑战
【10月更文挑战第37天】在移动应用开发的广阔舞台上,安卓和iOS这两大操作系统扮演着主角。它们各自拥有独特的特性、优势以及面临的开发挑战。本文将深入探讨这两个平台在开发过程中的主要差异,从编程语言到用户界面设计,再到市场分布的不同影响,旨在为开发者提供一个全面的视角,帮助他们更好地理解并应对在不同平台上进行应用开发时可能遇到的难题和机遇。
|
12天前
|
XML 存储 Java
探索安卓开发之旅:从新手到专家
【10月更文挑战第35天】在数字化时代,安卓应用的开发成为了一个热门话题。本文旨在通过浅显易懂的语言,带领初学者了解安卓开发的基础知识,同时为有一定经验的开发者提供进阶技巧。我们将一起探讨如何从零开始构建第一个安卓应用,并逐步深入到性能优化和高级功能的实现。无论你是编程新手还是希望提升技能的开发者,这篇文章都将为你提供有价值的指导和灵感。
|
10天前
|
存储 API 开发工具
探索安卓开发:从基础到进阶
【10月更文挑战第37天】在这篇文章中,我们将一起探索安卓开发的奥秘。无论你是初学者还是有经验的开发者,这篇文章都将为你提供有价值的信息和建议。我们将从安卓开发的基础开始,逐步深入到更复杂的主题,如自定义组件、性能优化等。最后,我们将通过一个代码示例来展示如何实现一个简单的安卓应用。让我们一起开始吧!
|
11天前
|
存储 XML JSON
探索安卓开发:从新手到专家的旅程
【10月更文挑战第36天】在这篇文章中,我们将一起踏上一段激动人心的旅程,从零基础开始,逐步深入安卓开发的奥秘。无论你是编程新手,还是希望扩展技能的老手,这里都有适合你的知识宝藏等待发掘。通过实际的代码示例和深入浅出的解释,我们将解锁安卓开发的关键技能,让你能够构建自己的应用程序,甚至贡献于开源社区。准备好了吗?让我们开始吧!
23 2
|
12天前
|
Android开发
布谷语音软件开发:android端语音软件搭建开发教程
语音软件搭建android端语音软件开发教程!
|
20天前
|
编解码 Java Android开发
通义灵码:在安卓开发中提升工作效率的真实应用案例
本文介绍了通义灵码在安卓开发中的应用。作为一名97年的聋人开发者,我在2024年Google Gemma竞赛中获得了冠军,拿下了很多项目竞赛奖励,通义灵码成为我的得力助手。文章详细展示了如何安装通义灵码插件,并通过多个实例说明其在适配国际语言、多种分辨率、业务逻辑开发和编程语言转换等方面的应用,显著提高了开发效率和准确性。
|
17天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
19天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
28 5
下一篇
无影云桌面