android中提供了非常方便的xml定义界面的方式,但是有时候我们为了实现一些效果或者是对一些界面进行封装,我们还是需要去自定义界面。好在android中也提供了比较方面的自定义界面方式。在自定义界面的时候,可能觉得官方提供的属性不够用或者是不方面用在自己定义的界面中,那么我们也可以自己去定义属于自己的自定义属性,接下来我们就去看看怎么自定义属性。
自定义属性,我们需要在values文件夹下新建一个attrs.xml文件,我们自定义的属性就放在该文件中。
attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Custome_Attrs">
<attr name="custome_font_color" format="color|reference" />
<attr name="custome_font_size" format="dimension" />
<attr name="custome_text" format="string|reference" />
<attr name="custome_ico" format="reference" />
</declare-styleable>
</resources>
在attrs.xml中,自定义的属性要放在<declare-styleable>节点内。<declare-styleable>的name属性是必填的,因为后面我们需要通过这个name去获取我们自定义的属性的值。在<declare-styleable>内的一个个<attr>节点就是我们定义的属性了,name属性是属性的名称,format是属性的格式,如果可以有多种格式的话,使用“|”去分割,比如一个属性可以直接设置一个颜色值,也可以设置一个颜色的资源id,就是通过format="color|reference"来实现的。
其中format共有以下9种:
reference | 参考某一资源ID |
color | 颜色值 |
boolean | 布尔值 |
dimension | 尺寸值 |
float | 浮点值 |
integer | 整型值 |
fraction | 百分数 |
enum | 枚举值 |
string | 字符串 |
reference,参考资源的id,我们在布局xml使用方式如:@string/hollow_world,其值为资源id,我们自定义颜色,字符串和图片属性的时候,基本上都需要使用该格式;color,颜色值,使用方式就是如:android:background="#FFFFFF";dimension,尺寸值,使用方法如:android:text_size="18sp",可以使用px、sp和dp。剩下的几个就不一一说明了。
接下来来看下如何在代码中接受我们自定义的属性的值,下面是自定义控件的代码:
CustomerViewAttrs.java
package com.hero.customeattrsdemo;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import java.util.zip.Inflater;
/**
* Created by Hero on 2014/11/3.
*/
public class CustomerViewAttrs extends LinearLayout{
private int customeFontColor;
private float customFontSize;
private String customeText;
private Drawable ico;
private ImageView mImageView;
private TextView mTextView;
public CustomerViewAttrs(Context context) {
super(context);
initView();
initDate();
}
public CustomerViewAttrs(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Custome_Attrs);
int resourceId;
//这里获取custome_font_color属性的值,设置默认值为-1;
customeFontColor = a.getColor(R.styleable.Custome_Attrs_custome_font_color, -1);
if(customeFontColor == -1){
//这里获取custom_font_color的资源id,再给他设置一个默认值,为黑色
//这里获取到的resourceId为资源id,即为R文件中的值,我们可以通过这个资源id去获取相应的值
resourceId = a.getResourceId(R.styleable.Custome_Attrs_custome_font_color, android.R.color.black);
//通过资源地获取颜色值
customeFontColor = getResources().getColor(resourceId);
}
//获取custome_font_size的像素值
customFontSize = a.getDimensionPixelSize(R.styleable.Custome_Attrs_custome_font_size, -1);
if(customFontSize == -1){
//获取custome_font_size的dp值
customFontSize = a.getDimension(R.styleable.Custome_Attrs_custome_font_size, 18);
}
//获取custom_font_text字符串值
customeText = a.getString(R.styleable.Custome_Attrs_custome_text);
if (customeText == null){
//字符串的资源id,如果字符的资源id为-1,则说明没有设置该属性的值
resourceId = a.getResourceId(R.styleable.Custome_Attrs_custome_text, -1);
if (resourceId != -1){
customeText = getResources().getString(resourceId);
}
}
//获取ico
ico = a.getDrawable(R.styleable.Custome_Attrs_custome_ico);
a.recycle(); //千万别忘记调用该方法了
initView();
initDate();
}
public void initView(){
inflate(getContext(), R.layout.customer_attr_view, this);
mImageView = (ImageView) findViewById(R.id.ico);
mTextView = (TextView) findViewById(R.id.txt);
setOrientation(HORIZONTAL);
}
public void initDate(){
if (ico != null){
mImageView.setImageDrawable(ico);
}
mTextView.setTextColor(customeFontColor);
mTextView.setTextSize(customFontSize);
if(customeText != null){
mTextView.setText(customeText);
}
}
}
其中最主要的就是public CustomerViewAttrs(Context context, AttributeSet attrs)方法了。在该方法中,通过
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.Custome_Attrs);
去获取属性的集合。TypedArray中,可以通过getColor()、getDraw()、getInteger()、getBoolean()、getFloat()、getResourceId()等方法去获取我们自定义的属性值,其中我们拿获取custome_font_color属性来举例。
因为custome_font_color属性的format为“color|reference”,所以在给custome_font_color属性赋值的时候可以使用custome_font_color=“#FFFFFF”这样,直接给他一个颜色值,同时也可以使用custome_font_color=“@color/whilte”这样给他赋一个颜色的资源id。那么我们获取他的值的时候,要将2中情况都考虑到,所以
customeFontColor = a.getColor(R.styleable.Custome_Attrs_custome_font_color, -1);
先通过getColor()方法获取前面一种直接设置的颜色值,并给他一个如果没有取到值时的默认值。如果是通过custome_font_color=“#FFFFFF”方式赋值的,那么通过这一步我们可以获取到“#FFFFFF”的color值,如果是通过custome_font_color=“@color/whilte”赋值,他的值会是我们设置的默认值-1;
if(customeFontColor == -1){
//这里获取custom_font_color的资源id,再给他设置一个默认值,为黑色
//这里获取到的resourceId为资源id,即为R文件中的值,我们可以通过这个资源id去获取相应的值
resourceId = a.getResourceId(R.styleable.Custome_Attrs_custome_font_color, android.R.color.black);
//通过资源地获取颜色值
customeFontColor = getResources().getColor(resourceId);
}
如果customeFontColor的值为-1,那么就说明是通过custome_font_color=“@color/whilte”方式赋的值,然后我们在通过
resourceId = a.getResourceId(R.styleable.Custome_Attrs_custome_font_color, android.R.color.black);
去获取颜色的资源id,并给他一个默认的颜色资源id,防止没有设置该属性时,获取不到值。获取资源Id后,通过Resources的getColor()方法获取颜色值。
customeFontColor = getResources().getColor(resourceId);
这样不论custome_font_color是通过那种方式赋值,我们就都可以获取到他的值了。其他的几个属性也是如此,同样也可以通过这种方法类取值,只是获取具体值的时候,可能不在是getColor()方法了,而是getString()、getDraw()等方法。取完值后,别忘了调用调用:
a.recycle();
去将资源给释放掉。接下来我们就可以在布局文件中使用我们自定义的视图和属性了。
fragment_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
tools:context=".MainActivity$PlaceholderFragment">
<com.hero.customeattrsdemo.CustomerViewAttrs
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:custome_font_color="@android:color/holo_orange_dark"
app:custome_font_size="25dp"
app:custome_text="@string/hello_world"
app:custome_ico="@drawable/ic_launcher"/>
</RelativeLayout>
其中有一点需要注意的是我们要引入我们自定义的属性,需要加入
xmlns:app="http://schemas.android.com/apk/res-auto"
其中,app是前缀,我们可以任意设置,不过设置成什么,我们就要在后面使用什么,比如app:custome_font_color="@android:color/holo_orange_dark"。后面的http://schemas.android.com/apk/res-auto也可以写成http://schemas.android.com/apk/res/+包名(AndroidManiFest.xml中的package值),不过在lib项目中使用自定义的属性的话,需要使用http://schemas.android.com/apk/res-auto,否则回报:Error:(12) No resource identifier found for attribute '属性名' in package 包名' 错误。
好了,就到这里吧,再说一句吧,今天写这边文章的主要原因就是因为最后面一句话,今天在公司,在lib项目中自定义界面,在通过
xmlns:app="http://schemas.android.com/apk/res/+包名"
时,就是包No resource identifier found for attribute '属性名' in package 包名'的错误。然后又去查找原因,找来找去,也没找到问题,又查看以前写的自定义界面代码,发现就是这么用的啊,xmlns:app="http://schemas.android.com/apk/res/+包名"这么用的啊,最后实在无法,只能求助于公司大牛,公司大牛看后,淡定的指出那个应用用错了,要使用
xmlns:app="http://schemas.android.com/apk/res-auto"
果然修改之后,就不再报错了。所以今天特意写下这边文章来记录一下,省得以后再遇到同样的问题。
源码:链接: http://pan.baidu.com/s/1bnhbpwF 密码: 1cd5