Android中的万能适配器——base-adapter-helper解析

在Android开发中,我们经常会用到ListView、GridView,每次编码的时候都需要为他们写对应的Adapter,写多了就感觉很烦躁了,因为基本的编程思想都是一样的,但是每次都要重复去写,所以我们能不能把它们抽象成一个通用的模板,这样就不用每次都重复写相同的代码了,直接重复使用,这样不是更好,下面我们就来介绍介绍一个开源项目base-adapter-helper。

传统Adapter的编码思路,主要看Adapter中的getView方法。

public View getView(int pos, View convertView, ViewGroup parent){ 
    ViewHolder holder; 
    if (convertView == null) { 
        convertView = mInflater.inflate(R.layout.list_item, null); 
        holder = new ViewHolder();  
        holder.text = (TextView) convertView.findViewById(R.id.text)); 
        holder.icon = (ImageView) convertView.findViewButId(R.id.icon)); 
        convertView.setTag(holder); 
    } else { 
        holder = (ViewHolder) convertView.getTag(); 
    }  
    holder.text.setText(DATA[pos]); 
    holder.icon.setImageBitmap((pos & 1) == 1 ? mIcon1 : mIcon2); 
    return convertView; 
}

static class ViewHolder { 
    TextView text; 
    ImageView icon; 
}

上面使用了一个ViewHolder用来缓存对应Item中的view,并且重用移出的Item,它对应的就是convertView。这样注意为了节省资源,提高效率。这种写法大家都应该很熟悉了。
下面来看看base-adapter-helper是怎样对其进行抽象封装的。首先来看看它的类继承图。

github链接:base-adapter-helper

Android中的万能适配器——base-adapter-helper解析_第1张图片

可以看到BaseQuickAdapter继承自BaseAdapter,同样我们重点关注它的getView函数。

@Override
public View getView(int position, View convertView, ViewGroup parent) {
    if (getItemViewType(position) == 0) {
        final H helper = getAdapterHelper(position, convertView, parent);
        T item = getItem(position);
        helper.setAssociatedObject(item);
        convert(helper, item);
        return helper.getView();
    }

    return createIndeterminateProgressView(convertView, parent);
}

private View createIndeterminateProgressView(View convertView, ViewGroup parent) {
    if (convertView == null) {
        FrameLayout container = new FrameLayout(context);
        container.setForegroundGravity(Gravity.CENTER);
        ProgressBar progress = new ProgressBar(context);
        container.addView(progress);
        convertView = container;
    }
    return convertView;
}

下面我们分析分析getView的代码:

  • 第3行代码就是获取该postion的Item类型,上面定义了两种类型的Item,一种是我们需要显示的View的Item,一种是底部的加载的View的Item。当Item类型为0是就为需要显示的Item,当Item类型为1是就为底部加载的Item,上面的第11行代码的createIndeterminateProgressView就是创建底部用来的加载的Item,可以看到它是一个ProgressBar。另外可以通过showIndeterminateProgress(boolean)来显示或者隐藏这个item。
Android中的万能适配器——base-adapter-helper解析_第2张图片
  • 第4行代码的getAdapterHelper获取一个BaseAdapterHelper对象,我们可以看到,在BaseQuickAdapter类中,getAdapterHelper是一个抽象函数,所以我们来看看BaseQuickAdapter的子类QuickAdapter,在这个类中它实现了getAdapterHelper方法,执行的实质是BaseAdapterHelper的静态get方法。另外需要提到的是,QuickAdapter类的构造函数有一个参数为layoutResId,它就是传入我们要显示Item的布局文件。
static BaseAdapterHelper get(Context context, View convertView, ViewGroup parent, int layoutId, int position) {
    if (convertView == null) {
        return new BaseAdapterHelper(context, parent, layoutId, position);
    }

    // Retrieve the existing helper and update its position
    BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
    existingHelper.position = position;
    return existingHelper;
}

