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

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


#1楼

Google似乎更新了其开发者页面,并在那里添加了各种培训。

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


#2楼

除了大多数投票的答案。

obtainStyledAttributes()

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

也许,我在某些方面有误,但是关于obtainStyledAttributes()的Android文档很差。

扩展标准UI组件

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

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

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


#3楼

非常感谢第一个答案。

至于我,我只有一个问题。 在给我的视图充气时,我有一个错误:

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

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

希望这会有所帮助!


#4楼

Android开发人员指南有一个名为Building Custom Components的部分 。 不幸的是, 对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>

请注意在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 ,按照这个线程可能在未来改变。 它仍然在文档中,因为在一个地方查看所有这些常量是有用的。

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

在顶级xml元素中包含名称空间声明xmlns:app="http://schemas.android.com/apk/res-auto" 。 命名空间提供了一种方法来避免在不同模式使用相同元素名称时有时会发生的冲突(有关详细信息,请参阅此文章 )。 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"
/> 

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

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

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

链接


#5楼

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

             <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布局文件。


#6楼

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

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

鉴于该库具有包“com.example.library.customview”并且工作项目具有包“com.example.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" />

将工作:

<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" />
发布了0 篇原创文章 · 获赞 2 · 访问量 6609

猜你喜欢

转载自blog.csdn.net/asdfgh0077/article/details/104082487
今日推荐