對於ListView資料的重新整理大家都知道,改變Adapter的資料來源,然後調用Adapter的notifyDateSetChanged()方法即可。
但是在做公司項目的時候,有個下載模組,因為可能同時下載好幾個資料,所以用的listview展示所有正在下載的內容。因為下載進度要即時更新,所以要不停的調用notifyDateSetChanged重新整理資料。這樣會不停的重新繪製整個listview的介面,效能開銷非常大。而且如果每個item有圖片的話,每個item的圖片都需要重新載入,就算圖片做了記憶體緩衝,重新整理一下圖片也會閃一下,不停的重新整理就會導致各個item的圖片不停的閃,體驗一點都不好。
那麼對於上面問題,有沒有解決辦法呢?當然是有的。我們可以針對某一個item進行局部更新,而不影響其它沒有修改的item。那麼具體如何?的呢?我們看下面的代碼。
private void updateView(int itemIndex) {
//得到第一個可顯示控制項的位置,
int visiblePosition = mListView.getFirstVisiblePosition();
//只有當要更新的view在可見的位置時才更新,不可見時,跳過不更新
if (itemIndex - visiblePosition >= 0) {
//得到要更新的item的view
View view = mListView.getChildAt(itemIndex - visiblePosition);
//調用adapter更新介面
mAdapter.updateView(view, itemIndex);
}
}
這個函數主要是根據傳入的itemIndex來擷取第itemIndex的資料所顯示的view。itemIndex就是要修改的資料再List集合中的位置,比如我這裡下載進度有更新,發了一個廣播這裡接收到了,需要修改該下載內容的進度條,廣播接收器可以這麼寫:
@Override
public void onReceive(Context context, Intent intent) {
AppContent appContent = intent.getParcelableExtra("appContent");
if(appContent == null) return;
int itemIndex = 0;
for(AppContent appContent1 : mList) {
if(appContent.getUrl().equals(appContent1.getUrl())) {
itemIndex = mList.indexOf(appContent1);
appContent1.setDownloadPercent(appContent.getDownloadPercent());
break;
}
}
updateView(itemIndex);
}
下面看Adapter的具體代碼:
public class AppContentAdapter extends BaseAdapter{
private List<AppContent> mDates = null;
private Context mContext;
public AppContentAdapter(Context context) {
this.mContext = context;
}
@Override
public int getCount() {
return mDates.size();
}
@Override
public Object getItem(int position) {
return mDates.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
public void setDates(List<AppContent> mDates) {
this.mDates = mDates;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if (convertView == null) {
holder = new ViewHolder();
convertView = LayoutInflater.from(mContext).inflate(
R.layout.listitem_download, null);
holder.statusIcon = (DownloadPercentView) convertView.findViewById(R.id.status_icon);
holder.name = (TextView) convertView.findViewById(R.id.name);
holder.downloadPercent = (TextView) convertView.findViewById(R.id.download_percent);
holder.progressBar = (ProgressBar) convertView.findViewById(R.id.progressbar);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
setData(holder, position);
return convertView;
}
/**
* 設定viewHolder的資料
* @param holder
* @param itemIndex
*/
private void setData(ViewHolder holder, int itemIndex) {
AppContent appContent = mDates.get(itemIndex);
holder.name.setText(appContent.getName());
holder.progressBar.setProgress(appContent.getDownloadPercent());
setIconByStatus(holder.statusIcon, appContent.getStatus());
if(appContent.getStatus() == AppContent.Status.PENDING) {
holder.downloadPercent.setVisibility(View.INVISIBLE);
} else {
holder.downloadPercent.setVisibility(View.VISIBLE);
holder.statusIcon.setProgress(appContent.getDownloadPercent());
holder.downloadPercent.setText("下載進度:" + appContent.getDownloadPercent() + "%");
}
}
/**
* 局部重新整理
* @param view
* @param itemIndex
*/
public void updateView(View view, int itemIndex) {
if(view == null) {
return;
}
//從view中取得holder
ViewHolder holder = (ViewHolder) view.getTag();
holder.statusIcon = (DownloadPercentView) view.findViewById(R.id.status_icon);
holder.name = (TextView) view.findViewById(R.id.name);
holder.downloadPercent = (TextView) view.findViewById(R.id.download_percent);
holder.progressBar = (ProgressBar) view.findViewById(R.id.progressbar);
setData(holder, itemIndex);
}
/**
* 根據狀態設定表徵圖
* @param downloadPercentView
* @param status
*/
private void setIconByStatus(DownloadPercentView downloadPercentView, AppContent.Status status) {
downloadPercentView.setVisibility(View.VISIBLE);
if(status == AppContent.Status.PENDING) {
downloadPercentView.setStatus(DownloadPercentView.STATUS_PEDDING);
}
if(status == AppContent.Status.DOWNLOADING) {
downloadPercentView.setStatus(DownloadPercentView.STATUS_DOWNLOADING);
}
if(status == AppContent.Status.WAITING) {
downloadPercentView.setStatus(DownloadPercentView.STATUS_WAITING);
}
if(status == AppContent.Status.PAUSED) {
downloadPercentView.setStatus(DownloadPercentView.STATUS_PAUSED);
}
if(status == AppContent.Status.FINISHED) {
downloadPercentView.setStatus(DownloadPercentView.STATUS_FINISHED);
}
}
private class ViewHolder {
private DownloadPercentView statusIcon;
private TextView name;
private TextView downloadPercent;
private ProgressBar progressBar;
}
}
ListView中 局部重新整理Item 實現下載進度條局部更新
當更新當前正在下載的任務的時候,使用 notifyDataSetChanged();方法會使整個頁面都會重新整理。
而且進度更新比較頻繁,這就造成了記憶體的消耗和頁面卡頓(在進度更新很頻繁的情況),筆者甚至出現了卡住頁面無法進行操作的情況。
所以想到了能不能局部重新整理某個Item。也查了下資料,問題解決。
解決思路:
通過listview.getFirstVisiblePosition()方法擷取到顯示的item的首個位置 ,再根據position, 計算出view的位置。擷取到具體的view後,對view進行操作,就能夠實現局部重新整理了。
關鍵代碼:
public void updateView(int itemIndex) {
//得到第一個可顯示控制項的位置,
int visiblePosition = mListView.getFirstVisiblePosition();
//只有當要更新的view在可見的位置時才更新,不可見時,跳過不更新
if (itemIndex - visiblePosition >= 0) {
//得到要更新的item的view
View view = mListView.getChildAt(itemIndex - visiblePosition);
//從view中取得holder
ViewHolder holder = (ViewHolder) view.getTag();
HashMap<String, Object> item = data.get(itemIndex);
//擷取到具體的控制項,
holder.name = (TextView) view.findViewById(R.id.name);
holder.process = (ProcessBar) view.findViewById(R.id.process);
.......
//對控制項進行操作
holder.process.setMax(item.get("max"));
holder.process.setProgress(item.get("progress"));
......
}
}