万用适配器(封装通用的基类GeneralAdapter)

本文介绍了如何在Android开发中创建一个通用的适配器GeneralAdapter,以提高ListView适配器的代码复用性。通过ViewHolder和SparseArray实现,详细讲解了通用ViewHolder和GeneralAdapter的编写,并提供了实例测试,展示了如何在实际项目中减少代码量并提升效率。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、万用适配器简介

  我们在做Android开发的时候,经常会用到ListView这个控件,我之前写过一篇文章:BaseAdapter与ListView解析。一般来说,我们都会为每一个ListView去建立一个适配器,那这样一个大型项目,不是要建立很多个适配器?!所以,为了提高代码的复用性,我们可以打造一个通用的适配器GeneralAdapter。


二、ViewHolder和GeneralAdapter的编写

  一般我们编写Adapter时都会用到ViewHolder,通过convertView.setTag与convertView进行绑定,然后当convertView已经加载过时,直接从与之对于的ViewHolder(getTag)中取得convertView布局中的控件。也就是说,实际上们每个convertView会绑定一个ViewHolder对象,这个viewHolder主要用于帮convertView存储布局中的控件。所以我们只要写出一个通用的ViewHolder,然后对于任意的convertView,提供一个对象让其setTag即可;所以我们使用容器,专门存每个Item布局中的所有控件,而且还要能够查找出来;既然需要查找,那么ListView肯定是不行了,需要一个键值对进行保存,键为控件的Id,值为控件的引用,我们使用比Map效率更高的SparseArray这个类。

以下是ViewHolder类的代码,具体的解释写在代码注释里面:

/**
 * Created by ZYQ on 2016/10/14.
 * 一个通用的ViewHolder类
 */
public class ViewHolder {

    // 使用了SparseArray<View>用于存储与之对于的convertView的所有的控件
    private SparseArray<View> mViews;
    private int mPosition;
    private View mConvertView;

    public ViewHolder(Context context, ViewGroup parent, int layoutID, int position) {

        this.mPosition = position;
        this.mViews = new SparseArray<View>();
        mConvertView = LayoutInflater.from(context).inflate(layoutID, parent, false);
        mConvertView.setTag(this);

    }

    // 拿到一个ViewHolder对象
    public static ViewHolder get(Context context, View convertView,
                                 ViewGroup parent, int layoutID, int position) {

        if (convertView == null) {
            return new ViewHolder(context, parent, layoutID, position);
        } else {
            ViewHolder holder = (ViewHolder) convertView.getTag();
            holder.mPosition = position;
            return holder;
        }
    }

    /**
     * 使用泛型T,返回值是View的一个子类
     * 通过viewID来获取控件
     * 如果没有则将控件添加到SparseArray容器中
     */
    public <T extends View> T getView(int viewID) {
        View view = mViews.get(viewID);
        if (view == null) {
            view = mConvertView.findViewById(viewID);
            mViews.put(viewID, view);
        }
        return (T) view;
    }

    public View getmConvertView() {
        return mConvertView;
    }

    /**
     * 封装setText方法
     * 通过getView获得并设置TextView的值
     */
    public ViewHolder setText(int viewID, String text) {
        TextView tv = getView(viewID);
        tv.setText(text);
        return this;
    }

    /**
     * 封装setImage方法
     * 通过getView获得并设置ImageView的图片
     */
    public ViewHolder setImage(int viewID, int imageResID) {
        ImageView imageView = getView(viewID);
        imageView.setImageResource(imageResID);
        return this;
    }
}

接下来我们需要的另一个重要的类就是GeneralAdapter。这个通用的Adapter一般需要保持一个List对象,存储一个Bean的集合,不同的ListView,Bean肯定是不同的,这个GeneralAdapter肯定需要支持泛型,内部维持一个List。

GeneralAdapter的代码如下,具体的解释写在代码注释里面:

/**
 * Created by ZYQ on 2016/10/15.
 * 将GeneralAdapter写成任何适配器的抽象基类
 * 只有getView()中的convert方法是需要子类去实现的
 */
public abstract class GeneralAdapter<T> extends BaseAdapter {

    // 使用泛型<T>可以装载多种Bean类型
    protected List<T> mDatas;
    protected Context mContext;
    protected LayoutInflater mInflater;
    protected int mLayoutID;

    public GeneralAdapter(Context context, List<T> datas, int layoutID) {
        this.mContext = context;
        this.mDatas = datas;
        mInflater = LayoutInflater.from(context);
        this.mLayoutID = layoutID;
    }

    @Override

    public int getCount() {
        return mDatas.size();
    }

    @Override
    public T getItem(int position) {
        return mDatas.get(position);
    }

    @Override
    public long getItemId(int position) {
        return position;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        // 实例化一个viewHolder
        ViewHolder viewHolder = ViewHolder.get(mContext, convertView, parent, mLayoutID, position);
        convert(viewHolder, getItem(position));
        return viewHolder.getmConvertView();
    }

    // 对外公布的convert方法,并将holder和Bean对象传出去
    public abstract void convert(ViewHolder holder, T t);
}

完成了GeneralAdapter的编写之后,我们再也不需要为每一个ListView编写一个Adapter了,我们只需要在Activity里面使用匿名内部类就行了。


三、万用适配器实例测试

