Android experience high-scalability
Preface
This article shows you how to write a scalable adapter. This adapter is mainly used when there are multiple items, of course, only one type is also applicable
Implementation
Undoubtedly, we need to inherit BaseAdapter, override getCount, getItemId, getItem, getView, and other methods. We need to hold the dataset, so we should declare a List member variable and hold the Context object, in addition, this adapter is generic, so it should be generic, so the initial code should be like this.
Public abstract class BaseTypeAdapter
Extends BaseAdapter {protected List
MList; protected Context mContext; public BaseTypeAdapter (Context context, List
List) {mContext = context; mList = list;} @ Override public View getView (int position, View convertView, ViewGroup parent) {// to be implemented} @ Override public int getCount () {return mList. size () ;}@ Override public T getItem (int position) {return mList. get (position) ;}@ Override public long getItemId (int position) {return position ;}}
The conventional method reuse convertView in the getView method, and use the ViewHolder mode to bind data to the layout and event binding. Considering the scalability, these operations are all moved to a unified class for encoding. This class is the view rendering class AdapterTypeRender, and this view rendering class should have multiple subclass implementations, so we need to get an object like this, and the specific object we get is implemented by a subclass of our adapter. We only need to provide an abstract method, because the rendering class of the view rendering class may be different for each item, you also need to pass in a position method, as shown below:
public abstract AdapterTypeRender getAdapterTypeRender(int position);
Then implement our getView method. In this case, we refer to the ViewHolder mode. The general method is to create a new ViewHolder if convertView is null and bind it to convertView through setTag. Otherwise, the ViewHolder is obtained through getTag. In this way, our view rendering class also adopts this method.
@ Overridepublic View getView (int position, View convertView, ViewGroup parent) {AdapterTypeRender typeRender; if (null = convertView) {typeRender = getAdapterTypeRender (position ); // obtain this view rendering class convertView = typeRender through subclass implementation. getConvertView (); // get the rendered view convertView in the view rendering class. setTag (R. id. item_render_type, typeRender); // tag using the setTag method. Because setTag is used in multiple places, a key is given, which is located in ids. declare typeRender. bindEvents (); // event binding} else {TypeRender = (AdapterTypeRender) convertView. getTag (R. id. item_render_type); // otherwise, get} convertView through getTag. setTag (R. id. item_position, position); // because the binding event uses the same listener, you need to pass in the position. Here, you can directly use satTag, and then obtain if (null! = TypeRender) {T item = (T) getItem (position); // obtains the object class typeRender. bindDatas (item) of the corresponding item; // binds data} return convertView ;}
The corresponding ids. xml file is
The view rendering class has many implementations, so we should extract the public method as the interface, as shown below:
public interface AdapterTypeRender
{ View getConvertView(); void bindEvents(); void bindDatas(T item);}
To make code easier, we can implement a basic Renderer. Other Renderer can inherit this Renderer. This Renderer should have functions like ViewHolder to save the views of items. SparseArray is used here. This Renderer is generic because it is common. The encoding is as follows.
public abstract class BaseTypeRender
implements AdapterTypeRender
{ protected Context mContext; protected BaseTypeAdapter
mBaseTypeAdapter; protected View mConvertView; public BaseTypeRender(Context context, BaseTypeAdapter
baseTypeAdapter, int resID) { mContext = context; mBaseTypeAdapter = baseTypeAdapter; mConvertView = ((LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)).inflate(resID,null); } @Override public View getConvertView() { return mConvertView; } public static
V obtainView(View convertView, int id) { SparseArray
holder = (SparseArray
) convertView.getTag(); if (holder == null) { holder = new SparseArray
(); convertView.setTag(holder); } View childView = holder.get(id); if (childView == null) { childView = convertView.findViewById(id); holder.put(id, childView); } return (V) childView; }}
As mentioned above, we have reused a listener, so all event clicks and other events need to be marked with a position. In the getView method, we have set this position through the setTag method, so we need to encapsulate this listener, as shown below:
public abstract class OnCovertViewClickListener implements View.OnClickListener { private View mConvertView; private int positionId; public OnCovertViewClickListener(View convertView, int positionId) { mConvertView = convertView; this.positionId = positionId; } @Override public void onClick(View v) { int position=(int)mConvertView.getTag(positionId); onClickCallBack(v,position); } public abstract void onClickCallBack(View convertView, int position);}
So far, all our codes have basically ended, so let's apply them.
Practice
The effect we want to achieve is very simple, it is a simple chat interface, the layout is very simple, the code will not be posted, here first, otherwise the concept is not clear.
We can see from the figure that there are two types of items: one is the portrait on the left and the other is the portrait on the right. let's first implement the Renderer on the left, inherit from our basic Renderer. Obviously, there are only two views in this layout, one is the Avatar and the other is the message content. At the same time, we set event listening for this Renderer, this listener is defined in the adapter.
Public class TextFromRender extends BaseTypeRender
{Private TextView from; private ImageView photo; public TextFromRender (Context context, BaseTypeAdapter baseTypeAdapter) {super (context, baseTypeAdapter, R. layout. text_from_item) ;}@ Override public void bindEvents () {// listener OnCovertViewClickListener listener = new OnCovertViewClickListener (mConvertView, R. id. item_position) {@ Override public void onClickCallBack (View convertView, int position) {sw Itch (convertView. getId () {case R. id. photo: // if you click the Avatar MyAdapter. onMessageItemListener messageItemListener = (MyAdapter) mBaseTypeAdapter ). getOnMessageItemListener (); if (null! = MessageItemListener) {messageItemListener. onPhotoClicked (position); // callback} break; case R. id. from: // if you click the message MyAdapter. onMessageItemListener messageItemListener1 = (MyAdapter) mBaseTypeAdapter ). getOnMessageItemListener (); if (null! = MessageItemListener1) {messageItemListener1.onMessageClicked (position); // callback} break ;}}; // set listener obtainView (mConvertView, R. id. photo ). setOnClickListener (listener); obtainView (mConvertView, R. id. from ). setOnClickListener (listener);} @ Override public void bindDatas (Message item) {// bind data from = obtainView (mConvertView, R. id. from); from. setText (item. getContent ());}}
In general, we implement another Renderer, but we only bind data without setting event clicks.
public class TextToRender extends BaseTypeRender
{ private TextView to; public TextToRender(Context context, BaseTypeAdapter baseTypeAdapter) { super(context, baseTypeAdapter, R.layout.text_to_item); } @Override public void bindEvents() { } @Override public void bindDatas(Message item) { to= obtainView(mConvertView,R.id.to); to.setText(item.getContent()); }}
Then implement our Adapter. we need to define the listener interface used in the Renderer.
public interface OnMessageItemListener{ void onPhotoClicked(int position); void onMessageClicked(int position); } private OnMessageItemListener onChatItemListener; public void setOnMessageItemListener(OnMessageItemListener onChatItemListener) { this.onChatItemListener = onChatItemListener; } public OnMessageItemListener getOnMessageItemListener() { return onChatItemListener; }
In addition, inherit the BaseTypeAdapter method and rewrite several necessary methods. In particular, the abstract function getAdapterTypeRender of the parent class is rewritten, and the total number of item types.
public class MyAdapter extends BaseTypeAdapter
{ public MyAdapter(Context context, List
list) { super(context, list); } @Override public AdapterTypeRender getAdapterTypeRender(int position) { AdapterTypeRender
typeRender=null; switch (getItemViewType(position)){ case Message.TYPE_FROM: typeRender=new TextFromRender(mContext,this); break; case Message.TYPE_TO: typeRender=new TextToRender(mContext,this); break; } return typeRender; } @Override public int getItemViewType(int position) { return mList.get(position).getType(); } @Override public int getViewTypeCount() { return Message.TOTAL; } public interface OnMessageItemListener{ void onPhotoClicked(int position); void onMessageClicked(int position); } private OnMessageItemListener onChatItemListener; public void setOnMessageItemListener(OnMessageItemListener onChatItemListener) { this.onChatItemListener = onChatItemListener; } public OnMessageItemListener getOnMessageItemListener() { return onChatItemListener; }}
Our message entity class
public class Message{ public static final int TYPE_FROM=0x00; public static final int TYPE_TO=0x01; public static final int TOTAL=2; private int type; private String content; public int getType() { return type; } public void setType(int type) { this.type = type; } public String getContent() { return content; } public void setContent(String content) { this.content = content; }}
Finally, use it in Activity.
private void initView() { mListView= (ListView) findViewById(R.id.listview); mAdapter=new MyAdapter(this,mList); mListView.setAdapter(mAdapter); mAdapter.setOnMessageItemListener(new MyAdapter.OnMessageItemListener() { @Override public void onPhotoClicked(int position) { Toast.makeText(MainActivity.this, from photo:+position, Toast.LENGTH_SHORT).show(); } @Override public void onMessageClicked(int position) { Message message=mAdapter.getItem(position); Toast.makeText(MainActivity.this, from message:+message.getContent(), Toast.LENGTH_SHORT).show(); } }); }
Summary
To some extent, it is also an mvp mode, but the Adapter is used as the Presenter layer, for relevant articles, see a new idea of implementing the MVP model in android.
In fact, it seems very troublesome to write a lot of code, but in the long run, its extension is flexible and it is a good way to write. Mastering is a process of accumulating knowledge.