900字范文,内容丰富有趣,生活中的好帮手!
900字范文 > 你还在用notifyDataSetChanged? 你已经out了

你还在用notifyDataSetChanged? 你已经out了

时间:2019-03-28 15:55:09

相关推荐

你还在用notifyDataSetChanged? 你已经out了

帖子中讲述的项目使用了listView这个控件,而且自定义了adapter。在更新item的进度条时发现每次使用notifyDataSetChanged(),都会去调用自定义adapter中的getView方法。这时问题就出现了,用notifyDataSetChanged方法去更新listView中的item,是更新需要更新的Item呢?还是更新所有的item呢?如果是更新所有的item那么效率不就会很低吗?有什么办法可以解决这个问题呢?

怀着心中的疑惑,我开始了这次的实验。。。

我的想法很简单现实模拟远程下载文件,创建一个Activity做主界面,主界面采用listView。然后自定义一个adapter实现BaseAdapter,再创建一个线程类,线程类当中采用循环的方式不断的往adapter发送消息.然后使用notifyDataSetChanged方法更新界面,在调用getView方法时在控制台输出语句,这样我就可以知道notifyDatatSetChanged方法执行时是更新一个item还是更新所有的item了。

有了思路就好办了,我们先建立一个类,叫FileState。

package edu.notify.viking.entity;public class FileState {String fileName;//文件名字int completeSize;//完成的长度boolean state;//文件状态,true为已经完成,false为未完成public String getFileName() {return fileName;}public void setFileName(String fileName) {this.fileName = fileName;}public int getCompleteSize() {return completeSize;}public void setCompleteSize(int completeSize) {pleteSize = completeSize;}public boolean isState() {return state;}public void setState(boolean state) {this.state = state;}}

这个类中有3个属性,分别是文件名字,文件已经下载的长度,还有文件当前的状态。然后就是get与set方法。这个类的作用我想大家应该一眼就明白了,没错,既然使用了listView,我在主界面就想着要定义一个List,这个list当中的内容当然就是我们FileState啦。

接着我们去实现主界面。创建主界面MainActivity

package edu.notify.viking.activity;import java.util.ArrayList;import java.util.List;import edu.notify.viking.adapter.MyAdapter;import edu.notify.viking.entity.FileState;import android.app.Activity;import android.os.Bundle;import android.widget.ListView;public class MainActivity extends Activity {private List<FileState> list=new ArrayList<FileState>();private ListView listView;/** Called when the activity is first created. */@Overridepublic void onCreate(Bundle savedInstanceState){super.onCreate(savedInstanceState);setContentView(R.layout.main);initFileState();//先对FileState进行初始化initUI();//对界面进行初始化}/*** 把数据放进list中,因为是测试所以我手工添加数据* **/private void initFileState(){//给FileState赋值for(int i =1;i<8;i++){FileState fileState=new FileState();fileState.setFileName(i+".mp3");//名字fileState.setCompleteSize(100);//初始化下载程度fileState.setState(true);list.add(fileState);}FileState f=new FileState();f.setFileName("8.mp3");f.setCompleteSize(0);f.setState(false);list.add(f);}private void initUI(){listView = (ListView)this.findViewById(R.id.listview);MyAdapter adapter = new MyAdapter(list,this);listView.setAdapter(adapter);adapter.setListView(listView);}}

因为是模拟,所以主界面很简单,只有2个属性,一个List<FileState>,这里面的内容用来显示到listview上,还有一个就是咱们的ListView啦。咱们先对FileState进行初始化,否则就没内容显示。我使用了一个循环,把文件的名字取为1.mp3---7.mp3,这7个文件的状态都是true,也就是已经下载完成。然后单独的初始化了8.mp3这个文件,这个文件完成度为0,状态也是false,我这么做的目的相信聪明的你,已经明白了。按照正常的逻辑,我们去更新界面,这些已经下载完成的文件,我们就没有必要再让他们去更新,只用更新正在下载中的文件就可以了。但是使用notifyDatatSetChanged方法真的能满足我们的需求吗?

接着我创建了自定义的adapter,并将他与listView绑定在一起。然后将listView传进了adapter中。

那我们来看看adapter中是如何实现的吧。新建一个adapter,取名叫MyAdatper继承BaseAdapter.

<font face="宋体">package edu.notify.viking.adapter;import java.util.List;import edu.notify.viking.activity.R;import edu.notify.viking.down.Downloader;import edu.notify.viking.entity.FileState;import android.content.Context;import android.os.Handler;import android.os.Message;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;public class MyAdapter extends BaseAdapter {private List<FileState> list;private Context context;private LayoutInflater inflater=null;private ListView listView;private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {if(msg.what==1){String name=(String)msg.obj;int length=msg.arg1;for(int i=0;i<list.size();i++){FileState fileState=list.get(i);if(fileState.getFileName().equals(name)){fileState.setCompleteSize(length);list.set(i, fileState);break;}}notifyDataSetChanged();}}};public MyAdapter(List<FileState> list,Context context){inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);this.list=list;}class ViewHolder{public TextView fileName;//文件名称public ProgressBar progressBar;//进度条public TextView percent;//百分比public ImageView down;//下载}public int getCount() {// TODO Auto-generated method stubreturn list.size();}public Object getItem(int position) {// TODO Auto-generated method stubreturn list.get(position);}public long getItemId(int position) {// TODO Auto-generated method stubreturn position;}public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView==null){convertView=inflater.inflate(R.layout.main_item, null);holder=new ViewHolder();holder.fileName=(TextView)convertView.findViewById(R.id.fileName);holder.progressBar=(ProgressBar)convertView.findViewById(R.id.down_progressBar);holder.percent = (TextView) convertView.findViewById(R.id.percent_text);holder.down = (ImageView) convertView.findViewById(R.id.down_view);convertView.setTag(holder);}else{holder=(ViewHolder)convertView.getTag();}FileState fileState=list.get(position);final String name = fileState.getFileName();System.out.println(name+"---run getView");//如果文件状态为已经下载if(fileState.isState()==true){holder.fileName.setText(fileState.getFileName());//下载完成的文件,进度条被隐藏holder.progressBar.setVisibility(ProgressBar.INVISIBLE);//设置为已下载holder.percent.setText("已下载");//下载完成的文件,下载按钮被隐藏,防止重复下载holder.down.setVisibility(ImageView.INVISIBLE);}else{holder.fileName.setText(fileState.getFileName());holder.progressBar.setVisibility(ProgressBar.VISIBLE);holder.progressBar.setProgress(fileState.getCompleteSize());holder.percent.setText(fileState.getCompleteSize()+"%");holder.down.setOnClickListener(new View.OnClickListener(){public void onClick(View v) {Downloader down= new Downloader(name,mHandler);down.download();}});if(fileState.getCompleteSize()==100){holder.progressBar.setVisibility(ProgressBar.INVISIBLE);holder.percent.setText("已下载");holder.down.setVisibility(ProgressBar.INVISIBLE);fileState.setState(true);list.set(position, fileState);}}return convertView;}public void setListView(ListView listView) {this.listView = listView;}}</font>

为了运行的效率,我在adapter中定义了一个内部类,ViewHolder,其中的属性都是我们要绘制出来的控件。主要的绘制工作在于getView这个方法,在getView中我们对变量初始化以后,就将list当中对应的FileState拿了出来,根据文件的状态进行了分别的处理,下载完成的怎么怎么显示。。。下载为完成的怎么怎么显示。。。这些都不难,很容易就看明白了。在这里面我们实现了下载按钮这个点击事件,这个事件中的代码也很简单,就是创建一个Downloader对象,然后调用其中的download方法进行下载。在这个类中我们看到,其中创建了一个Handler对象,并在里面实现它的消息处理方法handleMessage。当我们点击下载图标时会把这个Handler对象传进Downloader这个类中。当文件开始下载时,下载完成的数据长度将会传到handlerMessage这个方法中被处理,我们在这个方法中对FileState与list做了更新,然后调用了notifyDatatSetChanged方法.

接下来我们来看最后一个类。创建Downloader类。

package edu.notify.viking.down;import java.util.Map;import android.os.Handler;import android.os.Message;public class Downloader {private String fileName;private Handler mHandler;public Downloader(String fileName, Handler handler){super();this.fileName = fileName;mHandler = handler;}public void download(){new MyThread().start();}class MyThread extends Thread{@Overridepublic void run(){for(int i=0;i<=100;i++){System.out.println("i:"+i);try {this.currentThread().sleep(1000);} catch (InterruptedException e) {// TODO Auto-generated catch blocke.printStackTrace();}Message msg=Message.obtain();msg.what=1;msg.obj=fileName;msg.arg1=i;mHandler.sendMessage(msg);}}}}

这个类用于开启一个单独的线程来模拟下载的环节。其中的内容很简单,无非是接收从adapter中传递过来的数据,当用户点击下载图标时,执行download方法。在线程类的run方法中,我们做了一个想0-100的循环,当然为了让大家看到进度条的更新,我们让线程每次休眠1秒钟。然后用adapter对象中传递过来的handler对象发送message。这时候我们的adapter类中的handleMessage方法就可以收到消息,并进行处理,最后调用notifyDataSetChanged方法了。好的,我们把xml文件也先给大家。

先是main.xml

<font face="宋体"><?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:orientation="vertical"android:layout_width="fill_parent"android:layout_height="fill_parent"><ListViewandroid:id="@+id/listview"android:layout_width="fill_parent"android:layout_height="fill_parent"android:fastScrollEnabled="true"/></LinearLayout></font>

复制代码 然后是main_item.xml

<font face="宋体"><?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="/apk/res/android"android:orientation="horizontal"android:layout_width="fill_parent"android:layout_height="fill_parent"><TextViewandroid:id="@+id/fileName"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"/><ProgressBarandroid:id="@+id/down_progressBar"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_weight="1"style="@android:style/Widget.ProgressBar.Horizontal"/><TextViewandroid:id="@+id/percent_text"android:layout_width="wrap_content"android:layout_height="wrap_content"/><ImageView android:id="@+id/down_view"android:layout_width="wrap_content"android:layout_height="wrap_content"android:src="@drawable/down"/></LinearLayout></font>

复制代码 这两个布局文件很简单,大家一看就明白了,现在我们来测试一下,看看notifyDataSetChanged方法后,getView一共会执行几次?

-7-25 20:28 上传下载附件(362.59 KB)

从图中我们可以看见,尽管我们只是想更新8.mp3这个item的进度条,但是所有的进度条都被更新了,每次使用notifyDataSetChanged方法,对会调用8次getView方法。天哪。这个效率。。。

这不是我们想要的,我们想要的是什么?我们想要的就是如果只需要更新8.mp3这个item,那么其他的item将保持不变,不需要更新他们。那么我们该怎么解决这个问题呢?

其实这个问题的解决也不麻烦,所谓难者不会,会者不难啊。我们只需要修改adapter这个类,在其中添加一个方法即可。现在我们来看更新后的adapter类。

<font face="宋体">package edu.notify.viking.adapter;import java.util.List;import edu.notify.viking.activity.R;import edu.notify.viking.down.Downloader;import edu.notify.viking.entity.FileState;import android.content.Context;import android.os.Handler;import android.os.Message;import android.view.LayoutInflater;import android.view.View;import android.view.ViewGroup;import android.widget.BaseAdapter;import android.widget.ImageView;import android.widget.ListView;import android.widget.ProgressBar;import android.widget.TextView;public class MyAdapter extends BaseAdapter {private List<FileState> list;private Context context;private LayoutInflater inflater=null;private ListView listView;private Handler mHandler = new Handler(){@Overridepublic void handleMessage(Message msg) {if(msg.what==1){String name=(String)msg.obj;int length=msg.arg1;for(int i=0;i<list.size();i++){FileState fileState=list.get(i);if(fileState.getFileName().equals(name)){fileState.setCompleteSize(length);list.set(i, fileState);updateView(i);//用我们自己写的方法 break;}}//notifyDataSetChanged();不用了}}};public MyAdapter(List<FileState> list,Context context){inflater = (LayoutInflater)context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);this.list=list;}class ViewHolder{public TextView fileName;//文件名称public ProgressBar progressBar;//进度条public TextView percent;//百分比public ImageView down;//下载}/*** 用于更新我们想要更新的item* @param itemIndex 想更新item的下标* **/private void updateView(int itemIndex){ //得到第1个可显示控件的位置,记住是第1个可显示控件噢。而不是第1个控件int visiblePosition = listView.getFirstVisiblePosition(); //得到你需要更新item的ViewView view = listView.getChildAt(itemIndex - visiblePosition);FileState fileState=list.get(itemIndex);final String name=fileState.getFileName();System.out.println(name+"---run updateView");if(fileState.isState()==false){ViewHolder holderOne=new ViewHolder();//start:初始化holderOne.fileName=(TextView)view.findViewById(R.id.fileName);holderOne.progressBar=(ProgressBar)view.findViewById(R.id.down_progressBar);holderOne.percent = (TextView) view.findViewById(R.id.percent_text);holderOne.down = (ImageView) view.findViewById(R.id.down_view);//endholderOne.fileName.setText(fileState.getFileName());holderOne.progressBar.setVisibility(ProgressBar.VISIBLE);holderOne.progressBar.setProgress(fileState.getCompleteSize());holderOne.percent.setText(fileState.getCompleteSize()+"%");holderOne.down.setOnClickListener(new View.OnClickListener(){public void onClick(View v) {Downloader down= new Downloader(name,mHandler);down.download();}});if(fileState.getCompleteSize()==100){holderOne.progressBar.setVisibility(ProgressBar.INVISIBLE);holderOne.percent.setText("已下载");holderOne.down.setVisibility(ProgressBar.INVISIBLE);fileState.setState(true);list.set(itemIndex, fileState);}}} public int getCount() {// TODO Auto-generated method stubreturn list.size();}public Object getItem(int position) {// TODO Auto-generated method stubreturn list.get(position);}public long getItemId(int position) {// TODO Auto-generated method stubreturn position;}public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;if(convertView==null){convertView=inflater.inflate(R.layout.main_item, null);holder=new ViewHolder();holder.fileName=(TextView)convertView.findViewById(R.id.fileName);holder.progressBar=(ProgressBar)convertView.findViewById(R.id.down_progressBar);holder.percent = (TextView) convertView.findViewById(R.id.percent_text);holder.down = (ImageView) convertView.findViewById(R.id.down_view);convertView.setTag(holder);}else{holder=(ViewHolder)convertView.getTag();}FileState fileState=list.get(position);final String name = fileState.getFileName();System.out.println(name+"---run getView");//如果文件状态为已经下载if(fileState.isState()==true){holder.fileName.setText(fileState.getFileName());//下载完成的文件,进度条被隐藏holder.progressBar.setVisibility(ProgressBar.INVISIBLE);//设置为已下载holder.percent.setText("已下载");//下载完成的文件,下载按钮被隐藏,防止重复下载holder.down.setVisibility(ImageView.INVISIBLE);}else{holder.fileName.setText(fileState.getFileName());holder.progressBar.setVisibility(ProgressBar.VISIBLE);holder.progressBar.setProgress(fileState.getCompleteSize());holder.percent.setText(fileState.getCompleteSize()+"%");holder.down.setOnClickListener(new View.OnClickListener(){public void onClick(View v) {Downloader down= new Downloader(name,mHandler);down.download();}});if(fileState.getCompleteSize()==100){holder.progressBar.setVisibility(ProgressBar.INVISIBLE);holder.percent.setText("已下载");holder.down.setVisibility(ProgressBar.INVISIBLE);fileState.setState(true);list.set(position, fileState);}}return convertView;}public void setListView(ListView listView) {this.listView = listView;}}</font>

在类中我们对handleMessage做了一点修改。

-7-25 21:22 上传下载附件(85.08 KB)

看到红色箭头的地方就是修改过的。然后多添加了一个updateView方法。这个方法需要传入你想更新的item下标。

最核心的地方是这2句代码。

-7-25 20:56 上传下载附件(21.68 KB)

主要的意思就是根据你想要更新的item下标去得到这个item的View对象,这样你就可以为所欲为啦。哈哈哈哈哈。。。

咱们来看看运行效果。

-7-25 21:08 上传下载附件(280.63 KB)

这里我改为同时更新2个item,看看这样会不会出错,可以看到,运行的很正常。控制台的打印结果也只是更新了7-8.mp3这2个item。

好了,文章到此已经结束了,肯定有人要骂我了,这么简单的东西,你啰嗦了半天。整的我都没怎么听懂。

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。