打造ListView万能Adapter,且支持一行多列的显示方式

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_28779083/article/details/61419297

老规矩,先上效果图(以每行三列为例)

这里写图片描述

下面就是放出源码,最后再给出应用的实例:

为了更好的实现view的复用,我们将ViewHolder 进行了封装。MtjBaseViewHolder,包括了ImageView、Button、TextView等常用的控件都封装在内。自己可以根据需求,进行拓展。例如源码末尾,拓展出的“使用Glide为ImageView设置网络图片”。拓展的方法,相信一看就知道,无非就是通过传入的viewid去get这个控件——getTextView(viewid),然后set这个控件想展示的数据。OK,源码:

/**
 * Created by Administrator on 2017/1/2.
 */
public class MtjBaseViewHolder {

    /**
     * layout文件中的控件集合 SparseArray用法与HashMap类似,性能比HashMap更优
     */
    private SparseArray<View> mViews;
    /**
     * BaseAdapter中的getView方法中对应的参数
     */
    private View mConvertView;
    private Context context;

    /**
     * 私有,禁止外部实例化
     * 
     * @param context
     * @param parent
     *            BaseAdapter中的getView方法中对应的参数
     * @param layoutId
     *            layout资源文件ID
     */
    private MtjBaseViewHolder(Context context, ViewGroup parent, int layoutId) {
        this.mViews = new SparseArray<View>();
        this.mConvertView = LayoutInflater.from(context).inflate(layoutId,
                parent, false);
        this.mConvertView.setTag(this);
        this.context = context;
    }

    /**
     *
     * @param context
     * @param convertView
     *            BaseAdapter中的getView方法中对应的参数
     * @param parent
     *            BaseAdapter中的getView方法中对应的参数
     * @param layoutId
     *            layout资源文件ID
     * @return
     */
    public static MtjBaseViewHolder get(Context context, View convertView,
            ViewGroup parent, int layoutId) {
        if (convertView == null) {
            return new MtjBaseViewHolder(context, parent, layoutId);
        }
        return (MtjBaseViewHolder) convertView.getTag();
    }

    /**
     * 根据ViewID获取控件对象,先从mViews集合中查找, 如果存在则直接返回该对象;
     * 不存在则从布局文件中获取该对象,然后添加到mViews集合中,然后再返回该对象;
     * 
     * @param <T>
     * @param viewid
     *            控件ID
     * @param <t>
     * @return
     */
    @SuppressWarnings("unchecked")
    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;
    }

    /**
     * 返回BaseAdapter中的getView方法中对应的参数(convertView)
     * 
     * @return
     */
    public View getConvertView() {
        return mConvertView;
    }

    /**
     * 获取TextView控件
     * 
     * @param viewid
     *            控件ID
     * @return
     */
    public TextView getTextView(int viewid) {
        return (TextView) getView(viewid);
    }

    /**
     * 获取Button控件
     * 
     * @param viewid
     *            控件ID
     * @return
     */
    public Button getButton(int viewid) {
        return (Button) getView(viewid);
    }

    /**
     * 获取ImageView控件
     * 
     * @param viewid
     *            控件ID
     * @return
     */
    public ImageView getImageView(int viewid) {
        return (ImageView) getView(viewid);
    }

    /**
     * 获取ImageButton控件
     * 
     * @param viewid
     *            控件ID
     * @return
     */
    public ImageButton getImageButton(int viewid) {
        return (ImageButton) getView(viewid);
    }

    /**
     * 获取LinearLayout控件
     * 
     * @param viewid
     *            控件ID
     * @return
     */
    public LinearLayout getLinearLayout(int viewid) {
        return (LinearLayout) getView(viewid);
    }

    /**
     * 设置TextView内容
     * 
     * @param viewid
     *            TextView控件ID
     * @param content
     *            要设置的内容
     * @return
     */
    public MtjBaseViewHolder setText(int viewid, String content) {
        getTextView(viewid).setText(content);
        return this;
    }

    /**
     * 为ImageView设置图片
     * 
     * @param viewid
     *            ImageView控件ID
     * @param resid
     *            要设置的图片资源ID
     * @return
     */
    public MtjBaseViewHolder setImageResource(int viewid, int resid) {
        getImageView(viewid).setImageResource(resid);
        return this;
    }

    /**
     * 使用Glide为ImageView设置网络图片(需要导入Glide包)
     * 
     * @param viewid
     *            ImageView控件ID
     * @param img_path
     *            要设置的图片资源ID
     * @param default_img
     *            默认图片
     * @param load_type
     *            加载类型 【 0:正常 1:圆角(默认5dp圆角)2:圆形图片】
     * @param db
     *            load_type = 1 时 ,设置的图片圆角大小,使用默认大小时 传 0
     * 
     * @return
     */
    public MtjBaseViewHolder setImageGlide(int viewid, int img_path,
            int default_img, int load_type, int dp) {
        switch (load_type) {
        case 0:
            Glide.with(context).load(img_path).placeholder(default_img)
                    .error(default_img).crossFade().into(getImageView(viewid));
            break;
        case 1:
            if (dp <= 0) {
                Glide.with(context).load(img_path).placeholder(default_img)
                        .transform(new GlideRoundTransform(context))
                        .error(default_img).crossFade()
                        .into(getImageView(viewid));
            } else {
                Glide.with(context).load(img_path).placeholder(default_img)
                        .transform(new GlideRoundTransform(context, dp))
                        .error(default_img).crossFade()
                        .into(getImageView(viewid));
            }
            break;
        case 2:
            Glide.with(context).load(img_path).placeholder(default_img)
                    .transform(new GlideCircleTransform(context))
                    .error(default_img).crossFade().into(getImageView(viewid));
            break;
        default:
            break;
        }
        return this;
    }

}

