狠狠撸

狠狠撸Share a Scribd company logo
RecyclerView
droid girls meetup #2
Yuki Anzai (@yanzm)
? blog : Y.A.M の雑記帳
? y-anz-m.blogspot.com
? twitter : @yanzm (やんざむ)
? uPhyca Inc. (株式会社ウフィカ)
あんざいゆき
RecyclerView
A flexible view for
providing a limited window
into a large data set.
RecyclerView
RecyclerView は
サポートライブラリ
https://developer.android.com/topic/libraries/support-
library/features.html#v7-recyclerview
RecyclerView
大規模なデータセットに、
限定されたウィンドウを
提供するための柔軟なビュー
RecyclerView
大規模なデータセットの一部を
ビューを再利用しながら
表示するためのコンポーネント
RecyclerView
ListViewとかGridView
みたいなやつ
RecyclerViewの構成
? RecyclerView
? データを表示するためのスクロール可能な
View
? RecyclerView
? データを表示するためのスクロール可能な
View
? RecyclerView.LayoutManager
? アイテム用のビューのサイズを計算し、配
置する
RecyclerViewの構成
? RecyclerView.Adapter
? RecyclerViewに表示するデータセットを
管理し、アイテム用のViewにデータを紐
づける
RecyclerViewの構成
? RecyclerView.Adapter
? RecyclerViewに表示するデータセットを
管理し、アイテム用のViewにデータを紐
づける
? RecyclerView.ViewHolder
? アイテム用のビューとメタデータを保持す
る
RecyclerViewの構成
RecyclerViewの構成
RecyclerView LayoutManager
Adapter
子ビュー配置
子ビュー
(ViewHolder)
にデータ紐付
Recycler
ViewHolderを再利用
再利用する
ViewHolder
ListViewの構成
RecyclerView LayoutManager
Adapter
子ビュー配置
子ビュー
(ViewHolder)
にデータ紐付
Recycler
ViewHolderを再利用
再利用する
ViewHolder
ListView
ListAdapter
ListViewの構成
ListView =
RecyclerView + LayoutManager + Recycler
+ その他もろもろの機能
ListAdapter = Adapter + ViewHolder
ListViewの構成
ListView =
RecyclerView + LayoutManager + Recycler
+ その他もろもろの機能
ListAdapter = Adapter + ViewHolder
ListView vs RecyclerView
ListView RecyclerView
区切り線 ? 自分で実装
listSelector ?
onItemClick ? 自分で実装
choiceMode ?
*
* android.support.v7.widget.DividerItemDecoration が 25.0.0 で追加されました
ListView vs RecyclerView
ListView RecyclerView
Filter ? 自分で実装
FadingEdge ? 自分で実装
Header, Footer ? 自分で実装
StaggeredGrid ?
ListView vs RecyclerView
ListView RecyclerView
追加?削除の
アニメーション
自分で実装 ?
Swipe to Dismiss 自分で実装 ?
Drag & Drop 自分で実装 ?
横スクロール配置 ?
RecyclerViewの
一番シンプルな
使い方
gradle の設定
dependencies {?
…?
?
compile 'com.android.support:recyclerview-v7:25.1.0'?
}
最低限必要なもの
? RecyclerView
? RecyclerView.LayoutManager
? RecyclerView.Adapter
? RecyclerView.ViewHolder
最低限必要なもの
? RecyclerView
? RecyclerView.LayoutManager
? RecyclerView.Adapter
? RecyclerView.ViewHolder
<android.support.v7.widget.RecyclerView?
android:id="@+id/recycler_view"?
android:layout_width="match_parent"?
android:layout_height="match_parent"?
app:layoutManager="LinearLayoutManager"/>
RecyclerView + LayoutManager
<android.support.v7.widget.RecyclerView?
android:id="@+id/recycler_view"?
android:layout_width="match_parent"?
android:layout_height="match_parent"?
app:layoutManager="android.support.v7.widget.LinearLayou
RecyclerView + LayoutManager
RecyclerView + LayoutManager
<android.support.v7.widget.RecyclerView?
android:id="@+id/recycler_view"?
android:layout_width="match_parent"?
android:layout_height="match_parent"?
app:layoutManager="net.yanzm.sample.MyLayoutManager"/>
最低限必要なもの
? RecyclerView
? RecyclerView.LayoutManager
? RecyclerView.Adapter
? RecyclerView.ViewHolder
public class ViewHolder extends RecyclerView.ViewHolder {?
?
private static final int LAYOUT_ID = android.R.layout.simple_list_item_1;?
?
@NonNull?
public static ViewHolder create(@NonNull LayoutInflater inflater,?
ViewGroup parent) {?
return new ViewHolder(inflater.inflate(LAYOUT_ID, parent, false));?
}?
?
final TextView textView;?
?
private ViewHolder(View itemView) {?
super(itemView);?
textView = (TextView) itemView.findViewById(android.R.id.text1);?
}?
}
ViewHolder
public class SimpleAdapter extends RecyclerView.Adapter<ViewHolder> {?
?
@NonNull?
private final List<String> data;?
?
public SimpleAdapter(@NonNull List<String> data) {?
this.data = data;?
}?
?
@Override?
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {?
final LayoutInflater inflater = LayoutInflater.from(parent.getContext())
return ViewHolder.create(inflater, parent);?
}?
?
@Override?
public void onBindViewHolder(ViewHolder holder, int position) {?
final String text = data.get(position);?
holder.textView.setText(text);?
}?
?
@Override?
public int getItemCount() {?
return data.size();?
}?
}
Adapter
public class MainActivity extends AppCompatActivity {?
?
@Override?
protected void onCreate(Bundle savedInstanceState) {?
super.onCreate(savedInstanceState);?
setContentView(R.layout.activity_main);?
?
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recycler_view)
recyclerView.setHasFixedSize(true);?
?
List<String> data = new ArrayList<>();?
for (int i = 0; i < 30; i++) {?
data.add("Item : " + i);?
}?
?
final SimpleAdapter adapter = new SimpleAdapter(data);?
recyclerView.setAdapter(adapter);?
}?
}
Activity
droidgirls Recyclerview
LinearLayoutManager
GridLayoutManager
StaggeredGridLayoutManager
LinearLayoutManager
? 設定項目
? orientation
? reverseLayout
? stackFromEnd
orientation
? デフォルトはLinearLayoutManager.VERTICAL
<?xml version="1.0" encoding="utf-8"?>?
<android.support.v7.widget.RecyclerView?
…?
android:orientation="horizontal"?
app:layoutManager="LinearLayoutManager" />
new LinearLayoutManager(this,
LinearLayoutManager.HORIZONTAL, false);
layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
or
or
droidgirls Recyclerview
app:reverseLayout="true" app:stackFromEnd="true"
GridLayoutManager
? 設定項目
? spanCount
? orientation
? reverseLayout
? stackFromEnd
spanCount
? 列数、デフォルトは1
<?xml version="1.0" encoding="utf-8"?>?
<android.support.v7.widget.RecyclerView?
…?
app:layoutManager="GridLayoutManager"?
app:spanCount="2" />
new GridLayoutManager(this, 2);
layoutManager.setSpanCount(2);
or
or
droidgirls Recyclerview
StaggeredGridLayoutManager
? 設定項目
? spanCount
? orientation
? reverseLayout
droidgirls Recyclerview
ItemDecorationで
Dividerをつける
DividerItemDecoration
? v25.0.0 で DividerItemDecoration が追加された
のでそれを使うのが簡単
? https://developer.android.com/reference/
android/support/v7/widget/
DividerItemDecoration.html
? 一番最後のアイテムの下にも描画されてしまう
recyclerView.addItemDecoration(
new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
ItemDecoration
? 装飾を行うためのクラス
? アイテム用のViewのoffsetを指定
? onDraw()でRecyclerViewの下に描画
? onDrawOver()でRecyclerVieの上に描画
アイテム用のViewのOffsetを指定
final int offset = (int) (8 * getResources().getDisplayMetrics().density);?
?
RecyclerView.ItemDecoration itemDecoration = new RecyclerView.ItemDecoration() {?
@Override?
public void getItemOffsets(Rect outRect, View view,?
RecyclerView parent, RecyclerView.State state) {?
outRect.set(offset, offset, offset, offset);?
}?
};?
recyclerView.addItemDecoration(itemDecoration);
droidgirls Recyclerview
アイテム用のViewのOffsetを指定
RecyclerView.ItemDecoration itemDecoration = new RecyclerView.ItemDecoration() {?
@Override?
public void getItemOffsets(Rect outRect, View view,?
RecyclerView parent, RecyclerView.State state) {?
int position = ((RecyclerView.LayoutParams) view.getLayoutParams())?
.getViewLayoutPosition();?
if (position == 0) {?
outRect.set(offset, offset, offset, offset);?
} else {?
outRect.set(offset, 0, offset, offset);?
}?
}?
};?
?
recyclerView.addItemDecoration(itemDecoration);
droidgirls Recyclerview
public class DividerDecoration extends RecyclerView.ItemDecoration {?
?
private final Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG);?
private final int dividerHeight;?
?
public DividerDecoration(Resources res) {?
paint.setColor(Color.GRAY);?
dividerHeight = (int) (4 * res.getDisplayMetrics().density);?
}?
?
@Override?
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, ?
RecyclerView.State state) {?
final int position = ((RecyclerView.LayoutParams) view.getLayoutParams())?
.getViewLayoutPosition();?
// 位置が2番目以降なら上部にdividerを描画したいので、?
// divider分だけ上をあける?
?
int top = position == 0 ? 0 : dividerHeight;?
outRect.set(0, top, 0, 0);?
}
Dividerを描画
@Override?
public void onDrawOver(Canvas c, RecyclerView parent, RecyclerView.State state) {?
super.onDrawOver(c, parent, state);?
// アイテムのビューより上に描画される?
?
?
final RecyclerView.LayoutManager manager = parent.getLayoutManager();?
final int left = parent.getPaddingLeft();?
final int right = parent.getWidth() - parent.getPaddingRight();?
final int childCount = parent.getChildCount();?
for (int i = 1; i < childCount; i++) {?
final View child = parent.getChildAt(i);?
final RecyclerView.LayoutParams params =?
(RecyclerView.LayoutParams) child.getLayoutParams();?
if (params.getViewLayoutPosition() == 0) {?
continue;?
}?
?
// ViewCompat.getTranslationY()を入れないと?
// 追加?削除のアニメーション時の位置が変になる?
final int top = manager.getDecoratedTop(child)?
- params.topMargin + Math.round(ViewCompat.getTranslationY(child));?
final int bottom = top + dividerHeight;?
c.drawRect(left, top, right, bottom, paint);?
}?
}?
}?
Dividerを描画
droidgirls Recyclerview
onItemClick
OnItemClick
? 方法がいくつかある
? ViewHolderのitemViewに
View.setOnClickListener
? ItemDecorationでタップされたアイテムの
位置にselectorを描画
OnItemClick
? 方法がいくつかある
? ViewHolderのitemViewに
View.setOnClickListener
? ItemDecorationでタップされたアイテムの
位置にselectorを描画
v17 leanback
library はこっち
public class SimpleAdapter extends RecyclerView.Adapter<ViewHolder> {?
?
…?
?
protected void onItemClicked(String text) {?
}?
?
@Override?
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {?
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());?
final ViewHolder holder = ViewHolder.create(inflater, parent);?
holder.itemView.setOnClickListener(new View.OnClickListener() {?
@Override?
public void onClick(View v) {?
final int position = holder.getAdapterPosition();?
final String text = data.get(position);?
onItemClicked(text);?
}?
});?
return holder;?
}?
?
…?
}
OnItemClick
public class MainActivity extends AppCompatActivity {?
?
@Override?
protected void onCreate(Bundle savedInstanceState) {?
super.onCreate(savedInstanceState);?
setContentView(R.layout.activity_main);?
?
…?
?
final SimpleAdapter adapter = new SimpleAdapter(data) {?
@Override?
protected void onItemClicked(String text) {?
super.onItemClicked(text);?
Toast.makeText(MainActivity.this, text, Toast.LENGTH_SHORT).show();?
}?
};?
recyclerView.setAdapter(adapter);
OnItemClick
データの追加?削除?変更
notify**
? ArrayAdapterに相当するものは用意されていない
? データの追加?削除?変更時にはnotifyItem**()を呼ぶ
? 苍辞迟颈蹿测顿补迟补厂别迟颁丑补苍驳别诲()はそれ以外のときだけにする
notify**
? notifyItemChanged(int position)
? positionの位置のアイテムの変更された
? notifyItemInserted(int position)
? posiitonの位置にアイテムが追加された
? notifyItemRemoved(int position)
? positionの位置のアイテムが削除された
notify**
? notifyItemMoved(int fromPosition, int toPosition)
? fromPositionにあったアイテムがtoPositionに移動した
? notifyItemRangeChanged(int positionStart, int itemCount)
? positionStartからitemCount個のアイテムが変更された
? notifyItemRangeInserted(int positionStart, int itemCount)
? positionStartにitemCount個のアイテムが追加された
notify**
? notifyItemRangeRemoved(int positionStart, int itemCount)
? positionStartからitemCount個のアイテムが削除された
? notifyDataSetChanged()
? データセットが変更された
public abstract class ArrayAdapter<T, VH extends RecyclerView.ViewHolder> extends
RecyclerView.Adapter<VH> {?
?
private final Object lock = new Object();?
private final List<T> objects;?
?
public ArrayAdapter() {?
this(new ArrayList<T>());?
}?
?
public ArrayAdapter(List<T> objects) {?
this.objects = objects;?
}?
?
public void add(@NonNull T object) {?
final int position;?
synchronized (lock) {?
position = objects.size();?
objects.add(object);?
}?
notifyItemInserted(position);?
}?
ArrayAdapter的なRecyclerView用Adapter
public void addAll(@NonNull Collection<? extends T> collection) {?
final int itemCount = collection.size();?
final int positionStart;?
synchronized (lock) {?
positionStart = objects.size();?
objects.addAll(collection);?
}?
notifyItemRangeInserted(positionStart, itemCount);?
}?
?
public void insert(@NonNull T object, int index) {?
synchronized (lock) {?
objects.add(index, object);?
}?
notifyItemInserted(index);?
}?
?
public void remove(@NonNull T object) {?
final int position = objects.indexOf(object);?
synchronized (lock) {?
objects.remove(object);?
}?
notifyItemRemoved(position);?
}?
}
ArrayAdapter的なRecyclerView用Adapter
ItemViewType
getItemViewType()
? レイアウトの種類(ViewHolder の種類)に応じて別の値
を返すようにする
? データの種類ではない
? 同じ itemViewType で ViewHolder が再利用される
Header と Footer をつくる
? レイアウトの種類(ViewHolder の種類)に応じて別の値
を返すようにする
? データの種類ではない
? 同じ itemViewType で ViewHolder が再利用される
Header と Footer をつくる
public class SimpleAdapter2 extends RecyclerView.Adapter {?
?
private static final int VIEW_TYPE_HEADER = 0;?
private static final int VIEW_TYPE_FOOTER = 1;?
private static final int VIEW_TYPE_ITEM = 2;?
?
…?
?
@Override?
public int getItemCount() {?
return data.size() + 2; // header + footer?
}?
?
@Override?
public int getItemViewType(int position) {?
if (position == 0) {?
return VIEW_TYPE_HEADER;?
}?
if (position == getItemCount() - 1) {?
return VIEW_TYPE_FOOTER;?
}?
return VIEW_TYPE_ITEM;?
}?
}
Header と Footer をつくる
public class SimpleAdapter2 extends RecyclerView.Adapter {?
?
…?
?
@Override?
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {?
final LayoutInflater inflater = LayoutInflater.from(parent.getContext());?
switch (viewType) {?
case VIEW_TYPE_HEADER: {?
return HeaderViewHolder.create(inflater, parent);?
}?
case VIEW_TYPE_FOOTER: {?
return FooterViewHolder.create(inflater, parent);?
}?
case VIEW_TYPE_ITEM: {?
final ViewHolder holder = ViewHolder.create(inflater, parent);?
holder.itemView.setOnClickListener(new View.OnClickListener() {?
@Override?
public void onClick(View v) {?
final int position = holder.getAdapterPosition();?
final String text = data.get(position);?
onItemClicked(text);?
}?
});?
return holder;?
}?
}?
?
return null;?
}
Header と Footer をつくる
public class SimpleAdapter2 extends RecyclerView.Adapter {?
?
…?
?
@Override?
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {?
if (holder instanceof ViewHolder) {?
final ViewHolder viewHolder = (ViewHolder) holder;?
final String text = data.get(position);?
viewHolder.textView.setText(text);?
}?
}
まとめ
? シンプルなList, Grid →
? ListView, GridView
? RecyclerView
? ListView, GridView では無理な配置 →
? RecyclerView
まとめ

More Related Content

droidgirls Recyclerview