在activity_main里面添加listView控件,然后编写item布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content">


    <ImageView
        android:id="@+id/imageView"
        android:layout_width="60dp"
        android:layout_height="60dp"
        android:src="@mipmap/ic_launcher"/>

    <TextView
        android:id="@+id/tv_title"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="Title"
        android:layout_toEndOf="@+id/imageView"
        android:gravity="center"
        android:textSize="25sp"/>

    <TextView
        android:id="@+id/tv_content"
        android:layout_width="match_parent"
        android:layout_height="30dp"
        android:text="Content what what what what"
        android:layout_toEndOf="@+id/imageView"
        android:layout_below="@id/tv_title"
        android:gravity="center_vertical"
        android:textSize="20sp"/>


</RelativeLayout>

接下来,我们只需要在MainActivity中实现ListView和初始化数据就好了(真正做项目开发的时候,为了降低代码的耦合度,还是建议遵循一定的设计模式,不要将数据的处理写在Activity里面,这里只是做个测试)

public class MainActivity extends AppCompatActivity {

    private  List<ItemBean> mDatas;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        initDatas();
        initView();

    }

    private void initDatas() {

        mDatas = new ArrayList<ItemBean>();
        for (int i = 0; i < 20; i++) {
            mDatas.add(new ItemBean(
                    R.mipmap.ic_launcher, "标题", "我是正文 我是正文 我是正文"));
        }

    }

    private void initView() {

        ListView mListView = (ListView) findViewById(R.id.listview_main);
        if (mListView != null) {
            // 使用匿名内部类简化代码
            mListView.setAdapter(new GeneralAdapter<ItemBean>(MainActivity.this, mDatas, R.layout.item) {
                @Override
                public void convert(ViewHolder holder, ItemBean itemBean) {
                    // 使用链式编程
                    holder.setImage(R.id.imageView, R.mipmap.ic_launcher)
                            .setText(R.id.tv_title, itemBean.getItemTitle())
                            .setText(R.id.tv_content, itemBean.getItemContent());
                }
            });
        }
    }

}

最终实现的效果图还是和上一篇文章一样:

这里写图片描述

这样一来,代码量是不是简化了不少呢?


The end! 。◕‿◕。

<think>我们计划封装一个通用的BaseRefreshFragment,用于处理使用SmartRefreshLayout的下拉刷新和上拉加载分页逻辑。 主要思路: 1. 定义基类,使用泛型来适应不同的数据模型(Data)和适配器Adapter)。 2. 基类封装分页相关的通用逻辑:页码管理、刷新和加载更多的触发、自动加载更多的设置等。 3. 提供抽象方法让子类实现,如获取数据的方法(loadData)、初始化RecyclerView(initRecyclerView)等。 4. 处理加载状态:刷新结束、加载结束、无更多数据等。 步骤: 1. 创建BaseRefreshFragment,继承自Fragment(这里以Fragment为例,Activity类似)。 2. 在布局中,我们约定使用SmartRefreshLayout包裹RecyclerView(布局由子类提供,但必须包含SmartRefreshLayout和RecyclerView)。 3. 基类中定义分页参数(如当前页码、每页数量)和状态标志(如是否正在加载、是否还有更多数据)。 4. 提供初始化RefreshLayout和RecyclerView的方法,并设置监听器。 5. 封装数据加载成功和失败后的回调方法,用于更新UI和分页状态。 注意:为了简化,我们假设使用ViewModel和LiveData来处理数据请求,但基类也可以不依赖这些,根据项目实际情况调整。 下面是一个基本的实现框架:</think>### Kotlin 中封装 SmartRefreshLayout 通用基类 `BaseRefreshFragment` 实现分页逻辑 下面通过完整代码实现一个高度可复用的分页逻辑基类,结合 `SmartRefreshLayout` 和 `ViewModel`: #### 1. 基类封装 `BaseRefreshFragment.kt` ```kotlin abstract class BaseRefreshFragment<Data, VM : BaseRefreshViewModel<Data>> : Fragment() { protected abstract val viewModel: VM protected abstract val adapter: BaseRecyclerAdapter<Data, *> protected lateinit var binding: FragmentBaseRefreshBinding // 布局必须包含 SmartRefreshLayout 和 RecyclerView abstract override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) initRefreshLayout() initRecyclerView() observeViewModel() loadFirstPage() } private fun initRefreshLayout() { binding.refreshLayout.apply { // 基础配置 setEnableRefresh(true) setEnableLoadMore(true) setEnableAutoLoadMore(true) // 滚动到底部自动加载 // 监听器 setOnRefreshListener { viewModel.refreshData() } setOnLoadMoreListener { viewModel.loadNextPage() } // 自定义头/尾部(可选) setRefreshHeader(ClassicsHeader(requireContext())) setRefreshFooter(ClassicsFooter(requireContext())) } } private fun initRecyclerView() { binding.recyclerView.apply { layoutManager = LinearLayoutManager(requireContext()) adapter = this@BaseRefreshFragment.adapter addItemDecoration(DividerItemDecoration(context, LinearLayoutManager.VERTICAL)) } } private fun observeViewModel() { viewModel.dataList.observe(viewLifecycleOwner) { result -> when (result) { is Resource.Success -> { adapter.submitList(result.data) handlePagingState(result.hasMore) } is Resource.Error -> { showError(result.message) finishRefreshAnimations(false) } Resource.Loading -> {/* 显示加载状态 */} } } } private fun handlePagingState(hasMore: Boolean) { binding.refreshLayout.apply { if (viewModel.isFirstPage()) { finishRefresh() if (!hasMore) setNoMoreData(true) } else { if (hasMore) finishLoadMore() else finishLoadMoreWithNoMoreData() } } } private fun finishRefreshAnim
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值