Android ListView改变数据源
2015-09-23 11:14
495 查看
原创性声明:本文系作者原创,转载请保留原文链接:
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<MyData> mLiData;
于是那位仁兄发现了那些问题。
往往将就的处理就是:Activity中的数据源变了,我们就重新构造一下适配器,或大同小异的方法。
带来的就是代码混乱不堪。
那我们就分析一下怎么处理:
那位仁兄是通过添加成员变量的方法来的,他的代码我就不copy了。、
MyAdapter类中有成员变量:private LinkedList<MyData> mLiData; 对应数据源
我们要处理数据源的变化,也就是要改变成员变量,那么就写几个需要的成员函数来实现需求,思路上很明确,也不错。但是我觉得有一些地方不合适,如下:
并不是ListView都需要改变数据源,至少在我们的项目中应当考虑存在需要改变的和不需要改变的。
从BaseAdapter及其父类,实现的接口类来看,google也是这个意思。那如果我们用成员函数去实现,我认为他不应当直接继承自baseAdapter 而应该是将MyAdapter改成抽象类,这个类再继承自MyAdapter,相应的,还需要有一个继承自MyAdapter的类,它不需要改变数据源的值。 至少我觉得这样才算“规范”(实际上我内心依旧觉得不规范),但是这样太“臃肿了”。
我们都知道,接口是对“行为”的抽象,我们将改变数据源的视作是行为一点问题都没有,而且我认为使用接口来实现会更加合理,我想大家也都会认可,这样做,我们不会将类的扩展性降低、将类的抽象程度变低、将类描述的范围更小。
所以我向那位仁兄提了个建议用接口实现。
也就是:
1主Activity的布局放一些测试性的button,用于触发设定的事件,
2主Activity负责控制生命周期执行的各个事件,绑定视图,获取数据,分发数据,构造适配器,为视图组件绑定适配器和响应事件
3数据类用于描述数据元,并提供对数据元操作的方法
4适配器类继承自BaseAdapter并实现数据源变化接口,重载getCount getView等几个关键方法
5接口类定义了数据源操作的方法(先少写几个)
先贴代码,后分析:
稍微丑一点,demo嘛,说明关键问题就行了,大家将就下,就不贴图了。
添加的时候,我们考虑数据源是否被初始化了(从代码上看是可能的,因为无参构造器中没有初始化数据源),如果是朝指定位置添加,要考虑这个位置,嗯,是否合适朝这个位置放,不合适是进行调整,还是拒绝操作,看你的设计了。
删除也要考虑是否有数据可以删啊。(好像我穿越了)
代码上应该没有让大家不能理解的地方吧?
适配器类,继承了BaseAdapter,实现了我们的接口类,因为是非抽象类,所以必须实现接口方法,这也正是我们想要的
我习惯在实现getItem的时候返回关联的数据,当然 return null;也是可以的,大家对这块不清楚的可以去找找相关的博客,csdn上是有的。
Activity类,似乎也没有什么要讲的。。。。
增加接口方法:替换数据源(这个描述并不是很准确,将原数据源中的数据换掉)
修改适配器类,当数据源没有数据时显示特定布局
1 and 2:
而第三点就值得寻味了,或许有朋友会和我一开始的潜意识一样,在getView方法中怎么怎么样,为什么这么说呢,不管你怎么怎么样,都有getView方法被调用了才行,或许有朋友研究过,getView不是一次性将所有的Item都获得到的,哪怕代码上也看得出不是一次可行的,而且,它多次调用的次数和用户可见的数量有关,当没有数据的时候,它不会被调用。so,是不是发现潜意识不可靠。
问题呢还是没有得到解决的。我想了好久也没有想到切入点,估计今天满脑子都是这个问题了。
代码就不传了。我想上面写的够全了。
等我把上面的问题想明白怎么破。。会再写一篇的。
原创性声明:本文系作者原创,转载请保留原文链接:
http://blog.csdn.net/a774057695/article/details/48677507
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<MyData> mLiData;
于是那位仁兄发现了那些问题。
往往将就的处理就是:Activity中的数据源变了,我们就重新构造一下适配器,或大同小异的方法。
带来的就是代码混乱不堪。
那我们就分析一下怎么处理:
那位仁兄是通过添加成员变量的方法来的,他的代码我就不copy了。、
分析:
先分析那位仁兄的。MyAdapter类中有成员变量:private LinkedList<MyData> mLiData; 对应数据源
我们要处理数据源的变化,也就是要改变成员变量,那么就写几个需要的成员函数来实现需求,思路上很明确,也不错。但是我觉得有一些地方不合适,如下:
并不是ListView都需要改变数据源,至少在我们的项目中应当考虑存在需要改变的和不需要改变的。
从BaseAdapter及其父类,实现的接口类来看,google也是这个意思。那如果我们用成员函数去实现,我认为他不应当直接继承自baseAdapter 而应该是将MyAdapter改成抽象类,这个类再继承自MyAdapter,相应的,还需要有一个继承自MyAdapter的类,它不需要改变数据源的值。 至少我觉得这样才算“规范”(实际上我内心依旧觉得不规范),但是这样太“臃肿了”。
我们都知道,接口是对“行为”的抽象,我们将改变数据源的视作是行为一点问题都没有,而且我认为使用接口来实现会更加合理,我想大家也都会认可,这样做,我们不会将类的扩展性降低、将类的抽象程度变低、将类描述的范围更小。
所以我向那位仁兄提了个建议用接口实现。
改进:
首先还是按照那位仁兄的大致框架来改进,先不考虑将各种方法都写全也就是:
1主Activity的布局放一些测试性的button,用于触发设定的事件,
2主Activity负责控制生命周期执行的各个事件,绑定视图,获取数据,分发数据,构造适配器,为视图组件绑定适配器和响应事件
3数据类用于描述数据元,并提供对数据元操作的方法
4适配器类继承自BaseAdapter并实现数据源变化接口,重载getCount getView等几个关键方法
5接口类定义了数据源操作的方法(先少写几个)
先贴代码,后分析:
主Activity布局:
<span style="font-size:18px;"><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingTop="20dp" tools:context="com.example.customlistview.MainActivity" > <Button android:id="@+id/bt_addAtLast" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentLeft="true" android:layout_alignParentStart="true" android:layout_alignParentTop="true" android:layout_marginLeft="25dp" android:layout_marginStart="25dp" android:text="addAtLast" /> <Button android:id="@+id/bt_addAtPosition" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/bt_addAtLast" android:layout_toEndOf="@+id/bt_addAtLast" android:layout_alignParentTop="true" android:layout_marginLeft="25dp" android:layout_marginStart="25dp" android:text="addAtPosition" /> <Button android:id="@+id/bt_deleteAtPosition" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_below="@+id/bt_addAtLast" android:layout_marginLeft="25dp" android:layout_marginStart="25dp" android:text="deleteAtPosition" /> <Button android:id="@+id/bt_deleteAll" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_toRightOf="@+id/bt_deleteAtPosition" android:layout_toEndOf="@+id/bt_deleteAtPosition" android:layout_below="@+id/bt_addAtLast" android:layout_marginLeft="25dp" android:layout_marginStart="25dp" android:text="deleteAll" /> <ListView android:id="@+id/main_list" android:layout_below="@+id/bt_deleteAtPosition" android:layout_width="match_parent" android:layout_height="match_parent" > </ListView> </RelativeLayout></span>
稍微丑一点,demo嘛,说明关键问题就行了,大家将就下,就不贴图了。
主Activity类:
<span style="font-size:18px;">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<MyData> 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"; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); BindView(); mContext = this; mData = new LinkedList<MyData>(); 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); } @Override public 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; } @Override public 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); } @Override public void onClick(View v) { // TODO Auto-generated method stub boolean 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()); } } } </span>
适配器类:
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<MyData> mLiData; public CustomAdapter() {} public CustomAdapter(LinkedList<MyData> mData, Context mContext) { this.mLiData = mData; this.mContext = mContext; } private class ViewHolder { ImageView mImageView; TextView mTextView; } @Override public int getCount() { return mLiData.size(); } /* * 这里我返回数据源中的值。 */ @Override public Object getItem(int position) { return mLiData.get(position); } @Override public long getItemId(int position) { return position; } @Override public 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 我打着插入。。。想想还是改成了添加。。。感觉自己病了 */ //=======================================================================// @Override public boolean AddItem(MyData data) { if (mLiData == null) mLiData = new LinkedList<MyData>(); mLiData.add(data); notifyDataSetChanged(); return true; } @Override public boolean AddItem(int position , MyData data) { if (mLiData == null) mLiData = new LinkedList<MyData>(); //注意超过了数据源的实际条目数时,需要的是添加到尾部,而不是直接添加,更不是在中间补充空值 //当然,你也可以认为必须朝该位置插入,不能满足时通知前台要求不被许可,不执行。 我的例子中就采用这样处理 if (position > getCount()) // AddItem(data); //这里对应调整向朝尾部添加 return false; else mLiData.add(position,data); notifyDataSetChanged(); return true; } @Override public boolean DeleteItem(MyData data) { // 效率太低,意义不大,这个方法可以不用去考虑 return false; } @Override public boolean DeleteItem(int position) { if (mLiData == null) return false; if (position >= getCount()) return false; mLiData.remove(position); notifyDataSetChanged(); return true; } @Override public void Clear() { if (mLiData == null) mLiData = new LinkedList<MyData>(); 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"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <ImageView android:id="@+id/img_icon" android:contentDescription="@string/hello_world" android:layout_width="56dp" android:layout_height="56dp"/> <TextView android:id="@+id/txt_content" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:layout_marginLeft="10dp" android:layout_marginStart="10dp" android:textSize="18sp" /> </LinearLayout>
分析:
接口类,我们定义了几个方法,再行文之时,我觉得应当都定义为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<MyData> datas); void ReplaceAll(LinkedList<MyData> datas); -------------------------------------------------------------------- @Override public void AddAll(LinkedList<MyData> datas) { if (mLiData == null) mLiData = new LinkedList<MyData>(); for(int i = 0;i<datas.size();i++) { mLiData.add(datas.get(i)); } } @Override public void ReplaceAll(LinkedList<MyData> datas) { mLiData = new LinkedList<MyData>(); for(int i = 0;i<datas.size();i++) { mLiData.add(datas.get(i)); } }
而第三点就值得寻味了,或许有朋友会和我一开始的潜意识一样,在getView方法中怎么怎么样,为什么这么说呢,不管你怎么怎么样,都有getView方法被调用了才行,或许有朋友研究过,getView不是一次性将所有的Item都获得到的,哪怕代码上也看得出不是一次可行的,而且,它多次调用的次数和用户可见的数量有关,当没有数据的时候,它不会被调用。so,是不是发现潜意识不可靠。
问题呢还是没有得到解决的。我想了好久也没有想到切入点,估计今天满脑子都是这个问题了。
代码就不传了。我想上面写的够全了。
等我把上面的问题想明白怎么破。。会再写一篇的。
原创性声明:本文系作者原创,转载请保留原文链接:
http://blog.csdn.net/a774057695/article/details/48677507
相关文章推荐
- Android模似器硬件加速
- Android开发之四大组件
- android 屏幕长亮 和 解锁
- Android上玩玩Hook:Cydia Substrate实战
- android自定义的notification
- Android自定义Notification的具体实现
- 解析android中隐藏与显示软键盘及不自动弹出键盘的实现方法
- android解析 ramdisk.img boot.img system.img
- 妙用View的keepScreenOn保持屏幕常亮,android屏幕常亮
- Android获取手机电话簿信息
- Android基础:数据存储(一):文件存储的工具类UtilsFile
- Android中AsyncTask使用
- 疑惑·android全屏显示相关
- Android软件安全与逆向分析入门-贰-熟悉Dalvik字节码
- android 播放assets文件里视频文件的问题
- 一个Android事件注入框架详解
- Android studio java文件显示j爆红
- 关于“Unable to execute dex: Cannot merge new index 65576 into a non-jumbo instruction!”
- android 拨号,短信实现
- Android中获取缓存大小和清除缓存功能