我如何声明一个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布局文件。

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

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

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

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

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

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

希望这对你有所帮助!

很好的参考。谢谢! 它的补充:

如果您碰巧包含一个库项目,该库项目为自定义视图声明了自定义属性,那么您必须声明您的项目名称空间,而不是库的名称空间。例如:

假定该库有包"com.example.library. library "。Customview”,工作项目有包“com.example”。customview”,那么:

将不工作(显示错误“错误:在包中没有为属性“newAttr”找到资源标识符? “com.example.library。customview”):

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.library.customview"
        android:id="@+id/myView"
        app:newAttr="value" />

将工作:

<com.library.CustomView
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res/com.example.customview"
        android:id="@+id/myView"
        app:newAttr="value" />