1. 程式人生 > >在listview中嵌入viewpager帶來的卡頓問題及解決

在listview中嵌入viewpager帶來的卡頓問題及解決

現在許多應用中都有用到listviewviewpager共存的介面,像知乎日報這樣

                                   

當下方的listview滾動時,上面的圖片也會跟著滾動,簡單的來說就是在listview中巢狀一個viewpager,並且將viewpager放在第一個item裡面。

容易遇到的問題

自己第一次嘗試寫這種介面的時候變遇到了一個bug,在我多次對listview進行上下滾動之後(讓viewpager出現,再消失,再出現,迴圈),viewpager的切換出現卡頓,甚至不響應切換的動作,系統也沒有報錯,但這樣是遠遠不能拿來實用的。

問題分析

Listview的中的第一行viewpager因為多次每一次都出現需要重新生成匯入,記憶體不足,而導致出現卡頓的現象。

解決這問題,需要了解listview的原理,網上有張圖很能說明問題


(圖為網上查詢)

從圖中可以看出,listview保持了一個回收器,不可見的檢視會被放在回收器裡面,並不會直接銷燬,當另一個不可見的檢視變成可見的時候,便會從回收期裡將檢視匯出,再次使用。像圖中的例子,整個列表的顯示過程中,我們只需要同時維持7個listitem便可以了,即使有成千上萬個需要顯示。對listview的效能優化也是依據的這個。(listview的優化是充分利用回收機制,和靈活的Holder的使用,這個網上很多,就不細說了)

原理圖中只使用了一種檢視,如果在一個列表中包含多種不同檢視怎麼辦。其實這也是解決我這次問題的關鍵,在Recycler中對回收的檢視都是有按照Type儲存的,在重寫的ListView的介面卡中,我們可以重寫一個方法,getViewTypeCount(),用它來設定Recycler中的Type數量。然後再重寫getItemViewType(int position)來為每個position位置上的item設定自己的檢視type。然後再讓我們來走一下滾動時,檢視轉換流程,向上滾動時消失的檢視將按照type儲存在Recycler的相應位置,然後下方剛出現的item8的convertView便是根據item8的type在Recycler取出的。這樣就能通過按照type區分,實現多種檢視共同快取。

解決方法

將第一行的viewpager和下面的listitem都通過Recycler實現快取,這樣對於viewpager,不是造成每一次都需要重新穿建。

核心程式碼主要是在ListView的重新寫的介面卡中

package com.example.listviewtest;

import java.util.List;


import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class MyListAdapter extends BaseAdapter{
	private static final int TYPE_LISTVIEW = 0;
	private static final int TYPE_VIEWPAGER = 1;
	private static final int TYPE_COUNT = 2;
	private Context mContext;
	private List<String> list;
	private View viewPager;
	
	public MyListAdapter(Context mContext, List<String> list) {
		super();
		this.mContext = mContext;
		this.list=list;
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size()+1;
	}

	@Override
	public Object getItem(int position) {
		// TODO Auto-generated method stub
		if (position==0) {
			return viewPager;
		} else {
			return list.get(position-1);
		}
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		Holder holder = null;
		switch(getItemViewType(position)){
		case TYPE_LISTVIEW:
			if(convertView==null){
				holder = new Holder();
				convertView=LayoutInflater.from(mContext)
						.inflate(R.layout.list_item, null);
				holder.tv=(TextView)convertView.findViewById(R.id.tv);
				convertView.setTag(holder);
			}else{
				holder = (Holder) convertView.getTag();
			}
			break;
		case TYPE_VIEWPAGER:
			
			if(convertView==null){
				convertView=viewPager;
			}
			return convertView;
		}
		if(viewPager!=null){
			position--;
		}
		holder.tv.setText(list.get(position));
		return convertView;
	}

	public void setViewPager(View view) {
		// TODO Auto-generated method stub
		viewPager=view;
	}
	@Override
	public int getItemViewType(int position) {
		// TODO Auto-generated method stub
		if(viewPager!=null){
			return position > 0 ? TYPE_LISTVIEW : TYPE_VIEWPAGER;  
		}else{
			return TYPE_LISTVIEW;
		}
		
	}

	@Override
	public int getViewTypeCount() {
		// TODO Auto-generated method stub
		return TYPE_COUNT;  
	}
	
	public class Holder {
	public TextView tv;
	}
}
按照這樣的修改之後,實現的下圖所示的效果,執行起來超級流暢,bug問題完美解決掉了

Demo下載地址http://download.csdn.net/detail/u012568402/8194703點選開啟連結

希望讀者能有所收穫