【Android】通用系列 —— 用简单通用的方式操作ListView

【关键词】

通用系列 ListView


【问题】
  • 在使用ListView的过程中,总是不断的重复写Adapter,ViewHolder等代码,很繁琐;
  • 想个办法,不论是简单的Item布局还是复杂的Item布局,都可以用同一种方案快速实现出来;
【效果图】

【分析】

/**

 * 特色功能:

 * ★ 通用(使用ListView和GridView的地方都可以用此类来简化)

 * ★ 简单,不需要重写BaseAdapter;

 * ★ 删除伴随着动画(不突兀);

 * ★ 数据和ListView都托管给了LouAdapter;

 * ★ 强制使用ViewHolder缓存;

 * ★ 局部刷新;

 * ★ 多选模式/单选模式的控制;(需要给Item布局设置selector才可以在视觉上区分是否为选中模式)

 * ★ 绑定ListView到this(即在使用的时候不用给ListView 设置Adapter);

 * ★ 以前在使用ListView的时候需要做这些处理:

 * {@code

 * // 查找ListView;

 * // 设置Adapter;

 * // 如果布局复杂的话还需要重新Adapter;

 * // 绑定数据;

 * }

 * ★ 现在只需要简单分配参数,即使是复杂的布局也可以很快实现出来:

 * {@code

 * ListView listView = new ListView(mContext);

 * int layoutId = android.R.layout.simple_list_item_1;

 * // <> 中的内容是你的Bean元素;

 * // 第一个参数(listView):被托管的ListView;

 * // 第二个参数(layoutId):ListView的Item布局;

 * // 方法assign:用来分配Bean和Item布局里视图的对应关系;

 * LouAdapter<String> adapter = new LouAdapter<String>(listView, layoutId) { @Override protected void assign(ViewHolder holder, String s) {holder.putText(android.R.id.text1, s);}};

 * }

 */
【解决方案】
  • 将ListView和数据都托管给LouAdapter,后期修改数据和更新视图的过程中,只需引用LouAdapter的实例即可;

    换句话说,只在实例化LouAdapter的时候用到了ListView,其他时候的数据操作都只是对LouAdapter的简单引用;

  • 至于视图和Bean的映射,通过assign方法分配即可;
  • 修改某个Item的数据后,通过获取对应的Item视图进行单独刷新;

【代码】

「效果图的代码」


package com.lou.as.lou;

 

import android.app.Activity;

import android.os.Bundle;

import android.view.View;

import android.widget.AdapterView;

import android.widget.ListView;

 

import com.lyloou.android.activity.LouActivity;

import com.lyloou.android.adapter.LouAdapter;

import com.lyloou.android.util.Utoast;

 

import java.util.Arrays;

 

public class DemoAdapterActivity extends LouActivity {

 

    private Activity mContext;

 

    @Override

    protected void onCreate(Bundle savedInstanceState) {

        super.onCreate(savedInstanceState);

        mContext = this;

        setContentView(initListView());

    }

 

    private View initListView() {

        final LouAdapter<String> adapter = new LouAdapter<String>(new ListView(mContext), android.R.layout.simple_list_item_1) {

            @Override

            protected void assign(ViewHolder holder, String s) {

                // 用视图显示数据

                holder.putText(android.R.id.text1, s);

            }

        };

 

        // 初始化列表

        adapter.initList(Arrays.asList("添加元素", "长按更新元素"));

 

        // 添加点击事件

        adapter.getBindView().setOnItemClickListener(new AdapterView.OnItemClickListener() {

            @Override

            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {

                if (adapter.getItem(position).equals("添加元素")) {

                    // 添加元素

                    adapter.addItem("我是新元素" + adapter.getCount());

                } else if (adapter.getItem(position).equals("删除我呀")) {

                    // 删除元素(有删除动画)

                    adapter.deleteItemWithAnim(position);

                } else if (adapter.getItem(position).contains("我是新元素")) {

                    adapter.getList().set(position, "删除我呀");

                    adapter.updateView(position);

                } else if (adapter.getItem(position).equals("好吧,我投降")) {

                    adapter.deleteItemWithAnim(position);

                }

            }

        });

        adapter.getBindView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {

            @Override

            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {

 

                if (adapter.getItem(position).equals("长按更新元素")) {

                    adapter.getList().set(position, "已被更新的元素");

                    adapter.updateView(position); // 局部更新

                } else if (adapter.getItem(position).equals("已被更新的元素")) {

                    adapter.getList().set(position, "都更新过了,还按我做啥");

                    adapter.updateView(position);

                } else if (adapter.getItem(position).equals("都更新过了,还按我做啥")) {

                    adapter.getList().set(position, "再按我,我就消失");

                    adapter.updateView(position);

                } else if (adapter.getItem(position).equals("再按我,我就消失")) {

                    adapter.getList().set(position, "好吧,我投降");

                    adapter.updateView(position);

                } else if (adapter.getItem(position).equals("添加元素")) {

                    Utoast.show(mContext, "滚犊子,连我也想删?\n活得不耐烦了!");

                    return true;

                }

                return false;

            }

        });

 

