那么还是针对我们之前写的自定义控件:开关按钮为例来说,在之前的基础上,我们来看看有哪些属性是可以自定义的:按钮的背景图片,按钮的滑块图片,和按钮的状态(是开还是关),实际上都应该是可以在xml文件中直接定义的。

不妨先来看看之前我们在代码中不依靠自定义属性的时候,是如何写的,我们可以在initView方法中找到这样几行代码:

backgroundBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_background); slideButton = BitmapFactory.decodeResource(getResources(),
R.drawable.slide_button); currentState=false;

会发现,我们是直接引用的资源文件,而不是在布局xml文件中使用的定义属性的方式,下面我们一步步来看看要怎么样做才可以定义使用上自定义属性:

第一步:在res/values文件夹中添加attrs.xml文件

实际上,这个文件名并不一定要写成attrs.xml,但是按照android源码的写法并且也便于别人查看代码的时候明确这个文件的用意,还是写成attrs.xml。

下面要如何写呢,我们还是可以参看一下安卓的源码:打开源码文件夹下\frameworks\base\core\res\res\values\attrs.xml文件,我们会发现这里面定义了很多attr的标签,里面不乏一些我们常见的属性:

<attr name="layout_width" format="dimension">

等等,在layout_width这个标签上面我们还可以发现一个

<declare-styleable name="ViewGroup_Layout">

declare-styleable标签的里包含了很多根ViewGruop相关的属性。

而在这个attrs.xml文件的最外面,是一个<resources>的标签

到这里,我们基本上就明白了一个attrs.xml文件的结构了:

首先要一个<resources>的父标签,然后里面可以包含一个declare-styleable的标签,在这个标签里面我们再定义出三个attr 标签,分别代表我们需要定义的三个属性:按钮的背景图片,按钮的滑块图片,和按钮的状态;那么剩下的一个问题就是attr标签中的format代表什么意思。实际上format代表的是这条属性的值的类型:

1.reference:参考指定Theme中资源ID,这个类型意思就是你传的值可以是引用资源  
     2.string:字符串,如果你想别人既能直接写值也可以用类似"@string/test"引用资源的方式,可以写成format="string|reference"  
     3.Color:颜色  
     4.boolean:布尔值  
     5.dimension:尺寸值  
     6.float:浮点型  
     7.integer:整型  
     8.fraction:百分数  
     9.enum:枚举 ,如果你提供的属性只能让别人选择,不能随便传入,就可以写成这样  
        <attr name="language">  
                <enum name="china" value="1"/>  
                <enum name="English" value="2"/>  
        </attr>

10.flag:位或运算

declare-styleable子元素:  
  
定义一个styleable对象,每个styleable对象就是一组attr属性的集合 注意:这里的name属性并不是一定要和自定义类名相同,只是为了好区分对应类的属性而已  
  
  
注意:上面的属性资源文件定义了该属性之后,至于到底是哪个自定义View组件中来使用该属性,该属性到底能发挥什么作用, 就不归该属性资源文件管了,也就是说这个属性资源文件是个公共的,大家都可以用,但是为了方便管理,一般都是一个自定义View里的属性写成一个declare-styleable集合.属性资源所定义的属性到底可以返回什么作用,取决于自定义组件的代码实现

在这里,我们的attrs.xml文件写成下面这样:

<?xml version="1.0" encoding="utf-8"?>
<resources> <declare-styleable name="DJSwitch"> <attr name="current_state" format="boolean"/>
<attr name="backgroundImage" format="reference"/>
<attr name="slideImage" format="reference"/> </declare-styleable> </resources>

第二步:在自定义控件的类中拿到attrs.xml文件中定义的属性的对应的值,然后赋值给我们需要设置的变量,在这里就是 背景图片,滑块图片和开关状态 这三个值。

要如何做呢?我们先将上面给出的

backgroundBitmap = BitmapFactory.decodeResource(getResources(),
R.drawable.switch_background); slideButton = BitmapFactory.decodeResource(getResources(),
R.drawable.slide_button); currentState=false;

这三句注释掉,然后换成下面的代码

