View自定义属性步骤与分析

来源:互联网 发布:linux端口重定向 编辑:程序博客网 时间:2024/05/22 03:27

我们在自定义View的过程中,通常会让用户通过自定义属性值来控制View的显示效果。那么我们应该如何自定义属性和使用这些属性呢?
第一:我们需要在工程目录下res/values新建一个attr.xml文件,在该文件中定义我们自己的自定义的属性名称。

<?xml version="1.0" encoding="utf-8"?><resources>    <declare-styleable name="myTextView">        <attr name="mColor" format="color"/>        <attr name="mBoolean" format="boolean"/>        <attr name="mDimension" format="dimension"/>        <attr name="mFloat" format="float"/>        <attr name="mInteger" format="integer"/>        <attr name="mString" format="string"/>        <attr name="mEnum" format="enum"/>        <attr name="mFlag">            <flag name="one" value="1"/>            <flag name="two" value="2"/>        </attr>        <attr name="mFraction" format="fraction"/>        <attr name="mReference" format="reference"/>    </declare-styleable></resources>

第二: 我们就可以在我们布局文件使用这些自定义属性了。

<com.lgy.typearray.MyTextView        android:layout_width="match_parent"        android:layout_height="match_parent"        lgy:mColor="@color/material_blue_grey_900"        lgy:mBoolean="true"        lgy:mDimension="@dimen/abc_action_bar_progress_bar_size"        android:text="Hello World!" />

第三:我们此时就可以在我们自定义View的构造函数中获取我们在布局文件中设置的对应值。

public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {   super(context, attrs, defStyleAttr);   mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);   mPaint.setColor(Color.RED);   TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);   mPaint.setColor(typedArray.getColor(R.styleable.myTextView_mColor, Color.RED));   boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);   Log.i(TAG, "mBoolean:" + mBoolean);   float mDimension = typedArray.getDimension(R.styleable.myTextView_mDimension, 14f);   Log.i(TAG, "mDimension:" + mDimension);   int mDimensionSize = typedArray.getDimensionPixelSize(R.styleable.myTextView_mDimension, 14);   Log.i(TAG, "mDimensionSize:" + mDimensionSize);   int mDimensionPixelOffset = typedArray.getDimensionPixelOffset(R.styleable.myTextView_mDimension,14);   Log.i(TAG, "mDimensionPixelOffset:" + mDimensionPixelOffset);   typedArray.recycle();}

到此使用方法就介绍好了。
下面我想在介绍的就是我们在attr.xml中 format对应值。
1、color 代表颜色值。
在attr.xml中我们可以<attr name="mColor" format="color"/>,
在我们布局文件中我们可以lgy:mColor="@color/mytextView",
颜色mytextView是我们在定义<color name="mytextView">#5896ae</color>,
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
typedArray.recycle();

运行代码结果图:
这里写图片描述
2、boolean代表真假true false
在attr.xml中我们可以<attr name="mBoolean" format="boolean"/>,
在我们布局文件中我们可以lgy:mBoolean="true",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
typedArray.recycle();

运行代码结果图:
这里写图片描述
3、float代表浮点数
在attr.xml中我们可以<attr name="mFloat" format="float"/>,
在我们布局文件中我们可以lgy:mFloat="5",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
typedArray.recycle();

运行代码结果图:
这里写图片描述
4、integer 代表整形
在attr.xml中我们可以<attr name="mInteger" format="integer"/>,
在我们布局文件中我们可以lgy:mInteger="16",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
int mInteger = typedArray.getInteger(R.styleable.myTextView_mInteger,1);
Log.i(TAG,"mInteger:" + mInteger);
int mInt = typedArray.getInt(R.styleable.myTextView_mInteger,2);
Log.i(TAG,"mInt:" + mInt);
typedArray.recycle();

运行代码结果图:
这里写图片描述
5、string 代表字符串
在attr.xml中我们可以<attr name="mString" format="string"/>,
在我们布局文件中我们可以lgy:mString="自定义字符串",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
int mInteger = typedArray.getInteger(R.styleable.myTextView_mInteger,1);
Log.i(TAG,"mInteger:" + mInteger);
int mInt = typedArray.getInt(R.styleable.myTextView_mInteger,2);
Log.i(TAG,"mInt:" + mInt);
String mString = typedArray.getString(R.styleable.myTextView_mString);
Log.i(TAG,"mString:" + mString);
typedArray.recycle();

运行代码结果图:
这里写图片描述
6、enum 代表枚举类型
在attr.xml中我们可以:

<attr name="mEnum" format="enum">
<enum name="five" value="5"/>
<enum name="six" value="6"/>
<enum name="seven" value="7"/>
</attr>

