Activity之滾動控件Recycler
基本用法
RecyclerView 不僅實現了和 ListView 同樣的效果,而且還優化了 ListView 存在的各種不足。
RecyclerView 也是新增的控件,以必須先在項目的 build.gradle 中添加相應的依賴庫才能使用:
compile 'com.android.support:recyclerview-v7:24.2.1'
修改佈局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
這裏把寬度和高度都定義爲 match_parent,這樣 RecyclerView 就能佔滿整個屏幕。因爲 RecyclerView 也不是系統內置的 SDK,所以這裏引用的是完整的包路徑。
然後爲 RecyclerView 創建一個適配器(繼承自RecyclerView.Adapter<CatAdapter.ViewHolder>
):
public class CatAdapter extends RecyclerView.Adapter<CatAdapter.ViewHolder> {
private List<Cat> cats;
static class ViewHolder extends RecyclerView.ViewHolder {
ImageView image;
TextView name;
public ViewHolder(View view) {
super(view);
image = (ImageView) view.findViewById(R.id.image);
name = (TextView) view.findViewById(R.id.name);
}
}
public CatAdapter(List<Cat> cats) {
this.cats = cats;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cat_item, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder holder, int position) {
Cat cat = cats.get(position);
holder.image.setImageResource(cat.getImageId());
holder.name.setText(cat.getName());
}
@Override
public int getItemCount() {
return cats.size();
}
}
先定義了一個內部類 ViewHolder,它繼承自 RecyclerView.ViewHolder。然後在 ViewHolder 的構造函數中傳入一個 View 參數,它是 RecyclerView 子項的最外層佈局,所以我們可以通過它來取得佈局中的 ImageView 和 TextView 的實例。
CatAdapter
的構造函數用於把要展示的數據源傳遞進來,並賦值給一個類變量 cats。
因爲 CatAdapter 繼承自 RecyclerView.Adapter
,所以必須重寫以下三個方法:
- onCreateViewHolder - 創建 ViewHolder 實例,我們把 cat_item 的佈局加載進來,創建了一個 ViewHolder 實例。
- onBindViewHolder - 對 RecyclerView 的子項數據進行賦值,這個方法會在每個子項被滾動到屏幕內時進行。
- getItemCount - 返回 RecyclerView 的子項總數。
在活動類中使用 RecyclerView :
public class MainActivity extends AppCompatActivity {
private List<Cat> cats = new ArrayList<>();
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
RecyclerView recyclerView=(RecyclerView)findViewById(R.id.recycler_view);
LinearLayoutManager layoutManager=new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
CatAdapter adapter=new CatAdapter(cats);
recyclerView.setAdapter(adapter);
}
/**
* 初始化數據
*/
private void init() {
cats.add(new Cat("貓1", R.drawable.cat1));
cats.add(new Cat("貓2", R.drawable.cat2));
cats.add(new Cat("貓3", R.drawable.cat3));
cats.add(new Cat("貓4", R.drawable.cat4));
cats.add(new Cat("貓5", R.drawable.cat5));
cats.add(new Cat("貓6", R.drawable.cat6));
cats.add(new Cat("貓7", R.drawable.cat7));
cats.add(new Cat("貓8", R.drawable.cat8));
cats.add(new Cat("貓9", R.drawable.cat9));
}
}
這裏創建了 LinearLayoutManager
的線性佈局對象,傳遞給了 recyclerView.setLayoutManager()
方法。
橫向滾動
把 cat_item 中的元素變爲垂直排列:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:orientation="vertical"
>
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp" />
</LinearLayout>
這裏還把 LinearLayout 的寬度設爲 100dp,即固定的值。用 match_parent ,會讓子項就佔滿了整個屏幕。把 ImageView
與 TextView
都設置爲水平居中,用 android:layout_marginTop
,讓文字與圖片保持了一段距離。
修改活動類的代碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
...
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
...
}
橫向排列
ListView 的佈局排列是由自身來管理的,所以存在一定的侷限性;而 RecyclerView 把佈局的工作交給了 LayoutManager,LayoutManager 制定了一系列可擴展的佈局排列接口。
瀑布流佈局
使用 StaggeredGridLayoutManager 實現瀑布流佈局
修改 cat_item 佈局文件:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_margin="5dp"
>
<ImageView
android:id="@+id/image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
/>
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="left"
android:layout_marginTop="10dp" />
</LinearLayout>
把 LinearLayout 的寬度改爲 wrap_content,這樣寬度會根據實際的佈局列數自動適配。 還使用 layout_margin 讓子項之間留出一定的間距,最後將 TextView 改爲居左對齊。
修改活動類的代碼:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view);
StaggeredGridLayoutManager layoutManager=new StaggeredGridLayoutManager(3,StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(layoutManager);
CatAdapter adapter = new CatAdapter(cats);
recyclerView.setAdapter(adapter);
}
創建了 StaggeredGridLayoutManager 的實例,它的構造函數接受兩個參數,第一個參數用於指定佈局的列數,第二個參數用於指定佈局的排列方向。
點擊事件
RecyclerView 沒有像 ListView 一樣的 setOnItemClickListener() 事件,所以需要自己給子項具體的 View 註冊點擊事件。
ListView 的 setOnItemClickListener()
註冊的是子項的點擊事件,但如果想要註冊點擊的是子項裏具體的某個按鈕時,使用 ListView 實現起來就比較麻煩。所以 RecyclerView 直接擯棄了子項點擊事件的監聽器,把所有的點擊事件都交給具體的 View 去註冊實現。
修改適配器:
static class ViewHolder extends RecyclerView.ViewHolder {
View catView;
ImageView image;
TextView name;
public ViewHolder(View view) {
super(view);
catView = view;
image = (ImageView) view.findViewById(R.id.image);
name = (TextView) view.findViewById(R.id.name);
Log.d(TAG, "ViewHolder: image:" + image);
Log.d(TAG, "ViewHolder: name:" + name);
}
}
public CatAdapter(List<Cat> cats) {
this.cats = cats;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.cat_item, parent, false);
final ViewHolder holder = new ViewHolder(view);
holder.catView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Cat cat = cats.get(position);
Toast.makeText(v.getContext(), "你點擊了 View " + cat.getName(), Toast.LENGTH_SHORT).show();
}
});
holder.image.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = holder.getAdapterPosition();
Log.d(TAG, "onClick: position:" + position);
Cat cat = cats.get(position);
Toast.makeText(v.getContext(), "你點擊了圖片 " + cat.getName(), Toast.LENGTH_SHORT).show();
}
});
return holder;
}