1. 程式人生 > >Adapter之大資料滑動效率優化和分頁載入資料

Adapter之大資料滑動效率優化和分頁載入資料

         在Android中如果要做到大資料分頁載入則需要我們的Activity實現OnScrollListener滾動條監聽介面。當如果要做的更加高大上。比如需要在使用者滑動至列表的底部,觸碰摸個區域,則需要實現OnTouchListener介面,等等。

       這裡先講載入大資料

       首先需要在main.xml申明一個ListView,存放資料

<?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"  
    android:paddingLeft="3dp"  
    android:paddingRight="3dp">
    
    <!-- 當ListActivity時,ListView中就需要@id/android:list,否則logCat會報錯
    	 Android內建的名為list的id,因為我們後面要使用到ListActivity,我們的MainActivity繼承於它 -->
    <TextView android:id="@+id/txtView"
        android:text="編號"
        android:textSize="24dp"
        android:textColor="#ff00FF"
        android:background="#80D640"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:gravity="center_horizontal"/>	 
    <ListView android:id="@id/android:list"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"/>  
</LinearLayout>  
       由於ListView試圖元件中,需要封裝每一個item,所以需要layout中申明:

        list_item.xml

<?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中單個列表項的佈局檔案,從效果圖中可以看到,這裡只使用到了一個TextView元件 -->
    <!-- 在getView或者bindview裡面首先用一文字TextView顯示在本地,同時啟動執行緒去網上獲取圖片資源,一旦某TextView的資料得到就立馬重新整理將那張本地的TextView換掉 -->
    <TextView  android:id="@+id/list_item_text"  
        android:layout_width="fill_parent"  
        android:layout_height="fill_parent"  
        android:gravity="center"  
        android:textSize="20sp"  
        android:paddingTop="10dp"  
        android:paddingBottom="10dp"/>  
</LinearLayout> 
       在加上我們的效果時,當滑動至列表的底部也就是應用程式窗體底部時,想要載入下一行,所以需要一個按鈕來觸發 load_more.xml
<?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="wrap_content">  
  <Button android:id="@+id/loadMoreButton"  
        android:layout_width="fill_parent"  
        android:layout_height="wrap_content"  
        android:text="load more"  
        android:onClick="loadMore"/>  
</LinearLayout>  
        在使用BaseAdapter介面卡載入大量資料
package com.lol.huixin.adapter;

import java.util.List;
import java.util.Map;

import com.lol.huixin.loaddatamore.R;
import com.lol.huixin.views.ViewHolder;

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 ListViewAdapter extends BaseAdapter {
	private List<Map<String,Object>> items;  
    private LayoutInflater inflater;
	
    public ListViewAdapter(Context context, List<Map<String,Object>> items) {  
        this.items = items;  
        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);        
    }
    
	@Override
	public int getCount() {
		return items.size();
	}

	@Override
	public Object getItem(int position) {
		return items.get(position);
	}

	@Override
	public long getItemId(int position) {
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder=null;
		if (convertView == null) {  
			holder=new ViewHolder();
			convertView = inflater.inflate(R.layout.list_item, null);  
			holder.list_item_text=(TextView) convertView.findViewById(R.id.list_item_text);
			convertView.setTag(holder);
        } else{
        	holder=(ViewHolder)convertView.getTag();
        } 
		holder.list_item_text.setText((String)items.get(position).get("bianhao"));
        return convertView; 
	}

	/** 
     * 新增列表項 
     * @param item 
     */  
    public void addItem(Map<String,Object> listmap) {  
        items.add(listmap);  
    }
}
      MainActivity.java中呼叫
package com.lol.huixin.loaddatamore;

import android.os.Bundle;
import android.os.Handler;
import android.util.Log;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.lol.huixin.adapter.ListViewAdapter;

import android.app.ListActivity;
import android.view.Menu;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.Button;
import android.widget.ListView;

