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 } } }