【Android View】自定义属性

在自定义view过程中有时为了方便需要自定义属性,本篇就来总结回顾下这个知识点。

1、需求

在这里插入图片描述
如上,是一个Google登录的按钮,这里要求这个按钮在"登录"、“注册”页面上的文案是不同的,自定义view时暴漏出一个setText方法固然能够实现,但是为了在Activity/fragment文件中少写代码,我们还可以自定义属性。

2、实现过程

(1)自定义属性

在res/values/attrs.xml 文件下操作

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <!--自定义属性:注意这里name填自定义view的名字即可,固定写法-->
    <declare-styleable name="GoogleSignButton">
     <!-- 定义属性名为text,属性值为String类型,根据需求我们还可以定义为其他类型如reference、boolean-->
        <attr name="text" format="string" />
    </declare-styleable>
</resources>

(2)自定义view中处理自定义属性


class GoogleSignButton @JvmOverloads constructor(
    context: Context,
    attributeSet: AttributeSet?,
    defStyleAttr: Int = 0
) :
    ConstraintLayout(context, attributeSet, defStyleAttr) {

    val binding: LayoutBtnGoogleSigninBinding by BindViewGroup(R.layout.layout_btn_google_signin)

    init {
      initCustomAttrs(attributeSet)
    }
    // 处理自定义属性
    private fun initCustomAttrs(attributeSet:AttributeSet?) {
        val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.GoogleSignButton)
        for (i in 0 until typedArray.indexCount) {
            when (typedArray.getIndex(i)) {
                R.styleable.GoogleSignButton_text -> setText(typedArray.getText(typedArray.getIndex(i)).toString())
            }
        }
        typedArray.recycle()
    }

    fun setText(text: String) {
        binding.tvText.text = text
    }
}

(3)xml中使用

                <com.zenni.widgets.GoogleSignButton
                    app:text="@string/continue_with_google"
                    android:id="@+id/socialLogin"
                    android:layout_width="279dp"
                    android:layout_height="48dp" />
3、常见的两种属性处理方式

(1)使用AttributeSet#getAttributeValue(String namespace, String name)

  • getAttributeValue用来获取string类型的值,该类还提供了一系列重载如getAttributeBooleanValue、getAttributeIntValue、getAttributeFloatValue

  • namespace使用http://schemas.android.com/apk/res-auto这个字符串就行。name就是你的attrs.xml中定义的name

  • 最后代码中for (int i=0;i<attrs.getAttributeCount();i++) 从属性集合中获取。

(2) 使用TypedArray#obtainStyledAttributes(@Nullable AttributeSet set, @NonNull @StyleableRes int[] attrs)

  • set,构造中的参数拿来使用即可
  • attrs,固定写法R.styleable.自定义view类名。
4、小结

(1)对AttributeSet 的理解

可通过AttributeSet可以获得布局文件中定义的所有属性的key和value.

通过AttributeSet 就可以获得,那还要TypedArray干嘛?

其实AttributeSet 会有些弊端,比如textview的text属性你使用了引用字符串的方式,这时使用AttributeSet来获取值时要注意啦。而使用安卓提供的TypedArray可以轻易获取。

(2)TypedArray


public final TypedArray obtainStyledAttributes(@StyleableRes int[] attrs) 

public final TypedArray obtainStyledAttributes(@StyleRes int resid, @StyleableRes int[] attrs)
//自定义view中常用
public final TypedArray obtainStyledAttributes(AttributeSet set, @StyleableRes int[] attrs)

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

StyleableRes到底是啥?

其实你获取TypedArray 使用context.obtainStyledAttributes(attrs, R.styleable.SettingView)传递的第二个参数底层就生成了id数组这个id系统生成的。

(3)浅析TypedArray#obtainStyledAttributes

发现这四个方法底层都是调用Context类的getTheme().obtainStyledAttributes方法。再深入发现又调用了Resources类的obtainStyledAttributes方法,最终是Resources类的实现类ResourcesImpl的obtainStyledAttributes进行处理。

//ResourcesImpl.java
   TypedArray obtainStyledAttributes(@NonNull Resources.Theme wrapper,
                AttributeSet set,
                @StyleableRes int[] attrs,
                @AttrRes int defStyleAttr,
                @StyleRes int defStyleRes) {
            synchronized (mKey) {
                final int len = attrs.length;
                final TypedArray array = TypedArray.obtain(wrapper.getResources(), len);

                // XXX note that for now we only work with compiled XML files.
                // To support generic XML files we will need to manually parse
                // out the attributes from the XML file (applying type information
                // contained in the resources and such).
                final XmlBlock.Parser parser = (XmlBlock.Parser) set;
                mAssets.applyStyle(mTheme, defStyleAttr, defStyleRes, parser, attrs,
                        array.mDataAddress, array.mIndicesAddress);
                array.mTheme = wrapper;
                array.mXml = parser;
                return array;
            }
        }
           

这个我第一次是在RecyclerView提供的默认分割线实现类中看到


public class DividerItemDecoration extends ItemDecoration {
    ...
    ...
    private static final int[] ATTRS = new int[]{16843284}; // id 数组,16843284为某资源id值
    ...
    ...


    public DividerItemDecoration(Context context, int orientation) {
        TypedArray a = context.obtainStyledAttributes(ATTRS);  // 获取TypedArray对象
        this.mDivider = a.getDrawable(0);
        if (this.mDivider == null) {
            Log.w("DividerItem", "@android:attr/listDivider was not set in the theme used for this DividerItemDecoration. Please set that attribute all call setDrawable()");
        }

        a.recycle();
        this.setOrientation(orientation);
    }

初次碰到时会发现,不是自定义view中,AttributeSet肯定用不了了,只能使用TypedArray获取啦。

The end

参考

Android 深入理解Android中的自定义属性

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值