Java 在RecyclerView中查找完全可见的项目(如TikTok和Instagram故事)

Java 在RecyclerView中查找完全可见的项目(如TikTok和Instagram故事),java,android,android-recyclerview,Java,Android,Android Recyclerview,因此,我这里有一个定制的recyclerview,用于实现TikTok和Instagram story等全屏人像视频播放功能。除了RecyclerView每次都找不到当前可见的项目外,其他一切都很好。如果recyclerview滚动过快,或者使用swiperefreshlayout多次刷新if数据而不暂停,则在前一种情况下viewholder标记的位置将不正确,在后一种情况下,即使第一项完全可见,viewholder标记的位置也将变为-1。这是最重要的部分,因为这是recyclerview决定播

因此,我这里有一个定制的recyclerview,用于实现TikTok和Instagram story等全屏人像视频播放功能。除了RecyclerView每次都找不到当前可见的项目外,其他一切都很好。如果recyclerview滚动过快,或者使用swiperefreshlayout多次刷新if数据而不暂停,则在前一种情况下viewholder标记的位置将不正确,在后一种情况下,即使第一项完全可见,viewholder标记的位置也将变为-1。这是最重要的部分,因为这是recyclerview决定播放哪个视频的地方,就像在TikTok或Instagram故事中一样。playVideo方法是进行所有可见项计算的地方。出了什么问题?有更好的解决办法吗?希望你能回答。问候

public class TiktokRecyclerview extends RecyclerView {

    int playPosition = - 1;
    boolean scrollingUp = false;
    int targetPosition;
    int currentPosition;
    Context context;
    TiktokViewHolder holder;

    public TiktokRecyclerview (@NonNull Context context) {
        super(context);
        init(context);
    }
    public TiktokRecyclerview (@NonNull Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    private void init(final Context context) {
        this.context = context;
        addOnScrollListener(new OnScrollListener () {
            @Override
            public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) {
                super.onScrolled(recyclerView, dx, dy);
                scrollingUp = dy > 0;
            }
            @Override
            public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                    playVideo(context);
                }
            }
        });
    }

    private void playVideo(Context context) {
        if (!scrollingUp) {
            targetPosition =
                ((androidx.recyclerview.widget.LinearLayoutManager) Objects . requireNonNull (getLayoutManager())).findLastVisibleItemPosition();
        } else {
            targetPosition =
                ((androidx.recyclerview.widget.LinearLayoutManager) Objects . requireNonNull (getLayoutManager())).findFirstVisibleItemPosition();
        }


        if (targetPosition != playPosition) { // Checks whether source and destination scroll item are same

            playPosition = targetPosition;

            if (!scrollingUp) {
                currentPosition =
                    ((androidx.recyclerview.widget.LinearLayoutManager) Objects . requireNonNull (getLayoutManager())).findLastCompletelyVisibleItemPosition();
            } else {
                currentPosition =
                    ((androidx.recyclerview.widget.LinearLayoutManager) Objects . requireNonNull (getLayoutManager())).findFirstCompletelyVisibleItemPosition();
            }

            View child = getChildAt (currentPosition);
            if (child == null) {
                return;
            }
            if (child.getTag() == null) {
                return;
            }
            holder = (TiktokViewHolder) child . getTag ();
            Log.d(TAG, "" + holder);
        }
    }
}

不知道你为什么在RecyclerView中使用vh标签,因为现在它们有了ViewHolder

但是,您可以从
View child=getChildAt(currentPosition)开始删除所有代码并替换为:
holder=(RVAdapter.TiktokViewHolder)查找布局位置(currentPosition)的查看文件夹

所以你的代码应该是:

  ...
  ...
  if (targetPosition != playPosition) { // Checks whether source and destination scroll item are same

        playPosition = targetPosition;

        if (!scrollingUp) {
            currentPosition =
                ((androidx.recyclerview.widget.LinearLayoutManager) Objects . requireNonNull (getLayoutManager())).findLastCompletelyVisibleItemPosition();
        } else {
            currentPosition =
                ((androidx.recyclerview.widget.LinearLayoutManager) Objects . requireNonNull (getLayoutManager())).findFirstCompletelyVisibleItemPosition();
        }

       //You may need to check if currentPosition is valid
        if (currentPosition < 0 || currentPosition >= getChildCount()) return;

        holder = (RVAdapter.TiktokViewHolder) findViewHolderForLayoutPosition(currentPosition);
        Log.d(TAG, "" + holder);
    }
}
。。。
...
如果(targetPosition!=playPosition){//检查源和目标滚动项是否相同
播放位置=目标位置;
如果(!scrollingUp){
当前位置=
((androidx.recyclerview.widget.LinearLayoutManager)对象.requireNonNull(getLayoutManager()).findLastCompletelyVisibleItemPosition();
}否则{
当前位置=
((androidx.recyclerview.widget.LinearLayoutManager)对象.requireNonNull(getLayoutManager()).findFirstCompletelyVisibleItemPosition();
}
//您可能需要检查currentPosition是否有效
if(currentPosition<0 | | currentPosition>=getChildCount())返回;
holder=(RVAdapter.TiktokViewHolder)查找布局位置(currentPosition)的查看文件夹;
日志d(标签“+”持有人);
}
}

这将为您提供完全可见的确切位置

谢谢您的回答。但我听说FindViewHolder for LayoutPosition不稳定,可能会抛出nullpointer异常。我应该继续吗?我试过使用你的代码,但现在它根本不起作用。它只播放第一个视频,当我向下滚动时,它不会检测到更改。第一:
findViewHolderForLayoutPosition
不会有任何问题,尤其是在
scroll\u STATE\u IDLE
STATE之后调用时,因为视图是可见/渲染的,并且ViewHolder应该存在:此解决方案仅在指定位置获得适当的ViewHolder,它与更改检测无关。。。除非
currentPosition
无效(检查行块),否则仅当SwiperFresh以快速刷新多次时才会发生。有没有办法阻止用户刷新过快?我现在唯一能想到的方法就是删除当前的recyclerview,并在每次刷新后添加一个新的。这是个好主意吗?我假设这个bug与recyclerview代码无关,而是因为快速刷新。