首页 文章

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

提问于
浏览
450

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

6 回答

  • 11

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

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

  • 816

    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 使用 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_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"/>
    

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

    链接

  • 26

    很好的参考 . 谢谢!除此之外:

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

    鉴于该库具有包“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" />
    
  • 5

    除了大多数投票的答案 .

    obtainStyledAttributes()

    当我们使用android:xxx prdefined属性创建自定义视图时,我想添加一些关于obtainStyledAttributes()用法的文字 . 特别是当我们使用TextAppearance时 .
    正如"2. Creating constructors"中提到的,自定义视图在其创建时获取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中声明自定义视图时,会生成属性索引的特殊常量 . 我们可以这样提取 Value : 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方法解析并提供所有属性 .

  • 0

    非常感谢第一个答案 .

    至于我,我只有一个问题 . 在给我的视图充气时,我遇到了一个错误:java.lang.NoSuchMethodException:MyView(Context,Attributes)

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

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

    希望这会有所帮助!

  • 89

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

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

相关问题