GitHub |
RecyclerView
RecyclerView 小工具是比較進階和彈性的 ListView 版本。這個小工具是一個用來顯示大型資料集的容器,只要維護少數幾個視圖,就可極有效率地捲動資料集。您的資料集元素在執行階段會根據使用者操作動作或網路事件而變更。
RecyclerView 類別會提供下列項目,簡化大型資料集的顯示和處理方式:
- 版面配置管理員,用來將項目定位 (將它設為橫向或直向,或者以網格形式顯示)
- 常見項目操作 (例如移除或新增項目) 的預設動畫
- CoordinatorLayout 也只支援 RecyclerView 而不支援 LisView
- RecyclerView 架構,比 ListView多了一個LayoutManager
如果要使用 RecyclerView 小工具,您必須指定配接器和版面配置管理員。
如要建立配接器,請延伸 RecyclerView.Adapter 類別。
RecyclerView 提供下列內建的版面配置管理員:
- LinearLayoutManager 在垂直或水平捲動清單中顯示項目。
- GridLayoutManager 會在網格中顯示項目。
- StaggeredGridLayoutManager 會在交錯網格中顯示項目。
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());
}
}
留言
張貼留言