Android之滾動控件Recycler

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 ,會讓子項就佔滿了整個屏幕。把 ImageViewTextView 都設置爲水平居中,用 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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章