Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android開發筆記(一百二十二)循環器視圖RecyclerView

Android開發筆記(一百二十二)循環器視圖RecyclerView

編輯:關於Android編程

RecyclerView

RecyclerView是Android在support-v7庫中新推出控件,中文別名為循環器視圖,它的功能非常強大,可分別實現ListView、GridView,以及瀑布流網格的顯示效果。

RecyclerView相關工程在sdk中的路徑為sdk\extras\android\support\v7\recyclerview,不過幸好用它不像用Toolbar那樣麻煩,要想使用Toolbar得先導入並引用v7-appcompat工程,而使用RecyclerView只需像其他第三方jar一樣往libs目錄添加android-support-v7-recyclerview.jar就好了。
但是若在Eclipse/ADT中調用RecyclerView,可能app運行時會報錯“Caused by: java.lang.NoClassDefFoundError: android.support.v7.recyclerview.R$styleable”,這時就不能使用sdk\extras\android\support\v7\recyclerview下面的jar包,而要到extras/android/m2repository/com/android/support/recyclerview-v7目錄下,在版本號21.0.0的子目錄中找到recyclerview-v7-21.0.0.aar,該aar文件其實是個壓縮文件,解壓該文件可得到classes.jar,將該jar包更名並加入到你的工程,上面的運行錯誤應該就沒有了。

下面看看強悍的RecyclerView都提供了哪些常用方法:
setAdapter : 設置列表項的適配器。有關適配器的詳細說明見下一標題。
setLayoutManager : 設置列表項的布局管理器。目前有三種,分別是:線性布局管理器LinearLayoutManager、網格布局管理器GridLayoutManager、瀑布流網格布局管理器StaggeredGridLayoutManager。有關布局管理器的詳細說明見本文的後半部分。
addItemDecoration : 添加列表項的分割線。
removeItemDecoration : 移除列表項的分割線。
setItemAnimator : 設置列表項的增刪動畫。
addOnItemTouchListener : 添加列表項的觸摸監聽器。因為RecyclerView沒有實現列表項的點擊接口,所以開發者可通過這裡的觸摸監聽器來監控用戶手勢。
removeOnItemTouchListener : 移除列表項的觸摸監聽器。


循環適配器

RecyclerView有專門的適配器類,即RecyclerView.Adapter。在調用RecyclerView的setAdapter方法前,我們要先實現一個從RecyclerView.Adapter派生而來的數據適配器,用來定義列表項的布局與具體操作。下面是與RecyclerView.Adapter相關的常用方法:

下面是自定義適配器必須要重寫的方法:
getItemCount : 獲得列表項的數目。
onCreateViewHolder : 創建整個布局的視圖持有者。輸入參數中包括視圖類型,可根據視圖類型加載不同的布局,從而實現帶頭部的列表布局。
onBindViewHolder : 綁定每項的視圖持有者。
下面是可以重寫也可以不重寫的方法:
getItemViewType : 返回每項的視圖類型。這裡返回的視圖類型給onCreateViewHolder方法使用。
getItemId : 獲得每項的編號。
下面是可以直接調用的方法:
notifyItemInserted : 通知適配器在指定位置插入了新項。
notifyItemRemoved : 通知適配器在指定位置刪除了原有項。
notifyItemChanged : 通知適配器在指定位置的項目發生了變化。
notifyDataSetChanged : 通知適配器整個列表的數據發生了變化。


總的來說,RecyclerView.Adapter與我們之前經常遇到的BaseAdapter在處理流程上是基本一致的,當然它們之間也有不小的差異,下面是RecyclerView.Adapter和其他適配器的主要區別:
1、自帶ViewHolder及其重用功能,無需開發者手工重用ViewHolder;
2、未自帶列表項的點擊和長按功能,需要開發者自己實現點擊和長按事件的監聽;
3、增加區分不同列表項的視圖類型,方便開發者根據類型加載不同的布局;
4、可單獨對個別項進行增刪改操作,無需刷新整個列表;


