代码之家  ›  专栏  ›  技术社区  ›  Alexander Oleynikov

定义自定义属性

  •  431
  • Alexander Oleynikov  · 技术社区  · 14 年前

    我需要实现我自己的属性,比如 com.android.R.attr

    在官方文档中找不到任何内容,所以我需要有关如何定义这些属性以及如何从代码中使用它们的信息。

    4 回复  |  直到 7 年前
        1
  •  906
  •   Austyn Mahoney Janak    11 年前

    目前最好的文档是源文件。你可以看一下 here (attrs.xml) .

    您可以在顶部定义属性 <resources> 元素或元素内部 <declare-styleable> 元素。如果要在多个地方使用attr,我将它放在根元素中。注意,所有属性共享相同的全局命名空间。这意味着即使在 <声明可样式> 元素可以在其外部使用,并且不能创建具有不同类型的相同名称的其他属性。

    <attr> 元素有两个XML属性 name format . 名称 让您称之为某物,这就是您最终在代码中引用它的方式,例如, R.attr.my_attribute . 这个 格式 根据所需属性的“类型”,属性可以具有不同的值。

    • 引用-如果它引用另一个资源ID(例如,“@color/my_color”,“@layout/my_layout”)。
    • 颜色
    • 布尔
    • 浮动
    • 整数
    • 一串
    • 分数
    • 枚举-通常隐式定义
    • 标志-通常隐式定义

    您可以使用 | ,例如, format="reference|color" .

    enum 属性定义如下:

    <attr name="my_enum_attr">
      <enum name="value1" value="1" />
      <enum name="value2" value="2" />
    </attr>
    

    flag 属性是相似的,但需要定义值以便将它们位或位一起使用:

    <attr name="my_flag_attr">
      <flag name="fuzzy" value="0x01" />
      <flag name="cold" value="0x02" />
    </attr>
    

    除了属性,还有 <声明可样式> 元素。这允许您定义自定义视图可以使用的属性。通过指定 <Atg≫ 元素,如果它以前定义过,则不指定 格式 . 如果您希望重用Android ATTR,例如,Android:Gravity,那么您可以在 名称 ,如下所述。

    自定义视图的示例 <声明可样式> :

    <declare-styleable name="MyCustomView">
      <attr name="my_custom_attribute" />
      <attr name="android:gravity" />
    </declare-styleable>
    

    在自定义视图上用XML定义自定义属性时,需要做一些事情。首先,必须声明一个名称空间才能找到属性。在根布局元素上执行此操作。通常只有 xmlns:android="http://schemas.android.com/apk/res/android" . 您现在还必须添加 xmlns:whatever="http://schemas.android.com/apk/res-auto" .

    例子:

    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout
      xmlns:android="http://schemas.android.com/apk/res/android"
      xmlns:whatever="http://schemas.android.com/apk/res-auto"
      android:orientation="vertical"
      android:layout_width="fill_parent"
      android:layout_height="fill_parent">
    
        <org.example.mypackage.MyCustomView
          android:layout_width="fill_parent"
          android:layout_height="wrap_content"
          android:gravity="center"
          whatever:my_custom_attribute="Hello, world!" />
    </LinearLayout>
    

    最后,要访问该自定义属性,通常在自定义视图的构造函数中这样做,如下所示。

    public MyCustomView(Context context, AttributeSet attrs, int defStyle) {
      super(context, attrs, defStyle);
    
      TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0);
    
      String str = a.getString(R.styleable.MyCustomView_my_custom_attribute);
    
      //do something with str
    
      a.recycle();
    }
    

    结束。:)

        2
  •  84
  •   Neil Miller    12 年前

    Qberticus的答案是好的,但缺少一个有用的细节。如果要在库中实现这些功能,请替换:

    xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
    

    用:

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

    否则,使用库的应用程序将有运行时错误。

        3
  •  15
  •   JJD    10 年前

    上面的答案非常详细地涵盖了所有事情,除了一些事情。

    首先,如果没有样式,那么 (Context context, AttributeSet attrs) 方法签名将用于实例化首选项。在这种情况下,只需使用 context.obtainStyledAttributes(attrs, R.styleable.MyCustomView) 去拿打字机。

    其次,它不涉及如何处理plaurals资源(数量字符串)。这些不能用typedarray处理。这里是seekbarpreference中的一段代码,它根据首选项的值设置首选项的摘要,并格式化其值。如果该首选项的xml将android:summary设置为文本字符串或字符串resuce,则该首选项的值将被格式化为字符串(该字符串中应包含%d,以获取该值)。如果android:summary设置为plaurals资源,则用于格式化结果。

    // Use your own name space if not using an android resource.
    final static private String ANDROID_NS = 
        "http://schemas.android.com/apk/res/android";
    private int pluralResource;
    private Resources resources;
    private String summary;
    
    public SeekBarPreference(Context context, AttributeSet attrs) {
        // ...
        TypedArray attributes = context.obtainStyledAttributes(
            attrs, R.styleable.SeekBarPreference);
        pluralResource =  attrs.getAttributeResourceValue(ANDROID_NS, "summary", 0);
        if (pluralResource !=  0) {
            if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {
                pluralResource = 0;
            }
        }
        if (pluralResource ==  0) {
            summary = attributes.getString(
                R.styleable.SeekBarPreference_android_summary);
        }
        attributes.recycle();
    }
    
    @Override
    public CharSequence getSummary() {
        int value = getPersistedInt(defaultValue);
        if (pluralResource != 0) {
            return resources.getQuantityString(pluralResource, value, value);
        }
        return (summary == null) ? null : String.format(summary, value);
    }
    

    • 这只是一个例子,但是,如果您想要在首选项屏幕上设置摘要,那么您需要调用 notifyChanged() 在偏好中 onDialogClosed 方法。
        4
  •  4
  •   Helios    7 年前

    传统的方法充满了样板代码和笨拙的资源处理。所以我做了 Spyglass framework . 为了演示它是如何工作的,下面是一个示例,演示如何创建一个显示字符串标题的自定义视图。

    步骤1:创建自定义视图类。

    public class CustomView extends FrameLayout {
        private TextView titleView;
    
        public CustomView(Context context) {
            super(context);
            init(null, 0, 0);
        }
    
        public CustomView(Context context, AttributeSet attrs) {
            super(context, attrs);
            init(attrs, 0, 0);
        }
    
        public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
            init(attrs, defStyleAttr, 0);
        }
    
        @RequiresApi(21)
        public CustomView(
                Context context, 
                AttributeSet attrs,
                int defStyleAttr,
                int defStyleRes) {
    
            super(context, attrs, defStyleAttr, defStyleRes);
            init(attrs, defStyleAttr, defStyleRes);
        }
    
        public void setTitle(String title) {
            titleView.setText(title);
        }
    
        private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
            inflate(getContext(), R.layout.custom_view, this);
    
            titleView = findViewById(R.id.title_view);
        }
    }
    

    步骤2:在 values/attrs.xml 资源文件:

    <resources>
        <declare-styleable name="CustomView">
            <attr name="title" format="string"/>
        </declare-styleable>
    </resources>
    

    第3步:应用 @StringHandler 注释到 setTitle 方法告诉SpyGlass框架在视图膨胀时将属性值路由到此方法。

    @HandlesString(attributeId = R.styleable.CustomView_title)
    public void setTitle(String title) {
        titleView.setText(title);
    }
    

    现在您的类有了SpyGlass注释,SpyGlass框架将在编译时检测它并自动生成 CustomView_SpyglassCompanion 班级。

    步骤4:使用自定义视图中生成的类 init 方法:

    private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        inflate(getContext(), R.layout.custom_view, this);
    
        titleView = findViewById(R.id.title_view);
    
        CustomView_SpyglassCompanion
                .builder()
                .withTarget(this)
                .withContext(getContext())
                .withAttributeSet(attrs)
                .withDefaultStyleAttribute(defStyleAttr)
                .withDefaultStyleResource(defStyleRes)
                .build()
                .callTargetMethodsNow();
    }
    

    就是这样。现在,当您从XML实例化类时,SpyGlass伙伴将解释属性并进行所需的方法调用。例如,如果我们膨胀下面的布局,那么 设置标题 将与呼叫 "Hello, World!" 作为论据。

    <FrameLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:width="match_parent"
        android:height="match_parent">
    
        <com.example.CustomView
            android:width="match_parent"
            android:height="match_parent"
            app:title="Hello, World!"/>
    </FrameLayout>
    

    该框架不仅限于字符串资源,还具有许多不同的注释来处理其他资源类型。它还具有用于定义默认值的注释,以及在方法具有多个参数时传递占位符值的注释。

    有关更多信息和示例,请查看Github回购。

    推荐文章