跳到主要內容

Material Design - RecyclerView and AppBarLayout

GitHub

RecyclerView

RecyclerView 小工具是比較進階和彈性的 ListView 版本。
這個小工具是一個用來顯示大型資料集的容器,只要維護少數幾個視圖,就可極有效率地捲動資料集。您的資料集元素在執行階段會根據使用者操作動作或網路事件而變更。

RecyclerView 類別會提供下列項目,簡化大型資料集的顯示和處理方式:
  • 版面配置管理員,用來將項目定位 (將它設為橫向或直向,或者以網格形式顯示)
  • 常見項目操作 (例如移除或新增項目) 的預設動畫
  •  CoordinatorLayout 也只支援 RecyclerView 而不支援 LisView
  • RecyclerView 架構,比 ListView多了一個LayoutManager

如果要使用 RecyclerView 小工具,您必須指定配接器和版面配置管理員。
如要建立配接器,請延伸 RecyclerView.Adapter 類別。

RecyclerView 提供下列內建的版面配置管理員:
  • LinearLayoutManager 在垂直或水平捲動清單中顯示項目。
  • GridLayoutManager 會在網格中顯示項目。
  • StaggeredGridLayoutManager 會在交錯網格中顯示項目。
如要建立自訂版面配置管理員,請延伸 RecyclerView.LayoutManager 類別。

Material Design-AppBarLayout

AppBar 最初叫 ActionBar,後來改名為 Toolbar ,現在統稱叫 AppBar。
AppBarLayout 即是控制內容元件滑動時 AppBar 的顯示,需要在 CoordinatorLayout 底下才能運作。可輕易做到滑動 RecyclerView 時,自動顯示和隱藏 ToolBar

CollapsingToolbarLayout

它是 AppBarLayout 中唯一的child,而它的 child view 可加上 layout_collapseMode
去決定 CollapsingToolBarLayout 被隱藏時他們自身的顯示情況。
最常見的用法是在滑動時將一個大圖和文字的 AppBar 縮為純文字。

設定Layout

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <android.support.design.widget.AppBarLayout
        android:id="@+id/appbar"
        android:layout_height="wrap_content"
        android:layout_width="match_parent">

        <android.support.design.widget.CollapsingToolbarLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:layout_scrollFlags="scroll|snap|exitUntilCollapsed"
            app:contentScrim="?colorPrimary"
            app:expandedTitleMarginStart="4dp"
            app:expandedTitleMarginEnd="8dp">

            <ImageView
                android:id="@+id/app_bar_image"
                android:layout_width="match_parent"
                android:layout_height="192dp"
                app:layout_collapseMode="parallax"
                android:src="@mipmap/ic_launcher_round"
                android:scaleType="centerCrop"
                tools:ignore="ContentDescription"
                android:fitsSystemWindows="true"/>

            <android.support.v7.widget.Toolbar
                android:id="@+id/toolbar_recycler"
                android:layout_height="?attr/actionBarSize"
                android:layout_width="match_parent"/>

        </android.support.design.widget.CollapsingToolbarLayout>
    </android.support.design.widget.AppBarLayout>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recycler_View"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:clipToPadding="false"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        />

    <android.support.design.widget.FloatingActionButton
        android:layout_height="wrap_content"
        android:layout_width="wrap_content"
        app:layout_anchor="@id/appbar"
        app:layout_anchorGravity="bottom|right|end"
        android:src="@android:drawable/ic_input_add"
        android:layout_marginEnd="16dp"
        android:clickable="true"
        app:fabSize="mini"/>


</android.support.design.widget.CoordinatorLayout>


新增 item_contact.xml,它將會是 RecyclerView中每一個項目的 view。

將 item_contact的 background 改為 ?android:attr/selectableItemBackground,才會有 click 下去變色的效果。
<?xml version="1.0" encoding="utf-8"?>

<LinearLayout

    xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"

    android:layout_height="wrap_content"

    android:orientation="vertical">

    <TextView

        android:id="@+id/textContact"

        android:textSize="24sp"

        android:textStyle="bold"

        android:padding="8dp"

        android:layout_width="match_parent"

        android:layout_height="wrap_content"

        android:text=""/>

</LinearLayout>


先建立一個 model Contract.java

public class Contact {

    private String name;

    public Contact(){

    }

    public String getName() {

        return name;

    }

    public void setName(String name) {

        this.name = name;

    }


