其他
Android RecyclerView宫格拖拽效果实现
本文作者
作者:时光少年
链接:
https://juejin.cn/post/7348707728921853971
本文由作者授权发布。
前言
话说回来,我们现在基本不用去学习这类实现了,因为RecyclerView足够强大,通过简单的数据组装,是完全可以替代GridView和ListView的。
事件处理
public interface OnItemTouchListener {
//是否让RecyclerView拦截事件
boolean onInterceptTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
//拦截之后处理RecyclerView的事件
void onTouchEvent(@NonNull RecyclerView rv, @NonNull MotionEvent e);
//监听禁止拦截事件的请求结果
void onRequestDisallowInterceptTouchEvent(boolean disallowIntercept);
}
图像平移
class ItemTouchUIUtilImpl implements ItemTouchUIUtil {
static final ItemTouchUIUtil INSTANCE = new ItemTouchUIUtilImpl();
@Override
public void onDraw(Canvas c, RecyclerView recyclerView, View view, float dX, float dY,
int actionState, boolean isCurrentlyActive) {
if (Build.VERSION.SDK_INT >= 21) {
if (isCurrentlyActive) {
Object originalElevation = view.getTag(R.id.item_touch_helper_previous_elevation);
if (originalElevation == null) {
originalElevation = ViewCompat.getElevation(view);
float newElevation = 1f + findMaxElevation(recyclerView, view);
ViewCompat.setElevation(view, newElevation);
view.setTag(R.id.item_touch_helper_previous_elevation, originalElevation);
}
}
}
view.setTranslationX(dX);
view.setTranslationY(dY);
}
//省略一些有关或者无关的代码
}
View#getChildDrawingOrder(...)
void drawChildFunction(drawIndex,canvas){
children[mapChildIndex(drawIndex)].draw(canvas);
}
ArrayList<View> buildOrderedChildList() {
final int childrenCount = mChildrenCount;
if (childrenCount <= 1 || !hasChildWithZ()) return null;
if (mPreSortedChildren == null) {
mPreSortedChildren = new ArrayList<>(childrenCount);
} else {
// callers should clear, so clear shouldn't be necessary, but for safety...
mPreSortedChildren.clear();
mPreSortedChildren.ensureCapacity(childrenCount);
}
final boolean customOrder = isChildrenDrawingOrderEnabled();
for (int i = 0; i < childrenCount; i++) {
// add next child (in child order) to end of list
final int childIndex = getAndVerifyPreorderedIndex(childrenCount, i, customOrder);
// 映射View
final View nextChild = mChildren[childIndex];
final float currentZ = nextChild.getZ();
// 如果Z值大的话往后移动,5.0之前的代码没有这段
int insertIndex = i;
while (insertIndex > 0 && mPreSortedChildren.get(insertIndex - 1).getZ() > currentZ) {
insertIndex--;
}
mPreSortedChildren.add(insertIndex, nextChild);
}
return mPreSortedChildren;
}
private void addChildDrawingOrderCallback() {
if (Build.VERSION.SDK_INT >= 21) {
return; // we use elevation on Lollipop
}
if (mChildDrawingOrderCallback == null) {
mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {
@Override
public int onGetChildDrawingOrder(int childCount, int i) {
if (mOverdrawChild == null) {
return i;
}
int childPosition = mOverdrawChildPosition;
if (childPosition == -1) {
childPosition = mRecyclerView.indexOfChild(mOverdrawChild);
mOverdrawChildPosition = childPosition;
}
if (i == childCount - 1) {
//将最后索引位置展示被拖拽的View
return childPosition;
}
//后面的View 绘制顺序往前移动
return i < childPosition ? i : i + 1;
}
};
}
mRecyclerView.setChildDrawingOrderCallback(mChildDrawingOrderCallback);
}
数据更新
@Override
public boolean onItemMove(int fromPosition, int toPosition) {
Collections.swap(mDataList, fromPosition, toPosition);
notifyItemMoved(fromPosition, toPosition);
return true;
}
@Override
public void clearView(View view) {
if (Build.VERSION.SDK_INT >= 21) {
final Object tag = view.getTag(R.id.item_touch_helper_previous_elevation);
if (tag instanceof Float) {
ViewCompat.setElevation(view, (Float) tag);
}
view.setTag(R.id.item_touch_helper_previous_elevation, null);
}
view.setTranslationX(0f);
view.setTranslationY(0f);
}
图片分片
Bitmap srcInputBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.image_4);
Bitmap source = Bitmap.createScaledBitmap(srcInputBitmap, width, height, true);
srcInputBitmap.recycle();
int colCount = spanCount;
int rowCount = 6;
int spanImageWidthSize = source.getWidth() / colCount;
int spanImageHeightSize = (source.getHeight() - rowCount * padding/2) / rowCount;
Bitmap[] bitmaps = new Bitmap[rowCount * colCount];
for (int i = 0; i < rowCount; i++) {
for (int j = 0; j < colCount; j++) {
int y = i * spanImageHeightSize;
int x = j * spanImageWidthSize;
Bitmap bitmap = Bitmap.createBitmap(source, x, y, spanImageWidthSize, spanImageHeightSize);
bitmaps[i * colCount + j] = bitmap;
}
}
public class SimpleItemDecoration extends RecyclerView.ItemDecoration {
public int delta;
public SimpleItemDecoration(int padding) {
delta = padding;
}
@Override
public void getItemOffsets(Rect outRect, View view,
RecyclerView parent, RecyclerView.State state) {
int position = parent.getChildAdapterPosition(view);
RecyclerView.Adapter adapter = parent.getAdapter();
int viewType = adapter.getItemViewType(position);
if(viewType== Bean.TYPE_GROUP){
return;
}
GridLayoutManager layoutManager = (GridLayoutManager) parent.getLayoutManager();
//列数量
int cols = layoutManager.getSpanCount();
//position转为在第几列
int current = layoutManager.getSpanSizeLookup().getSpanIndex(position,cols);
//可有可无
int currentCol = current % cols;
int bottomPadding = delta / 2;
if (currentCol == 0) { //第0列左侧贴边
outRect.left = 0;
outRect.right = delta / 4;
outRect.bottom = bottomPadding;
} else if (currentCol == cols - 1) {
outRect.left = delta / 4;
outRect.right = 0;
outRect.bottom = bottomPadding;
//最后一列右侧贴边
} else {
outRect.left = delta / 4;
outRect.right = delta / 4;
outRect.bottom = bottomPadding;
}
}
}
更新数据
mLinearLayoutManager = new GridLayoutManager(this, spanCount, LinearLayoutManager.VERTICAL, false);
mLinearLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup(){
@Override
public int getSpanSize(int position) {
if(mAdapter.getItemViewType(position) == Bean.TYPE_GROUP){
return spanCount;
}
return 1;
}
});
mAdapter = new RecyclerViewAdapter();
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.addItemDecoration(new SimpleItemDecoration(padding));
ItemTouchHelper itemTouchHelper = new ItemTouchHelper(new GridItemTouchCallback(mAdapter));
itemTouchHelper.attachToRecyclerView(mRecyclerView);
public class GridItemTouchCallback extends ItemTouchHelper.Callback {
private final ItemTouchCallback mItemTouchCallback;
public GridItemTouchCallback(ItemTouchCallback itemTouchCallback) {
mItemTouchCallback = itemTouchCallback;
}
@Override
public int getMovementFlags(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder) {
if(viewHolder.getItemViewType() == Bean.TYPE_GROUP){
return 0; //设置此类型的View不可拖动
}
// 上下左右拖动
int dragFlags = ItemTouchHelper.UP | ItemTouchHelper.DOWN | ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT;
return makeMovementFlags(dragFlags, 0);
}
@Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
// 通知Adapter移动View
return mItemTouchCallback.onItemMove(viewHolder.getAdapterPosition(), target.getAdapterPosition());
}
@Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
// 通知Adapter删除View
mItemTouchCallback.onItemRemove(viewHolder.getAdapterPosition());
}
@Override
public void onChildDraw(@NonNull Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
super.onChildDraw(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
@Override
public void onChildDrawOver(Canvas c, RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, float dX, float dY, int actionState, boolean isCurrentlyActive) {
Log.d("GridItemTouch","dx="+dX+", dy="+dY);
super.onChildDrawOver(c, recyclerView, viewHolder, dX, dY, actionState, isCurrentlyActive);
}
}
public static int makeMovementFlags(int dragFlags, int swipeFlags) {
return makeFlag(ACTION_STATE_IDLE, swipeFlags | dragFlags)
| makeFlag(ACTION_STATE_SWIPE, swipeFlags)
| makeFlag(ACTION_STATE_DRAG, dragFlags);
}
当然,删除和拖拽都不要的viewHolder,那么直接返回0。
最后推荐一下我做的网站,玩Android: wanandroid.com ,包含详尽的知识体系、好用的工具,还有本公众号文章合集,欢迎体验和收藏!
推荐阅读:
扫一扫 关注我的公众号
如果你想要跟大家分享你的文章,欢迎投稿~
┏(^0^)┛明天见!