在我们布局文件中我们可以lgy:mEnum="six",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
int mInteger = typedArray.getInteger(R.styleable.myTextView_mInteger,1);
Log.i(TAG,"mInteger:" + mInteger);
int mInt = typedArray.getInt(R.styleable.myTextView_mInteger,2);
Log.i(TAG,"mInt:" + mInt);
String mString = typedArray.getString(R.styleable.myTextView_mString);
Log.i(TAG,"mString:" + mString);
int mEnum = typedArray.getInteger(R.styleable.myTextView_mEnum,5);
Log.i(TAG,"mEnum:" + mEnum);
int mEnumInt = typedArray.getInt(R.styleable.myTextView_mEnum,7);
Log.i(TAG,"mEnumInt:" + mEnumInt);
typedArray.recycle();

运行代码结果图:
这里写图片描述
7、flag 代表自定义类型
在attr.xml中我们可以:

<attr name="mFlag">
<flag name="one" value="1"/>
<flag name="two" value="2"/>
</attr>

在我们布局文件中我们可以lgy:mFlag="two",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
int mInteger = typedArray.getInteger(R.styleable.myTextView_mInteger,1);
Log.i(TAG,"mInteger:" + mInteger);
int mInt = typedArray.getInt(R.styleable.myTextView_mInteger,2);
Log.i(TAG,"mInt:" + mInt);
String mString = typedArray.getString(R.styleable.myTextView_mString);
Log.i(TAG,"mString:" + mString);
int mEnum = typedArray.getInteger(R.styleable.myTextView_mEnum,5);
Log.i(TAG,"mEnum:" + mEnum);
int mEnumInt = typedArray.getInt(R.styleable.myTextView_mEnum,7);
Log.i(TAG,"mEnumInt:" + mEnumInt);
int mFlag = typedArray.getInteger(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlag:" + mFlag);
int mFlagInt = typedArray.getInt(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlagInt:" + mFlagInt);
typedArray.recycle();

运行代码结果图:
这里写图片描述
8、reference 代表引用 (布局文件、资源文件等)
在attr.xml中我们可以<attr name="mReference" format="reference"/>
在我们布局文件中我们可以lgy:mReference="@android:layout/simple_list_item_1",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
int mInteger = typedArray.getInteger(R.styleable.myTextView_mInteger,1);
Log.i(TAG,"mInteger:" + mInteger);
int mInt = typedArray.getInt(R.styleable.myTextView_mInteger,2);
Log.i(TAG,"mInt:" + mInt);
String mString = typedArray.getString(R.styleable.myTextView_mString);
Log.i(TAG,"mString:" + mString);
int mEnum = typedArray.getInteger(R.styleable.myTextView_mEnum,5);
Log.i(TAG,"mEnum:" + mEnum);
int mEnumInt = typedArray.getInt(R.styleable.myTextView_mEnum,7);
Log.i(TAG,"mEnumInt:" + mEnumInt);
int mFlag = typedArray.getInteger(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlag:" + mFlag);
int mFlagInt = typedArray.getInt(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlagInt:" + mFlagInt);
int mReference = typedArray.getResourceId(R.styleable.myTextView_mReference,0);
Log.i(TAG,"mReference:" + mReference);
typedArray.recycle();

运行代码结果图:
这里写图片描述
9、fraction 代表百分数
在attr.xml中我们可以<attr name="mFraction" format="fraction"/>
在我们布局文件中我们可以lgy:mFraction="30%",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
int mInteger = typedArray.getInteger(R.styleable.myTextView_mInteger,1);
Log.i(TAG,"mInteger:" + mInteger);
int mInt = typedArray.getInt(R.styleable.myTextView_mInteger,2);
Log.i(TAG,"mInt:" + mInt);
String mString = typedArray.getString(R.styleable.myTextView_mString);
Log.i(TAG,"mString:" + mString);
int mEnum = typedArray.getInteger(R.styleable.myTextView_mEnum,5);
Log.i(TAG,"mEnum:" + mEnum);
int mEnumInt = typedArray.getInt(R.styleable.myTextView_mEnum,7);
Log.i(TAG,"mEnumInt:" + mEnumInt);
int mFlag = typedArray.getInteger(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlag:" + mFlag);
int mFlagInt = typedArray.getInt(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlagInt:" + mFlagInt);
int mReference = typedArray.getResourceId(R.styleable.myTextView_mReference,0);
Log.i(TAG,"mReference:" + mReference);
float mFraction = typedArray.getFraction(R.styleable.myTextView_mFraction,2,4,0);
Log.i(TAG,"mFraction:" + mFraction);
typedArray.recycle();

运行代码结果图:
设置lgy:mFraction="30%"
**注意:**lgy:mFraction=”30%”
设置lgy:mFraction="30%p"
**注意:**lgy:mFraction=”30%p”
注意到我们设置lgy:mFraction=”“值不同得到的结果不一样,那么我们在调用getFraction方法中需要传递4个参数,那么它们分别代表什么呢?为什么得到参数不一样呢?
我们需要查看getFraction源码:

public float getFraction(int index, int base, int pbase, float defValue) {
index *= AssetManager.STYLE_NUM_ENTRIES;
final int[] data = mData;
final int type = data[index+AssetManager.STYLE_TYPE];
if (type == TypedValue.TYPE_NULL) {
return defValue;
} else if (type == TypedValue.TYPE_FRACTION) {
return TypedValue.complexToFraction(
data[index+AssetManager.STYLE_DATA], base, pbase);
}
throw new UnsupportedOperationException("Can't convert to fraction: type=0x"
+ Integer.toHexString(type));
}

通过查看源码我可以知道第一个参数和第四参数明白什么意思,第二参数和第三个参数目前我们知道和我们最终返回结果相关,我只有继续往下查看源码:

public static float complexToFraction(int data, float base, float pbase)
{
switch ((data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK) {
case COMPLEX_UNIT_FRACTION:
return complexToFloat(data) * base;
case COMPLEX_UNIT_FRACTION_PARENT:
return complexToFloat(data) * pbase;
}
return 0;
}

哦!通过查看源码我们知道了,当我们设置参数是COMPLEX_UNIT_FRACTION的时候需要*第二个参数base,当我们设置参数是COMPLEX_UNIT_FRACTION_PARENT的时候需要*第三个参数pbase。p代表父容器父布局。
现在我们在看一下我们的例子,当我们设置30%的时候,需要0.3 * 2 = 0.6我们看一下结果是0.599999,当我们设置30%p的时候,需要0.3 * 4 = 1.2我们看一下结果是1.199999,最终返回是一个浮点数。同时我们在看一下COMPLEX_UNIT_FRACTION和COMPLEX_UNIT_FRACTION_PARENT的源码解释就可以明白了。

    /** {@link #TYPE_FRACTION} complex unit: A basic fraction of the overall     *  size. */    public static final int COMPLEX_UNIT_FRACTION = 0;    /** {@link #TYPE_FRACTION} complex unit: A fraction of the **parent** size. */    public static final int COMPLEX_UNIT_FRACTION_PARENT = 1;

10、dimension 代表尺寸值
在attr.xml中我们可以<attr name="mDimension" format="dimension"/>
在我们布局文件中我们可以lgy:mDimension="32dp",
我们在代码中获取我们设置对应值

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.myTextView);
int mColor = typedArray.getColor(R.styleable.myTextView_mColor, Color.RED);
Log.i(TAG,"mColor:" + mColor);
boolean mBoolean = typedArray.getBoolean(R.styleable.myTextView_mBoolean, false);
Log.i(TAG, "mBoolean:" + mBoolean);
float mFloat = typedArray.getFloat(R.styleable.myTextView_mFloat,0.0f);
Log.i(TAG,"mFloat:" + mFloat);
int mInteger = typedArray.getInteger(R.styleable.myTextView_mInteger,1);
Log.i(TAG,"mInteger:" + mInteger);
int mInt = typedArray.getInt(R.styleable.myTextView_mInteger,2);
Log.i(TAG,"mInt:" + mInt);
String mString = typedArray.getString(R.styleable.myTextView_mString);
Log.i(TAG,"mString:" + mString);
int mEnum = typedArray.getInteger(R.styleable.myTextView_mEnum,5);
Log.i(TAG,"mEnum:" + mEnum);
int mEnumInt = typedArray.getInt(R.styleable.myTextView_mEnum,7);
Log.i(TAG,"mEnumInt:" + mEnumInt);
int mFlag = typedArray.getInteger(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlag:" + mFlag);
int mFlagInt = typedArray.getInt(R.styleable.myTextView_mFlag,1);
Log.i(TAG,"mFlagInt:" + mFlagInt);
int mReference = typedArray.getResourceId(R.styleable.myTextView_mReference,0);
Log.i(TAG,"mReference:" + mReference);
float mFraction = typedArray.getFraction(R.styleable.myTextView_mFraction,2,4,0);
Log.i(TAG,"mFraction:" + mFraction);
float mDimension = typedArray.getDimension(R.styleable.myTextView_mDimension, 14f);
Log.i(TAG, "mDimension:" + mDimension);
typedArray.recycle();

运行代码结果图:
这里写图片描述
我们设置lgy:mDimension=”32dp”不同单位得到的值是不相同的。
32dp –>01-03 03:49:23.800 10694-10694/com.lgy.typearray I/MyTextView: mDimension:48.0
32sp –>01-03 03:53:43.210 14638-14638/com.lgy.typearray I/MyTextView: mDimension:48.0
32px –>01-03 03:54:30.430 15446-15446/com.lgy.typearray I/MyTextView: mDimension:32.0
32pt –>01-03 03:57:24.300 18042-18042/com.lgy.typearray I/MyTextView: mDimension:120.41481
32in –>01-03 03:58:07.660 18733-18733/com.lgy.typearray I/MyTextView: mDimension:8669.866
32mm –>01-03 03:59:17.210 19773-19773/com.lgy.typearray I/MyTextView: mDimension:341.3333
为什么设置不同单位得到值不同呢?这也需要查看源码:

public float getDimension(int index, float defValue) {        index *= AssetManager.STYLE_NUM_ENTRIES;        final int[] data = mData;        final int type = data[index+AssetManager.STYLE_TYPE];        if (type == TypedValue.TYPE_NULL) {            return defValue;        } else if (type == TypedValue.TYPE_DIMENSION) {            return TypedValue.complexToDimension(                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);        }        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"                + Integer.toHexString(type));    }

此时看不出什么门道来,继续往下看TypedValue.complexToDimension方法

public static float complexToDimension(int data, DisplayMetrics metrics)    {        return applyDimension(            (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,            complexToFloat(data),            metrics);    }

继续看applyDimension方法

public static float applyDimension(int unit, float value,                                       DisplayMetrics metrics)    {        switch (unit) {        case COMPLEX_UNIT_PX:            return value;        case COMPLEX_UNIT_DIP:            return value * metrics.density;        case COMPLEX_UNIT_SP:            return value * metrics.scaledDensity;        case COMPLEX_UNIT_PT:            return value * metrics.xdpi * (1.0f/72);        case COMPLEX_UNIT_IN:            return value * metrics.xdpi;        case COMPLEX_UNIT_MM:            return value * metrics.xdpi * (1.0f/25.4f);        }        return 0;    }

到此豁然开朗了吧!
获取尺寸的方法还有getDimensionPixelSize方法和getDimensionPixelOffset方法。那么它们有什么不同呢?
再一次通过查看源码我就可以得出结论.

public int getDimensionPixelOffset(int index, int defValue) {        index *= AssetManager.STYLE_NUM_ENTRIES;        final int[] data = mData;        final int type = data[index+AssetManager.STYLE_TYPE];        if (type == TypedValue.TYPE_NULL) {            return defValue;        } else if (type == TypedValue.TYPE_DIMENSION) {            return TypedValue.complexToDimensionPixelOffset(                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);        }        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"                + Integer.toHexString(type));    }
public int getDimensionPixelSize(int index, int defValue) {        index *= AssetManager.STYLE_NUM_ENTRIES;        final int[] data = mData;        final int type = data[index+AssetManager.STYLE_TYPE];        if (type == TypedValue.TYPE_NULL) {            return defValue;        } else if (type == TypedValue.TYPE_DIMENSION) {            return TypedValue.complexToDimensionPixelSize(                data[index+AssetManager.STYLE_DATA], mResources.mMetrics);        }        throw new UnsupportedOperationException("Can't convert to dimension: type=0x"                + Integer.toHexString(type));    }

通过源码知道2个方法分别都调用了TypedValue.complexToDimensionPixelOffset,TypedValue.complexToDimensionPixelSize,继续看源码:

public static int complexToDimensionPixelOffset(int data,            DisplayMetrics metrics)    {        return (int)applyDimension(                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,                complexToFloat(data),                metrics);    }
public static int complexToDimensionPixelSize(int data,            DisplayMetrics metrics)    {        final float value = complexToFloat(data);        final float f = applyDimension(                (data>>COMPLEX_UNIT_SHIFT)&COMPLEX_UNIT_MASK,                value,                metrics);        final int res = (int)(f+0.5f);        if (res != 0) return res;        if (value == 0) return 0;        if (value > 0) return 1;        return -1;    }

最终2个方法也都调用了applyDimension方法,只不过两者调用了applyDimension方法强制转换int,complexToDimensionPixelSize方法得到值之后又做了简单处理。
那么现在我们可以知道getDimension、getDimensionPixelSize、getDimensionPixelOffset三者区别:
相同点:不同单位尺寸设置得到不同结果。都会调用applyDimension方法。
不同点:getDimension返回float类型,getDimensionPixelSize、getDimensionPixelOffset都是返回int类型。

就写这些吧!

1 0