下面是RecyclerView.Adapter的一個自定義類的代碼例子:
import com.example.exmrecycler.R;
import com.example.exmrecycler.interfaces.OnItemClickListener;
import com.example.exmrecycler.interfaces.OnItemLongClickListener;

import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;

public class RecyclerAdapter extends RecyclerView.Adapter 
		implements OnItemClickListener, OnItemLongClickListener {
	private final static String TAG = "RecyclerAdapter";
	private Context mContext;
	private LayoutInflater mInflater;
	private int mType;
	private String[] mTitleArray;
	
	public RecyclerAdapter(Context context, int type, String[] titleArray) {
		mContext = context;
		mInflater = LayoutInflater.from(context);
		mType = type;
		mTitleArray = titleArray;
	}

	@Override
	public int getItemCount() {
		return mTitleArray.length;
	}

	@Override
	public ViewHolder onCreateViewHolder(ViewGroup vg, int viewType) {
		View v = null;
		ViewHolder holder = null;
		v = mInflater.inflate(R.layout.list_title, vg, false);
		holder = new TitleHolder(v);
		return holder;
	}

	@Override
	public void onBindViewHolder(ViewHolder vh, final int position) {
		TitleHolder holder = (TitleHolder) vh;
		holder.tv_seq.setText(""+(position+1));
		holder.tv_title.setText(mTitleArray[position]);
		LayoutParams params = holder.ll_item.getLayoutParams();
		if (mType == 1) {  //表示是線性布局
			params.height = 50;
			holder.ll_item.setLayoutParams(params);
		} else if (mType == 2) {  //表示是網格布局
			params.height = 100;
			holder.ll_item.setLayoutParams(params);
		} else {  //表示是瀑布流網格布局
			params.height = (int) Math.round(300 * Math.random());
			if (params.height < 60) {
				params.height = 60;
			}
			//很奇怪,setLayoutParams對瀑布流網格不起作用,只能用setHeight
			holder.tv_title.setHeight(params.height);
		}
		
		//列表項的點擊事件需要自己實現
		holder.ll_item.setOnClickListener(new OnClickListener() {
			@Override
			public void onClick(View v) {
				if (mOnItemClickListener != null) {
					mOnItemClickListener.onItemClick(v, position);
				}
			}
		});
		holder.ll_item.setOnLongClickListener(new OnLongClickListener() {
			@Override
			public boolean onLongClick(View v) {
				if (mOnItemLongClickListener != null) {
					mOnItemLongClickListener.onItemLongClick(v, position);
				}
				return true;
			}
		});
	}
	
	@Override
	public int getItemViewType(int position) {
		//這裡返回每項的類型,開發者可自定義頭部類型與一般類型,
		//然後在onCreateViewHolder方法中根據類型加載不同的布局,從而實現帶頭部的網格布局
		return 0;
	}
	
	@Override
	public long getItemId(int position) {
		return position;
	}

	public class TitleHolder extends RecyclerView.ViewHolder {
		public LinearLayout ll_item;
		public TextView tv_seq;
		public TextView tv_title;

		public TitleHolder(View v) {
			super(v);
			ll_item = (LinearLayout) v.findViewById(R.id.ll_item);
			tv_seq = (TextView) v.findViewById(R.id.tv_seq);
			tv_title = (TextView) v.findViewById(R.id.tv_title);
		}
		
	}

	private OnItemClickListener mOnItemClickListener;

	public void setOnItemClickListener(OnItemClickListener listener) {
		this.mOnItemClickListener = listener;
	}

	private OnItemLongClickListener mOnItemLongClickListener;

	public void setOnItemLongClickListener(OnItemLongClickListener listener) {
		this.mOnItemLongClickListener = listener;
	}

	@Override
	public void onItemClick(View view, int position) {
		String desc = String.format("您點擊了第%d項,內容是%s", position+1, mTitleArray[position]);
		Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
	}
	
	@Override
	public void onItemLongClick(View view, int position) {
		String desc = String.format("您長按了第%d項,內容是%s", position+1, mTitleArray[position]);
		Toast.makeText(mContext, desc, Toast.LENGTH_SHORT).show();
	}

}


