android 自定义View基础(1)

android 自定义View基础(1)

本文主要是学习:
http://mp.weixin.qq.com/s?__biz=MzAxMTI4MTkwNQ==&mid=2650820236&idx=1&sn=6dec4ff1efeda3224b5a40fdad862404#rd

http://blog.csdn.net/lmj623565791/article/details/45022631

总结的。

源码地址:

https://github.com/aloe-all/CustomView-1-

自定义View 的基本步骤:
1、在 res/values/attrs.xml 自定义View(CostomView)属性
2、在CostomView.java 的构造方法中获取自定义View的属性
3、onMesure //测量View的大小
4、onLayout //view 在屏幕展现的位置
5、onDraw //把view 画出来,展示在手机屏幕上

一、在 res/values/attrs.xml 自定义View(CostomView)属性
res/values/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomView">
        <attr name="text" format="string"/>
        <attr name="textsize" format="integer"/>
        <attr name="textcolor" format="integer"/>
    </declare-styleable>
</resources>

在CostomView.java 的构造方法中获取自定义View的属性

public class CustomView extends View {
    private static final String TAG = CustomView.class.getSimpleName();

    public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);

        //拿到第一个自定义属性
        String str = typedArray.getString(R.styleable.CustomView_text); 

        //拿到第二个自定义属,如果没有就赋值默认值10
        int textSize = typedArray.getInteger(R.styleable.CustomView_textsize, 10);

        //拿到第三个自定义属,如果没有就赋值默认值5
        int textColor = typedArray.getInteger(R.styleable.CustomView_textcolor, 5); //拿到第一个自定义属

        //xml布局文件的值通过Log打出来
        Log.d(TAG, ">>>>>>str: " + str);
        Log.d(TAG, ">>>>>>textSize: " + textSize);
        Log.d(TAG, ">>>>>>textColor: " + textColor);

        //资源回收
        typedArray.recycle();
    }
}

使用自定义的属性:

xmlns:custom="http://schemas.android.com/apk/res-auto"

这个是命名空间声明,android studio 这样下;
eclipse 这样写:

xmlns:custom="http://schemas.android.com/apk/com.example.crg.customview1_attributes"
 <com.example.crg.customview1_attributes.CustomView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        custom:text="helloworld"
        custom:textcolor="6"
        custom:textsize="7"
        />

程序运行结果如下:

06-09 03:12:42.084 1873-1873/com.example.crg.customview1_attributes D/CustomView: >>>>>>str: helloworld
06-09 03:12:42.084 1873-1873/com.example.crg.customview1_attributes D/CustomView: >>>>>>textSize: 7
06-09 03:12:42.084 1873-1873/com.example.crg.customview1_attributes D/CustomView: >>>>>>textColor: 6

我们已经在 自定义View(CostomView)里拿到了 xml里设定的值了,然后就可以,使用这些值,测量,布局,绘制出来了;

二、理解 AttributeSet 和 TypedArray

