是否有可能在Android中添加由标准视图组成的自定义视图,并在代码中使用它,但布局只知道标准视图?



我有这个伪布局结构在一个单独的文件中表示自定义视图:

<FrameLayout>
<TextView/>
<Button/>
</FrameLayout>

,这是我的伪布局的片段:

<ConstraintLayout>
<TextView/>
<MyCustomView/>
<TextView/>
</ConstraintLayout>

我想在两个需要自己的内部TextViewButton的样式的地方重用MyCustomView,但我不知道用一种简单的方式来做到这一点。如果我可以将片段布局中的MyCustomView扩展为:

,则会容易得多。
<ConstraintLayout>
<TextView/>
<FrameLayout> <!--(or MyCustomView instead of FrameLayout, it would be a better indicator of what it is)-->
<TextView style="fancy_background"/>
<Button/>
</FrameLayout>
<TextView/>
</ConstraintLayout>

这在Android中是否可行?如果是,如何设置?

我会使用自定义属性。

假设你有一个"自定义视图"这需要一个标题和一个图像。您可以在attrs.xml中添加以下内容:

<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="MyCustomView">
<attr name="title" format="string|reference" />
<attr name="android:drawable" />
</declare-styleable>
</resources>

然后在代码中实现这个自定义视图:

(像这样…请记住,这是"伪代码",我是从我的自定义视图中获取的,但为了达到这个目的,它被大大简化了…)在这个自定义视图中,我可以提供标题和图像,还将使用自定义背景(不包括在演示中):)

class MyCustomView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
@AttrRes defStyle: Int = 0
) : ConstraintLayout(context, attrs, defStyle) {
private val title: MaterialTextView
private val image: ImageView
init {
View.inflate(context, R.layout.HERE_GOES_THE_LAYOUT, this).also {
title = it.findViewById(R.id.title)
image = it.findViewById(R.id.image
// set some properties (just an example)
it.setBackgroundResource(R.drawable.EXAMPLE_OF_A_BACKGROUND)
it.isClickable = true
it.isFocusable = true
// I want this view to be clickable and provide material feedback...
val foregroundResId = context.theme.getThemeAttributeValue(R.attr.selectableItemBackground, false)
it.foreground = context.theme.getDrawable(foregroundResId)
}

context.theme.obtainStyledAttributes(attrs, R.styleable.MyCustomView, defStyle, 0).run {
initProperties(this)
recycle()
}
}
private fun initProperties(typedArray: TypedArray) = with(typedArray) {
// The names of these attrs are a mix from attr.xml + the attr name.
getString(R.styleable.MyCustomView_title).also { setTitle(it) }
getDrawable(R.styleable.MyCustomView_android_drawable).also { setImage(it) }
}
private fun setTitle(title: CharSequence?) {
title.text = title
}
private fun setImage(source: Drawable?) {
source?.let {
image.setImageDrawable(it)
}
}
}

这是我的布局支持:R.layout.HERE_GOES_THE_LAYOUT

这里是一个简化的版本:

<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:layout_height="wrap_content"
tools:parentTag="androidx.constraintlayout.widget.ConstraintLayout">
<ImageView
android:id="@+id/image"
android:layout_width="40dp"
android:layout_height="40dp"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/title"
app:layout_constraintBottom_toBottomOf="parent" />
<com.google.android.material.textview.MaterialTextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
app:layout_constraintStart_toEndOf="@id/image"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toBottomOf="parent"
android:gravity="center_vertical" />
</merge>

怎么用呢?

在任何布局中!例如:

<Some ViewGroup like LinearLayout It Doesn't Matter .... >
<com.the.package.of.your.MyCustomView
android:id="@+id/yourCustomViewOne"
android:layout_width="match_parent"
android:layout_height="wrap_content"
<!-- HERE ARE THE TWO 'custom' ATTRIBUTES -->
android:drawable="@drawable/some_drawable_of_your_choice"
app:title="@string/some_string" />
<com.the.package.of.your.MyCustomView
android:id="@+id/yourCustomViewTwo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:drawable="@drawable/a_different_drawable_perhaps"
app:title="@string/and_also_a_different_string />
</Some ViewGroup like LinearLayout It Doesn't Matter>

希望你明白了。你可以添加更多自定义属性,甚至(像我一样)使用Android属性,并在内部赋予它你自己的含义(重写Android属性并滥用它可能会让你的用户感到困惑,所以要"聪明")。例如:如果上面的自定义视图有一个以上的图像,使用android:Drawable可能不是一个很好的选择,因为用户不会确定什么是可绘制的东西设置…在这个例子中,它工作得很好。因为你只有一张图片。

最后但并非最不重要的是,不要忽视可访问性!在你认为合适的地方提供描述,用TalkBack测试等等

这如何回答这个问题?

嗯,你不需要"展开"。对于自定义视图,您只需要封装动态属性,以便使用小部件/视图的任何人都可以通过XML(或编程)提供值,而您的自定义视图在幕后完成工作。

可以使用include + databinding

步骤1。为您的自定义视图创建如下布局:

<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="myText"
type="java.lang.String" />
</data>

<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{myText}" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</FrameLayout>
</layout>

步骤2。包含在Fragment/Activity Layout

<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
</data>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">

<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent">
<include
android:id="@+id/MyCustomView"
layout="@layout/my_custom_view"
app:myText="@{`aaa`}"
/>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

你可能需要使用绑定适配器来传递样式作为属性

最新更新