44. アイテム用のViewのOffsetを指定
final int offset =
(int) (8 * getResources().getDisplayMetrics().density);
final 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);
45.
46. アイテム用のViewのOffsetを指定
final 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);
47.
48. private static 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) {
int position = ((RecyclerView.LayoutParams)
view.getLayoutParams()).getViewLayoutPosition();
Dividerを描画
49. dividerHeight
= (int) (4 * res.getDisplayMetrics().density);
}
@Override
public void getItemOffsets(Rect outRect,
View view,
RecyclerView parent,
RecyclerView.State state) {
int position = ((RecyclerView.LayoutParams)
view.getLayoutParams()).getViewLayoutPosition();
// 位置が2番目以降なら上部にdividerを描画したいので、
// divider分だけ上をあける
int top = position == 0 ? 0 : dividerHeight;
outRect.set(0, top, 0, 0);
}
@Override
public void onDrawOver(Canvas c, RecyclerView parent,
RecyclerView.State state) {
super.onDrawOver(c, parent, state);
// アイテムのビューより上に描画される
}
Dividerを描画
50. @Override
public void onDraw(Canvas c, RecyclerView parent,
RecyclerView.State state) {
super.onDraw(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()を入れないと
Dividerを描画
51.
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を描画
56. // selectableItemBackgroundに指定されている
// リソースIDの値を取得しておく
TypedValue val = new TypedValue();
if (getTheme() != null) {
getTheme().resolveAttribute(
android.R.attr.selectableItemBackground,
val, true);
}
final int backgroundResId = val.resourceId;
final SimpleAdapter adapter = new SimpleAdapter(this, data) {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
final ViewHolder viewHolder
= super.onCreateViewHolder(parent, viewType);
viewHolder.itemView.setBackgroundResource(backgroundRes
viewHolder.itemView.setOnClickListener(
OnItemClick
57. final SimpleAdapter adapter = new SimpleAdapter(this, data) {
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
final ViewHolder viewHolder
= super.onCreateViewHolder(parent, viewType);
viewHolder.itemView.setBackgroundResource(backgroundRes
viewHolder.itemView.setOnClickListener(
new View.OnClickListener() {
@Override
public void onClick(View v) {
int position = viewHolder
.getAdapterPosition();
Toast.makeText(v.getContext(),
"Position = " + position,
Toast.LENGTH_SHORT).show();
}
});
return viewHolder;
}
};
recyclerView.setAdapter(adapter);
OnItemClick
61. notify**
• notifyItemRangeChanged(int positionStart, int itemCount) :
positionStartからitemCount個のアイテムが変更された
• notifyItemRangeInserted(int positionStart, int itemCount) :
positionStartにitemCount個のアイテムが追加された
• notifyItemRangeRemoved(int positionStart, int
itemCount) : positionStartからitemCount個のアイテムが削
除された
• notifyDataSetChanged() : データセットが変更された
62. public abstract class RecyclerArrayAdapter<T, VH extends
RecyclerView.ViewHolder>
extends RecyclerView.Adapter<VH> {
private final Object lock = new Object();
private final Context context;
private final List<T> objects;
public RecyclerArrayAdapter(Context context) {
this(context, new ArrayList<T>());
}
public RecyclerArrayAdapter(Context context, List<T> objects
this.context = context;
this.objects = objects;
}
public void add(@NonNull T object) {
final int position = objects.size();
ArrayAdapter的なRecyclerView用Adapter
63.
public RecyclerArrayAdapter(Context context, List<T> objects
this.context = context;
this.objects = objects;
}
public void add(@NonNull T object) {
final int position = objects.size();
synchronized (lock) {
objects.add(object);
}
notifyItemInserted(position);
}
public void addAll(@NonNull Collection<? extends T> collecti
final int positionStart = objects.size();
final int itemCount = collection.size();
synchronized (lock) {
objects.addAll(collection);
}
notifyItemRangeInserted(positionStart, itemCount);
}
public void insert(@NonNull T object, int index) {
synchronized (lock) {
ArrayAdapter的なRecyclerView用Adapter
64. 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) {
int position = getPosition(object);
synchronized (lock) {
objects.remove(object);
}
notifyItemRemoved(position);
}
public void clear() {
final int itemCount = objects.size();
synchronized (lock) {
objects.clear();
}
notifyItemRangeRemoved(0, itemCount);
ArrayAdapter的なRecyclerView用Adapter
66. ItemTouchHelper
• RecyclerViewに swipe to dismiss と drag & drop による並
び替え機能を追加するためにユーティリティクラス
ItemTouchHelper.Callback callback = …;
ItemTouchHelper itemTouchHelper =
new ItemTouchHelper(callback);
itemTouchHelper.attachToRecyclerView(recyclerView);
67. swipe to dismiss
• ItemTouchHelper.Callbackのコンストラクタの第2引数で
スワイプ方向を指定
• スワイプされたらonSwiped()が呼ばれる
68. swipe to dismiss
int swipeDirs = ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
ItemTouchHelper.Callback callback =
new ItemTouchHelper.SimpleCallback(0, swipeDirs) {
…
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder,
int direction) {
int position = viewHolder.getAdapterPosition();
adapter.remove(adapter.getItem(position));
}
};
69. drag and drop
• ItemTouchHelper.Callbackのコンストラクタの第1引数で
ドラッグ方向を指定
• ドロップされたらonMove()が呼ばれる
• ドラッグが開始できるようになったタイミングで
onSelectedChanged()が呼ばれる
• ドラッグを終了するときにclearView()が呼ばれる
70. int dragDirs = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
ItemTouchHelper.Callback callback = new
ItemTouchHelper.SimpleCallback(dragDirs, 0) {
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
adapter.move(from, to);
return true;
}
…
};
drag and drop
71. int dragDirs = ItemTouchHelper.UP | ItemTouchHelper.DOWN;
ItemTouchHelper.Callback callback = new
ItemTouchHelper.SimpleCallback(dragDirs, 0) {
@Override
public boolean onMove(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder,
RecyclerView.ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
adapter.move(from, to);
return true;
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHo
int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setBackgroundColor(Color.LTGRAY)
}
drag and drop
72. int to = target.getAdapterPosition();
adapter.move(from, to);
return true;
}
@Override
public void onSelectedChanged(RecyclerView.ViewHolder viewHo
int actionState) {
if (actionState != ItemTouchHelper.ACTION_STATE_IDLE) {
viewHolder.itemView.setBackgroundColor(Color.LTGRAY)
}
super.onSelectedChanged(viewHolder, actionState);
}
@Override
public void clearView(RecyclerView recyclerView,
RecyclerView.ViewHolder viewHolder) {
super.clearView(recyclerView, viewHolder);
viewHolder.itemView.setBackgroundColor(Color.TRANSPARENT
}
…
}
};
drag and drop
76. public class SimpleListLayoutManager extends
RecyclerView.LayoutManager {
@Override
public RecyclerView.LayoutParams generateDefaultLayoutParams()
return new RecyclerView.LayoutParams(
ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler,
RecyclerView.State state) {
// 現在表示されている一番上のビューの位置を保持しておく
final View lastTopView = getChildCount() > 0 ?
getChildAt(0) : null;
final int lastTop = lastTopView != null ?
lastTopView.getTop() : getPaddingTop();
final int firstPosition = lastTopView != null ?
getPosition(lastTopView) : 0;
独自のLayoutManager
77. final int lastTop = lastTopView != null ?
lastTopView.getTop() : getPaddingTop();
final int firstPosition = lastTopView != null ?
getPosition(lastTopView) : 0;
// 現在のビューをスクラップにする
detachAndScrapAttachedViews(recycler);
int top = lastTop;
int bottom;
final int parentLeft = getPaddingLeft();
final int parentRight = getWidth() - getPaddingRight();
final int parentBottom = getHeight() - getPaddingBottom();
final int count = state.getItemCount();
for (int i = 0; firstPosition + i < count &&
top < parentBottom; i++, top = bottom) {
View v = recycler.getViewForPosition(firstPosition + i)
addView(v, i);
measureChildWithMargins(v, 0, 0);
bottom = top + getDecoratedMeasuredHeight(v);
layoutDecorated(v, parentLeft, top, parentRight, bottom
}
}
}
独自のLayoutManager