View自定义属性步骤与分析

我们在自定义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类型。

就写这些吧!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值