帖子中講述的項目使用了listView這個控件,而且自定義了adapter。在更新item的進度條時發現每次使用notifyDataSetChanged(),都會去調用自定義adapter中的getView方法。這時問題就出現了,用notifyDataSetChanged方法去更新listView中的item,是更新需要更新的Item呢?還是更新所有的item呢?如果是更新所有的item那么效率不就會很低嗎?有什么辦法可以解決這個問題呢?
懷着心中的疑惑,我開始了這次的實驗。。。
我的想法很簡單現實模擬遠程下載文件,創建一個Activity做主界面,主界面采用listView。然后自定義一個adapter實現BaseAdapter,再創建一個線程類,線程類當中采用循環的方式不斷的往adapter發送消息.然后使用notifyDataSetChanged方法更新界面,在調用getView方法時在控制台輸出語句,這樣我就可以知道notifyDatatSetChanged方法執行時是更新一個item還是更新所有的item了。
有了思路就好辦了,我們先建立一個類,叫FileState。
復制代碼
這個類中有3個屬性,分別是文件名字,文件已經下載的長度,還有文件當前的狀態。然后就是get與set方法。這個類的作用我想大家應該一眼就明白了,沒錯,既然使用了listView,我在主界面就想着要定義一個List,這個list當中的內容當然就是我們FileState啦。
接着我們去實現主界面。創建主界面MainActivity
復制代碼
復制代碼
復制代碼
復制代碼
復制代碼
懷着心中的疑惑,我開始了這次的實驗。。。
我的想法很簡單現實模擬遠程下載文件,創建一個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) {
- this.completeSize = completeSize;
- }
- public boolean isState() {
- return state;
- }
- public void setState(boolean state) {
- this.state = state;
- }
- }
接着我們去實現主界面。創建主界面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. */
- @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方法真的能滿足我們的需求嗎?
接着我創建了自定義的adapter,並將他與listView綁定在一起。然后將listView傳進了adapter中。
那我們來看看adapter中是如何實現的吧。新建一個adapter,取名叫MyAdatper繼承BaseAdapter.
接着我創建了自定義的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()
- {
- @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;
- }
- }
- </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
- {
- @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);
- }
- }
- }
- }
這個類用於開啟一個單獨的線程來模擬下載的環節。其中的內容很簡單,無非是接收從adapter中傳遞過來的數據,當用戶點擊下載圖標時,執行download方法。在線程類的run方法中,我們做了一個想0-100的循環,當然為了讓大家看到進度條的更新,我們讓線程每次休眠1秒鍾。然后用adapter對象中傳遞過來的handler對象發送message。這時候我們的adapter類中的handleMessage方法就可以收到消息,並進行處理,最后調用notifyDataSetChanged方法了。好的,我們把xml文件也先給大家。
先是main.xml
先是main.xml
- <font face="宋體"><?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>
- </font>
然后是main_item.xml
- <font face="宋體"><?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></font>
這兩個布局文件很簡單,大家一看就明白了,現在我們來測試一下,看看notifyDataSetChanged方法后,getView一共會執行幾次?
從圖中我們可以看見,盡管我們只是想更新8.mp3這個item的進度條,但是所有的進度條都被更新了,每次使用notifyDataSetChanged方法,對會調用8次getView方法。天哪。這個效率。。。
這不是我們想要的,我們想要的是什么?我們想要的就是如果只需要更新8.mp3這個item,那么其他的item將保持不變,不需要更新他們。那么我們該怎么解決這個問題呢?
其實這個問題的解決也不麻煩,所謂難者不會,會者不難啊。我們只需要修改adapter這個類,在其中添加一個方法即可。現在我們來看更新后的adapter類。
從圖中我們可以看見,盡管我們只是想更新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()
- {
- @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;
- }
- }
- </font>
在類中我們對handleMessage做了一點修改。
看到紅色箭頭的地方就是修改過的。然后多添加了一個updateView方法。這個方法需要傳入你想更新的item下標。
最核心的地方是這2句代碼。
主要的意思就是根據你想要更新的item下標去得到這個item的View對象,這樣你就可以為所欲為啦。哈哈哈哈哈。。。
咱們來看看運行效果。
這里我改為同時更新2個item,看看這樣會不會出錯,可以看到,運行的很正常。控制台的打印結果也只是更新了7-8.mp3這2個item。
好了,文章到此已經結束了,肯定有人要罵我了,這么簡單的東西,你啰嗦了半天。整的我都沒怎么聽懂。
不管怎樣,都要感謝CCTV,MTV,還有KTV。當然還有我們安卓巴士提供的這個平台。希望安卓巴士越辦越好。
看到紅色箭頭的地方就是修改過的。然后多添加了一個updateView方法。這個方法需要傳入你想更新的item下標。
最核心的地方是這2句代碼。
主要的意思就是根據你想要更新的item下標去得到這個item的View對象,這樣你就可以為所欲為啦。哈哈哈哈哈。。。

咱們來看看運行效果。
這里我改為同時更新2個item,看看這樣會不會出錯,可以看到,運行的很正常。控制台的打印結果也只是更新了7-8.mp3這2個item。
好了,文章到此已經結束了,肯定有人要罵我了,這么簡單的東西,你啰嗦了半天。整的我都沒怎么聽懂。
不管怎樣,都要感謝CCTV,MTV,還有KTV。當然還有我們安卓巴士提供的這個平台。希望安卓巴士越辦越好。
