Best Practices for Android Custom BaseAdapter

Best Practices for Android Custom BaseAdapter

Although many new projects are using RecyclerView, many developers still prefer ListView or GridView in some scenarios, and then they need to write many Adapters. When the project team started a new project, a colleague brought a ***Adapter mentioned on the Internet. When using it, it was found that it was very inconvenient even in a single view type when the logic judgment was complicated, not to mention using multiple view types in the adapter. This is just a personal opinion, and it may not grasp the essence. This is a blog post about ***Adapter in the Android Rapid Development Series to create the best ListView GridView Adapter.

Of course, with the use of RecyclerView, there are also many blogs on the Internet about encapsulating RecyclerView's multi-view type Adapter. MultiType 3.0 is a relatively comprehensive Adapter written by a great god. This blog Android Complex Multi-Type List View New Writing Method: MultiType 3.0 has detailed usage. *** The adapter itself is not very convenient to use, so I refer to the implementation of the Adapter in RecyclerView to simply encapsulate the BaseAdapter. The purpose of encapsulation is to write less code and to make the logic look clearer. We know that in the implementation of RecyclerView's Adapter, it separates view creation from data binding, and also separates the search and creation of View. This article mainly introduces how to encapsulate the use of BaseAdapter to be consistent with the use of RecyclerView's Adapter. Since we often use simple view types in Adapter, that is, single-type views, this article encapsulates the single-view type Adapter separately, which uses stricter data type checks than the multi-view type Adapter, and is also much more convenient to use.

Use of Adapter in RecyclerView

When using RecyclerView's Adapter, we first need to inherit a static inner class Adapter of RecyclerView, and then rewrite three methods. In fact, the following three methods must be rewritten because they are all abstract methods.

  • getItemCount()
  • onBindViewHolder(VH holder, int position)
  • onCreateViewHolder(ViewGroup parent, int viewType)

Generally, you can rewrite the above three methods, but if there are multiple view types, in the third method

In the onCreateViewHolder() method, we can also see that there is a parameter called viewType. The function of this parameter is to create different ViewHolders for different viewTypes. Therefore, we need to rewrite a method getItemViewType(int position). The implementation method for multiple view types is very similar to that of BaseAdapter. In BaseAdapter, we need to rewrite a method getViewTypeCount() in addition to this, but this method is not required in the Adapter of RecyclerView.

Simple Type Adapter

  1. private class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
  2.  
  3. @Override
  4. public   int getItemCount() {
  5. return   COUNT ;
  6. }
  7.  
  8. @Override
  9. public void onBindViewHolder(MyViewHolder holder, int position) {
  10. holder.textView.setText( "TEXT_" + position);
  11. }
  12.  
  13. @Override
  14. public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  15. View   view = LayoutInflater. from (getApplicationContext()).inflate(R.layout.item_text, parent, false );
  16. MyViewHolder holder = new MyViewHolder( view );
  17. return holder;
  18. }
  19.  
  20. }
  21.  
  22. private static class MyViewHolder extends RecyclerView.ViewHolder {
  23. private TextView textView;
  24.  
  25. public MyViewHolder( View itemView) {
  26. super(itemView);
  27. textView = (TextView) itemView.findViewById(R.id.textView);
  28. }
  29. }

Complex Type Adapter

  1. private class MyAdapter extends RecyclerView.Adapter<ViewHolder> {
  2.  
  3. @Override
  4. public   int getItemCount() {
  5. return   COUNT ;
  6. }
  7.  
  8. @Override
  9. public   int getItemViewType( int position) {
  10. return position % 2 == 0 ? TYPE_IMAGE : TYPE_TEXT;
  11. }
  12.  
  13. @Override
  14. public void onBindViewHolder(ViewHolder holder, int position) {
  15. int type = getItemViewType(position);
  16. switch (type) {
  17. case TYPE_TEXT:
  18. ((MyTextHolder) holder).textView.setText( "TEXT_" + position);
  19. break;
  20. case TYPE_IMAGE:
  21. ((MyImageHolder) holder).imageView.setImageResource(R.drawable.image);
  22. break;
  23. }
  24. }
  25.  
  26. @Override
  27. public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  28. View   view ;
  29. ViewHolder holder = null ;
  30. switch (viewType) {
  31. case TYPE_TEXT:
  32. view = LayoutInflater. from (getApplicationContext()).inflate(R.layout.item_text, parent, false );
  33. holder = new MyTextHolder( view );
  34. break;
  35. case TYPE_IMAGE:
  36. view = LayoutInflater. from (getApplicationContext()).inflate(R.layout.item_image, parent, false );
  37. holder = new MyImageHolder( view );
  38. break;
  39. }
  40. return holder;
  41. }
  42. }
  43.  
  44. private class MyTextHolder extends RecyclerView.ViewHolder {
  45. private TextView textView;
  46.  
  47. public MyTextHolder( View itemView) {
  48. super(itemView);
  49. textView = (TextView) itemView.findViewById(R.id.textView);
  50. }
  51. }
  52.  
  53. private class MyImageHolder extends ViewHolder {
  54. private ImageView imageView;
  55.  
  56. public MyImageHolder( View itemView) {
  57. super(itemView);
  58. imageView = (ImageView) itemView.findViewById(R.id.imageView);
  59. }
  60. }

