有了思路就好办了,我们先建立一个类,叫FileState。
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) { this.completeSize = completeSize; } public boolean isState() { return state; } public void setState(boolean state) { this.state = state; } }这 个类中有3个属性,分别是文件名字,文件已经下载的长度,还有文件当前的状态。然后就是get与set方法。这个类的作用我想大家应该一眼就明白了,没 错,既然使用了listView,我在主界面就想着要定义一个List,这个list当中的内容当然就是我们FileState啦。
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. */ @Override public 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方法真的能满足我们的需求吗?
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() { @Override public 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 stub return list.size(); } public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } public long getItemId(int position) { // TODO Auto-generated method stub return 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; } }为 了运行的效率,我在adapter中定义了一个内部类,ViewHolder,其中的属性都是我们要绘制出来的控件。主要的绘制工作在于getView这 个方法,在getView中我们对变量初始化以后,就将list当中对应的FileState拿了出来,根据文件的状态进行了分别的处理,下载完成的怎么 怎么显示。。。下载为完成的怎么怎么显示。。。这些都不难,很容易就看明白了。在这里面我们实现了下载按钮这个点击事件,这个事件中的代码也很简单,就是 创建一个Downloader对象,然后调用其中的download方法进行下载。在这个类中我们看到,其中创建了一个Handler对象,并在里面实现 它的消息处理方法handleMessage。当我们点击下载图标时会把这个Handler对象传进Downloader这个类中。当文件开始下载时,下 载完成的数据长度将会传到handlerMessage这个方法中被处理,我们在这个方法中对FileState与list做了更新,然后调用了 notifyDatatSetChanged方法.
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 { @Override public 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 block e.printStackTrace(); } Message msg=Message.obtain(); msg.what=1; msg.obj=fileName; msg.arg1=i; mHandler.sendMessage(msg); } } } }
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <ListView android:id="@+id/listview" android:layout_width="fill_parent" android:layout_height="fill_parent" android:fastScrollEnabled="true" /> </LinearLayout>然后是main_item.xml
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="fill_parent" android:layout_height="fill_parent" > <TextView android:id="@+id/fileName" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" /> <ProgressBar android:id="@+id/down_progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_weight="1" style="@android:style/Widget.ProgressBar.Horizontal" /> <TextView android: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>这两个布局文件很简单,大家一看就明白了,现在我们来测试一下,看看notifyDataSetChanged方法后,getView一共会执行几次?
从图中我们可以看见,尽管我们只是想更新8.mp3这个item的进度条,但是所有的进度条都被更新了,每次使用notifyDataSetChanged方法,对会调用8次getView方法。天哪。这个效率。。。
这不是我们想要的,我们想要的是什么?我们想要的就是如果只需要更新8.mp3这个item,那么其他的item将保持不变,不需要更新他们。那么我们该怎么解决这个问题呢?
其实这个问题的解决也不麻烦,所谓难者不会,会者不难啊。我们只需要修改adapter这个类,在其中添加一个方法即可。现在我们来看更新后的adapter类。
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() { @Override public 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的View View 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); //end holderOne.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 stub return list.size(); } public Object getItem(int position) { // TODO Auto-generated method stub return list.get(position); } public long getItemId(int position) { // TODO Auto-generated method stub return 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; } }在类中我们对handleMessage做了一点修改。
看到红色箭头的地方就是修改过的。然后多添加了一个updateView方法。这个方法需要传入你想更新的item下标。
最核心的地方是这2句代码。
主要的意思就是根据你想要更新的item下标去得到这个item的View对象,这样你就可以为所欲为啦。哈哈哈哈哈。。。
咱们来看看运行效果。
这里我改为同时更新2个item,看看这样会不会出错,可以看到,运行的很正常。控制台的打印结果也只是更新了7-8.mp3这2个item。