Android 创建复合视图
示例
阿化合物视图是一个自定义ViewGroup其会被视为由周围的程序代码的单一视图。这样的ViewGroup在类似DDD的设计中非常有用,因为它可以对应于一个聚合(在此示例中为Contact)。可以在显示联系人的所有位置重复使用它。
这意味着周围的控制器代码(“活动”,“片段”或“适配器”)可以简单地将数据对象传递到视图,而无需将其拆散到许多不同的UI小部件中。
这有助于代码重用,并根据SOLID原则进行更好的设计。
布局XML
这通常是您开始的地方。您已经有一些XML可以重用,可能是作为<include/>。将其解压缩到一个单独的XML文件中,并将根标记包装在一个<merge>元素中:
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="@+id/photo"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_alignParentRight="true" />
<TextView
android:id="@+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/photo" />
<TextView
android:id="@+id/phone_number"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/name"
android:layout_toLeftOf="@id/photo" />
</merge>此XML文件可以在AndroidStudio的布局编辑器中正常运行。您可以像对待其他任何布局一样对待它。
复合ViewGroup
获得XML文件后,创建自定义视图组。
import android.annotation.TargetApi;
import android.content.Context;
import android.os.Build;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.RelativeLayout;
import android.widget.ImageView;
import android.widget.TextView;
import myapp.R;
/**
* A compound view to show contacts.
*
* This class can be put into an XML layout or instantiated programmatically, it
* will work correctly either way.
*/
public class ContactView extends RelativeLayout {
//该类扩展了RelativeLayout,因为它带有自动
//(MATCH_PARENT,MATCH_PARENT)其子项的布局。你可以扩展
//如果您想要更多控制,则使用原始android.view.ViewGroup类。见
//注意在布局XML中为什么您不想扩展复杂的视图
//例如RelativeLayout。
//1.实现超类构造函数。
public ContactView(Context context) {
super(context);
init(context, null);
}
//省略了两个额外的构造函数,以简化示例
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public ContactView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
init(context, attrs);
}
//2.通过使用`this`作为父对象来扩展XML来初始化视图
private TextView mName;
private TextView mPhoneNumber;
private ImageView mPhoto;
private void init(Context context, AttributeSet attrs) {
LayoutInflater.from(context).inflate(R.layout.contact_view, this, true);
mName = (TextView) findViewById(R.id.name);
mPhoneNumber = (TextView) findViewById(R.id.phone_number);
mPhoto = (ImageView) findViewById(R.id.photo);
}
//3.定义域模型中表示的设置器。这就是例子
//所有关于。所有控制器代码都只能调用此setter而不是摆弄
//很多字符串,可见性选项,颜色,动画等。如果您不使用
//自定义视图,此代码通常会以静态辅助方法(错误)或副本结尾
//此代码的一部分将被复制粘贴到整个地方(更糟)。
public void setContact(Contact contact) {
mName.setText(contact.getName());
mPhoneNumber.setText(contact.getPhoneNumber());
if (contact.hasPhoto()) {
mPhoto.setVisibility(View.VISIBLE);
mPhoto.setImageBitmap(contact.getPhoto());
} else {
mPhoto.setVisibility(View.GONE);
}
}
}该init(Context,AttributeSet)方法是您读取任何自定义XML属性的地方,如将属性添加到视图中所述。
放置好这些片段后,您就可以在应用程序中使用它了。
在XML中的用法
这是一个示例fragment_contact_info.xml,说明如何将单个ContactView放在消息列表的顶部:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- The compound view becomes like any other view XML element -->
<myapp.ContactView
android:id="@+id/contact"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<android.support.v7.widget.RecyclerView
android:id="@+id/message_list"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"/>
</LinearLayout>代码中的用法
这RecyclerView.Adapter是显示联系人列表的示例。此示例说明了完全没有View操纵的情况下,控制器代码可获得多少清洁度。
package myapp;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.ViewGroup;
public class ContactsAdapter extends RecyclerView.Adapter<ContactsViewHolder> {
private final Context context;
public ContactsAdapter(final Context context) {
this.context= context;
}
@Override
public ContactsViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
ContactView v = new ContactView(context); // <--- this
return new ContactsViewHolder(v);
}
@Override
public void onBindViewHolder(ContactsViewHolder holder, int position) {
Contact contact = this.getItem(position);
holder.setContact(contact); // <--- this
}
static class ContactsViewHolder extendsRecyclerView.ViewHolder{
public ContactsViewHolder(ContactView itemView) {
super(itemView);
}
public void setContact(Contact contact) {
((ContactView) itemView).setContact(contact); // <--- this
}
}
}