RecyclerView扩展快捷菜单功能后适配ConcatAdapter

背景介绍

RecyclerView要实现长按弹出快捷菜单,需要实现ContextMenu.ContextMenuInfo接口,并进行相应的处理。

	public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {
        public int position;
        public long id;

        public RecyclerViewContextMenuInfo(int position, long id) {
            this.position = position;
            this.id = id;
        }
    }
	@Override
    public boolean showContextMenuForChild(View originalView) {
        int longPressPosition = getChildAdapterPosition(originalView);
        if(longPressPosition >= 0) {
            long longPressId = getAdapter().getItemId(longPressPosition);
            mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
            return super.showContextMenuForChild(originalView);
        }
        return false;
    }
    @Override
    protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
        return mContextMenuInfo;
    }

具体实现参考:RecyclerView+ContextMenu实现菜单项

以上是背景介绍哈!那我遇到的问题是啥呢?最近官网不是增加了ConcatAdapter(MergeAdapter)么,可以自由添加好多Adapter了,当然也可以实现添加首尾布局的功能了。具体用法请看:RecyclerView更新之-ConcatAdapter(MergeAdapter)
于是我就将想在项目中使用ConcatAdatper.addAdapter()方法来试试,嗯,一试还挺香的,而且RecyclerView.ViewHolder中还提供了getAbsoluteAdapterPosition()和getBindingAdapterPosition()两个方法,用以获取列表中的绝对坐标及各自adapter的绑定坐标。

问题出现

于是问题就来了,当我长按获取条目position的时候,获取到的position是包含HeaderAdapter中的条目的。代码中是这样的:

    @Override
    public boolean onContextItemSelected(@NonNull MenuItem item) {
        int position = ((EaseRecyclerView.RecyclerViewContextMenuInfo) item.getMenuInfo()).position;
        EaseUser user = adapter.getItem(position);
        if(item.getItemId() == R.id.action_friend_delete) {
            showDeleteDialog(user);
        }
        onChildContextItemSelected(item, user);
        return super.onContextItemSelected(item);
    }

对应到扩展的RecyclerView中的逻辑就是:

	@Override
    public boolean showContextMenuForChild(View originalView) {
        int longPressPosition = getChildAdapterPosition(originalView);
        if(longPressPosition >= 0) {
            long longPressId = getAdapter().getItemId(longPressPosition);
            mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
            return super.showContextMenuForChild(originalView);
        }
        return false;
    }

出现问题的就是下面这句代码:

int longPressPosition = getChildAdapterPosition(originalView);

那我们来看看它的怎么实现的

    /**
     * Return the adapter position that the given child view corresponds to.
     *
     * @param child Child View to query
     * @return Adapter position corresponding to the given view or {@link #NO_POSITION}
     */
    public int getChildAdapterPosition(@NonNull View child) {
        final ViewHolder holder = getChildViewHolderInt(child);
        return holder != null ? holder.getAbsoluteAdapterPosition() : NO_POSITION;
    }

嗯,最终调用的就是holder.getAbsoluteAdapterPosition(),所以返回条目在ConcatAdapter的绝对坐标是可以理解的了。
但是项目中,我们一般不需要它的绝对坐标,而且在上面的场景下,返回holder.getBindingAdapterPosition()才是对的。

改造逻辑

那么问题的纠结点就在是不是可以返回相应的binding position了,在RecyclerView中看了看,没有找到相关的方法,看来官方还没有提供相应的方法,目前只能从holder.getBindingAdapterPosition()这里入手了。
那么开始进行吧,先仿照getChildAdapterPosition()提供一个相似的方法,如下:

    public int getChildBindingAdapterPosition(@NonNull View child) {
        final RecyclerView.ViewHolder holder = getChildViewHolderInt(child);
        return holder != null ? holder.getBindingAdapterPosition() : NO_POSITION;
    }

但是啊,RecyclerView的getChildViewHolderInt(child)是一个非public的static方法,没有办法直接用。
但是看这个方法的主要目的是返回相应的RecyclerView的ViewHolder,RecyclerView中是有提供的其他的方法的,看来可以换一种其他方法来实现。
实现如下:

    //这里的getChildViewHolderInt是自己构造的,RecyclerView的是一个非public的static方法
    RecyclerView.ViewHolder getChildViewHolderInt(View child) {
        if (child == null) {
            return null;
        }
        return getChildViewHolder(child);
    }

而getChildViewHolder()的代码中是这样的:

    /**
     * Retrieve the {@link ViewHolder} for the given child view.
     *
     * @param child Child of this RecyclerView to query for its ViewHolder
     * @return The child view's ViewHolder
     */
    public ViewHolder getChildViewHolder(@NonNull View child) {
        final ViewParent parent = child.getParent();
        if (parent != null && parent != this) {
            throw new IllegalArgumentException("View " + child + " is not a direct child of "
                    + this);
        }
        return getChildViewHolderInt(child);
    }