Custom BaseAdapter

Before customizing the base class, let's do a brief analysis. We need to customize an Adapter that supports a single view, and also customize an Adapter that supports multiple view types. Both classes must inherit BaseAdapter. First, extract the common part of the two classes and define it as MyAdapter.

  1. public abstract class MyAdapter<T> extends BaseAdapter {
  2.  
  3. protected List<T> dataList = new ArrayList<>();
  4. protected Context context;
  5. protected LayoutInflater inflater;
  6.  
  7. public MyAdapter(Context context) {
  8. this.context = context;
  9. inflater = LayoutInflater. from (context);
  10. }
  11.  
  12. public void setDataList(List<T> dataList) {
  13. this.dataList = dataList;
  14. notifyDataSetChanged();
  15. }
  16.  
  17. @Override
  18. public   int getCount() {
  19. if ( null == dataList) {
  20. return 0;
  21. }
  22. return dataList.size () ;
  23. }
  24.  
  25. @Override
  26. public T getItem( int position) {
  27. return dataList.get(position);
  28. }
  29.  
  30. @Override
  31. public long getItemId( int position) {
  32. return position;
  33. }
  34.  
  35. }

There is no getView() method in the Adapter implementation of RecyclerView. Let's analyze how to split the getView() method. Generally, we implement the getView() method in the following process.

  1. public   View getView( int position, View convertView, ViewGroup parent) {
  2. ViewHolder holder = null ;
  3. if ( null == convertView) {
  4. //Fill the layout
  5. convertView=inflater.inflate(R.layout.item_layout, parent, false );
  6. holder = new ViewHolder();
  7. //Query the control by ID
  8. holder.textView=(TextView)convertView.findViewById(R.id.textView);
  9. holder.imageView=(ImageView)convertView.findViewById(R.id.imageView);
  10. convertView .setTag(holder);
  11. } else {
  12. holder = (ViewHolder) convertView.getTag();
  13. }
  14. //Assignment logic
  15. return convertView;
  16. }
  17. //An empty ViewHolder
  18. public   static class ViewHolder{
  19. TextView textView;
  20. ImageView imageView;
  21. }

Isn't interface-oriented programming a popular programming method in Java? In Android development, there is also a development method called Holder-oriented programming. The above code is the traditional way to implement ViewHolder. In terms of implementation, it does not do anything, but just acts as a carrier to carry the controls we need. We let ViewHolder do more things, and put most of the logic that it needs to do when convertView==null into ViewHolder.

  1. public class ViewHolder {
  2. private final View itemView;
  3.  
  4. public ViewHolder( View itemView) {
  5. if ( null == itemView) {
  6. throw new IllegalArgumentException( "itemView must not be null" );
  7. } else {
  8. this.itemView = itemView;
  9. itemView.setTag(this);
  10. }
  11. }
  12.  
  13. public   View getItemView() {
  14. return itemView;
  15. }
  16. }

The itemView in ViewHolder is the convertView in the getView() method, which happens to be the root View of the item, similar to the itemView in the ViewHolder construction method in RecyclerView. Since different views need to create different ViewHolders, we can set the method for creating ViewHolder as an abstract method and expose it. In addition, when assigning values, we also need to assign values ​​according to specific business, and also set an abstract method.

  1. public abstract class SimpleAdapter<T,VH extends ViewHolder> extends MyAdapter<T> {
  2.  
  3. public SimpleAdapter(Context context) {
  4. super(context);
  5. }
  6.  
  7. public   View getView( int position, View convertView, ViewGroup parent) {
  8. VH holder = null ;
  9. if ( null == convertView) {
  10. holder = onCreateViewHolder(parent);
  11. convertView = holder.getItemView();
  12. } else {
  13. holder = (VH) convertView.getTag();
  14. }
  15. onBindViewHolder(holder, position);
  16. return convertView;
  17. }
  18.  
  19. public abstract void onBindViewHolder(VH holder, int position);
  20.  
  21. public abstract VH onCreateViewHolder(ViewGroup parent);
  22.  
  23. }

When setting up an Adapter with multiple view types, you only need to pass in an additional viewType parameter when creating the ViewHolder.

  1. public abstract class MultiAdapter<T> extends MyAdapter<T> {
  2.  
  3. public MultiAdapter(Context context) {
  4. super(context);
  5. }
  6.  
  7. @Override
  8. public   View getView( int position, View convertView, ViewGroup parent) {
  9. ViewHolder holder = null ;
  10. if ( null == convertView) {
  11. holder = onCreateViewHolder(parent, getItemViewType(position));
  12. convertView = holder.getItemView();
  13. } else {
  14. holder = (ViewHolder) convertView.getTag();
  15. }
  16. onBindViewHolder(holder, position);
  17. return convertView;
  18. }
  19.  
  20. public abstract void onBindViewHolder(ViewHolder holder, int position);
  21.  
  22. public abstract ViewHolder onCreateViewHolder(ViewGroup parent, int viewType);
  23.  
  24. }

