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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2695646/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-20 06:59:00  来源:igfitidea点击:

Declaring a custom android UI element using XML

xmlandroiduser-interface

提问by Casebash

How do I declare an Android UI element using XML?

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

回答by Casebash

The Android Developer Guide has a section called Building Custom Components. Unfortunately, the discussion of XML attributesonly covers declaring the control inside the layout file and not actually handling the values inside the class initialisation. The steps are as follows:

Android 开发人员指南有一个部分称为构建自定义组件。不幸的是,对 XML 属性的讨论只涉及在布局文件中声明控件,而不是实际处理类初始化中的值。步骤如下:

1. Declare attributes in values\attrs.xml

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>

Notice the use of an unqualified name in the declare-styleabletag. Non-standard android attributes like extraInformationneed to have their type declared. Tags declared in the superclass will be available in subclasses without having to be redeclared.

请注意在declare-styleable标记中使用了非限定名称。非标准的 android 属性,比如extraInformation需要声明它们的类型。在超类中声明的标签将在子类中可用,而无需重新声明。

2. Create constructors

2. 创建构造函数

Since there are two constructors that use an AttributeSetfor initialisation, it is convenient to create a separate initialisation method for the constructors to call.

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

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.MyCustomViewis 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_textcontains the android_textattribute for MyCustomView. Attributes can then be retrieved from the TypedArrayusing various getfunctions. If the attribute is not defined in the defined in the XML, then nullis returned. Except, of course, if the return type is a primitive, in which case the second argument is returned.

R.styleable.MyCustomView是一个自动生成的int[]资源,其中每个元素都是一个属性的 ID。通过将属性名称附加到元素名称,为 XML 中的每个属性生成属性。例如,R.styleable.MyCustomView_android_text包含 的android_text属性MyCustomView。然后可以从TypedArray使用各种get函数中检索属性。如果该属性未在 XML 中定义,则null返回。当然,除非返回类型是原始类型,在这种情况下返回第二个参数。

If you don't want to retrieve all of the attributes, it is possible to create this array manually.The ID for standard android attributes are included in android.R.attr, while attributes for this project are in R.attr.

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

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

Please note that you should notuse anything in android.R.styleable, as per this threadit may change in the future. It is still in the documentation as being to view all these constants in the one place is useful.

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

3. Use it in a layout files such as layout\main.xml

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 articlefor 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.

xmlns:app="http://schemas.android.com/apk/res-auto"在顶级 xml 元素中包含命名空间声明。命名空间提供了一种方法来避免在不同模式使用相同元素名称时有时会发生冲突(有关详细信息,请参阅本文)。URL 只是一种唯一标识模式的方式 -实际上不需要在该 URL 上托管任何内容。如果这似乎没有做任何事情,那是因为您实际上不需要添加命名空间前缀,除非您需要解决冲突。

<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"
/> 

Reference the custom view using the fully qualified name.

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

Android LabelView Sample

Android LabelView 示例

If you want a complete example, look at the android label view sample.

如果您想要一个完整的示例,请查看 android 标签视图示例。

LabelView.java

标签视图.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

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"/>

This is contained in a LinearLayoutwith a namespace attribute: xmlns:app="http://schemas.android.com/apk/res-auto"

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

Links

链接

回答by Andy

Great reference. Thanks! An addition to it:

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

If you happen to have a library project included which has declared custom attributes for a custom view, you have to declare your project namespace, not the library one's. Eg:

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

Given that the library has the package "com.example.library.customview" and the working project has the package "com.example.customview", then:

鉴于该库具有包“com.example.library.customview”并且工作项目具有包“com.example.customview”,则:

Will not work (shows the error " error: No resource identifier found for attribute 'newAttr' in package 'com.example.library.customview'" ):

将不起作用(显示错误“错误:在包 'com.example.library.customview' 中找不到属性 'newAttr' 的资源标识符”):

<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" />

Will work:

将工作:

<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" />

回答by yuriy.weiss

Addition to most voted answer.

除了大多数投票的答案。

obtainStyledAttributes()

获得样式属性()

I want to add some words about obtainStyledAttributes() usage, when we create custom view using android:xxx prdefined attributes. Especially when we use TextAppearance.
As was mentioned in "2. Creating constructors", custom view gets AttributeSet on its creation. Main usage we can see in TextView source code (API 16).

当我们使用 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.

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

What is not mentioned in documentation - order of result TypedArray elements.
When custom view is declared in attrs.xml, special constants for attribute indexes are generated. And we can extract values this way: a.getString(R.styleable.MyCustomView_android_text). But for manual int[]there are no constants. I suppose, that getXXXValue(arrayIndex) will work fine.

文档中未提及的内容 - 结果 TypedArray 元素的顺序。
在 attrs.xml 中声明自定义视图时,会生成属性索引的特殊常量。我们可以提取值是这样的:a.getString(R.styleable.MyCustomView_android_text)。但是对于手动int[]来说,没有常量。我想, getXXXValue(arrayIndex) 会正常工作。

And other question is: "How we can replace internal constants, and request standard attributes?" We can use android.R.attr.* values.

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

So if we want to use standard TextAppearance attribute in custom view and read its values in constructor, we can modify code from TextView this way:

所以如果我们想在自定义视图中使用标准的 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();
}

Where CustomLabel is defined:

定义 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>

Maybe, I'm mistaken some way, but Android documentation on obtainStyledAttributes() is very poor.

也许,我在某种程度上弄错了,但是关于 getStyledAttributes() 的 Android 文档非常糟糕。

Extending standard UI component

扩展标准 UI 组件

At the same time we can just extend standard UI component, using all its declared attributes. This approach is not so good, because TextView for instance declares a lot of properties. And it will be impossible to implement full functionality in overriden onMeasure() and onDraw().

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

But we can sacrifice theoretical wide reusage of custom component. Say "I know exactly what features I will use", and don't share code with anybody.

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

Then we can implement constructor CustomComponent(Context, AttributeSet, defStyle). After calling super(...)we will have all attributes parsed and available through getter methods.

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

回答by mitch000001

It seems that Google has updated its developer page and added various trainings there.

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

One of them deals with the creation of custom views and can be found here

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

回答by user2346922

Thanks a lot for the first answer.

非常感谢第一个回答。

As for me, I had just one problem with it. When inflating my view, i had a bug : java.lang.NoSuchMethodException : MyView(Context, Attributes)

至于我,我只有一个问题。膨胀我的视图时,我有一个错误: java.lang.NoSuchMethodException : MyView(Context, Attributes)

I resolved it by creating a new constructor :

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

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

Hope this will help !

希望这会有所帮助!

回答by Akshay Paliwal

You can include any layout file in other layout file as-

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

             <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>

here the layout files in include tag are other .xml layout files in the same res folder.

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