    public static List<Contact> contactsSampleList(){
        List<Contact> contactList = new ArrayList<>();
        for (int i = 0; i < 30; i++){
            Contact contact = new Contact();
            contact.setName("Contacts Person -" + i);
            contactList.add(contact);
        }
        return contactList;
    }

}


建立 ContactsAdapter

先在 ContactsAdapter 中加入 MyViewHolder,
RecyclerView 沒有像有  Listview.setItemClickListener()的方法,要自已處理Click Event

建立一個 interface
public interface MyViewHolderClick {

            void clickOnView(View v, int position);

        }


 public static class MyViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{

        public TextView nameTextView ;


        //建立一個點擊recyclerView item 的interface

        public MyViewHolderClick mListener;


        public MyViewHolder(View itemView,MyViewHolderClick listener) {

            super(itemView);

             mListener = listener;

            nameTextView = (TextView)itemView.findViewById(R.id.textContact);

            itemView.setOnClickListener(this);

        }


        @Override
        public void onClick(View v) {
            mListener.clickOnView(v,getLayoutPosition());
        }

    }


 完整的 ContactsAdapter.java

public class ContactsAdapter extends RecyclerView.Adapter<ContactsAdapter.MyViewHolder> {

    private List<Contact> mContact;

    public ContactsAdapter (List<Contact> contacts){

        mContact = contacts;

    }

 

    public static class MyViewHolder extends RecyclerView.ViewHolder

                 implements View.OnClickListener{
        public TextView nameTextView ;


        //建立一個點擊recyclerView item 的interface
        public MyViewHolderClick mListener;

        public MyViewHolder(View itemView,MyViewHolderClick listener) {
            super(itemView);
             mListener = listener;
            nameTextView = (TextView)itemView.findViewById(R.id.textContact);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            mListener.clickOnView(v,getLayoutPosition());
        }
    }


    @Override

    public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {

        Context context = parent.getContext();

        View contactView = LayoutInflater.from(context).inflate(R.layout.item_contact,parent,false);

        

        MyViewHolder myViewHolder = 

             new MyViewHolder(contactView, new MyViewHolderClick() {
            

          @Override
            public void clickOnView(View view, int position) {
                Contact contact = mContact.get(position);
                Snackbar.make(view, contact.getName(), Snackbar.LENGTH_SHORT).show();
            }
        });

        return myViewHolder;

    }


    @Override

    public void onBindViewHolder(MyViewHolder holder, int position) {

        Contact contact = mContact.get(position);

        TextView nameTextView = holder.nameTextView;

        nameTextView.setText(contact.getName());

    }


    @Override

    public int getItemCount() {

        return mContact.size();

    }

}


項目分隔線

RecyclerView 是沒有內建項目之間的分隔線的。
要加上的話,需要自行使用 ItemDecoration,編寫對應的 class,
這樣你可以自行決定項目間的間距和分隔線的外觀等。

DividerItemDecoration.java
package com.admin.claire.materialdesignpatterns;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;

/**
 * 為RecyclerView 加上項目分隔線
 */

public class DividerItemDecoration extends RecyclerView.ItemDecoration{

    private Drawable mDivider; //分配者
    private int mOrientation;
    public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
    public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;

    //使用系統主題中的R.attr.listDivider作為Item間的分割線
    private static final int[] ATTRS = new int[]{
            android.R.attr.listDivider
    };

    public DividerItemDecoration (Context context, int orientation){
        final TypedArray typedArray = context.obtainStyledAttributes(ATTRS);
        mDivider = typedArray.getDrawable(0);
        typedArray.recycle();
        setOrientation(orientation);
    }