Use of custom BaseAdapter

Single view type SimpleAdapter use

  1. public class TextAdapter extends SimpleAdapter<String, TextAdapter.TextHolder> {
  2.  
  3. public TextAdapter(Context context) {
  4. super(context);
  5. }
  6.  
  7. @Override
  8. public void onBindViewHolder(TextHolder holder, int position) {
  9. holder.textView.setText(getItem(position));
  10. }
  11.  
  12. @Override
  13. public TextHolder onCreateViewHolder(ViewGroup parent) {
  14. View convertView=inflater.inflate(R.layout.item_text, parent, false );
  15. return new TextHolder(convertView);
  16. }
  17.  
  18. static class TextHolder extends ViewHolder{
  19.  
  20. public TextView textView;
  21.  
  22. public TextHolder( View itemView) {
  23. super(itemView);
  24. textView=(TextView) itemView.findViewById(R.id.textView);
  25. }
  26. }
  27. }

Here we use two generics, one is the data type String supported in ViewHolder, and the other is the ViewHolder we need to create, so that the return value of the onCreateViewHolder method will automatically return our custom ViewHolder. For more information about generics, please refer to Java Generic Usage Analysis. The use of single view type Adapter is much more convenient than RecyclerView's Adapter.

Use of multiple view types

  1. public class RichAdapter extends MultiAdapter<String> {
  2.  
  3. private static final int TEXT = 0;
  4. private static final int PIC = 1;
  5.  
  6. public RichAdapter(Context context) {
  7. super(context);
  8. }
  9.  
  10. @Override
  11. public   int getViewTypeCount() {
  12. return 2;
  13. }
  14.  
  15. @Override
  16. public   int getItemViewType( int position) {
  17. if (position % 3 == 0) {
  18. return PIC;
  19. } else {
  20. return TEXT;
  21. }
  22. }
  23.  
  24. @Override
  25. public void onBindViewHolder(ViewHolder holder, int position) {
  26. switch (getItemViewType(position)) {
  27. case TEXT:
  28. TextHolder textHolder=(TextHolder) holder;
  29. textHolder.textView.setText(getItem(position));
  30. break;
  31. case PIC:
  32. ImageHolder imageHolder=(ImageHolder) holder;
  33. imageHolder.imageView.setImageResource(R.drawable.image);
  34. break;
  35. }
  36. }
  37.  
  38. @Override
  39. public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
  40. View itemView = null ;
  41. ViewHolder holder = null ;
  42. switch (viewType) {
  43. case TEXT:
  44. itemView = inflater.inflate(R.layout.item_text, parent, false );
  45. holder = new TextHolder(itemView);
  46. break;
  47. case PIC:
  48. itemView = inflater.inflate(R.layout.item_image, parent, false );
  49. holder = new ImageHolder(itemView);
  50. break;
  51. }
  52. return holder;
  53. }
  54.  
  55. private static class TextHolder extends ViewHolder {
  56. TextView textView;
  57.  
  58. public TextHolder( View itemView) {
  59. super(itemView);
  60. textView = (TextView) itemView.findViewById(R.id.textView);
  61. }
  62.  
  63. }
  64.  
  65. private static class ImageHolder extends ViewHolder {
  66. ImageView imageView;
  67.  
  68. public ImageHolder( View itemView) {
  69. super(itemView);
  70. imageView = (ImageView) itemView.findViewById(R.id.imageView);
  71. }
  72. }
  73. }

The usage here is almost identical to that of RecyclerView. The only difference is that an additional getViewTypeCount() method is written. This method must be overridden when ListView or GridView uses BaseAdapter to implement multiple types of views.

<<:  Android modularization exploration and practice

>>:  AI in Plain Language: Is it really that difficult to understand deep learning? Junior high school math in just 10 minutes

Recommend

Featured | How do major brands write Father’s Day copy?

Father's Day, as the name suggests, is a holi...

Is this plant called "Wintersweet" or "Wintersweet"?

As the Cold Dew solar term passes, the weather be...

Who "killed" Luo Yonghao?

A friend said he wanted to buy a ticket to attend...

Is it useful to arrange a Wenchang Tower before the exam?

Many parents ask whether the Wenchang Tower reall...

Wuwei Classroom Novice Trading Practice Class "Trading Market"

Introduction to the resources of "Trading Qu...

Where are the best cherry blossoms in China? Take this cherry blossom map

Cherry blossoms are a plant native to China. Wild...

How to gain user trust? You can start from these 9 aspects

When Dangdang held the “50 off for purchases over...

The material that made Sora a success! How good is BMI resin?

Recently, the US Open AI Research Center released...