        return adapter.getBindView();

    }

}


「通用源代码」


package com.lyloou.android.adapter;

 

import android.content.Context;

import android.graphics.Bitmap;

import android.graphics.Color;

import android.graphics.PorterDuff;

import android.graphics.drawable.Drawable;

import android.util.SparseArray;

import android.util.SparseBooleanArray;

import android.view.LayoutInflater;

import android.view.View;

import android.view.ViewGroup;

import android.view.animation.Animation;

import android.view.animation.Transformation;

import android.widget.AbsListView;

import android.widget.BaseAdapter;

import android.widget.CheckBox;

import android.widget.CompoundButton;

import android.widget.ImageView;

import android.widget.TextView;

 

import com.lyloou.android.util.Uscreen;

import com.lyloou.android.util.Uview;

 

import java.util.ArrayList;

import java.util.List;

 

/**

* Created by Lou on 2016/3/23.

* Updated By Lou on 2016/05/07.

*/

 

/**

* 特色功能:

* ★ 通用(使用ListView和GridView的地方都可以用此类来简化)

* ★ 简单,不需要重写BaseAdapter;

* ★ 删除伴随着动画(不突兀);

* ★ 动态添加元素;

* ★ 强制使用ViewHolder缓存;

* ★ 局部刷新;

* ★ 多选模式/单选模式的控制;

* ★ 绑定ListView到this(即在使用的时候不用给ListView 设置Adapter);

* ★ 以前在使用ListView的时候需要做这些处理:

* {@code

* // 查找ListView;

* // 设置Adapter;

* // 如果布局复杂的话还需要重新Adapter;

* // 绑定数据;

* }

* ★ 现在只需要简单分配参数,即使是复杂的布局也可以很快实现出来:

* {@code

* ListView listView = new ListView(mContext);

* int layoutId = android.R.layout.simple_list_item_1;

* // <> 中的内容是你的Bean元素;

* // 第一个参数(listView):被托管的ListView;

* // 第二个参数(layoutId):ListView的Item布局;

* // 方法assign:用来分配Bean和Item布局里视图的对应关系;

* LouAdapter<String> adapter = new LouAdapter<String>(listView, layoutId) { @Override protected void assign(ViewHolder holder, String s) {holder.putText(android.R.id.text1, s);}};

* }

*/

public abstract class LouAdapter<T> extends BaseAdapter {

 

    private ArrayList<T> mLists;

    private Context mContext;

    private AbsListView mListView;

    private int mLayoutId;

 

    public LouAdapter(AbsListView listView, int layoutId) {

        mContext = listView.getContext();

        mListView = listView;

        mLayoutId = layoutId;

        mLists = new ArrayList<T>();

        mListView.setAdapter(this);

    }

 

    @Override

    public int getCount() {

        return mLists.size();

    }

 

    @Override

    public T getItem(int position) {

        return mLists.get(position);

    }

 

    @Override

    public long getItemId(int position) {

        return position;

    }

 

    @Override

    public View getView(int position, View convertView, ViewGroup parent) {

        return updateView(position, convertView);

    }

 

    public AbsListView getBindView() {

        return mListView;

    }

 

    // -------- 添加 choice调用(2016.03.25)