最后那个“使用Glide为ImageView设置网络图片”是我项目中用到的,如果你们想要,需要导入相应的包。

接下来就是Adapter了,注解中写的比较清楚,就不废话了,直接代码:

public abstract class MtjBaseAdapter<T> extends BaseAdapter {

    protected Context mContext;
    protected List<T> listDatas = null;
    protected int mLayoutId;
    protected int column = 1;// 每行要显示的列数[默认是1]
    protected int line_int;// 计算得到的行数
    protected int column_yu;// 一行多列,不能整除时,最后一行的列数

    /**
     * 适配器
     * 
     * @param context
     *            上下文
     * @param data
     *            数据源
     * @param layoutId
     *            layout资源文件ID
     * @param setcolumn
     *            设置每行要显示的列数[默认是1]
     */
    public MtjBaseAdapter(Context context, List<T> data, int layoutId,
            int setcolumn) {
        this.mContext = context;
        this.listDatas = data;
        this.mLayoutId = layoutId;
        if (setcolumn >= 1) {
            column = setcolumn;
        }
    }

    @Override
    public int getCount() {
        column_yu = listDatas.size() % column;
        if (column_yu > 0) {
            line_int = listDatas.size() / column + 1;
        } else {
            line_int = listDatas.size() / column;
        }
        return line_int;
    }

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

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

    /**
     * 添加单条数据项
     * 
     * @param item
     */
    public void addItem(T item) {
        this.listDatas.add(item);
    }

    /**
     * 设置数据源
     * 
     * @param data
     */
    public void setListDatas(List<T> data) {
        this.listDatas = data;
    }

    /**
     * 清除数据源
     */
    public void clear() {
        this.listDatas.clear();
    }

