Cas*_*ash 835

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>
Run Code Online (Sandbox Code Playgroud)

请注意在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();
}
Run Code Online (Sandbox Code Playgroud)

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

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

int attrsWanted[]=new int[]{android.R.attr.text, R.attr.textColor};
Run Code Online (Sandbox Code Playgroud)

请注意,您应使用任何东西android.R.styleable,按照这个线程可能在未来改变.它仍然在文档中,因为在一个地方查看所有这些常量是有用的.

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

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"
/> 
Run Code Online (Sandbox Code Playgroud)

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

Android LabelView示例

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

LabelView.java

 TypedArray a=context.obtainStyledAttributes(attrs, R.styleable.LabelView);
 CharSequences=a.getString(R.styleable.LabelView_text);
Run Code Online (Sandbox Code Playgroud)

attrs.xml

<declare-styleable name="LabelView">
    <attr name="text"format="string"/>
    <attr name="textColor"format="color"/>
    <attr name="textSize"format="dimension"/>
</declare-styleable>
Run Code Online (Sandbox Code Playgroud)

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"/>
Run Code Online (Sandbox Code Playgroud)

它包含在LinearLayout带有namespace属性的a中:xmlns:app="http://schemas.android.com/apk/res-auto"

链接

  • 我想补充一点,如果你的根元素需要你的自定义命名空间,你将不得不添加标准的android命名空间和你自己的自定义命名空间,否则你可能会遇到构建错误. (14认同)
  • 这个答案是我能够找到的关于自定义XML参数的互联网上最清晰的资源.谢谢你,Casebash. (11认同)
  • xmlns的目的是什么:app namespace和res-auto? (4认同)
  • 由于某种原因,可视化编辑器拒绝使用android:text的书面文本值,但设备使用它就好了.怎么会 ? (2认同)
  • @androiddeveloper似乎Eclipse编辑器拒绝使用所有android:属性的值.我想知道它是否是一个功能或错误 (2认同)
  • @RobinJ:将其放入构造函数中。LayoutInflater.from(getContext())。inflate(R.layout.YOUR_LAYOUT,this);` (2认同)

And*_*ndy 91

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

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

鉴于该库具有包"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" />
Run Code Online (Sandbox Code Playgroud)

将工作:

<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" />
Run Code Online (Sandbox Code Playgroud)

  • 这已经在ADT 17预览中修复了.要从库中使用应用程序的命名空间,请声明`xmlns:app ="http://schemas.android.com/apk/res-auto"`参见http://code.google.com/p/android/中的注释57问题/细节?ID = 9656 (47认同)
  • 包括你的自定义命名空间现在返回错误`Suspicious namespace:你的意思是http:// schemas.android.com/apk/res-auto` (2认同)

yur*_*iss 27

除了大多数投票的答案.

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();
Run Code Online (Sandbox Code Playgroud)

我们在这里能看到什么?
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();
}
Run Code Online (Sandbox Code Playgroud)

定义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>
Run Code Online (Sandbox Code Playgroud)

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

扩展标准UI组件

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

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

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


小智 11

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

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


小智 5

非常感谢第一个答案.

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

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

public MyView(Context context, AttributeSet attrs) {
     super(context, attrs);
     // some code
}
Run Code Online (Sandbox Code Playgroud)

希望这会有所帮助!