//由於我們這裡模擬載入大量資料的樣本,所以就需要有個滾動條,所以需要實現滾動條的介面
public class MainActivity extends ListActivity implements OnScrollListener{
	private ListView listView;  
    private int visibleLastIndex = 0;   //最後的可視項索引  
    private int visibleItemCount;       // 當前視窗可見項總數  
    private ListViewAdapter adapter;    //資料介面卡
    private View loadMoreView;  
    private Button loadMoreButton;  
    private Handler handler = new Handler(); 
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		//相當於例項化一個Layout,並在這個例項化的Layout中加一個按鈕
		loadMoreView = getLayoutInflater().inflate(R.layout.load_more, null);  
        loadMoreButton = (Button) loadMoreView.findViewById(R.id.loadMoreButton);  
        listView = getListView();               //獲取id是list的ListView  
        listView.addFooterView(loadMoreView);   //設定列表底部檢視  
        //初始化資料
        initAdapter();  
        setListAdapter(adapter);                //自動為id是list的ListView設定介面卡  
        listView.setOnScrollListener(this);     //新增滑動監聽
	}

	@Override
	public boolean onCreateOptionsMenu(Menu menu) {
		// Inflate the menu; this adds items to the action bar if it is present.
		getMenuInflater().inflate(R.menu.main, menu);
		return true;
	}
	
	/** 
     * 初始化介面卡 
     */  
    private void initAdapter() {  
    	List<Map<String,Object>> listMap=new ArrayList<Map<String,Object>>();
        for (int i = 0; i < 10; i++) {  
        	Map<String,Object> map=new HashMap<String,Object>();
        	map.put("bianhao",String.valueOf(i+1)); 
        	listMap.add(map);
        }  
        adapter = new ListViewAdapter(this, listMap);  
    }

    /** 
     * 滑動狀態改變時被呼叫 
     */
	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		int itemsLastIndex = adapter.getCount() - 1;    //資料集最後一項的索引  
        int lastIndex = itemsLastIndex + 1;             //加上底部的loadMoreView項  
        if (scrollState == OnScrollListener.SCROLL_STATE_IDLE && visibleLastIndex == lastIndex) {  
            //如果是自動載入,可以在這裡放置非同步載入資料的程式碼  
            Log.i("LOADMORE", "loading...");  
        }
	}

	/** 
     * 滑動時被呼叫 
     */
	@Override
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
		this.visibleItemCount = visibleItemCount;  
        visibleLastIndex = firstVisibleItem + visibleItemCount - 1;  
	}

	  /** 
     * 點選按鈕事件 
     * @param view 
     */  
    public void loadMore(View view) {  
        loadMoreButton.setText("loading...");   //設定按鈕文字loading  
        handler.postDelayed(new Runnable() {  
            @Override  
            public void run() {  
                loadData();  
                adapter.notifyDataSetChanged(); //資料集變化後,通知adapter  
                listView.setSelection(visibleLastIndex - visibleItemCount + 1); //設定選中項  
                  
                loadMoreButton.setText("load more");    //恢復按鈕文字  
            }  
        }, 2000);  
    }  
      
    /** 
     * 模擬載入資料 
     */  
    private void loadData() {  
        int count = adapter.getCount();  
        for (int i = count; i < count + 10; i++) {  
        	Map<String,Object> map=new HashMap<String,Object>();
        	map.put("bianhao",String.valueOf(i+1));
            adapter.addItem(map);  
        }  
    }  
    
}
   這個時候我們注意到了一個奇怪的現象:ViewHolder類
package com.lol.huixin.views;

import java.io.Serializable;

import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;

@SuppressWarnings("serial")
public class ViewHolder implements Serializable{
	public ImageView img;
	//public TextView title;
	public TextView list_item_text;
	public TextView info;
	public Button viewBtn;
}
      專案結構:

      
        執行效果:也是ok的。

        然後我就在想為什麼這樣寫了?

       1.首先ListViewAdapter中有一個有參構造方法,我們在MainActivity呼叫時傳入了兩個引數,引數一是需要裝載值ListView中的資料,引數二是當前上下文物件Context,

        inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE)

        從當前layout的上下文作用域中得到SystemService中的LAYOUT_INFLATER_SERVICE東東,那麼LAYOUT_INFLATER_SERVICE是什麼東東,Android官網給與瞭解釋:

String android.content.Context.LAYOUT_INFLATER_SERVICE = "layout_inflater"
See Also:
Google說:使用帶有getSystemService檢索android.view.LayoutInflater進行充氣佈局資源於該上下文。
也就是說我們通過LayoutInflater這個類例項化一個layout,將我們load_more.xml中的按鈕給動態加至ListView中
2.我們知道Android中提高滑動效率,就意味著需要修改GetView方法,那麼我們那樣寫的目的何在
   原因有兩點:
          一>adapter每次載入時,都需要去建立一個item中的內容,也就是Layout和其中的元件,很影響效率,如果我們把元件進行封裝是不是會優化效率呢?在這,如果在記憶體中已經分配了使用LayoutInflater建立了一個Layout呢?我們是不是不需要在此建立
         二>我們都知道在看書的時候,要想定位到上次看過的地方,最簡單的方法就是在書中做個標記,同樣的android也支援這樣一種寫法
       convertView.setTag(holder);   //做資料標記