/**
* 初始化View
*/
private void initView(Context context, AttributeSet attrs) {
/*
* 从资源库中加载一个image对象
* ios UIImage *backgroundImage = [UIImage imageNamed:@"app_switch_bg"];
* 也就是说,android里面的图像实体bitmap,可以当成是ios中的UIImage
* 区别在于,ios中UIImage可以通过类方法来获取,android里面则是通过工厂方法来实现
*/
/*switch_bg_img = BitmapFactory.decodeResource(getResources(), R.drawable.app_switch_bg);
switch_btn_img = BitmapFactory.decodeResource(getResources(), R.drawable.app_switch_btn);
*/ // 可以把这个TypedArray看成是一个包含了DJSwitch属性集合的HashMap
TypedArray typedArray = context.obtainStyledAttributes(attrs,R.styleable.DJSwitch);
// 获取该集合中共有多少个index
int indexCount = typedArray.getIndexCount(); for (int i = 0; i < indexCount; i++) {
int id = typedArray.getIndex(i);
switch (id) {
case R.styleable.DJSwitch_backgroundImage:
switch_bg_img = ((BitmapDrawable)typedArray.getDrawable(id)).getBitmap();
break;
case R.styleable.DJSwitch_slideImage:
switch_btn_img = ((BitmapDrawable)typedArray.getDrawable(id)).getBitmap();
break;
case R.styleable.DJSwitch_current_state:
currentState = typedArray.getBoolean(id, false);
break;
default:
break;
}
} switchBtnMaxSlideDistance = switch_bg_img.getWidth() - switch_btn_img.getWidth();
if (currentState) {
switchBtnX = switchBtnMaxSlideDistance;
} else {
switchBtnX = 0;
}
typedArray.recycle();
// 添加监听
setOnClickListener(new MyOnSwitchClickListener()); // 可以理解为ios中的addTarget方法,或addGestureRecognizer
}

注释上面已经写得很清楚了,TypedArray会把XML文件所引用的自定义属性和值保存在一个Map表中,因此我们可以根据该Map的键(即:属性的ID)取出该属性对应的值。

第三步:在布局文件中使用自定义属性,并设置属性值:

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:alex="http://schemas.android.com/apk/res/com.example.test"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
> <!-- 为了清楚的看见该View的大小及位置,给其定义背景 -->
<com.example.test.view.DJSwitch
android:id="@+id/sw_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff0000"
android:layout_centerInParent="true"
alex:current_state="false"
alex:backgroundImage="@drawable/app_switch_bg"
alex:slideImage="@drawable/app_switch_btn"
/> </RelativeLayout>

在上面的代码,我们发现我们写成了alex:这样的标头,这个实际上是命名空间的简写(默认是app),所以我们必须要添加一个命名空间,参看一下Android的命名空间是如何写的:

xmlns:android="http://schemas.android.com/apk/res/android"

在这里xmlns:android里面的android,是可以变化的,这里我们就改为alex,然后对于http://schemas.android.com/apk/res/android这个部分,最后的android也要改的,这里必须改成整个应用的包名,我们可以去清单文件中查找,这里是com.example.togglebutton,所以整个写下来就是:xmlns:alex="http://schemas.android.com/apk/res/com.example.test"

于是这里一个完整的布局文件写为:

<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:alex="http://schemas.android.com/apk/res/com.example.test"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
> <!-- 为了清楚的看见该View的大小及位置,给其定义背景 -->
<com.example.test.view.DJSwitch
android:id="@+id/sw_switch"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="#ff0000"
android:layout_centerInParent="true"
alex:current_state="false"
alex:backgroundImage="@drawable/app_switch_bg"
alex:slideImage="@drawable/app_switch_btn"
/> </RelativeLayout>

至此,在XML自定义按钮属性的基本步骤已经全部完毕了。不过如果我们想同时在代码中设置该按钮的属性该如何做呢?,很简单,在代码中为这三个自定义的按钮属性分别建立一个成员变量。

DJSwitch.java

/**
* 标识当前按钮状态
* 类似于iOS中.h文件里声明property 为BOOL类型的属性
*/
private boolean currentState;
/** 滑动按钮背景 */
private Bitmap switch_bg_img;
/** 滑动按钮滑块 */
private Bitmap switch_btn_img;

同时提供三个接口方法

/**
* 设置当前按钮开关状态
* @param isOpen true: 开,false: 关
*/
public void setCurrentSwitchState(boolean isOpen) {
this.currentState = isOpen;
if (isOpen) {
switchBtnX = switchBtnMaxSlideDistance;
} else {
switchBtnX = 0;
}
invalidate();
} /**
* 设置当前按钮背景图片
* @param resId 资源ID
*/
public void setBackgroundImage(int resId) {
switch_bg_img = BitmapFactory.decodeResource(getResources(), resId);
// 因背景图片更改,须同时更新滑块最大移动距离
switchBtnMaxSlideDistance = switch_bg_img.getWidth() - switch_btn_img.getWidth();
invalidate(); // 相当于iOS中setNeedDisplay方法
} /**
* 设置滑动按钮图片
* @param resId 资源ID
*/
public void setSlideBtnImage(int resId) {
switch_btn_img = BitmapFactory.decodeResource(getResources(), resId);
switchBtnMaxSlideDistance = switch_bg_img.getWidth() - switch_btn_img.getWidth();
invalidate();
}

MainActivity.java

package com.example.test;

import com.example.test.view.DJSwitch;

import android.app.Activity;
import android.os.Bundle; public class MainActivity extends Activity { private DJSwitch sw_switch; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initUIView();
} private void initUIView() {
sw_switch = (DJSwitch) findViewById(R.id.sw_switch);
sw_switch.setCurrentSwitchState(true);
sw_switch.setSlideBtnImage(R.drawable.ic_launcher);
} }