布局管理器

布局管理器LayoutManager是RecyclerView的精髓,也是RecyclerView之所以強悍的源泉。它不但提供了三類布局管理,分別實現類似ListView、GridView、瀑布流網格的效果,而且可在代碼中隨時由RecyclerView調用setLayoutManager方法設置新的布局;一旦調用了setLayoutManager方法,界面就會根據新布局刷新列表項,這個特性特別適合於手機在豎屏/橫屏之間的顯示切換(如豎屏時展示ListView,橫屏時展示GridView),也適合在不同屏幕分辨率如手機/平板之間的顯示切換(如手機上展示ListView,平板上展示GridView)。


LinearLayoutManager

線性布局管理器LinearLayoutManager類似於LinearLayout,當它是垂直方向布局時,則展示效果類似於ListView;當它是水平方向布局時,則展示效果類似於HorizontalListView,當然這個HorizontalListView不是Android的原生控件,而是大神們自定義的控件。
下面是LinearLayoutManager的常用方法:
構造函數 : 可指定列表的方向與是否為相反方向開始布局。
setOrientation : 單獨設置列表的方向。
setReverseLayout : 單獨設置是否為相反方向開始布局。默認false,如果設置為true,那麼垂直方向將從下往上開始布局,水平方向將從右往左開始布局。


下面是線性布局的效果截圖:
\


下面是LinearLayoutManager的示例代碼:
import com.example.exmrecycler.adapter.RecyclerAdapter;
import com.example.exmrecycler.widget.DividerItemDecoration;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.widget.LinearLayout;

public class LinearActivity extends Activity {
	
	private RecyclerView rv_linear;
	private LinearLayoutManager mLayoutManager;
	private RecyclerAdapter mAdapter;
	private String[] starArray = {"水星", "金星", "地球", "火星", "木星", "土星"};
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龍年", "蛇年",
			"馬年", "羊年", "猴年", "雞年", "狗年", "豬年"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_linear);
		
		rv_linear = (RecyclerView) findViewById(R.id.rv_linear);
		
		mLayoutManager = new LinearLayoutManager(this);
		mLayoutManager.setOrientation(LinearLayout.VERTICAL);
		rv_linear.setLayoutManager(mLayoutManager);
		
		mAdapter = new RecyclerAdapter(this, 1, yearArray);
		rv_linear.setAdapter(mAdapter);
		
		rv_linear.setItemAnimator(new DefaultItemAnimator());
		rv_linear.addItemDecoration(new DividerItemDecoration(this, 
				DividerItemDecoration.VERTICAL_LIST));
	}
	
}


GridLayoutManager

網格布局管理器GridLayoutManager類似於GridLayout,GridLayout是Android4.0新增的布局類型。話說Android陸陸續續增加了一些布局,比如前幾節提到的側滑布局SlidingPaneLayout和DrawerLayout,還有下一節要介紹的SwipeRefreshLayout(詳細說明參見《Android開發筆記(一百二十三)下拉刷新布局》),這些新布局著實增加了廣大碼農的學習時間,所以還是能省則省,從展示效果來看,GridLayoutManager類似於GridView,所以就不再另外學習GridLayout了。
下面是GridLayoutManager的常用方法:
構造函數 : 可指定網格的列數。
setSpanCount : 單獨設置網格的列數。
setSpanSizeLookup : 設置列表項的占位規則。默認一項占一列,如果想某項占多列,則可在此設置自定義的占位規則,即由抽象類GridLayoutManager.SpanSizeLookup派生出具體的實現類。


下面是網格布局的效果截圖:
\


下面是GridLayoutManager的示例代碼:
import com.example.exmrecycler.adapter.RecyclerAdapter;
import com.example.exmrecycler.widget.DividerGridItemDecoration;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;

public class GridActivity extends Activity {
	
