原创性声明:本文系作者原创,转载请保留原文链接:

http://blog.csdn.net/a774057695/article/details/48677507





首先先给一个朋友的博客的链接:

http://blog.csdn.net/coder_pig/article/details/48631595

这位仁兄对listview 数据源发生改变 进行了一定的分析,

本文也是和这位仁兄简单沟通后决定去写的,因为我对ListView改变数据源真的是受!够!了!

我现在希望将这系列的问题统统解决,就像有一个标准化的流程一样去处理。

好了,我现在只想静静,不要问我静静是谁,真的是我远房表妹。


正文:

我想诸位都受过一个折磨:

样式不好看!样式不好看!样式不好看!

然后我们自定义ListView的适配器,也即是写一个扩展自BaseAdapter的类,然后提供自定义的布局.

/**当我们学会这样干的时候,往往已经对simpleadapter看不上眼了*/

然后我们会在相应的Activity类中定义这样的成员变量:

private ListView mListView = null;

private [自定义的适配器类名] mListViewAdapter = null; //我就假定它叫MyAdapter好吗?除非代码块中出现了实际的类名,我们都用这个约定的名字


一切都顺理成章,然后问题来了:

我们会在onCreate时,通过findViewById方法实现mListView的实例化并将其与视图组件绑定在一起。

顺势实例化适配器,甚至我们定义了绑定数据源的构造器 public MyAdapter(数据源类 data , Context context) { ……} 这样的代码块。

实例化前先得到了数据源,一般方法名命名为GetData

实例化后为 mListView设置了适配器 mListView.setAdapter(mListViewAdapter);

ok,ok,我们看到问题了:数据往往不是一开始就准备好的!

于是代码混乱了,为何会混乱呢?

根源在于:适配器的数据源是定义在MyAdapter中的,虽然他的源头还在我们的Activity中。

但是我们的适配器不认我们认定的源头,它只认MyAdapter中的,生活中看脸,代码中看作用域,规范的做我们是不会让Activity中的数据直接向适配器暴露(我指的是public等,不要认为我们不把数据给适配器),我们都是通过构造器交给适配器的,那么适配器认可的数据源就是MyAdapter中定义的那个成员变量,例如这样的:private LinkedList mLiData;

于是那位仁兄发现了那些问题。

往往将就的处理就是:Activity中的数据源变了,我们就重新构造一下适配器,或大同小异的方法。

带来的就是代码混乱不堪。

那我们就分析一下怎么处理:

那位仁兄是通过添加成员变量的方法来的,他的代码我就不copy了。、

分析:

先分析那位仁兄的。

MyAdapter类中有成员变量:private LinkedList mLiData; 对应数据源

我们要处理数据源的变化,也就是要改变成员变量,那么就写几个需要的成员函数来实现需求,思路上很明确,也不错。但是我觉得有一些地方不合适,如下:

并不是ListView都需要改变数据源,至少在我们的项目中应当考虑存在需要改变的和不需要改变的。

从BaseAdapter及其父类,实现的接口类来看,google也是这个意思。那如果我们用成员函数去实现,我认为他不应当直接继承自baseAdapter 而应该是将MyAdapter改成抽象类,这个类再继承自MyAdapter,相应的,还需要有一个继承自MyAdapter的类,它不需要改变数据源的值。   至少我觉得这样才算“规范”(实际上我内心依旧觉得不规范),但是这样太“臃肿了”。

我们都知道,接口是对“行为”的抽象,我们将改变数据源的视作是行为一点问题都没有,而且我认为使用接口来实现会更加合理,我想大家也都会认可,这样做,我们不会将类的扩展性降低、将类的抽象程度变低、将类描述的范围更小。

所以我向那位仁兄提了个建议用接口实现。

改进:

首先还是按照那位仁兄的大致框架来改进,先不考虑将各种方法都写全 也就是: 1主Activity的布局放一些测试性的button,用于触发设定的事件, 2主Activity负责控制生命周期执行的各个事件,绑定视图,获取数据,分发数据,构造适配器,为视图组件绑定适配器和响应事件 3数据类用于描述数据元,并提供对数据元操作的方法 4适配器类继承自BaseAdapter并实现数据源变化接口,重载getCount getView等几个关键方法 5接口类定义了数据源操作的方法(先少写几个) 先贴代码,后分析:

主Activity布局:

    

稍微丑一点,demo嘛,说明关键问题就行了,大家将就下,就不贴图了。

主Activity类:

package [your package name];import java.util.LinkedList;import android.app.Activity;import android.content.Context;import android.os.Bundle;import android.util.Log;import android.view.Menu;import android.view.MenuItem;import android.view.View;import android.view.View.OnClickListener;import android.widget.Button;import android.widget.ListView;import android.widget.Toast;public class MainActivity extends Activity implements OnClickListener {private ListView mListView = null;private CustomAdapter mListAdapter = null;private LinkedList mData = null;private Context mContext = null;//==========\    测试数据   /===============================/** * 我们假定添加到第五条,位置应该是4,我们的位置和数据源一致,从0 开始,不考虑偏移值 * */private final int testPosition = 4;private MyData testData ;private int testDataId = 0;//==========/    测试数据定义结束 \=====================//=========\测试按钮 /=========================================private Button tBAdd,tBAddAt,tBDeleteAt,tBDeleteAll;private final String Tag = "MainActivity";@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_main);BindView();mContext = this;mData = new LinkedList();mListAdapter = new CustomAdapter(mData,mContext);mListView.setAdapter(mListAdapter);}private void BindView() {mListView =(ListView) findViewById(R.id.main_list);tBAdd = (Button) findViewById(R.id.bt_addAtLast);tBAddAt = (Button) findViewById(R.id.bt_addAtPosition);tBDeleteAt = (Button) findViewById(R.id.bt_deleteAtPosition);tBDeleteAll = (Button) findViewById(R.id.bt_deleteAll);tBAdd.setOnClickListener(this);tBAddAt.setOnClickListener(this);tBDeleteAll.setOnClickListener(this);tBDeleteAt.setOnClickListener(this);}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {// Inflate the menu; this adds items to the action bar if it is present.getMenuInflater().inflate(R.menu.main, menu);return true;}@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// Handle action bar item clicks here. The action bar will// automatically handle clicks on the Home/Up button, so long// as you specify a parent activity in AndroidManifest.xml.int id = item.getItemId();if (id == R.id.action_settings) {return true;}return super.onOptionsItemSelected(item);}@Overridepublic void onClick(View v) {// TODO Auto-generated method stubboolean op = true;StringBuffer msg  = new StringBuffer();testData = new MyData(R.drawable.ic_launcher, "testData,id:"+(testDataId++));boolean ret = false;switch (v.getId()) {case R.id.bt_addAtLast:ret = mListAdapter.AddItem(testData);msg.append("从尾部添加,成功了吗?").append(ret);break;case R.id.bt_addAtPosition:ret = mListAdapter.AddItem(testPosition, testData);msg.append("添加到第5条,成功了吗?").append(ret);break;case R.id.bt_deleteAtPosition:ret = mListAdapter.DeleteItem(testPosition);msg.append("删除第5条,成功了吗?").append(ret);break;case R.id.bt_deleteAll:mListAdapter.Clear();msg.append("全部清除");break;default:op = false;break;}if(op) {Toast.makeText(mContext, msg, Toast.LENGTH_SHORT).show();Log.i(Tag, msg.toString());}}}

适配器类:


package [your package name]import java.util.LinkedList;import android.content.Context;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.TextView;public class CustomAdapter extends BaseAdapter implements ChangeDataSetInterface{private Context mContext;// 依据原楼主的代码,我们定义自己的数据类型,使用类来描述private LinkedList mLiData;public CustomAdapter() {}public CustomAdapter(LinkedList mData, Context mContext) {        this.mLiData = mData;        this.mContext = mContext;    }private class ViewHolder {ImageView mImageView;TextView mTextView;}@Overridepublic int getCount() {return mLiData.size();}/*  * 这里我返回数据源中的值。 */@Overridepublic Object getItem(int position) {return mLiData.get(position);}@Overridepublic long getItemId(int position) {return position;}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder = null;        if(convertView == null){            convertView = LayoutInflater.from(mContext).inflate(R.layout.listview_item,parent,false);            holder = new ViewHolder();            holder.mImageView = (ImageView) convertView.findViewById(R.id.img_icon);            holder.mTextView = (TextView) convertView.findViewById(R.id.txt_content);            convertView.setTag(holder);        }else{            holder = (ViewHolder) convertView.getTag();        }        holder.mImageView.setImageResource(mLiData.get(position).getImgId());        holder.mTextView.setText(mLiData.get(position).getContent());        return convertView;}//=======================================================================///* * 以下是实现接口定义的方法 *  * 定义为Boolean 返回类型 是为了方便通知“前台”,成功了没有, * 注意 我们的position按照数据来,从0开始,当然你看着自己需求指定偏移量也可以 *  * (⊙﹏⊙)b 我打着插入。。。想想还是改成了添加。。。感觉自己病了*///=======================================================================//@Overridepublic boolean AddItem(MyData data) {if (mLiData == null)             mLiData = new LinkedList();        mLiData.add(data);        notifyDataSetChanged();        return true;}@Overridepublic boolean AddItem(int position , MyData data) {if (mLiData == null)             mLiData = new LinkedList();//注意超过了数据源的实际条目数时,需要的是添加到尾部,而不是直接添加,更不是在中间补充空值//当然,你也可以认为必须朝该位置插入,不能满足时通知前台要求不被许可,不执行。 我的例子中就采用这样处理if (position > getCount())//AddItem(data); //这里对应调整向朝尾部添加return false;elsemLiData.add(position,data);        notifyDataSetChanged();        return true;}@Overridepublic boolean DeleteItem(MyData data) {// 效率太低,意义不大,这个方法可以不用去考虑return false;}@Overridepublic boolean DeleteItem(int position) {if (mLiData == null)return false;if (position >= getCount())return false;        mLiData.remove(position);        notifyDataSetChanged();        return true;}@Overridepublic void Clear() {if (mLiData == null)             mLiData = new LinkedList();        mLiData.clear();        notifyDataSetChanged();}}

数据类:


package [your package name]public class MyData {private int imgId;    private String content;    public MyData() {}    public MyData(int imgId, String content) {        this.imgId = imgId;        this.content = content;    }    public int getImgId() {        return imgId;    }    public String getContent() {        return content;    }    public void setImgId(int imgId) {        this.imgId = imgId;    }    public void setContent(String content) {        this.content = content;    }}

接口类:


package [your package name]public interface ChangeDataSetInterface {boolean AddItem(MyData data);boolean AddItem(int position , MyData data);/** * deprecate * 遍历检索效率低,且实际意义不大。 * */boolean DeleteItem(MyData data);boolean DeleteItem(int position);void Clear();}

item的布局

(总觉得将adt升级了之后对我的提示让我很难过):
<?xml version="1.0" encoding="utf-8"?>        

分析:

接口类,我们定义了几个方法,再行文之时,我觉得应当都定义为boolean返回类型的,来标识操作的成功与否,先不改了。
boolean AddItem(MyData data);boolean AddItem(int position , MyData data);boolean DeleteItem(MyData data);boolean DeleteItem(int position);void Clear();
一共这么多方法,朝尾部添加,朝指定位置添加,删除指定的值(考虑到效率和逻辑性,我觉得这个方法不应当被设计),删除指定位置的值,全部清除,  好的,我们还应当添加一个添加全部数据的,先放着。 添加的时候,我们考虑数据源是否被初始化了(从代码上看是可能的,因为无参构造器中没有初始化数据源),如果是朝指定位置添加,要考虑这个位置,嗯,是否合适朝这个位置放,不合适是进行调整,还是拒绝操作,看你的设计了。 删除也要考虑是否有数据可以删啊。(好像我穿越了)
代码上应该没有让大家不能理解的地方吧?
适配器类,继承了BaseAdapter,实现了我们的接口类,因为是非抽象类,所以必须实现接口方法,这也正是我们想要的 我习惯在实现getItem的时候返回关联的数据,当然 return null;也是可以的,大家对这块不清楚的可以去找找相关的博客,csdn上是有的。
Activity类,似乎也没有什么要讲的。。。。

优化:

增加接口方法:添加数据源(这个描述并不是很准确,对应的情景是在原来的数据源后面增加一大波数据(你非要一条也行)) 增加接口方法:替换数据源(这个描述并不是很准确,将原数据源中的数据换掉) 修改适配器类,当数据源没有数据时显示特定布局
1 and 2:
void AddAll(LinkedList datas);void ReplaceAll(LinkedList datas);--------------------------------------------------------------------@Overridepublic void AddAll(LinkedList datas) {if (mLiData == null)             mLiData = new LinkedList();for(int i = 0;i datas) { mLiData = new LinkedList();for(int i = 0;i

而第三点就值得寻味了,或许有朋友会和我一开始的潜意识一样,在getView方法中怎么怎么样,为什么这么说呢,不管你怎么怎么样,都有getView方法被调用了才行,或许有朋友研究过,getView不是一次性将所有的Item都获得到的,哪怕代码上也看得出不是一次可行的,而且,它多次调用的次数和用户可见的数量有关,当没有数据的时候,它不会被调用。so,是不是发现潜意识不可靠。
问题呢还是没有得到解决的。我想了好久也没有想到切入点,估计今天满脑子都是这个问题了。 代码就不传了。我想上面写的够全了。
等我把上面的问题想明白怎么破。。会再写一篇的。

原创性声明:本文系作者原创,转载请保留原文链接:

http://blog.csdn.net/a774057695/article/details/48677507




更多相关文章

  1. SpringBoot 2.0 中 HikariCP 数据库连接池原理解析
  2. 一句话锁定MySQL数据占用元凶
  3. android脱壳,手机端实现,Fdex2
  4. android 通过jdbc访问mysql数据库--(含android studio 配置)通过测
  5. Android复杂自定义Listview实现
  6. contentprovider的学习实例总结
  7. android Cursor的自动管理方式
  8. android中的数据存取 之 File
  9. Context 传递数据

随机推荐

  1. Android(安卓)AndroidStaggeredGrid 开源
  2. 第七章 布局
  3. 总结Android中TabActivity 的使用方法
  4. Android 带动画的底部弹出视图
  5. android系统编译adt插件
  6. [置顶] Android 开发即时聊天工具 YQ 《
  7. Android桌面组件widget与快捷方式shortcu
  8. Android 进行单元测试难在哪-part2
  9. 说说 Android 中如何实现同时兼容手机与
  10. Android之SD卡插拔广播