最终调用的就是我们寻找的getChildViewHolderInt(child)方法。到了这里是不是就解决问题了呢?不是的。。。

进一步改造

因为在使用之前,笔者参考的EnhanceRecyclerView这个开源项目做了相应的更改,其中重写了RecyclerView的Adapter,而holder.getBindingAdapterPosition()能够获取到相应的坐标,是在ConcatAdapter中进行处理的,如下:

    public int getLocalAdapterPosition(
            Adapter<? extends ViewHolder> adapter,
            ViewHolder viewHolder,
            int globalPosition
    ) {
        ......
        int itemsBefore = countItemsBefore(wrapper);
        // local position is globalPosition - itemsBefore
        int localPosition = globalPosition - itemsBefore;
        ......
        return wrapper.adapter.findRelativeAdapterPositionIn(adapter, viewHolder, localPosition);
    }

需要计算出长按条目之前的adapter的数目之和:int itemsBefore = countItemsBefore(wrapper)。
所以啊,我们需要对自己的Adapter进行相应的处理。如下:

        @Override
        public int findRelativeAdapterPositionIn(@NonNull Adapter<? extends RecyclerView.ViewHolder> adapter,
                                                 @NonNull RecyclerView.ViewHolder viewHolder, int localPosition) {
            if(adapter == this) {
                return localPosition;
            }else {
                if(mAdapter instanceof ConcatAdapter) {
                    List<? extends Adapter<? extends RecyclerView.ViewHolder>> adapters = ((ConcatAdapter) mAdapter).getAdapters();
                    int prePosition = 0;
                    for(int i = 0; i < adapters.size(); i++) {
                        Adapter<? extends RecyclerView.ViewHolder> curAdapter = adapters.get(i);
                        if(curAdapter == adapter) {
                            return localPosition - prePosition;
                        }else {
                            prePosition += curAdapter.getItemCount();
                        }
                    }
                    return NO_POSITION;
                }
            }
            return super.findRelativeAdapterPositionIn(adapter, viewHolder, localPosition);
        }

嗯,到这里就可以获取到相对坐标了!
最终的相关代码如下:

public class MyRecyclerView extends RecyclerView {
   
   ......

    @Override
    protected ContextMenu.ContextMenuInfo getContextMenuInfo() {
        return mContextMenuInfo;
    }

    @Override
    public boolean showContextMenuForChild(View originalView) {
        int longPressPosition = getChildBindingAdapterPosition(originalView);
        if(longPressPosition >= 0) {
            long longPressId = getAdapter().getItemId(longPressPosition);
            mContextMenuInfo = new RecyclerViewContextMenuInfo(longPressPosition, longPressId);
            return super.showContextMenuForChild(originalView);
        }
        return false;
    }

    public int getChildBindingAdapterPosition(@NonNull View child) {
        final RecyclerView.ViewHolder holder = getChildViewHolderInt(child);
        return holder != null ? holder.getBindingAdapterPosition() : NO_POSITION;
    }

    RecyclerView.ViewHolder getChildViewHolderInt(View child) {
        if (child == null) {
            return null;
        }
        return getChildViewHolder(child);
    }

	......

    public class WrapperRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        
        ......
        
        @Override
        public int findRelativeAdapterPositionIn(@NonNull Adapter<? extends RecyclerView.ViewHolder> adapter,
                                                 @NonNull RecyclerView.ViewHolder viewHolder, int localPosition) {
            if(adapter == this) {
                return localPosition;
            }else {
                if(mAdapter instanceof ConcatAdapter) {
                    List<? extends Adapter<? extends RecyclerView.ViewHolder>> adapters = ((ConcatAdapter) mAdapter).getAdapters();
                    int prePosition = 0;
                    for(int i = 0; i < adapters.size(); i++) {
                        Adapter<? extends RecyclerView.ViewHolder> curAdapter = adapters.get(i);
                        if(curAdapter == adapter) {
                            return localPosition - prePosition;
                        }else {
                            prePosition += curAdapter.getItemCount();
                        }
                    }
                    return NO_POSITION;
                }
            }
            return super.findRelativeAdapterPositionIn(adapter, viewHolder, localPosition);
        }
		......
    }

    public static class RecyclerViewContextMenuInfo implements ContextMenu.ContextMenuInfo {
        public int position;
        public long id;

        public RecyclerViewContextMenuInfo(int position, long id) {
            this.position = position;
            this.id = id;
        }
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值