    //設置螢幕方向
    private void setOrientation(int orientation) {
        if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST){
            throw new IllegalArgumentException("invalid orientation");
        }
        mOrientation = orientation;
    }

    @Override
    public void onDraw(Canvas c, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST){
            drawVertical(c, parent, state);
        } else {
            drawHorizontal(c, parent, state);
        }
    }

    //畫垂直直線 |||
    private void drawVertical(Canvas c, RecyclerView parent,RecyclerView.State state) {
        //分隔線的左邊 = paddingLeft值
        final int left = parent.getPaddingLeft();
        //分隔線的右邊 = RecyclerView 寬度 - paddingRight值
        final int right = parent.getWidth() - parent.getPaddingRight();

        final  int childCount = parent.getChildCount(); //分隔線數量=item數量
        for (int i = 0; i < childCount; i++){
            final View child = parent.getChildAt(i);

            //獲得child的佈局
            final RecyclerView.LayoutParams params =
                    (RecyclerView.LayoutParams) child.getLayoutParams();

            //分隔線的上邊 = item的底部 + item根標籤的bottomMargin值
            final int top = child.getBottom() + params.bottomMargin;

            //分隔線的下邊 = 分隔線的上邊 + 分隔線本身高度
            final int bottom = top + mDivider.getIntrinsicHeight();

            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);

        }
    }

    //畫水平線 三
    private void drawHorizontal(Canvas c, RecyclerView parent, RecyclerView.State state) {
        final int top = parent.getPaddingTop();
        final int bottom = parent.getHeight() - parent.getPaddingBottom();

        final int childCount = parent.getChildCount();
        for (int i = 0; i < childCount; i++){
            final View child = parent.getChildAt(i);

            final RecyclerView.LayoutParams params =
                    (RecyclerView.LayoutParams) child.getLayoutParams();
            final int left = child.getRight() + params.rightMargin;
            final int right = left + mDivider.getIntrinsicHeight();
            mDivider.setBounds(left, top, right, bottom);
            mDivider.draw(c);
        }
    }

    //由於Divider也有長寬高,每一個Item需要向下或者向右偏移
    //獲取Item偏移量,此方法是為每個Item四周預留出空間,從而讓分隔線的繪製在預留的空間內
    @Override
    public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
        if (mOrientation == VERTICAL_LIST){
            //垂直方向的分隔線:item向下偏移一個分隔線的高度
            outRect.set(0,0,0,mDivider.getIntrinsicHeight());
        } else {
            //水平方向的分隔線:item向右偏移一個分隔線的寬度
            outRect.set(0,0, mDivider.getIntrinsicWidth(),0);
        }
    }
}


DividerItemDecoration畫的分割線是讀取系統的屬性android.R.attr.listDivider,
使用系統的listDivider好處就是就是方便我們去隨意的分隔線的樣式

更改分隔線的樣式

1.到res/values/styles.xml,在其中聲明android:listDivider屬性,然後使用我們自己的樣式
<style name="MyAppTheme" parent="Theme.AppCompat.Light.NoActionBar">
        <item name="colorPrimary">@color/purpleColorPrimary</item>
        <item name="colorPrimaryDark">@color/purpleDarkColorPrimary</item>
        <item name="colorAccent">@color/colorAccent</item>

        <item name="android:listDivider">@drawable/my_divider</item>
    </style>
2.在res/drawable目錄下聲明我們自己的樣式my_divider.xml
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <gradient
        android:centerColor="#b333e6"
        android:endColor="#7414be"
        android:startColor="#e411e0"
        android:type="linear" />
    <size android:height="4dp"/>

</shape>

最後在 Activity 中將 RecyclerView 和 ContactsAdapter 結合:

package com.admin.claire.materialdesignpatterns;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.Menu;
import android.view.MenuItem;

import com.admin.claire.materialdesignpatterns.adapter.ContactsAdapter;
import com.admin.claire.materialdesignpatterns.model.Contact;

import java.util.List;

public class RecyclerViewActivity extends AppCompatActivity {
    Toolbar toolbar_recycler;
    RecyclerView mRvContacts;
    ContactsAdapter mAdapter;
    RecyclerView.LayoutManager mLayoutMag;
    List<Contact> mContactList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_recycler_view);

        toolbar_recycler = (Toolbar)findViewById(R.id.toolbar_recycler);
        setSupportActionBar(toolbar_recycler);
        // add back arrow to toolbar <-
        if (getSupportActionBar() != null){
            getSupportActionBar().setDisplayHomeAsUpEnabled(true);
            getSupportActionBar().setDisplayShowHomeEnabled(true);
        }

        initRecyclerView();
    }

    private void initRecyclerView() {
        mRvContacts = (RecyclerView)findViewById(R.id.recycler_View);
        mRvContacts.setHasFixedSize(true);

        // use a linear layout manager
        mLayoutMag = new LinearLayoutManager(this);
        mRvContacts.setLayoutManager(mLayoutMag);

        //為RecyclerView 新增範例資料
        mContactList = Contact.contactsSampleList();
        mAdapter = new ContactsAdapter(mContactList);
        mRvContacts.setAdapter(mAdapter);

        //為RecyclerView 加上項目分隔線
        RecyclerView.ItemDecoration itemDecoration =
                new DividerItemDecoration(this, DividerItemDecoration.VERTICAL_LIST);
        mRvContacts.addItemDecoration(itemDecoration);

    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.menu_rv_appbar, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        int id = item.getItemId();
        switch (id){
            case R.id.action_add:
                addContact();
                return true;
            case R.id.action_remove:
                removeContacts();
                return true;
        }
        return super.onOptionsItemSelected(item);
    }

    private void addContact() {
        Contact contact = new Contact();
        contact.setName("Robert John");
        mContactList.add(1, contact);
        mAdapter.notifyItemInserted(1);
    }

    private void removeContacts(){
        mContactList.remove(mContactList.size()-1);
        mAdapter.notifyItemRemoved(mContactList.size());
    }
}




