我如何声明一个Android UI元素使用XML?


当前回答

谷歌似乎更新了它的开发者页面,并在那里增加了各种培训。

其中一个处理自定义视图的创建,可以在这里找到

其他回答

谷歌似乎更新了它的开发者页面,并在那里增加了各种培训。

其中一个处理自定义视图的创建,可以在这里找到

非常感谢你的第一个回答。

至于我,我只有一个问题。当我夸大我的观点时,我有一个bug: java.lang.NoSuchMethodException: MyView(上下文,属性)

我通过创建一个新的构造函数来解决这个问题:

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}

希望这对你有所帮助!

除了投票最多的答案。

obtainStyledAttributes ()

我想添加一些关于obtainStyledAttributes()用法的话,当我们使用android:xxx预定义属性创建自定义视图时。特别是当我们使用TextAppearance时。 正如在“2.”创建构造函数”,自定义视图在创建时获取AttributeSet。主要用法我们可以在TextView源代码(API 16)中看到。

final Resources.Theme theme = context.getTheme();

// TextAppearance is inspected first, but let observe it later

TypedArray a = theme.obtainStyledAttributes(
            attrs, com.android.internal.R.styleable.TextView, defStyle, 0);

int n = a.getIndexCount();
for (int i = 0; i < n; i++) 
{
    int attr = a.getIndex(i);
    // huge switch with pattern value=a.getXXX(attr) <=> a.getXXX(a.getIndex(i))
}
a.recycle();

What we can see here? obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) Attribute set is processed by theme according to documentation. Attribute values are compiled step by step. First attributes are filled from theme, then values are replaced by values from style, and finally exact values from XML for special view instance replace others. Array of requested attributes - com.android.internal.R.styleable.TextView It is an ordinary array of constants. If we are requesting standard attributes, we can build this array manually.

文档中没有提到的——结果TypedArray元素的顺序。 当在attrs.xml中声明自定义视图时,将生成用于属性索引的特殊常量。我们可以这样提取值:a.getString(r.s leable. mycustomview_android_text)。但是对于manual int[],没有常量。我认为,getXXXValue(arrayIndex)可以正常工作。

另一个问题是:“我们如何替换内部常量,并请求标准属性?”我们可以使用android.R.attr。*值。

因此,如果我们想在自定义视图中使用标准的TextAppearance属性,并在构造函数中读取它的值,我们可以这样修改TextView中的代码:

ColorStateList textColorApp = null;
int textSize = 15;
int typefaceIndex = -1;
int styleIndex = -1;

Resources.Theme theme = context.getTheme();

TypedArray a = theme.obtainStyledAttributes(attrs, R.styleable.CustomLabel, defStyle, 0);
TypedArray appearance = null;
int apResourceId = a.getResourceId(R.styleable.CustomLabel_android_textAppearance, -1);
a.recycle();
if (apResourceId != -1)
{
    appearance = 
        theme.obtainStyledAttributes(apResourceId, new int[] { android.R.attr.textColor, android.R.attr.textSize, 
            android.R.attr.typeface, android.R.attr.textStyle });
}
if (appearance != null)
{
    textColorApp = appearance.getColorStateList(0);
    textSize = appearance.getDimensionPixelSize(1, textSize);
    typefaceIndex = appearance.getInt(2, -1);
    styleIndex = appearance.getInt(3, -1);

    appearance.recycle();
}

其中CustomLabel定义如下:

<declare-styleable name="CustomLabel">
    <!-- Label text. -->
    <attr name="android:text" />
    <!-- Label text color. -->
    <attr name="android:textColor" />
    <!-- Combined text appearance properties. -->
    <attr name="android:textAppearance" />
</declare-styleable>

也许,我弄错了某种方式,但Android文档obtainStyledAttributes()是非常差的。

扩展标准UI组件

同时,我们可以扩展标准的UI组件,使用它所有声明的属性。 这种方法不是很好,因为TextView for instance声明了很多属性。这是不可能的 在onMeasure()和onDraw()中实现完整的功能。

但是我们可以牺牲自定义组件理论上的广泛重用。说“我确切地知道我将使用什么功能”,然后 不要与任何人共享代码。

然后我们可以实现构造函数CustomComponent(Context, AttributeSet, defStyle)。 在调用super(…)之后,我们将通过getter方法解析所有属性并使其可用。

您可以将任何布局文件作为-包含在其他布局文件中

             <RelativeLayout
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="10dp"
                android:layout_marginRight="30dp" >

                <include
                    android:id="@+id/frnd_img_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_imagefile"/>

                <include
                    android:id="@+id/frnd_video_file"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    layout="@layout/include_video_lay" />

                <ImageView
                    android:id="@+id/downloadbtn"
                    android:layout_width="30dp"
                    android:layout_height="30dp"
                    android:layout_centerInParent="true"
                    android:src="@drawable/plus"/>
            </RelativeLayout>

这里include标签中的布局文件是同一res文件夹中的其他.xml布局文件。

Android开发者指南中有一个章节叫做构建自定义组件。不幸的是,关于XML属性的讨论只涉及在布局文件中声明控件,而没有实际处理类初始化中的值。具体步骤如下:

1. 在值\attrs.xml中声明属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="MyCustomView">
        <attr name="android:text"/>
        <attr name="android:textColor"/>            
        <attr name="extraInformation" format="string" />
    </declare-styleable>
</resources>

注意在declare- stylesable标记中使用了非限定名称。像extrinformation这样的非标准android属性需要声明它们的类型。在父类中声明的标记将在子类中可用,而无需重新声明。

2. 创建构造函数

由于有两个构造函数使用AttributeSet进行初始化,因此可以方便地创建一个单独的初始化方法供构造函数调用。

private void init(AttributeSet attrs) { 
    TypedArray a=getContext().obtainStyledAttributes(
         attrs,
         R.styleable.MyCustomView);

    //Use a
    Log.i("test",a.getString(
         R.styleable.MyCustomView_android_text));
    Log.i("test",""+a.getColor(
         R.styleable.MyCustomView_android_textColor, Color.BLACK));
    Log.i("test",a.getString(
         R.styleable.MyCustomView_extraInformation));

    //Don't forget this
    a.recycle();
}

R.styleable.MyCustomView is an autogenerated int[] resource where each element is the ID of an attribute. Attributes are generated for each property in the XML by appending the attribute name to the element name. For example, R.styleable.MyCustomView_android_text contains the android_text attribute for MyCustomView. Attributes can then be retrieved from the TypedArray using various get functions. If the attribute is not defined in the defined in the XML, then null is returned. Except, of course, if the return type is a primitive, in which case the second argument is returned.

如果不想检索所有属性,可以手动创建该数组。标准android属性的ID包含在android. r中。这个项目的属性在R.attr中。

int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};

请注意,你不应该在android.R中使用任何东西。可样式的,根据这个线程,它可能会在未来改变。它仍然在文档中被认为在一个地方查看所有这些常量是有用的。

3.在布局文件中使用它,例如layout\main.xml

Include the namespace declaration xmlns:app="http://schemas.android.com/apk/res-auto" in the top level xml element. Namespaces provide a method to avoid the conflicts that sometimes occur when different schemas use the same element names (see this article for more info). The URL is simply a manner of uniquely identifying schemas - nothing actually needs to be hosted at that URL. If this doesn't appear to be doing anything, it is because you don't actually need to add the namespace prefix unless you need to resolve a conflict.

<com.mycompany.projectname.MyCustomView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@android:color/transparent"
    android:text="Test text"
    android:textColor="#FFFFFF"
    app:extraInformation="My extra information"
/> 

使用完全限定名引用自定义视图。

Android LabelView样本

如果你想要一个完整的例子,看看android标签视图示例。

LabelView.java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);

attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>

custom_view_1.xml

<com.example.android.apis.view.LabelView
    android:background="@drawable/blue"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    app:text="Blue" app:textSize="20dp"/>

它包含在线性布局中,具有命名空间属性:

链接

StackOverflow线程:为自定义控件检索XML属性 我如何使用obtainStyledAttributes与内部主题的Android 定义自定义属性+支持的属性格式列表