最终效果:

android自定义控件(1)-自定义控件属性的更多相关文章

  1. Android开发技巧——自定义控件之增加状态

    Android开发技巧--自定义控件之增加状态 题外话 这篇本该是上周四或上周五写的,无奈太久没写博客,前几段把我的兴头都用完了,就一拖再拖,直到今天.不想把这篇拖到下个月,所以还是先硬着头皮写了. ...

  2. Android开发技巧——自定义控件之使用style

    Android开发技巧--自定义控件之使用style 回顾 在上一篇<Android开发技巧--自定义控件之自定义属性>中,我讲到了如何定义属性以及在自定义控件中获取这些属性的值,也提到了 ...

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

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

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

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

  5. 荐 android 如何打包自定义控件(转)

    荐 android 如何打包自定义控件(转)   目录[-] 方式一:将项目打包成jar包 方式二:项目作为一个library 设计自定义的控件对android开发人员来说,是家常便饭了,但是多次做项 ...

  6. C#中的自定义控件中的属性、事件及一些相关特性的总结(转)

      摘要: C#中的自定义控件中的属性(Property).事件(Event)及一些相关特性(Attribute)的总结 今天学习了下C#用户控件开发添加自定义属性的事件,主要参考了MSDN,总结并实 ...

  7. android中xml tools属性详解

    第一部分 安卓开发中,在写布局代码的时候,ide可以看到布局的预览效果. 但是有些效果则必须在运行之后才能看见,比如这种情况:TextView在xml中没有设置任何字符,而是在activity中设置了 ...

  8. Android读取自定义View属性

    Android读取自定义View属性 attrs.xml : <?xml version="1.0" encoding="utf-8"?> < ...

  9. android中xmlns:tools属性详解

    今天读到一篇总结的非常棒的文章,写的逻辑很清晰也很实用,很少见到如此棒的文章了.就原文转发过来,我把格式给整理了一下,分享给园子里的各位朋友!好久没写博客了,就为2015年的11月留份纪念吧.希望对你 ...

  10. Mono for Android布局控件属性小结

    1. layout_weight 用于给一个线性布局中的诸多视图的重要度赋值. 所有的视图都有一个layout_weight值,默认为零,意思是需要显示 多大的视图就占据多大的屏幕空 间.若赋一个高于 ...

随机推荐

  1. Python学习(三)流程控制

    Python流程控制 本章介绍 python 的基础流程控制.包括 if 条件语句.for 循环 和 while 循环语句: continue 及 break 的用法等. 基本用法与 C 和 Java ...

  2. 如何设置ESXi中的虚拟机随主机一同启动?

    笔者新装了几台ESXi的主机, 其中一台上面运行着一台安装了vCenter的虚拟机.  笔者一路默认, 也没改什么设置. 在试图解决其他问题的过程中, 笔者重启了ESXi. 后来发现vCente登不进 ...

  3. 当Windows Server 2012的主DC出了问题, 如何迁移其上的FSMO角色?

    步骤如下: 1. 遵循https://support.microsoft.com/kb/255504中的transfer FSMO的步骤. 2. 这样做之后还没完. 因为Windows Server ...

  4. SQL IN

    here are some additional clause in the SQL language that can be used to simplify queries by decrease ...

  5. java设计模式5--原型模式(Prototype)

    本文地址:http://www.cnblogs.com/archimedes/p/java-prototype-pattern.html,转载请注明源地址. 原型模式 用原型实例指定创建对象的种类,并 ...

  6. KVM&amp;Libvirt基本概念及开发杂谈

    导读 大家好,本次肖力分享的主题是KVM&Libvirt基本概念及开发杂谈,内容有些凌乱松散,主要基于自己早期整理的笔记内容和实践感悟,有些内容难免有失偏颇,望见谅.前面先介绍下需要了解的基本 ...

  7. RS布局问题之块的不完美之完美

    早上一来,便传来喜讯...说我们做的报表太美.客户不敢看----于是便开启征程,亲自尝试了一把,如下面的操作,首次运行报表,在不考虑UI美观度的情况下,报表还是 在预测范围内显示的 那么接下来我们选择 ...

  8. Cognos清除本地高速缓存的利与弊

    场景:在开发报表初期,往往我们遇到过这种问题,我们手工修改了DB中的测试数据,但是返回报表看,数据还没有更新,难道是设计出问题了?NO,不要慌,这是因为Cognos为了查询效率设计了高速缓存的选项. ...

  9. Android Service+Socket 联网交互

    android中,联网操作有http连接和socket连接两大类.由于项目需要,我们采取的是Socket连接.鉴于平时连接频繁,因此把Socket连接放到Service里,需要从服务器端获取数据时,只 ...

  10. 微信小程序 - 五星评分(含半分)

    转载自:http://blog.csdn.net/column/details/13721.html     演示:     下载:小程序-星级评论.zip