那么还是针对我们之前写的自定义控件:开关按钮为例来说,在之前的基础上,我们来看看有哪些属性是可以自定义的:按钮的背景图片,按钮的滑块图片,和按钮的状态(是开还是关),实际上都应该是可以在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自定义控件(3)-自定义当前按钮属性的更多相关文章

  1. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  2. Android自定义控件实现带有清除按钮的EditText

    首先声明我也是参考了别人的思路,只是稍微做了下修改,增加显示密码与隐藏密码,没有输入字符串时让EditText进行抖动,废话少说这里附上效果图 效果很赞有木有 那么怎么实现这种效果呢?那就跟着我一起来 ...

  3. Android自定义控件之自定义ViewGroup实现标签云

    前言: 前面几篇讲了自定义控件绘制原理Android自定义控件之基本原理(一),自定义属性Android自定义控件之自定义属性(二),自定义组合控件Android自定义控件之自定义组合控件(三),常言 ...

  4. Android自定义控件之自定义组合控件(三)

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  5. android 自定义控件 使用declare-styleable进行配置属性(源码角度)

          android自定义styleableattrs源码 最近在模仿今日头条,发现它的很多属性都是通过自定义控件并设定相关的配置属性进行配置,于是便查询了解了下declare-styleabl ...

  6. 【转】android 自定义控件 使用declare-styleable进行配置属性(源码角度)

    原文网址:http://blog.csdn.net/vipzjyno1/article/details/23696537 最近在模仿今日头条,发现它的很多属性都是通过自定义控件并设定相关的配置属性进行 ...

  7. [学习总结]8、android 自定义控件 使用declare-styleable进行配置属性(源码角度)

    declare-styleable:declare-styleable是给自定义控件添加自定义属性用的. 官方的相关内部控件的配置属性文档:http://developer.android.com/r ...

  8. android 自定义控件——(五)按钮点击变色

    ----------------------------------按钮点击变色(源代码下有属性解释)------------------------------------------------- ...

  9. 【Android UI】自定义带按钮的标题栏

    自定义标题栏在很多的android app中很常见,可以说是一种很有用的UI设计方法.自 己也本着学习的态度,经过一番各种坑,终于实现了,现总结如下: 一:大致流程 1.      对指定的andro ...

随机推荐

  1. bzoj1103: [POI2007]大都市meg

    题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=1103 题目大意:在经济全球化浪潮的影响下,习惯于漫步在清晨的乡间小路的邮递员Blue Ma ...

  2. GitHub项目大全

    [微信网页版]: [查看被删的微信好友]https://github.com/0x5e/wechat-deleted-friends [网页版微信API,包含终端版微信及微信机器人]https://g ...

  3. Queue 先进先出队列的操作

    1.Queue定义 System.Collections.Queue类表示对象的先进先出集合,存储在 Queue(队列) 中的对象在一端插入,从另一端移除. 2.优点 1.能对集合进行顺序处理(先进先 ...

  4. spring jdbc分离数据库代码和java代码

    读取配置文件类 package com.eshore.ismp.contract.sql; import java.io.FileInputStream; import java.io.FileNot ...

  5. hihocoder #1327

    传送门 时间限制:10000ms 单点时限:1000ms 内存限制:256MB 描述 给定一个只包含小写字母'a'-'z'的字符串 S ,你需要将 S 中的字符重新排序,使得任意两个相同的字符不连在一 ...

  6. 利用css3选择器及css3边框做出的特效(1)

    利用border-radius及box-shadow制作圆角表格 界面效果图如下: css样式如下所示: * { margin:; padding:; } body { padding: 40px 1 ...

  7. SQLite的WAL机制

    标注:本文部分有黏贴这里的资料,另外还加了一些自己的笔记 使用CoreData或者SQLite3的时候,我们创建的数据库, 在存储的文件夹中有三个文件:分别为:**.sqlite  **.sqlite ...

  8. 今天执行grep命令差点把服务器搞崩

    grep "rst" -r ./ >> a.log 今天执行这个命令差点把服务器搞崩了. 本意是查找所有源代码文件中含有rst字符串的行,打印到文件a.log中,然后进 ...

  9. Oracle创建表空间及用户

    1.sys dba登录 2.创建临时表空间 create temporary tablespace wf_temp tempfile 'C:\APP\ADMINISTRATOR\ORADATA\PLA ...

  10. Runner之记计帐项目的典型用户和用户场景

    项目任务:编写日历选择界面和查明细界面(查看某一天的具体收支出状况) 1.背景 ①典型用户 (1)姓名:张云 (2)年龄:17~23 (3)收入:家长给的生活费与自己兼职(1500元/月) (4)代表 ...