    public void clearChoice() {

        mListView.clearChoices();

        updateChange();

        // selectAllChoice(false);

        mListView.postDelayed(new Runnable() {

            @Override

            public void run() {

                // 注意需要使用Runnable才能生效;

                // 参考资料[ListView selection remains persistent after exiting choice mode]

                // (http://stackoverflow.com/questions/9754170/listview-selection-remains-persistent-after-exiting-choice-mode)

                mListView.setChoiceMode(AbsListView.CHOICE_MODE_NONE);

            }

        }, 160);

    }

 

    public void setChoiceMode(int mode) {

        mListView.setChoiceMode(mode);

    }

 

    public int getChoiceMode() {

        return mListView.getChoiceMode();

    }

 

    public void deleteChoicedItem() {

        if (mListView.getChoiceMode() != AbsListView.CHOICE_MODE_NONE) {

 

            // 获取被选中的ITEM;

            SparseBooleanArray sparseBooleanArray = mListView.getCheckedItemPositions();

            ArrayList<T> deleteLists = new ArrayList<T>();

            for (int i = 0; i < sparseBooleanArray.size(); i++) {

                if (sparseBooleanArray.valueAt(i)) {

                    deleteLists.add(mLists.get(sparseBooleanArray.keyAt(i)));

                }

            }

            mLists.removeAll(deleteLists);

            mListView.clearChoices();

            updateChange();

        }

    }

 

    public void selectAllChoice(boolean selectAll) {

        for (int i = 0; i < mListView.getCount(); i++) {

            mListView.setItemChecked(i, selectAll);

        }

        updateChange();

    }

 

    // ~~~~~~~~~~~~

 

    /**

     * contain 来表示是否已经包含元素;(可能需要重写,如果允许重复的话,就不必要重写了);

     *

     * @param o 要判断的元素

     * @return 返回的结果

     */

    protected boolean contain(T o) {

        if (mLists.contains(o)) {

            return true;

        }

        return false;

    }

 

    /**

     * 用数据来更新视图;

     *

     * @param position

     * @param convertView

     * @return

     */

    private View updateView(int position, View convertView) {

        ViewHolder holder = ViewHolder.getInstance(mContext, mLayoutId, convertView, position);

        assign(holder, getItem(position));

        return holder.getConvertView();

    }

 

    /**

     * 当更改了某一个Item之后,可以通过updateView(position);的方式只更新这一个Item;

     *

     * @param position

     */

    public void updateView(int position) {

        View convertView = getIndexView(position);

        updateView(position, convertView);

    }

 

    /**

     * @param holder

     * @param t

     */

    protected abstract void assign(ViewHolder holder, T t);

 

 

    /**

     * 获取可见元素的View;

     *

     * @param position

     * @return

     */

    public View getIndexView(int position) {

        int currentIndex = position - mListView.getFirstVisiblePosition();

        if (currentIndex < 0 || currentIndex >= mLists.size()) {

            return null;

        }

        return mListView.getChildAt(currentIndex);

    }

 

 

    public void addItem(T o, boolean filter) {

        if (filter && contain(o)) {

            return;

        }

        mLists.add(o);

        updateChange();

    }

 

    /**

     * 默认过滤添加的元素;

     *

     * @param o

     */

    public void addItem(T o) {

        addItem(o, true);

    }

 

    public List<T> getList() {

        return mLists;

    }

 

    /**

     * 初始化元素

     *

     * @param list

     */

    public void initList(List<T> list) {

        mLists.clear();

        mLists.addAll(list);

        updateChange();

    }

 

    public void deleteItem(T o) {

        mLists.remove(o);

        updateChange();

    }

 

    public void deleteItem(int position) {

        mLists.remove(position);

        updateChange();

    }

 

    /**

     * 高度变为0后删除元素;

     *

     * @param position

     */

    public void deleteItemWithAnim(final int position) {

        final View view = getIndexView(position);

        final int initHeight = view.getMeasuredHeight();

        Animation anim = new Animation() {

            @Override

            protected void applyTransformation(float interpolatedTime,

                                               Transformation t) {

                if (mLists.size() > 0 && interpolatedTime == 1) {

                    // 高度为0时删除元素,并更新 adapter

                    if (view.getTag() instanceof ViewHolder) {

                        ((ViewHolder) view.getTag()).mark = ViewHolder.MARK.DELETE;

                        deleteItem(position);

                    }

                } else {

                    // 不断的改变高度,直到高度为0;

                    view.getLayoutParams().height = initHeight

                            - (int) (initHeight * interpolatedTime);

                    view.requestLayout();

                }

            }

        };

        anim.setDuration(300);

        view.startAnimation(anim);

    }

 

 