References

留言

這個網誌中的熱門文章

SQLite - RecyclerView and SearchView

設計資料庫表格 建立資料庫表格使用SQL的「CREATE TABLE」指令,這個指令需要指定表格的名稱, 還有這個表格用來儲存每一筆資料的欄位(Column)。 這些需要的表格欄位可以對應到主要類別中的欄位變數, 不過SQLite資料庫的資料型態只有下面這幾種,使用它們來決定表格欄位可以儲存的資料型態: INTEGER – 整數,對應Java 的byte、short、int 和long。 REAL – 小數,對應Java 的float 和double。 TEXT – 字串,對應Java 的String。 一個SQLite表格建議一定要包含一個可以自動為資料編號的欄位,欄位名稱固定為「 _id 」,型態為「INTEGER」。 後面加上「 PRIMARY KEY AUTOINCREMENT 」的設定,就可以讓SQLite自動為每一筆資料編號。 建立SQLiteOpenHelper類別 Android 提供許多方便與簡單的資料庫API,可以簡化應用程式處理資料庫的工作。 這些API都在「android.database.sqlite」套件,它們可以用來執行資料庫的管理和查詢的工作。 這個套件中的「 SQLiteOpenHelper 」類別,可以在應用程式中執行 建立資料庫與表格 的工作,應用程式第一次在裝置執行的時候,由它負責建立應用程式需要的資料庫與表格, 後續執行的時候開啟已經建立好的資料庫讓應用程式使用。 還有應用程式在運作一段時間以後,如果增加或修改功能,資料庫的表格也增加或修改了 ,它也可以為應用程式執行資料庫的修改工作,讓新的應用程式可以正常的運作。 Create New Java Class public class MyDBHelper extends SQLiteOpenHelper{ //資料庫名稱 public static final String DATABASE_NAME = "mydata.db"; //資料庫版本,資料結構改變的時後要更改這個數字,通常是加一 public static final int VERSION = 1; //資料庫物件,固定的欄位變數 private static SQLiteDatabase

Android using BottomNavigationView

Creating Navigation Menu menu_navigation.xml <?xml version="1.0" encoding="utf-8"?> <menu xmlns:android="http://schemas.android.com/apk/res/android"> <item android:id="@+id/action_home" android:icon="@drawable/ic_home" android:title="@string/action_home"/> <item android:id="@+id/action_profile" android:icon="@drawable/ic_person" android:title="@string/action_profile"/> <item android:id="@+id/action_settings" android:icon="@drawable/ic_settings" android:title="@string/action_settings"/> </menu> Creating Layout main_activity.xml <?xml version="1.0" encoding="utf-8"?> <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://

Material Design - Card View and Recycler View

build.gradle (Module.app) 添加CardView,RecyclerView和Glide依賴關係。  RecyclerView用於以網格方式顯示相冊。  CardView用於顯示單個專輯項目。  Glide用於顯示專輯封面圖片。 dependencies {      //CardView     compile 'com.android.support:cardview-v7:25.3.1'     //RecyclerView     compile 'com.android.support:recyclerview-v7:25.3.1'     //Glide ImageLoader....     compile 'com.github.bumptech.glide:glide:3.7.0' } 將以下字符串,顏色和調整資源添加到strings.xml,colors.xml和dimensions.xml文件。 string.xml <resources> <string name="app_name">Card View</string> <string name="action_settings">Settings</string> <string name="action_add_favourite">Add to Favourites</string> <string name="action_play_next">Play Next</string> <string name="backdrop_title">LOVE MUSIC</string> <string name="backdrop_subtitle">This season top 20 albums</string> </resources>