定义自定义attrs

Ale*_*kov 449 android android-resources android-attributes

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

在官方文档中找不到任何内容,因此我需要有关如何定义这些attrs以及如何在我的代码中使用它们的信息.

Ric*_*ler 934

目前最好的文档是源.你可以在这里看一下(attrs.xml).

您可以在顶部<resources>元素或元素内部定义属性<declare-styleable>.如果我要在多个地方使用attr,我会把它放在根元素中.请注意,所有属性共享相同的全局命名空间.这意味着即使您在<declare-styleable>元素内部创建了一个新属性,也可以在其外部使用它,并且您无法创建具有不同类型的相同名称的另一个属性.

一个<attr>元素有两个xml属性nameformat.name让你称之为某种东西,这就是你最终在代码中引用它的方式,例如R.attr.my_attribute.根据所需format属性的"类型",属性可以具有不同的值.

  • 引用 - 如果它引用另一个资源ID(例如,"@ color/my_color","@ layout/my_layout")
  • 颜色
  • 布尔
  • 尺寸
  • 浮动
  • 整数
  • 分数
  • 枚举 - 通常是隐式定义的
  • flag - 通常是隐式定义的

您可以使用|,例如,将格式设置为多种类型format="reference|color".

enum 属性可以定义如下:

<attr name="my_enum_attr">
  <enum name="value1" value="1" />
  <enum name="value2" value="2" />
</attr>
Run Code Online (Sandbox Code Playgroud)

flag 属性是相似的,除了需要定义的值,以便它们可以一起进行位:

<attr name="my_flag_attr">
  <flag name="fuzzy" value="0x01" />
  <flag name="cold" value="0x02" />
</attr>
Run Code Online (Sandbox Code Playgroud)

除了属性,还有<declare-styleable>元素.这允许您定义自定义视图可以使用的属性.您可以通过指定<attr>元素来执行此操作,如果之前已定义,则不指定元素format.如果你想重用一个android attr,例如,android:gravity,那么你可以在name,如下所示.

自定义视图的示例<declare-styleable>:

<declare-styleable name="MyCustomView">
  <attr name="my_custom_attribute" />
  <attr name="android:gravity" />
</declare-styleable>
Run Code Online (Sandbox Code Playgroud)

在自定义视图中以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>
Run Code Online (Sandbox Code Playgroud)

最后,要访问该自定义属性,通常在自定义视图的构造函数中执行此操作,如下所示.

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();
}
Run Code Online (Sandbox Code Playgroud)

结束.:)

  • 这是一个示例项目,演示了与自定义`View`一起使用的自定义属性:http://github.com/commonsguy/cw-advandroid/tree/master/Views/ColorMixer/ (14认同)
  • 如果您使用的是库项目中的自定义attrs:请参阅此问题:http://stackoverflow.com/questions/5819369/error-no-resource-identifier-found-for-attribute-adsize-in-package-com-googl - 如果你使用`xmlns:my ="http://schemas.android.com/apk/lib/my.namespace"` - 没有复制attrs.xml似乎有效.请注意,名称空间URI路径必须是/ apk/*lib*not/apk/res. (7认同)
  • `a.recycle()`在这里释放内存非常重要 (5认同)
  • @ThomNichols的`apk/lib`技巧对我来说不适用于来自库项目的参考格式的自定义属性.什么_did_工作是使用`APK/RES-auto`,如http://stackoverflow.com/a/13420366/22904正下方,并建议在http://stackoverflow.com/a/10217752 (2认同)

Nei*_*ler 85

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

xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
Run Code Online (Sandbox Code Playgroud)

有:

xmlns:whatever="http://schemas.android.com/apk/res-auto"
Run Code Online (Sandbox Code Playgroud)

否则,使用该库的应用程序将出现运行时错误.

  • 我认为它比那个年龄大,但在Qberticus写完答案之后肯定会增加很久.完全没有让他失望,只是增加了一个有用的细节. (12认同)
  • 我更新了Qbericus的答案,使用apk/res-auto来节省混淆. (11认同)
  • 这只是最近刚补充的...我想在几个星期之前.肯定是在Qberticus写完答案之后很久就加入了. (3认同)

Ste*_*ing 15

除了几件事之外,上面的答案非常详细地介绍了所有内容.

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

其次,它没有涉及如何处理文化资源(数量字符串).使用TypedArray无法处理这些问题.这是我的SeekBarPreference中的代码片段,它根据首选项的值设置首选项的摘要格式化其值.如果首选项的xml将android:summary设置为文本字符串或字符串resouce,则将首选项的值格式化为字符串(它应该包含%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);
}
Run Code Online (Sandbox Code Playgroud)
  • 这仅作为示例给出,但是,如果您想要在首选项屏幕上设置摘要,则需要调用notifyChanged()首选项的onDialogClosed方法.


Hel*_*ios 5

传统方法充满了样板代码和笨拙的资源处理。这就是为什么我制作了Spyglass框架。为了演示其工作原理,下面的示例显示了如何制作显示String标题的自定义视图。

步骤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);
    }
}
Run Code Online (Sandbox Code Playgroud)

步骤2:在values/attrs.xml资源文件中定义一个字符串属性:

<resources>
    <declare-styleable name="CustomView">
        <attr name="title" format="string"/>
    </declare-styleable>
</resources>
Run Code Online (Sandbox Code Playgroud)

步骤3:将@StringHandler注释应用于该setTitle方法,以告知Spyglass框架在视图放大时将属性值路由到该方法。

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

现在您的类具有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();
}
Run Code Online (Sandbox Code Playgroud)

而已。现在,当您从XML实例化类时,Spyglass伴随程序将解释属性并进行所需的方法调用。例如,如果我们增加以下布局,则将setTitle"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>
Run Code Online (Sandbox Code Playgroud)

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

有关更多信息和示例,请查看Github存储库。