public CustomView(Context context, AttributeSet attrs) {

点进去看源码的解释:

 A collection of attributes, as found associated with a tag in an XML
 * document.  Often you will not want to use this interface directly, instead
 * passing it to {@link android.content.res.Resources.Theme#obtainStyledAttributes(AttributeSet, int[], int, int)
 * Resources.Theme.obtainStyledAttributes()}
 * which will take care of parsing the attributes for you.  In particular,
 * the Resources API will convert resource references (attribute values such as
 * "@string/my_label" in the original XML) to the desired type
 * for you; if you use AttributeSet directly then you will need to manually
 * check for resource references
 * (with {@link #getAttributeResourceValue(int, int)}) and do the resource
 * lookup yourself if needed.  Direct use of AttributeSet also prevents the
 * application of themes and styles when retrieving attribute values.

public interface AttributeSet {

就是我们自定义 CustomView(Context context, AttributeSet attrs) 在布局文件里 使用的属性的集合;上面说的很清楚,我们使用

android.content.res.Resources.Theme#obtainStyledAttributes

自动为我们解析为特定类型的属性值;

 if you use   directly then you will need to manually
 * check for resource references

如果我们直接使用AttributeSet API 获取属性值,如果是引用类型的值,需要我们自己人为解析特定类型的值@string/my_label。
demo 如下:

<com.example.crg.customview1_attributes.CustomView
        android:layout_width="100dp"
        android:layout_height="100dp"
        custom:text="helloworld"
        custom:textcolor="6"
        custom:textsize="7"
        />

打印结果:

 D/CustomView: >>>>>>>>>>>>>>>>> attrCount: 5

 D/CustomView: key: layout_width >>>>>>>>> value: 100.0dip
 D/CustomView: key: layout_height >>>>>>>>> value: 100.0dip
 D/CustomView: key: text >>>>>>>>> value: helloworld
 D/CustomView: key: textsize >>>>>>>>> value: 7
 D/CustomView: key: textcolor >>>>>>>>> value: 6

结果一目了然,拿到了布局文件里 传入的5个属性,这5个属性传的都是具体的值,


然后看一个传入引用类型的demo:

<com.example.crg.customview1_attributes.CustomView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        custom:text="@string/demo_txt"
        custom:textcolor="@color/demo_textcolor"
        custom:textsize="@dimen/demo_size"
        />

结果如下:

D/CustomView: >>>>>>>>>>>>>>>>> attrCount: 5

D/CustomView: key: layout_width >>>>>>>>> value: -1
D/CustomView: key: layout_height >>>>>>>>> value: -2
D/CustomView: key: text >>>>>>>>> value: @2131099669
D/CustomView: key: textsize >>>>>>>>> value: @2131230789
D/CustomView: key: textcolor >>>>>>>>> value: @2131427349

这句注释写的很清楚:

if you use AttributeSet directly then you will need to manually
 * check for resource references
 * (with {@link #getAttributeResourceValue(int, int)}) and do the resource
 * lookup yourself if needed.

只需要两步:

int resValue = attrs.getAttributeResourceValue(i, 0);
"value: " + getResources().getString(resValue)

就能拿到 属性值了,

int attrCount = attrs.getAttributeCount();
        Log.d(TAG, ">>>>>>>>>>>>>>>>> attrCount: " + attrCount);

        for (int i = 0; i < attrCount; i++) {
            String key = attrs.getAttributeName(i);
            String value = attrs.getAttributeValue(i);
            Log.d(TAG, "key: " + key + " >>>>>>>>> " + "value: " + value);
            int resValue = attrs.getAttributeResourceValue(i, 0);
            if (i == 2) {

                Log.d(TAG, "key: " + key + " >>>>>getAttributeResourceValue()>>>> " + "value: " + getResources().getString(resValue));
            }

        }

结果如下:

 D/CustomView: >>>>>>>>>>>>>>>>> attrCount: 5
 D/CustomView: key: layout_width >>>>>>>>> value: -1
 D/CustomView: key: layout_height >>>>>>>>> value: -2
 D/CustomView: key: text >>>>>>>>> value: @2131099669
 D/CustomView: key: text >>>>>getAttributeResourceValue()>>>> value:    helloworld
 D/CustomView: key: textsize >>>>>>>>> value: @2131230789
 D/CustomView: key: textcolor >>>>>>>>> value: @2131427349

总结:
CustomView(Context context, AttributeSet attrs) AttributeSet attrs里面包含所有的CustomView布局文件里的属性,若果使用 AttributeSet 解析属性值,布局文件里如果有引用类型的,例如

custom:text="@string/demo_txt"

就需要两步,才能得到 属性值;而:

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView)

TypedArray 就像注释里写的,自动解析为特定类型的值,简化了我们的解析工作。

三、自定义属性可以使用android 已有的属性

例如使用 android:text 属性:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomView">
        <attr name="android:text"/>
        <attr name="textsize" format="integer|reference"/>
        <attr name="textcolor" format="integer|reference"/>
    </declare-styleable>
</resources>

上面是声明属性,用android 已有的属性,不需要 format
使用如下:直接android:text

<com.example.crg.customview1_attributes.CustomView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="@string/demo_txt"
        custom:textcolor="@color/demo_textcolor"
        custom:textsize="@dimen/demo_size"
        />

在自定义CustomView 里的构造方法中这样获取:

//拿到第一个自定义属性
        String str = typedArray.getString(R.styleable.CustomView_android_text);

四、obtainStyledAttributes()详解:

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView);

        //拿到第一个自定义属性
        String str = typedArray.getString(R.styleable.CustomView_android_text);

        //拿到第二个自定义属,如果没有就赋值默认值10
        int textSize = typedArray.getInteger(R.styleable.CustomView_textsize, 10);

        //拿到第三个自定义属,如果没有就赋值默认值5
        int textColor = typedArray.getInteger(R.styleable.CustomView_textcolor, 5); //拿到第一个自定义属
public final TypedArray obtainStyledAttributes(
            AttributeSet set, @StyleableRes int[] attrs) {
        return getTheme().obtainStyledAttributes(set, attrs, 0, 0);
    }

实际调用的是:getTheme().obtainStyledAttributes(set, attrs, 0, 0);
最终调用这个方法:

 public TypedArray obtainStyledAttributes(AttributeSet set,
                @StyleableRes int[] attrs, @AttrRes int defStyleAttr, @StyleRes int defStyleRes)

次方法总共有四个参数,前两个参数,上面已经熟悉了;下来看第四个参数:defStyleRes

第四个参数:

@param defStyleRes A resource identifier of a style resource that
         *                    supplies default values for the TypedArray,
         *                    used only if defStyleAttr is 0 or can not be found
         *                    in the theme.  Can be 0 to not look for defaults.

源码注释的解释:当第三个参数为0,或者在 theme 找不到时,才会为 TypedArray 提供默认值。
在 res/valus/styles.xml 中声明

<style name="customview_style">
        <item name="android:text">test test</item>
        <item name="textsize">10</item>
        <item name="textcolor">9</item>
    </style>

构造方法中使用:

TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView, 0, R.style.customview_style);
        String str = null;
        int textSize = 0;
        int textColor = 0;
        int n = typedArray.getIndexCount();
        for (int i = 0; i < n; i++){
            switch (typedArray.getIndex(i)){
                case R.styleable.CustomView_android_text:
                    str = typedArray.getString(typedArray.getIndex(i));
                    break;
                case R.styleable.CustomView_textsize:
                    textSize = typedArray.getInteger(typedArray.getIndex(i), 1);
                    break;
                case R.styleable.CustomView_textcolor:
                    textColor = typedArray.getInteger(typedArray.getIndex(i), 1);
                    break;

            }
        }
        Log.d(TAG, ">>>>>>>>>>>>>>>>> str: " + str);
        Log.d(TAG, ">>>>>>>>>>>>>>>>> textSize: " + textSize);
        Log.d(TAG, ">>>>>>>>>>>>>>>>> textColor: " + textColor);

xml布局文件什么都没声明,如下:

<com.example.crg.customview1_attributes.CustomView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />

运行结果如下:

D/CustomView: >>>>>>>>>>>>>>>>> str: test test
D/CustomView: >>>>>>>>>>>>>>>>> textSize: 10
D/CustomView: >>>>>>>>>>>>>>>>> textColor: 9

结果和我们在第四个参数中传的值一样 R.style.customview_style
当在使用自定义view 没有给定 值时,就会使用第四个参数的默认值。

第三个参数:

@param defStyleAttr:
An attribute in the current theme that contains a
reference to a style resource that supplies
defaults values for the TypedArray.  Can be
0 to not look for defaults.
defStyleAttr

当前主题的一个attribute,并且当前主题包含了一个 style resource 的应用, style resource 的应用 为 TypedArray 提供默认值。
实例如下:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="CustomView">
        <attr name="android:text"/>
        <attr name="textsize" format="integer|reference"/>
        <attr name="textcolor" format="integer|reference"/>
    </declare-styleable>

    <attr name="defStyleAttr_custom" format="reference"/>
</resources>
<resources>

    <!-- Base application theme. -->
    <style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
        <!-- Customize your theme here. -->
        <item name="defStyleAttr_custom">@style/defStyleAttr_style</item>
        <item name="colorPrimary">@color/colorPrimary</item>
        <item name="colorPrimaryDark">@color/colorPrimaryDark</item>
        <item name="colorAccent">@color/colorAccent</item>
    </style>
    <style name="customview_style">
        <item name="android:text">test test</item>
        <item name="textsize">10</item>
        <item name="textcolor">9</item>
    </style>
    <style name="defStyleAttr_style">
        <item name="android:text">defStyleAttr defStyleAttr</item>
        <item name="textsize">8</item>
        <item name="textcolor">7</item>
    </style>
</resources>
<com.example.crg.customview1_attributes.CustomView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        />
 public CustomView(Context context, AttributeSet attrs) {
        super(context, attrs);
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CustomView, R.attr.defStyleAttr_custom, R.style.customview_style);
        String str = null;
        int textSize = 0;
        int textColor = 0;
        int n = typedArray.getIndexCount();
        for (int i = 0; i < n; i++){
            switch (typedArray.getIndex(i)){
                case R.styleable.CustomView_android_text:
                    str = typedArray.getString(typedArray.getIndex(i));
                    break;
                case R.styleable.CustomView_textsize:
                    textSize = typedArray.getInteger(typedArray.getIndex(i), 1);
                    break;
                case R.styleable.CustomView_textcolor:
                    textColor = typedArray.getInteger(typedArray.getIndex(i), 1);
                    break;

            }
        }
        Log.d(TAG, ">>>>>>>>>>>>>>>>> str: " + str);
        Log.d(TAG, ">>>>>>>>>>>>>>>>> textSize: " + textSize);
        Log.d(TAG, ">>>>>>>>>>>>>>>>> textColor: " + textColor);
        //xml布局文件的值通过Log打出来


        //资源回收
        typedArray.recycle();
    }
D/CustomView: >>>>>>>>>>>>>>>>> str: defStyleAttr defStyleAttr
D/CustomView: >>>>>>>>>>>>>>>>> textSize: 8
D/CustomView: >>>>>>>>>>>>>>>>> textColor: 7

结果一幕了然,当第三个参数不为:0时,第四个参数不起作用。,只有defStyleAttr设置为0或者在当前的theme中没有找到相关属性时,才会去defStyleRes中读取,那么很明显优先级是defStyleAttr更高

那么对于第三个参数呢,实际上用的还是比较多的,比如看系统的Button,EditText,它们都会在构造里面指定第三个参数:

提供一些参数的样式,比如background,textAppearance,textColor等,所以当我们切换不同的主题时,你会发现控件的样式会发生一些变化,就是因为不同的主题,设置了一些不同的style。

那么推演到我们自定义的View,如果你的属性非常多,你也可以去提供默认的style,然后让用户去设置到theme里面即可。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值