我需要实现自己的属性,如在com.android.R.attr
在官方文档中没有发现任何东西,所以我需要关于如何定义这些attrs以及如何从我的代码中使用它们的信息。
我需要实现自己的属性,如在com.android.R.attr
在官方文档中没有发现任何东西,所以我需要关于如何定义这些attrs以及如何从我的代码中使用它们的信息。
当前回答
如果省略attr元素中的format属性,则可以使用它来引用XML布局中的类。
示例来自attrs.xml。 Android Studio理解类是从XML引用的 即。 重命名有效 找到用法 等等……
不要在…/src/main/res/values/attrs.xml中指定format属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
在布局文件/src/main/res/layout/activity__main_menu.xml中使用它
<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- make sure to use $ dollar signs for nested classes -->
<MyCustomView
app:give_me_a_class="class.type.name.Outer$Nested/>
<MyCustomView
app:give_me_a_class="class.type.name.AnotherClass/>
</SomeLayout>
/src/main/java/…/MyCustomView.kt
class MyCustomView(
context:Context,
attrs:AttributeSet)
:View(context,attrs)
{
// parse XML attributes
....
private val giveMeAClass:SomeCustomInterface
init
{
context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
{
try
{
// very important to use the class loader from the passed-in context
giveMeAClass = context::class.java.classLoader!!
.loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
.newInstance() // instantiate using 0-args constructor
.let {it as SomeCustomInterface}
}
finally
{
recycle()
}
}
}
其他回答
上面的答案涵盖了所有的细节,除了一些事情。
首先,如果没有样式,那么将使用(Context Context, AttributeSet attrs)方法签名来实例化首选项。在这种情况下,只需使用上下文即可。obtainStyledAttributes(attrs, r.s leable. mycustomview)来获取TypedArray。
Secondly it does not cover how to deal with plaurals resources (quantity strings). These cannot be dealt with using TypedArray. Here is a code snippet from my SeekBarPreference that sets the summary of the preference formatting its value according to the value of the preference. If the xml for the preference sets android:summary to a text string or a string resouce the value of the preference is formatted into the string (it should have %d in it, to pick up the value). If android:summary is set to a plaurals resource, then that is used to format the result.
// Use your own name space if not using an android resource.
final static private String ANDROID_NS =
"http://schemas.android.com/apk/res/android";
private int pluralResource;
private Resources resources;
private String summary;
public SeekBarPreference(Context context, AttributeSet attrs) {
// ...
TypedArray attributes = context.obtainStyledAttributes(
attrs, R.styleable.SeekBarPreference);
pluralResource = attrs.getAttributeResourceValue(ANDROID_NS, "summary", 0);
if (pluralResource != 0) {
if (! resources.getResourceTypeName(pluralResource).equals("plurals")) {
pluralResource = 0;
}
}
if (pluralResource == 0) {
summary = attributes.getString(
R.styleable.SeekBarPreference_android_summary);
}
attributes.recycle();
}
@Override
public CharSequence getSummary() {
int value = getPersistedInt(defaultValue);
if (pluralResource != 0) {
return resources.getQuantityString(pluralResource, value, value);
}
return (summary == null) ? null : String.format(summary, value);
}
这只是一个例子,但是,如果你想在首选项屏幕上设置摘要,那么你需要在首选项的onDialogClosed方法中调用notifyChanged()。
下面是创建自定义属性和视图的官方文档
Qberticus的回答很好,但是漏掉了一个有用的细节。如果你在库中实现这些替换:
xmlns:whatever="http://schemas.android.com/apk/res/org.example.mypackage"
:
xmlns:whatever="http://schemas.android.com/apk/res-auto"
否则,使用该库的应用程序将出现运行时错误。
传统的方法充满了样板代码和笨拙的资源处理。这就是我做望远镜框架的原因。为了演示它是如何工作的,这里有一个示例,展示如何创建一个显示String标题的自定义视图。
步骤1:创建自定义视图类。
public class CustomView extends FrameLayout {
private TextView titleView;
public CustomView(Context context) {
super(context);
init(null, 0, 0);
}
public CustomView(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs, 0, 0);
}
public CustomView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr, 0);
}
@RequiresApi(21)
public CustomView(
Context context,
AttributeSet attrs,
int defStyleAttr,
int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(attrs, defStyleAttr, defStyleRes);
}
public void setTitle(String title) {
titleView.setText(title);
}
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
}
}
步骤2:在values/attrs.xml资源文件中定义一个字符串属性:
<resources>
<declare-styleable name="CustomView">
<attr name="title" format="string"/>
</declare-styleable>
</resources>
步骤3:将@StringHandler注释应用到setTitle方法,告诉Spyglass框架在视图膨胀时将属性值路由到该方法。
@HandlesString(attributeId = R.styleable.CustomView_title)
public void setTitle(String title) {
titleView.setText(title);
}
现在你的类有了一个Spyglass注释,Spyglass框架将在编译时检测到它,并自动生成CustomView_SpyglassCompanion类。
步骤4:在自定义视图的init方法中使用生成的类:
private void init(AttributeSet attrs, int defStyleAttr, int defStyleRes) {
inflate(getContext(), R.layout.custom_view, this);
titleView = findViewById(R.id.title_view);
CustomView_SpyglassCompanion
.builder()
.withTarget(this)
.withContext(getContext())
.withAttributeSet(attrs)
.withDefaultStyleAttribute(defStyleAttr)
.withDefaultStyleResource(defStyleRes)
.build()
.callTargetMethodsNow();
}
就是这样。现在,当您从XML实例化类时,Spyglass同伴将解释属性并进行所需的方法调用。例如,如果我们放大下面的布局,那么setTitle将被调用,并以“Hello, World!”作为参数。
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:width="match_parent"
android:height="match_parent">
<com.example.CustomView
android:width="match_parent"
android:height="match_parent"
app:title="Hello, World!"/>
</FrameLayout>
框架并不局限于字符串资源,它有很多不同的注释来处理其他资源类型。它还有用于定义默认值的注释,以及用于在方法有多个参数时传入占位符值的注释。
查看Github回购以获得更多信息和示例。
如果省略attr元素中的format属性,则可以使用它来引用XML布局中的类。
示例来自attrs.xml。 Android Studio理解类是从XML引用的 即。 重命名有效 找到用法 等等……
不要在…/src/main/res/values/attrs.xml中指定format属性
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
....
<attr name="give_me_a_class"/>
....
</declare-styleable>
</resources>
在布局文件/src/main/res/layout/activity__main_menu.xml中使用它
<?xml version="1.0" encoding="utf-8"?>
<SomeLayout
xmlns:app="http://schemas.android.com/apk/res-auto">
<!-- make sure to use $ dollar signs for nested classes -->
<MyCustomView
app:give_me_a_class="class.type.name.Outer$Nested/>
<MyCustomView
app:give_me_a_class="class.type.name.AnotherClass/>
</SomeLayout>
/src/main/java/…/MyCustomView.kt
class MyCustomView(
context:Context,
attrs:AttributeSet)
:View(context,attrs)
{
// parse XML attributes
....
private val giveMeAClass:SomeCustomInterface
init
{
context.theme.obtainStyledAttributes(attrs,R.styleable.ColorPreference,0,0).apply()
{
try
{
// very important to use the class loader from the passed-in context
giveMeAClass = context::class.java.classLoader!!
.loadClass(getString(R.styleable.MyCustomView_give_me_a_class))
.newInstance() // instantiate using 0-args constructor
.let {it as SomeCustomInterface}
}
finally
{
recycle()
}
}
}