使用 XML 声明一个自定义的 android UI 元素

Posted

技术标签:

【中文标题】使用 XML 声明一个自定义的 android UI 元素【英文标题】:Declaring a custom android UI element using XML 【发布时间】:2011-02-11 08:16:42 【问题描述】:

如何使用 XML 声明 android UI 元素?

【问题讨论】:

如果有人寻找支持的内置属性格式列表,it can be found i.e. here。 很好的入门教程 -> Creating Compound Views on Android 【参考方案1】:

Google 似乎已经更新了它的开发者页面并在那里添加了各种培训。

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

【讨论】:

【参考方案2】:

Android 开发者指南中有一个名为 Building Custom Components 的部分。不幸的是,the discussion of XML attributes 只涉及在布局文件中声明控件,而不是实际处理类初始化中的值。步骤如下:

1。在values\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-styleable 标记中使用了非限定名称。像extraInformation 这样的非标准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 是自动生成的 int[] 资源,其中每个元素都是属性的 ID。通过将属性名称附加到元素名称来为 XML 中的每个属性生成属性。例如,R.styleable.MyCustomView_android_text 包含 MyCustomViewandroid_text 属性。然后可以使用各种get 函数从TypedArray 检索属性。如果该属性未在 XML 中定义,则返回 null。当然,如果返回类型是原始类型,则返回第二个参数。

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

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

请注意,您应该不要android.R.styleable 中使用任何内容,因为this thread 将来可能会更改。它仍然在文档中,因为在一个地方查看所有这些常量很有用。

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

在*** xml 元素中包含命名空间声明 xmlns:app="http://schemas.android.com/apk/res-auto"。命名空间提供了一种方法来避免不同模式使用相同元素名称时有时发生的冲突(有关更多信息,请参阅this article)。 URL 只是一种唯一标识模式的方式 - nothing actually needs to be hosted at that URL。如果这似乎没有做任何事情,那是因为您实际上不需要添加命名空间前缀,除非您需要解决冲突。

<com.mycompany.projectname.MyCustomView
    android:layout_
    android:layout_
    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_
    android:layout_
    app:text="Blue" app:textSize="20dp"/>

这包含在具有命名空间属性的LinearLayout 中:xmlns:app="http://schemas.android.com/apk/res-auto"

链接

*** Thread: Retrieving an XML attribute for custom control How do I use obtainStyledAttributes with internal themes of Android Defining custom attributes + list of supported attribute formats

【讨论】:

我想补充一点,如果您的根元素需要您的自定义命名空间,您必须同时添加标准 android 命名空间和您自己的自定义命名空间,否则您可能会遇到构建错误。 这个答案是我能找到的关于自定义 XML 参数的 Internet 上最清晰的资源。谢谢你,凯斯巴什。 由于某种原因,可视化编辑器拒绝使用 android:text 的书面文本值,但设备可以正常使用它。怎么来的? @androiddeveloper Eclipse 编辑器似乎拒绝使用所有 android: 属性的值。我想知道这是功能还是错误 xmlns:app namespace 和 res-auto 的作用是什么?【参考方案3】:

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

             <RelativeLayout
                android:layout_
                android:layout_
                android:layout_marginLeft="10dp"
                android:layout_marginRight="30dp" >

                <include
                    android:id="@+id/frnd_img_file"
                    android:layout_
                    android:layout_
                    layout="@layout/include_imagefile"/>

                <include
                    android:id="@+id/frnd_video_file"
                    android:layout_
                    android:layout_
                    layout="@layout/include_video_lay" />

                <ImageView
                    android:id="@+id/downloadbtn"
                    android:layout_
                    android:layout_
                    android:layout_centerInParent="true"
                    android:src="@drawable/plus"/>
            </RelativeLayout>

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

【讨论】:

我试过这个,我遇到的问题是包含的布局不能“适应”,不能创建泛型。例如,当我以类似的方式包含一个按钮时,如果我尝试在 xml 中设置文本,它确实可以工作。【参考方案4】:

非常感谢第一个回答。

至于我,我只有一个问题。当夸大我的观点时,我有一个错误: java.lang.NoSuchMethodException : MyView(Context, Attributes)

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

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

希望这会有所帮助!

【讨论】:

【参考方案5】:

添加到投票最多的答案。

获取样式属性()

当我们使用 android:xxx prdefined 属性创建自定义视图时,我想添加一些关于obtainStyledAttributes() 用法的内容。尤其是当我们使用 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();

我们可以在这里看到什么?obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) 属性集根据文档由主题处理。属性值是逐步编译的。首先从主题填充属性,然后将值替换为样式中的值,最后来自特定视图实例的 XML 中的精确值替换其他值。 请求的属性数组 - com.android.internal.R.styleable.TextView 它是一个普通的常量数组。如果我们请求标准属性,我们可以手动构建这个数组。

文档中未提及的内容 - 结果 TypedArray 元素的顺序。 在 attrs.xml 中声明自定义视图时,会生成属性索引的特殊常量。我们可以通过这种方式提取值:a.getString(R.styleable.MyCustomView_android_text)。但是对于手动 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>

也许,我弄错了,但有关 gainStyledAttributes() 的 Android 文档非常差。

扩展标准 UI 组件

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

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

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

【讨论】:

android:xxx 预定义属性在 eclipse gui 设计器中工作吗? 此类属性可被属性编辑器中的 Eclipse ADT 插件识别。如果未定义某些值,我可以从我的样式中看到默认值。并且不要忘记在你的类中添加@RemoteView 注解。 无法正常工作。 Eclipse 不断为 getText 加载空值,并为 getResourceId 抛出 android.content.res.Resources$NotFoundException,尽管应用程序在设备上运行良好。 对不起,我帮不了你。我只创建了演示项目来测试可能性,并没有遇到此类错误。 这比将自定义视图的自定义属性映射到其中包含的内置视图的内置属性要好得多。【参考方案6】:

很好的参考。谢谢! 一个补充:

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

鉴于库具有包“com.example.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" />

【讨论】:

这已在 ADT 17 预览版中修复。要使用库中的应用程序命名空间,请声明 xmlns:app="http://schemas.android.com/apk/res-auto" 请参阅 code.google.com/p/android/issues/detail?id=9656 中的评论 57 现在包含您的自定义命名空间会返回错误Suspicious namespace: Did you mean http://schemas.android.com/apk/res-auto 自定义命名空间以 res-auto 结尾,因为我们使用的是 Android Studio 和 Gradle。否则(例如某些 Eclipse 版本)它通常会以 lib/[你的包名] 结尾 自定义命名空间以 res-auto 结尾,因为我们使用的是 Android Studio 和 Gradle。否则(例如某些 Eclipse 版本)它通常会以 lib/[your package name] 结尾。即http://schemas.android.com/apk/lib/[your package name]

以上是关于使用 XML 声明一个自定义的 android UI 元素的主要内容,如果未能解决你的问题,请参考以下文章

Android 自定义视图 XML 中的复杂属性

Android 自定义ViewViewGroup和自定义属性

如何在 Android 中使用自定义权限?

android-自定义View

Android学习之自定义view详解

Android自定义View之实现流式布局