    /**
     * 刷新数据源
     */
    public void refresh() {
        this.notifyDataSetChanged();
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        MtjBaseViewHolder holder = MtjBaseViewHolder.get(mContext, convertView,
                parent, mLayoutId);
        List<T> models = new ArrayList<T>();
        int[] positions = null;
        //可以被整除,正常返回每行的数据
        if (column_yu == 0) {
            positions = new int[column];
            for (int i = 0; i < column; i++) {
                int posi = position * column + i;
                T model = listDatas.get(posi);
                models.add(model);
                positions[i] = posi;
            }
        } else {
            //不能整除时,判断
            // 是否是最后一行,是,返回剩余的列的数据
            if (position == listDatas.size() / column) {
                positions = new int[column_yu];
                for (int i = 0; i < column_yu; i++) {
                    int posi = position * column + i;
                    T model = listDatas.get(posi);
                    models.add(model);
                    positions[i] = posi;
                }
            } else {
                //否,正常返回每行的数据
                positions = new int[column];
                for (int i = 0; i < column; i++) {
                    int posi = position * column + i;
                    T model = listDatas.get(posi);
                    models.add(model);
                    positions[i] = posi;
                }
            }
        }
        convert(holder, positions, models);
        return holder.getConvertView();
    }

    /**
     * 在子类中实现该方法
     * 
     * @param holder
     *            列表项
     * @param positions
     * @param models
     *            每行的数据集,每行有几列就返回几个model。第一列对应数据下标0,一一对应,以此类推。
     *            不满列数的设置setVisibility(View.INVISIBLE);
     */
    public abstract void convert(MtjBaseViewHolder holder, int[] positions,
            List<T> models);

}

因为一行可能有多列,所以在子类中实现的convert()方法返回的每行的数据集,在设置数据的时候,根据下标去遍历设置1、2、3。。。列的数据。

好了,很罗嗦的把源码给整完了,下面是实例的应用了:

1、首先是item.xml,例如每行三列,结构是这样的,代码就不贴了。。。:

这里写图片描述

这里写图片描述

我们在布局里先将每一个 ll_layout_1 设置为 android:visibility=”invisible”。是因为最后一行不是满列时,也要占位啊。。。

2、继承MtjBaseAdapter,在convert()方法里,实现自己的数据设置。

public class twoitemAdapter extends MtjBaseAdapter<SchoolModel> {

    public twoitemAdapter(Context context, List<SchoolModel> data,
            int layoutId, int setcolumn) {
        super(context, data, layoutId, setcolumn);
        // TODO Auto-generated constructor stub
    }

    @Override
    public void convert(MtjBaseViewHolder holder, int[] positions,
            List<SchoolModel> models) {
        // TODO Auto-generated method stub
        for (int i = 0; i < positions.length; i++) {
            switch (i) {
            case 0:
                holder.getLinearLayout(R.id.ll_layout_1).setVisibility(
                        View.VISIBLE);
                holder.setText(R.id.tv_zuo, models.get(i).getSchool_id());
                break;
            case 1:
                holder.getLinearLayout(R.id.ll_layout_2).setVisibility(
                        View.VISIBLE);
                holder.setText(R.id.tv_you, models.get(i).getSchool_id());
                break;
            case 2:
                holder.getLinearLayout(R.id.ll_layout_3).setVisibility(
                        View.VISIBLE);
                holder.setText(R.id.tv_san, models.get(i).getSchool_id());
                break;
            default:
                break;
            }
        }
    }
}

这里就体现了我们封装的MtjBaseViewHolder的优势了,例如: holder.setText(R.id.tv_zuo, models.get(i).getSchool_id());。我们只需要知道控件的id,和要设置数据就行了,什么view的复用都不要我们去想。

3、 最后就是为listview设置适配器了:

/**
     * 参数说明
     * @param context 上下文
     * @param data 数据源
     * @param layoutId layout的资源id
     * @param setcolumn 设置一行要显示几列【默认是1】
     */
twoitemAdapter adapter = new twoitemAdapter(getPageContext(), list,R.layout.item_two_count, 3)

OK!从实现的多列实例来看,简直就是分分钟啊,50行代码有没有??关键是万能啊!能满足大多数的需求,有没有???

遵循记录与分享的原则,欢迎留言批评。

关注公众号,长期分享技术干货:

这里写图片描述

猜你喜欢

转载自blog.csdn.net/qq_28779083/article/details/61419297