    public void updateChange() {

        notifyDataSetChanged();

    }

 

    public void clear() {

        mLists.clear();

        updateChange();

    }

 

 

    /**

     * Created by Lou on 2016/3/23.

     */

    public static class ViewHolder {

        private SparseArray<View> mViews;

        private View mConvertView;

        private int mPosition;

 

        MARK mark;

 

        enum MARK {

            NORMAL, DELETE

        }

 

        private ViewHolder(Context context, int layoutId, int position) {

            mPosition = position;

            mViews = new SparseArray<View>();

            mConvertView = LayoutInflater.from(context).inflate(layoutId, null);

            mConvertView.setTag(this);

        }

 

        // 权限:default 包级私有

        static ViewHolder getInstance(Context context, int layoutId, View convertView, int position) {

            boolean needInflate = convertView == null || ((ViewHolder) convertView.getTag()).mark == MARK.DELETE;

            if (needInflate) {

                return new ViewHolder(context, layoutId, position);

            }

            return (ViewHolder) convertView.getTag();

        }

 

        public int getPosition() {

            return mPosition;

        }

 

        public View getConvertView() {

            return mConvertView;

        }

 

        @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;

        }

 

        public ViewHolder putText(int viewId, String text) {

            View v = getView(viewId);

            if (v instanceof TextView) {

                ((TextView) v).setText(text);

            }

            return this;

        }

 

        public ViewHolder putText(int viewId, int resId) {

            View v = getView(viewId);

            if (v instanceof TextView) {

                ((TextView) v).setText(resId);

            }

            return this;

        }

 

        public ViewHolder putImg(int viewId, int resId) {

            View v = getView(viewId);

            if (v instanceof ImageView) {

                ((ImageView) v).setImageResource(resId);

            }

            return this;

        }

 

        /**

         * @param viewId     视图Id

         * @param resId      图片资源Id

         * @param roundShape 是否是圆角图片

         * @return

         */

        public ViewHolder putImg(int viewId, int resId, boolean roundShape) {

            if (roundShape) {

                View v = getView(viewId);

                if (v instanceof ImageView) {

                    Bitmap bitmap = Uview.getBitmapByXfermode(v.getContext(),

                            resId,

                            Color.parseColor("#993382"),

                            Uscreen.dp2Px(v.getContext(), 48),

                            Uscreen.dp2Px(v.getContext(), 48),

                            PorterDuff.Mode.SRC_IN);

                    ((ImageView) v).setImageBitmap(bitmap);

                }

            } else {

                return putImg(viewId, resId);

            }

 

            return this;

        }

 

        public ViewHolder putImg(int viewId, Bitmap bitmap) {

            View v = getView(viewId);

            if (v instanceof ImageView) {

                ((ImageView) v).setImageBitmap(bitmap);

            }

            return this;

        }

 

        public ViewHolder putImg(int viewId, Drawable drawable) {

            View v = getView(viewId);

            if (v instanceof ImageView) {

                ((ImageView) v).setImageDrawable(drawable);

            }

            return this;

        }

 

        public void putCbtn(int viewId, int status, boolean clickable) {

            View v = getView(viewId);

            if (v instanceof CompoundButton) {

                CompoundButton cb = (CheckBox) v;

                if (status != 0) {

                    cb.setChecked(true);

                } else {

                    cb.setChecked(false);

                }

                cb.setClickable(clickable);

            }

        }

 

    }

}
【扩展】
【参考资料】
时间: 2024-10-10 15:09:03

【Android】通用系列 —— 用简单通用的方式操作ListView的相关文章

Android学习系列(15)--App列表之游标ListView(索引ListView)