从上面可以看到它也对convertView进行了重用,当convertView为null,那么我们需要创建一个BaseAdapterHelper,传入的就是要显示的位置position以及layoutId布局文件,这个position为前面getView中的那个position参数,layoutId为创建QuickAdapter传入的参数,就是Item的布局文件。
protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
    this.context = context;
    this.position = position;
    this.views = new SparseArray();
    convertView = LayoutInflater.from(context) //
            .inflate(layoutId, parent, false);
    convertView.setTag(this);
}
在BaseAdapterHelper的构造函数里面,定义了一个views,它其实就是传统Adapter里面的那个ViewHolder用来存放Item里面的各个view。convertView为我们要显示的Item的View,接着通过setTag函数将BaseAdapterHelper对象本身关联到convertView上面,所以我们知道每个Item对象都关联了一个BaseAdapterHelper对象。
当convertView不为null的时候,我们就直接可以复用这个convertView,首先从convertView中取出它关联的BaseAdapterHelper对象,这样就可以复用这个convertView对应的BaseAdapterHelper对象。
  • 接着分析最上面的代码,第5行代码getItem用来得到需要显示的数据,这里需要说明的是,我们应该把需要传入的数据放在一个List中,我们可以看看QuickAdapter的构造函数
public QuickAdapter(Context context, int layoutResId, List data) {
    super(context, layoutResId, data);
}
可以看到我们传入Item布局的layoutResId和要显示的数据data,data是List类型的。
  • 第6行代码是将我们向显示的数据项与BaseAdapterHelper对象关联起来。
  • 第7行代码convert函数同样是一个抽象函数,它需要我们进行实现,来具体设置对应的Item里面的内容。
  • 第8行代码是当BaseAdapterHelper对象设置为Item里面的内容之后,然后就可以得到这个Item的View对象进行返回。
下面我们来具体画个图来说明。假如我们要显示的是一个ListView。
当刚开始显示的时候,因为对应的convertView为null,所以会创建BaseAdapterHelper,核心代码如下:
protected BaseAdapterHelper(Context context, ViewGroup parent, int layoutId, int position) {
    this.context = context;
    this.position = position;
    this.views = new SparseArray();
    convertView = LayoutInflater.from(context) //
            .inflate(layoutId, parent, false);
    convertView.setTag(this);
}
Android中的万能适配器——base-adapter-helper解析_第3张图片
因为在BaseAdapterHelper对象中包含有对应的Item对应的convertView和对应的position以及 convertView里面的子view集合,因此我们可以直接通过BaseAdapterHelper对象来完成各项操作
Android中的万能适配器——base-adapter-helper解析_第4张图片

当List向上滑动的时候,第一个Item移出List,底部就需要再显示一个Item,这个时候getView里面的convertView就是第一个移出的View,我们可以直接对它重用来显示下一个Item,核心代码为:
BaseAdapterHelper existingHelper = (BaseAdapterHelper) convertView.getTag();
existingHelper.position = position;
他直接得到 BaseAdapterHelper对象,然后重新设置它对应的位置postion,因为BaseAdapterHelper对象中引用到了重用的convertView,这样就可以直接使用这个view的Item了。

下面来举个简单的例子:
public class MainActivity extends AppCompatActivity {
    private ListView listView;

    private List data = new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        data.add("text1");
        data.add("text2");
        data.add("text3");
        data.add("text4");
        data.add("text5");
        data.add("text6");

        listView = (ListView) findViewById(R.id.listview);
        QuickAdapter adapter = new QuickAdapter(this, R.layout.item, data) {
            @Override
            protected void convert(BaseAdapterHelper helper, String item) {
                helper.setText(R.id.textView, item);
            }
        };
        listView.setAdapter(adapter);
    }
    
}

也就是说我们只需要定义一个QuickAdapter重写convert方法就可以了,在convert方法里面我们直接使用 BaseAdapterHelper对象完成对数据内容item的设置就可以了。
Android中的万能适配器——base-adapter-helper解析_第5张图片

欢迎关注我的公众号:DroidMind

精品内容,独家发布


你可能感兴趣的:(第三方库)