代码之家  ›  专栏  ›  技术社区  ›  Casebash

使用xml声明自定义android ui元素

  •  456
  • Casebash  · 技术社区  · 15 年前

    如何使用xml声明android ui元素?

    6 回复  |  直到 8 年前
        1
  •  823
  •   Community Mohan Dere    8 年前

    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 标签。非标准Android属性,如 extraInformation 需要声明它们的类型。在超类中声明的标记将在子类中可用,而无需重新声明。

    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 包含 android_text 属性 MyCustomView . 然后可以从 TypedArray 使用各种 get 功能.如果属性未在XML中定义,则 null 被退回。当然,如果返回类型是基元,则会返回第二个参数。

    如果不想检索所有属性,可以手动创建这个数组。 android.R.attr ,而此项目的属性位于 R.attr .

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

    请注意你应该 使用任何东西 android.R.styleable ,按 this thread 将来可能会改变。仍然在文档中,因为在一个地方查看所有这些常量是有用的。

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

    包括命名空间声明 xmlns:app="http://schemas.android.com/apk/res-auto" 在顶级xml元素中。名称空间提供了一种方法,可以避免不同架构使用相同元素名称时有时发生的冲突(请参见 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”

    链接

        2
  •  90
  •   Andy    14 年前

    很好的参考。谢谢! 附加条款:

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

    假设库具有包“com.example.library.customview”,而工作项目具有包“com.example.customview”,则:

    将无法工作(显示错误“error:在包中找不到属性“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" />
    
        3
  •  26
  •   yuriy.weiss    13 年前

    除了大多数投票的答案。

    获取StyledAttributes()

    在使用android:xxx prdefined属性创建自定义视图时,我想添加一些关于obtainStyledAttributes()用法的单词。尤其是当我们使用文本外观时。
    如“2”所述。“正在创建构造函数”,自定义视图在创建时获取属性集。我们可以在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
    它是一个普通的常数数组。如果我们请求标准属性,我们可以手动构建这个数组。

    文档中未提及的内容-结果类型darray元素的顺序。
    在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声明了很多属性。这是不可能的 在overriden onMeasure()和onDraw()中实现全部功能。

    但我们可以牺牲自定义组件在理论上的广泛重用。说“我很清楚我将使用什么功能”,然后 不要和任何人共享代码。

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

        4
  •  11
  •   Davis Broda    8 年前

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

    其中一个用于创建自定义视图,可以找到 here

        5
  •  5
  •   Austyn Mahoney Janak    12 年前

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

    至于我,我只有一个问题。当放大我的视野时,我发现了一个错误: java.lang.NoSuchMethodException:MyView(上下文,属性)

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

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

    希望这会有帮助!

        6
  •  0
  •   Akshay Paliwal    11 年前

    您可以将任何布局文件包含在其他布局文件中,如下所示-

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