游标ListView,提供索引标签,使用户能够快速定位列表项.      也可以叫索引ListView,有的人称也为Tweaked ListView,可能更形象些吧.      一看图啥都懂了: 1.游标(Fast scroll thumb)      就是右边的那个拖动的方块,这个非常的简单: 1 2 3 4 5 <ListView     android:id="@+id/tweaked_list"     android:layout_width="fill_pa

通用系列 —— 快速搭建设置界面

[关键词] 设置界面 通用系列 [问题] · 减少重复性代码,快速搭建设置界面(通过简单的配置,就可以达到想要的布局): [效果图] [分析] · 设置界面大同小异,无非由标题,内容,图标等元素组成: · 既然每一个设置项都有Title,那么就用Title的strId来作为它的唯一标识(便于点击等处理): · 复杂的地方在分割线的处理方式上(是整行显示,还是不要显示,又或者是在图像后面显示): · 继承自LinearLayout,要可以添加分割线,可以添加tips提示: [解决方案] 参考下方「

Android学习系列(41)--Android Studio简单使用

1. 环境 UBUNTU 14.04 + Android Studio 0.8.2 2. 安装jdk openjdk-7是一个很好的选择: sudo apt-get update sudo apt-get install openjdk-7-jdk 不排除你需要选择一个默认版本: sudo update-alternatives --config java sudo update-alternatives --config javac 3. 安装Android Studio 在UBUNTU有两种

移动应用开发(IOS/android等)中一个通用的图片缓存方案讲解(附流程图)

在移动应用开发中,我们经常会遇到从网络请求图片到设备上展示的场景. 如果每次都重复发起请求,浪费流量.浪费电量,用户体验也不佳: 将图片持久化到磁盘也不失为一种策略:但每次从文件读取图片也存在一定的io开销,就算采用此策略,我们也需要控制磁盘缓存的容量,以免占用过多系统资源. 其实没有一个方案可以说是完美的方案,只有最适合自己业务需求的方案,才可以说是一个好方案. 我们下面所讲解的方案具备很强的通用性,设计思路简单而清晰: 1.假设每个网络图片的url具有唯一性,如果网络上的图片变化了,会引起输

通用 mapper的简单使用

通用 MAPPER的简单使用 官方  https://mapperhelper.github.io/docs/2.use/ 依赖 <dependency> <groupId>tk.mybatis</groupId> <artifactId>mapper</artifactId> <!-- 建议使用最新版本,最新版本请从项目首页查找 --> <version>x.x.x</version> </depend

linux下面简单通用的Makefile模板

简单通用的Makefile模板: ############################################## # # 单目录通用Makefile # 目标文件可自己的设定 # 始须调试程序,修改 CFLAGS 变量为-Wall -g # # wuyq 20140825 ############################################## # EXECUTABLE为目标的可执行文件名, 可以根据具体的情况对其进行修改. EXECUTABLE := spi

一个简单通用权限管理系统,求各位帮忙看看

最近为公司做一个能源行业的管理平台系统,既然系统定位为平台,自然需要插件化.高通用性等,当前正在进行权限部分的设计,由于本人最近一直忙于开发.设计工作,手中没有一个比较通用的权限管理系统,所以打算顺便借此机会设计一套,我对权限系统没有什么研究,看了一天园子里的相关文章,初步做了个数据库设计,希望各路大神帮忙看看,下面附上数据表结构图,献丑了... 一个简单通用权限管理系统,求各位帮忙看看,布布扣,bubuko.com

jquery提示消息,简单通用

jquery提示消息.简单通用 function showTips(txt,time,status) { var htmlCon = ''; if(txt != ''){ if(status != 0 && status != undefined){ htmlCon = '<div class="tipsBox" style="width:220px;padding:10px;background-color:#4AAF33;border-radius:

一步一步写一个简单通用的makefile(三)

上一篇一步一步写一个简单通用的makefile(二) 里面的makefile 实现对通用的代码进行编译,这一章我将会对上一次的makefile 进行进一步的优化. 优化后的makefile: #Hellomake #Magnum, 2014-10-20 # 指令编译器和选项 CC=gcc CFLAGS=-Wall # 需要链接库的库名,比如libm.a,就是-lm,需要去掉前面的lib和后面的.a LIBS=-lm # 设置默认搜索头文件的路径,优先是这个,然后是系统路径 IncludeDir