主要有三种方法可以实现自定义属性。
方法一:不使用命名空间,不使用attrs.xml文件。通过attrs.getAttributeResourceValue方法拿到属性值
方法二: 使用命名空间, 不使用attrs.xml文件。通过attrs.getAttributeResourceValue方法拿到属性值
方法三: 使用命名空间, 使用attrs.xml文件。通过context.obtainStyledAttributes(attrs,R.styleable.ImageTextView).getString()方法拿到属性值
第一种方法使用最简单,但获取的属性值多为字符串不能获取各种类型的值,第三种方法是用步骤多些,但可以获取各种类型的属性值,并且可以提供代码检错功能
第一种方法,直接设置属性值,通过attrs.getAttributeResourceValue拿到这个属性值。
(1)在xml文件中设置属性值
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<com.example.lenovo.custom_textview.ImageTextView1
android:id="@+id/itv"
iamgeBoolean="true"
iamgeColor="#00ff00"
iamgeDimension="100dp"
iamgeEnum1="enum2"
iamgeFlag="flag3"
iamgeFloat="0.8"
iamgeFraction="200%p"
iamgeInteger="100"
iamgeString="自定义属性"
imageReference="@drawable/trash"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
(2)在构造函数中拿到这个值
package com.example.lenovo.custom_textview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* Created by lenovo on 2016/2/18.
*/
public class ImageTextView1 extends TextView {
public ImageTextView1(Context context) {
super(context);
}
public ImageTextView1(Context context, AttributeSet attrs) {
super(context, attrs);
//可以获取所有属性值的字符串表示,int,flat,boolea,reference,string 类型能获取准确值
String iamgeDimension = attrs.getAttributeValue(null, "iamgeDimension");
int imageReference = attrs.getAttributeResourceValue(null, "imageReference", 0);
if (imageReference > 0) {
bitmap = BitmapFactory.decodeResource(getResources(), imageReference);
}
String iamgeColor = attrs.getAttributeValue(null, "iamgeColor");
String iamgeString = attrs.getAttributeValue(null, "iamgeString");
int iamgeInteger = attrs.getAttributeIntValue(null, "iamgeInteger", 0);
float iamgeFloat = attrs.getAttributeFloatValue(null, "iamgeFloat", 0);
boolean iamgeBoolean = attrs.getAttributeBooleanValue(null, "iamgeBoolean", false);
String iamgeFraction = attrs.getAttributeValue(null, "iamgeFraction");
String iamgeEnum1 = attrs.getAttributeValue(null, "iamgeEnum1");
String iamgeFlag = attrs.getAttributeValue(null, "iamgeFlag");
StringBuffer str = new StringBuffer();
str.append("iamgeDimension= " + iamgeDimension + "\n");
str.append("imageReference= " + imageReference + "\n");
str.append("iamgeColor= " + iamgeColor + "\n");
str.append("iamgeBoolean= " + iamgeBoolean + "\n");
str.append("iamgeString= " + iamgeString + "\n");
str.append("iamgeInteger= " + iamgeInteger + "\n");
str.append("iamgeFloat= " + iamgeFloat + "\n");
str.append("iamgeFraction= " + iamgeFraction + "\n");
str.append("iamgeEnum1= " + iamgeEnum1 + "\n");
str.append("iamgeFlag= " + iamgeFlag + "\n");
setText(str.toString());
}
public ImageTextView1(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Bitmap bitmap;
@Override
public void onDraw(Canvas canvas) {
if (bitmap != null) {
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Rect target = new Rect();
int textHeight = (int) getTextSize();
target.left = 0;
target.top = (int) (getMeasuredHeight() - getTextSize()) / 2 + 1;
target.bottom = target.top + textHeight;
target.right = (int) (textHeight * (bitmap.getWidth() / (float) bitmap.getHeight()));
canvas.drawBitmap(bitmap, src, target, getPaint());
canvas.translate(target.right + 2, 0);
}
super.onDraw(canvas);
}
}
第二种方法,使用自己的命名空间,通过attrs.getAttributeResourceValue拿到这个属性值。
(1)注意在xml文件中,需要声明一个命名空间,形式一般为为http:// + 这个view的包名(其实这个名字可以随便取名只要是个名字就行,只是一般遵循这个格式)如果为http://com.example.activity,注意的是xml 中的命名空间名字要和获取属性attrs.getAttributeValue(“http://com.example.activity”, “iamgeDimension”)中的命名空间名字一样
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:mobile="http://com.example.activity"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical"
tools:context=".MainActivity">
<com.example.lenovo.custom_textview.ImageTextView2
android:layout_width="wrap_content"
android:layout_height="wrap_content"
mobile:iamgeBoolean="true"
mobile:iamgeColor="@color/material_blue_grey_800"
mobile:iamgeDimension="100dp"
mobile:iamgeEnum1="enum2"
mobile:iamgeFlag="flag3"
mobile:iamgeFloat="0.8"
mobile:iamgeFraction="200%p"
mobile:iamgeInteger="100"
mobile:iamgeString="自定义属性"
mobile:imageReference="@drawable/trash" />
</LinearLayout>
(2)通过attrs.getAttributeResourceValue,其中第一个参数为命名空间。
package com.example.lenovo.custom_textview;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* Created by lenovo on 2016/2/18.
*/
public class ImageTextView2 extends TextView {
public ImageTextView2(Context context) {
super(context);
}
//命名空间
private final String namespace = "http://com.example.activity";
String tag = "ldq";
public ImageTextView2(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public ImageTextView2(Context context, AttributeSet attrs) {
super(context, attrs);
//可以获取所有属性值的字符串表示,int,flat,boolea,reference,string 类型能获取准确值
String iamgeDimension = attrs.getAttributeValue(namespace, "iamgeDimension");
int imageReference = attrs.getAttributeResourceValue(namespace, "imageReference", 0);
if (imageReference > 0) {
bitmap = BitmapFactory.decodeResource(getResources(), imageReference);
}
String iamgeColor = attrs.getAttributeValue(namespace, "iamgeColor");
String iamgeString = attrs.getAttributeValue(namespace, "iamgeString");
int iamgeInteger = attrs.getAttributeIntValue(namespace, "iamgeInteger", 0);
float iamgeFloat = attrs.getAttributeFloatValue(namespace, "iamgeFloat", 0);
boolean iamgeBoolean = attrs.getAttributeBooleanValue(namespace, "iamgeBoolean", false);
String iamgeFraction = attrs.getAttributeValue(namespace, "iamgeFraction");
String iamgeEnum1 = attrs.getAttributeValue(namespace, "iamgeEnum1");
String iamgeFlag = attrs.getAttributeValue(namespace, "iamgeFlag");
StringBuffer str = new StringBuffer();
str.append("iamgeDimension= " + iamgeDimension + "\n");
str.append("imageReference= " + imageReference + "\n");
str.append("iamgeColor= " + iamgeColor + "\n");
str.append("iamgeBoolean= " + iamgeBoolean + "\n");
str.append("iamgeString= " + iamgeString + "\n");
str.append("iamgeInteger= " + iamgeInteger + "\n");
str.append("iamgeFloat= " + iamgeFloat + "\n");
str.append("iamgeFraction= " + iamgeFraction + "\n");
str.append("iamgeEnum1= " + iamgeEnum1 + "\n");
str.append("iamgeFlag= " + iamgeFlag + "\n");
setText(str.toString());
}
private Bitmap bitmap;
@Override
public void onDraw(Canvas canvas) {
if (bitmap != null) {
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Rect target = new Rect();
int textHeight = (int) getTextSize();
target.left = 0;
target.top = (int) (getMeasuredHeight() - getTextSize()) / 2 + 1;
target.bottom = target.top + textHeight;
target.right = (int) (textHeight * (bitmap.getWidth() / (float) bitmap.getHeight()));
canvas.drawBitmap(bitmap, src, target, getPaint());
canvas.translate(target.right + 2, 0);
}
super.onDraw(canvas);
}
}
复制代码
第三种方法,通过自定义attrs.xml来实现 ,通过context.obtainStyledAttributes(attrs,R.styleable.ImageTextView).getString()方法拿到属性值
(1)自定义一个attrs.xml文件
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ImageTextView">
<attr name="iamgeDimension" format="dimension" />
<attr name="imageReference" format="reference" />
<attr name="iamgeColor" format="color" />
<attr name="iamgeString" format="string" />
<attr name="iamgeInteger" format="integer" />
<attr name="iamgeFloat" format="float" />
<attr name="iamgeBoolean" format="boolean" />
<attr name="iamgeFraction" format="fraction" />
<attr name="iamgeEnum1">
<enum name="enum1" value="1"></enum>
<enum name="enum2" value="2"></enum>
</attr>
<attr name="iamgeFlag">
<flag name="flag1" value="1"></flag>
<flag name="flag2" value="2"></flag>
<flag name="flag3" value="3"></flag>
</attr>
</declare-styleable>
</resources>
或者
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="iamgeDimension" format="dimension" />
<attr name="imageReference" format="reference" />
<attr name="iamgeColor" format="color" />
<attr name="iamgeString" format="string" />
<attr name="iamgeInteger" format="integer" />
<attr name="iamgeFloat" format="float" />
<attr name="iamgeBoolean" format="boolean" />
<attr name="iamgeFraction" format="fraction" />
<attr name="iamgeEnum1">
<enum name="enum1" value="1"></enum>
<enum name="enum2" value="2"></enum>
</attr>
<attr name="iamgeFlag">
<flag name="flag1" value="1"></flag>
<flag name="flag2" value="2"></flag>
<flag name="flag3" value="3"></flag>
</attr>
<declare-styleable name="ImageTextView">
<attr name="iamgeDimension"></attr>
<attr name="imageReference"></attr>
<attr name="iamgeColor"></attr>
<attr name="iamgeString"></attr>
<attr name="iamgeInteger"></attr>
<attr name="iamgeFloat"></attr>
<attr name="iamgeBoolean"></attr>
<attr name="iamgeFraction"></attr>
<attr name="iamgeEnum1"></attr>
<attr name="iamgeFlag"></attr>
</declare-styleable>
</resources>
两种方法都可以,自定义属性分两步:
定义公共属性
定义控件的主题样式
如上面的xml文件第一部分是公共的属性,第二部分是自定义控件MyCustomView的主题样式,该主题样式里的属性必须包含在公共属性里面。言外之意就是公共属性可以被多个自定义控件主题样式使用。
(2)在xml文件中使用这一属性,注意此时命名空间的书写规范。
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ImageTextView="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<com.example.lenovo.custom_textview.ImageTextView3
android:layout_width="wrap_content"
android:layout_height="wrap_content"
ImageTextView:iamgeDimension="100dp"
ImageTextView:imageReference="@drawable/trash"
ImageTextView:iamgeColor="@color/material_blue_grey_800"
ImageTextView:iamgeString="自定义属性"
ImageTextView:iamgeInteger="100"
ImageTextView:iamgeFloat="0.8"
ImageTextView:iamgeBoolean="true"
ImageTextView:iamgeFraction="200%p"
ImageTextView:iamgeEnum1="enum2"
ImageTextView:iamgeFlag="flag3"
/>
</LinearLayout>
(3)在代码中使用context.obtainStyledAttributes获得属性值
指 定为一个declare-styleable,而在declare-styleable 下的attr (即各属性)Android 的ADT 将会自动生成为declare-styleable的name 名字加上“_”加上对应attr(即属性名称)的名称,如上(ImageTextView_String)我们要得到Text 就需要R.styleable.ImageTextView_String,这一点的话可以看看R.java生成文件
package com.example.lenovo.custom_textview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.widget.TextView;
/**
* Created by lenovo on 2016/2/18.
*/
public class ImageTextView3 extends TextView {
public ImageTextView3(Context context) {
super(context);
}
public ImageTextView3(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ImageTextView);
// TypedArray是存放资源的array,1.通过上下文得到这个数组,attrs是构造函数传进来的,对应attrs.xml
// 获得xml里定义的属性,格式为 名称_属性名 后面是默认值
double iamgeDimension = typedArray.getDimension(R.styleable.ImageTextView_iamgeDimension, 0);
int imageReference = typedArray.getResourceId(R.styleable.ImageTextView_imageReference, 0);
bitmap = BitmapFactory.decodeResource(getResources(), imageReference);
Drawable drawable = typedArray.getDrawable(R.styleable.ImageTextView_imageReference);
double iamgeColor = typedArray.getColor(R.styleable.ImageTextView_iamgeColor, 0);
String iamgeString = typedArray.getString(R.styleable.ImageTextView_iamgeString);
double iamgeInteger = typedArray.getInteger(R.styleable.ImageTextView_iamgeInteger, 0);
double iamgeFloat = typedArray.getFloat(R.styleable.ImageTextView_iamgeFloat, 0);
boolean iamgeBoolean = typedArray.getBoolean(R.styleable.ImageTextView_iamgeBoolean, false);
// ImageTextView:iamgeFraction="200%"
// ImageTextView:iamgeFraction="200%p"
// double iamgeFraction = typedArray.getFraction(R.styleable.ImageTextView_iamgeFraction, 4, 5, 1);
// 1)如果mageTextView_iamgeFraction 是200%,那么result就是:200%*4 ~ 8
// 2)如果mageTextView_iamgeFraction 是200%p,那么result就是:200%*5 ~ 10
double iamgeFraction = typedArray.getFraction(R.styleable.ImageTextView_iamgeFraction, 4, 5, 1);
double iamgeEnum1 = typedArray.getInteger(R.styleable.ImageTextView_iamgeEnum1, 0);
double iamgeFlag = typedArray.getInteger(R.styleable.ImageTextView_iamgeFlag, 0);
// 为了保持以后使用该属性一致性,返回一个绑定资源结束的信号给资源
typedArray.recycle(); //调用结束后务必调用recycle()方法,否则这次的设定会对下次的使用造成影响
StringBuffer str = new StringBuffer();
str.append("iamgeDimension= " + iamgeDimension + "\n");
str.append("imageReference= " + imageReference + "\n");
str.append("iamgeColor= " + iamgeColor + "\n");
str.append("iamgeBoolean= " + iamgeBoolean + "\n");
str.append("iamgeString= " + iamgeString + "\n");
str.append("iamgeInteger= " + iamgeInteger + "\n");
str.append("iamgeFloat= " + iamgeFloat + "\n");
str.append("iamgeFraction= " + iamgeFraction + "\n");
str.append("iamgeEnum1= " + iamgeEnum1 + "\n");
str.append("iamgeFlag= " + iamgeFlag + "\n");
setText(str.toString());
}
public ImageTextView3(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
private Bitmap bitmap;
@Override
public void onDraw(Canvas canvas) {
if (bitmap != null) {
Rect src = new Rect(0, 0, bitmap.getWidth(), bitmap.getHeight());
Rect target = new Rect();
int textHeight = (int) getTextSize();
target.left = 0;
target.top = (int) (getMeasuredHeight() - getTextSize()) / 2 + 1;
target.bottom = target.top + textHeight;
target.right = (int) (textHeight * (bitmap.getWidth() / (float) bitmap.getHeight()));
canvas.drawBitmap(bitmap, src, target, getPaint());
canvas.translate(target.right + 2, 0);
}
super.onDraw(canvas);
}
}
总结:
这是这两种为Android 注册 属性的使用方法,那么两者有什么区别呢?
在这里我认为起码有五点,大家可以找找看还有什么区别:
第二种可以编译时报错,如果编程人员随便输入什么第一种是不会报错的,第二种可以支持代码检测功能。
第二种写法,跟Android 属性标准写法是一致的,而且可以统一书法规则。
第二种写法,可以支持数据格式的验证,比如我们在attrs上注明只支持integer 那么就不可以使用字符串,这是第一种达不到的。
第二种写法,可以为VIEW提供选择操作,比如如上我们使用的ENUM让VIEW对应的属性支持ENUM列表,或者为其提供BOOL等只有双项选择的操作。
第一种写法,所有的属性必须是引用自资源(不大确定,如果朋友有什么好的DEMO麻烦共享),第二种写法,可以即支持引用资源又可以直接输入做操作,为编程带来更多的方便性。
种种都说明,第二种写法更具规范性,功能更性,代码编写 也更优雅。