有了思路就好辦了,咱們先創建一個類,叫FileState。 html
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類。 java
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。
android