	private RecyclerView rv_grid;
	private GridLayoutManager mLayoutManager;
	private RecyclerAdapter mAdapter;
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龍年", "蛇年",
			"馬年", "羊年", "猴年", "雞年", "狗年", "豬年"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_grid);
		
		rv_grid = (RecyclerView) findViewById(R.id.rv_grid);
		
		mLayoutManager = new GridLayoutManager(this, 4);
//		mLayoutManager.setSpanCount(3);
//		//以下占位規則的意思是:第一項占四列,第二列和第三項各占兩列
//		//如果網格的列數為四,那麼第一項將占滿第一行,第二列和第三項平分第二行,第三行開始每行有四項
//		mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
//            @Override
//            public int getSpanSize(int position) {
//            	if (position == 0) {
//                    return 4;
//            	} else if (position==1 || position==2) {
//                    return 2;
//            	} else {
//                    return 1;
//            	}
//            }
//        });
		rv_grid.setLayoutManager(mLayoutManager);
		
		mAdapter = new RecyclerAdapter(this, 2, yearArray);
		rv_grid.setAdapter(mAdapter);
		
		rv_grid.setItemAnimator(new DefaultItemAnimator());
		rv_grid.addItemDecoration(new DividerGridItemDecoration(this));
	}
	
}


StaggeredGridLayoutManager

瀑布流網格相信大家多多少少都有所了解了,如有不太清楚的,可參考《Android開發筆記(二十二)瀑布流網格》。之前我們要想實現瀑布流效果,都得自定義控件或者借助於第三方開源庫如StaggeredGridView、PinterestLikeAdapterView等等;現在Android在support-v7庫中推出了StaggeredGridLayoutManager,這讓我們對瀑布流效果的開發大大簡化了,只要在適配器代碼中動態設置每個網格的高度,系統便會自動在界面上依次排列瀑布流網格。
下面是StaggeredGridLayoutManager的常用方法:
構造函數 : 可指定網格的列數和方向。
setSpanCount : 單獨設置網格的列數。
setOrientation : 單獨設置瀑布流布局的方向。LinearLayout.VERTICAL表示垂直方向,LinearLayout.HORIZONTAL表示水平方向,通常默認LinearLayout.VERTICAL。
setReverseLayout : 設置是否為相反方向開始布局。默認false,如果設置為true,那麼垂直方向將從下往上開始布局,水平方向將從右往左開始布局。


下面是瀑布流網格布局的效果截圖:
\


下面是StaggeredGridLayoutManager的示例代碼:
import com.example.exmrecycler.adapter.RecyclerAdapter;
import com.example.exmrecycler.widget.SpacesItemDecoration;

import android.app.Activity;
import android.os.Bundle;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.StaggeredGridLayoutManager;
import android.widget.LinearLayout;

public class StaggeredActivity extends Activity {
	
	private RecyclerView rv_staggered;
	private StaggeredGridLayoutManager mLayoutManager;
	private RecyclerAdapter mAdapter;
	private String[] yearArray = {"鼠年", "牛年", "虎年", "兔年", "龍年", "蛇年",
			"馬年", "羊年", "猴年", "雞年", "狗年", "豬年"};

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_staggered);
		
		rv_staggered = (RecyclerView) findViewById(R.id.rv_staggered);
		
		mLayoutManager = new StaggeredGridLayoutManager(4, LinearLayout.VERTICAL);
//		mLayoutManager.setSpanCount(3);
//		mLayoutManager.setOrientation(LinearLayout.HORIZONTAL);
//		mLayoutManager.setReverseLayout(true);
		rv_staggered.setLayoutManager(mLayoutManager);
		
		mAdapter = new RecyclerAdapter(this, 3, yearArray);
		rv_staggered.setAdapter(mAdapter);
		
		rv_staggered.setItemAnimator(new DefaultItemAnimator());
		//每項周圍的空隙是5,那麼項與項之間的間隔就是5+5=10。
		SpacesItemDecoration decoration=new SpacesItemDecoration(5);
		rv_staggered.addItemDecoration(decoration);
	}
	
}
  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved