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

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

评论

这些文档可能比您的帖子更新,但是为了保持最新状态,您可以在此处找到关于属性的良好官方文档:developer.android.com/training/custom-views/…

我推荐一篇不错的文章,并提供有关自定义属性的示例:amcmobileware.org/android/blog/2016/09/11/custom-attributes

一个小的工作示例可能会有所帮助:github.com/yujiaao/MergeLayout1

#1 楼

当前,最好的文档是源。您可以在这里(attrs.xml)进行查看。

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

<attr>元素具有两个xml属性nameformatname可以让您称呼它,这就是最终在代码中引用它的方式,例如R.attr.my_attribute。根据需要的属性的“类型”,format属性可以具有不同的值。


引用-如果引用了另一个资源ID(例如“ @ color / my_color”,“ @ layout / my_layout”)
color
尺寸
浮点数
整数
字符串
分数
枚举-通常隐式定义
标志-通常隐式定义

您可以使用|将格式设置为多种类型,例如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>


除了属性外,还有<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>


在自定义视图上以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();
}


结束。 :)

评论


这是一个示例项目,展示了与自定义视图一起使用的自定义属性:github.com/commonsguy/cw-advandroid/tree/master/Views/…

– CommonsWare
2010年8月9日在16:13

如果您正在使用来自库项目的自定义属性:请参见以下问题:stackoverflow.com/questions/5819369/…-如果您使用xmlns:my =“ http://schemas.android.com/apk/lib,这似乎可以工作/my.namespace”-不复制attrs.xml。请注意,名称空间URI路径必须是/ apk / * lib *而不是/ apk / res。

–thom_nic
2012年3月1日15:17

@ThomNichols apk / lib技巧对我从库项目中引用格式的自定义属性不起作用。起作用的是使用apk / res-auto,如下面stackoverflow.com/a/13420366/22904和stackoverflow.com/a/10217752中所建议的那样

–朱利奥·潘卡斯特利(Giulio Piancastelli)
13年1月14日,11:28



引用@Qberticus:“标志属性相似,不同之处在于需要定义值,以便可以将它们进行位运算”。在我看来,这有点低估了枚举和标志之间的主要区别:前者让我们选择一个且只有一个值,后者让我们结合多个值。我在这里用类似的问题写了一个更长的答案,现在发现这个问题后,我想我可以链接到该问题。

–Rad Haring
2014年3月13日在18:36



a.recycle()在这里释放内存非常重要

– Tash Pemhiwa
16年1月21日在8:34

#2 楼

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

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




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


否则,使用该库将产生运行时错误。

评论


这只是最近才添加的...我认为是在几周前。当然,在Qberticus写下答案后很久就添加了它。

–ArtOfWarfare
2012年11月19日20:38



我认为它早于此,但肯定是在Qberticus写下答案后很久才添加的。一点也不在乎他,只是添加了有用的细节。

–尼尔·米勒(Neil Miller)
2012年11月26日在21:20

我已经更新了Qbericus的答案,以使用apk / res-auto来避免混乱。

–复杂度
13年2月7日在17:08

#3 楼

上面的答案涵盖了除细节之外的所有细节。首先,如果没有样式,则将使用(Context context, AttributeSet attrs)方法签名实例化首选项。在这种情况下,只需使用context.obtainStyledAttributes(attrs, R.styleable.MyCustomView)来获取TypedArray。其次,它不涉及如何处理多语言资源(数量字符串)。这些不能使用TypedArray处理。这是我的SeekBarPreference的代码片段,用于设置首选项摘要,并根据首选项的值将其值格式化。如果首选项的xml将android:summary设置为文本字符串或字符串源,则将首选项的值格式化为字符串(应该在其中包含%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 楼

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

步骤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框架将在编译时检测到它-time并自动生成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伴随程序将解释属性并进行所需的方法调用。例如,如果我们膨胀以下布局,则将以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>


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

查看Github存储库以获取更多信息和示例。

评论


You can achieve the same with Google Data Binding - if there is no attribute binding for specific attribute, GDB tries to find set* method and uses it instead. In this case you'd have to wrote, say android:title="@{"Hello, world!"}".

– Spook
Feb 4 '19 at 6:06

#5 楼

如果您从format元素中省略了attr属性,则可以使用它来引用XML布局中的类。


attrs.xml中的示例。
Android Studio理解从XML


ie




Refactor > Rename works

Find Usages works
依此类推...





不要在... / src / main / res / values中指定format属性/attrs.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>

    <declare-styleable name="MyCustomView">
        ....
        <attr name="give_me_a_class"/>
        ....
    </declare-styleable>

</resources>


在某些布局文件中使用它... / src / main / res / layout / activity__main_menu.xml

<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
    xmlns:app="http://schemas.android.com/apk/res-auto">

    <!-- make sure to use $ dollar signs for nested classes -->
    <MyCustomView
        app:give_me_a_class="class.type.name.Outer$Nested/>

    <MyCustomView
        app:give_me_a_class="class.type.name.AnotherClass/>

</SomeLayout>


在您的视图中解析该类的初始化代码... / src / main / java /.../ MyCustomView.kt

class MyCustomView(
        context:Context,
        attrs:AttributeSet)
    :View(context,attrs)
{
    // parse XML attributes
    ....
    private val giveMeAClass:SomeCustomInterface
    init
    {
        context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
        {
            try
            {
                // very important to use the class loader from the passed-in context
                giveMeAClass = context::class.java.classLoader!!
                        .loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
                        .newInstance() // instantiate using 0-args constructor
                        .let {it as SomeCustomInterface}
            }
            finally
            {
                recycle()
            }
        }
    }