【问题标题】:Android how can I get current positon on recyclerview that user scrolled to itemAndroid如何获取用户滚动到项目的recyclerview的当前位置
【发布时间】:2016-07-07 14:06:39
【问题描述】:

在我的RecyclerView 中,我有一些用户可以滚动查看的项目。现在我想保存这个位置并在回来后滚动它。下面的代码总是返回 0,我无法保存:

recyclerMarketLists.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
    @Override
    public void onScrollChanged() {
        recyclerViewCurrentScrolledPosition = recyclerMarketLists.getScrollY();
        Log.e("Y: ", recyclerViewCurrentSceolledPosition + "");
    }
});

Logcat:

07-07 18:28:30.919 2124-2124/com.sample.presentationproject E/Y:: 0

【问题讨论】:

    标签: android android-recyclerview


    【解决方案1】:

    您正在尝试获取有关错误对象的信息。这不是RecyclerView 也不是Adapter 的责任,而是RecyclerView 的LayoutManager。

    我建议不要添加通用的ViewTreeObserver.OnScrollChangedListener(),而是添加RecyclerView.OnScrollListener 并使用为您提供newStateonScrollStateChanged(RecyclerView recyclerView, int newState) 回调,您应该使用SCROLL_STATE_IDLE 来获取其位置。含义:

    yourRecyclerView.getLayoutManager().findFirstVisibleItemPosition();
    

    正如 Rik van Velzen 指出的那样,您可能需要将您的 LayoutManagerLinearLayoutManagerGridLayoutManager (您必须转换为您正在使用的正确类型)才能访问这些 findVisibleXXXX() 方法。

    关于回调方法。希望我对您来说已经足够清楚了,您可以在此处找到有关这些类的文档:

    RecyclerView.OnScrollListener

    yigit's (Google) response on visible positions

    【讨论】:

    • linearLayoutManager.scrollToPositionWithOffset(intemIndex, pxFromTop);如果有帮助,请点赞我的回答。
    • 这是正确的吗:recyclerView.scrollToPosition(recyclerViewLastPosition);
    • 是的。你也可以使用 smoothScroll 来制作视觉糖果
    • 当我调用scrollToPosition 时,它永远不会结束滚动。有什么线索吗?
    • 取决于,您滚动到哪个位置?什么布局、数据集等?需要更多上下文:)
    【解决方案2】:

    感谢您的解决方案 Joaquim Ley。这有助于在不使用任何库的情况下创建 recyclerView horizontal 寻呼机。

    初始化recyclerView

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    
        setContentView(R.layout.activity_vocabulary_list);
    
        // Set up the recyclerView with the sections adapter.
        mRecyclerView = findViewById(R.id.list);
        mRecyclerView.setItemAnimator(new DefaultItemAnimator());
        mRecyclerView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
        mRecyclerView.setAdapter(new VocabularyListAdapter<>(vocabularyList));
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                super.onScrollStateChanged(recyclerView, newState);
                if (newState == RecyclerView.SCROLL_STATE_IDLE){
                    int position = getCurrentItem();
                    onPageChanged(position);
                }
            }
        });
        PagerSnapHelper snapHelper = new PagerSnapHelper();
        snapHelper.attachToRecyclerView(mRecyclerView);
    }
    
    
    
    public boolean hasPreview() {
       return getCurrentItem() > 0;
    }
    
    public boolean hasNext() {
        return mRecyclerView.getAdapter() != null &&
                getCurrentItem() < (mRecyclerView.getAdapter().getItemCount()- 1);
    }
    
    public void preview() {
        int position = getCurrentItem();
        if (position > 0)
            setCurrentItem(position -1, true);
    }
    
    public void next() {
        RecyclerView.Adapter adapter = mRecyclerView.getAdapter();
        if (adapter == null)
            return;
    
        int position = getCurrentItem();
        int count = adapter.getItemCount();
        if (position < (count -1))
            setCurrentItem(position + 1, true);
    }
    
    private int getCurrentItem(){
        return ((LinearLayoutManager)mRecyclerView.getLayoutManager())
                .findFirstVisibleItemPosition();
    }
    
    private void setCurrentItem(int position, boolean smooth){
        if (smooth)
            mRecyclerView.smoothScrollToPosition(position);
        else
            mRecyclerView.scrollToPosition(position);
    }
    

    【讨论】:

    • 非常感谢,请让我检查一下:)
    • PagerSnapHelper 控件滚动项目到项目,如 ViewPger
    • 很高兴我能帮上忙,虽然似乎每次触摸您的 RecyclerView 时您都在创建对象,但不确定这是性能最高的。
    【解决方案3】:

    这是 Kotlin 中的扩展函数:

    fun RecyclerView?.getCurrentPosition() : Int {
        return (this?.layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
    }
    

    您只需在 RecyclerView 上调用此函数即可使用它:

    val position = yourRecyclerView.getCurrentPosition()
    

    【讨论】:

    • 不建议通过 as 直接投射并使用 !! 。相反,通过 as? 后跟 elvis (?:0) 进行可空转换更安全
    【解决方案4】:

    在 Kotlin 中试试这个:

    myRecyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener() {
        override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
            super.onScrolled(recyclerView, dx, dy)
            val offset: Int = myRecyclerView.computeHorizontalScrollOffset()
            if (offset % myRecyclerView.width == 0) {
                val position: Int = offset / myRecyclerView.width
                Log.e("Current position is", position.toString())
            }
        }
    })
    

    【讨论】:

      【解决方案5】:

      我在 MainActivity 中进行了 7 次更改,并获得了像素级完美的结果。当从其他活动(例如 DetailActivity)返回时,我的 recyclerview 总是记得它以前的状态。

      第一步:像这样初始化layoutManager:

          // Initialize the layout manager
          moviePosterLayoutManager = (moviePosterRecyclerView.getLayoutManager() == null) ?
          new GridLayoutManager(this, numberOfColumns) :  moviePosterRecyclerView.getLayoutManager();
                  // create a new layoutManager                       // reuse the existing layoutManager
      

      第 2 步: 在 MainActivity 类中,创建一个 STATIC 变量来保存您在活动之间转换时的布局管理器状态:

      private static Parcelable layoutManagerState;
      

      第 3 步: 同样在 MainActivity () 中创建这个常量:

      public static final String KEY_LAYOUT_MANAGER_STATE = "layoutManagerState";

      第 4 步: 在 MainActivity 的 onCreate 方法中执行以下测试,(即

      protected void onCreate(@Nullable final Bundle savedInstanceState) {....
      
          if (layoutManagerState != null) {
              //moviePosterLayoutManager
              moviePosterLayoutManager.onRestoreInstanceState(layoutManagerState);
              moviePosterRecyclerView.setLayoutManager(moviePosterLayoutManager);
      
          } else {
      
              moviePosterRecyclerView.setLayoutManager(moviePosterLayoutManager);
              moviePosterRecyclerView.scrollToPosition(moviePosition);
                                       // the default position
      
          }
      

      第 5 步: 在您的 onSaveInstanceState 方法中,请务必 将您的 layoutManagerState 保存为 parcelable,如下所示:

      protected void onSaveInstanceState(Bundle outState) {

         layoutManagerState = moviePosterLayoutManager.onSaveInstanceState();
      
          // Save currently selected layout manager.            
         outState.putParcelable(KEY_LAYOUT_MANAGER_STATE,layoutManagerState);
      }
      

      第 6 步: 在 MainActivity 的 onResume() 方法中放置类似的代码:

      protected void onResume() {
          super.onResume();
      
      
          if (layoutManagerState != null) {
              moviePosterLayoutManager.onRestoreInstanceState(layoutManagerState);
          }
      
      }
      

      第 7 步: 请记住,所有这些代码都放在了我的 MainActivity.java 文件中。这很重要 要记住,layoutManager 负责维护的滚动位置 回收站视图。因此,保存 layoutManager 的状态至关重要。代码可以 模块化并放置在自定义recyclerview中,但我发现这对我来说更简单 实施。

      【讨论】:

      • 这对我来说效果很好——完美像素是正确的。
      • 你从哪里得到moviePosition值?
      【解决方案6】:

      对于类似的要求以及需要在滚动状态更改时执行的一些操作,我使用自定义滚动侦听器进行了此操作:

      class OnScrollListener(private val action: () -> Unit) : RecyclerView.OnScrollListener() {
      
      private var actionExecuted: Boolean = false
      private var oldScrollPosition: Int = 0
      
      override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
          super.onScrollStateChanged(recyclerView, newState)
          when (newState) {
              RecyclerView.SCROLL_STATE_DRAGGING -> actionExecuted = false
              RecyclerView.SCROLL_STATE_SETTLING -> actionExecuted = false
              RecyclerView.SCROLL_STATE_IDLE -> {
      
                  val scrolledPosition =
                      (recyclerView.layoutManager as? LinearLayoutManager)?.findFirstVisibleItemPosition() ?: return
      
                  if (scrolledPosition != RecyclerView.NO_POSITION && scrolledPosition != oldScrollPosition && !actionExecuted) {
                      action.invoke()
                      actionExecuted = true
                      oldScrollPosition = scrolledPosition
                  }
      
              }
          }
      }
      

      }

      这是因为 RecyclerView onScroll 的预先存在的错误会触发 2-3 次甚至更多次的回调。如果有一些动作要执行,那最终会执行多次。

      【讨论】:

      • 你能解释一下在哪里以及如何使用这个类吗? TNX。
      • @ReyhaneFarshbaf 您可以在使用回收站视图的地方使用此类,例如在片段或活动中。您可以通过传递操作 lambda 来创建 ScrollListener 的新实例。然后将其附加到回收站视图,如:recyclerView.addOnScrollListener(scrollListener)
      【解决方案7】:

      就我而言,我需要 Activity 中的当前位置视图,因此我创建了一个回调方法并使用以下方法。

      Adapter 类中添加此代码以获取视图的当前位置。

      将此添加到 onBindViewHolder 方法中。

      holder.imgProductParts.addOnAttachStateChangeListener(new View.OnAttachStateChangeListener() {
                  @Override
                  public void onViewAttachedToWindow(View v) {
                      onPositionChangeListener.onItemChanged(position);
                  }
      
                  @Override
                  public void onViewDetachedFromWindow(View v) { }
              });
      

      【讨论】:

        【解决方案8】:
            learnRecycleView
                .addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView,
                                           int dx, int dy) {
                        super.onScrolled(recyclerView, dx, dy);
                        int offset=learnRecycleView.computeHorizontalScrollOffset();
        
                        if (offset % learnRecycleView.getWidth() == 0) {
                            int position = offset / learnRecycleView.getWidth();
                            System.out.println("Current position is"+position);
                        }
                    }
                });
        

        【讨论】:

          【解决方案9】:

          没有一个解决方案有效,我通过使用布局管理器实现了这一点。 这是recyclerview的扩展功能

          fun RecyclerView.addScrollListener(onScroll: (position: Int) -> Unit) {
              var lastPosition = 0
              addOnScrollListener(object : RecyclerView.OnScrollListener() {
                  override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
                      super.onScrolled(recyclerView, dx, dy)
                      if (layoutManager is LinearLayoutManager) {
                          val currentVisibleItemPosition =
                              (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
          
                          if (lastPosition != currentVisibleItemPosition && currentVisibleItemPosition != RecyclerView.NO_POSITION) {
                              onScroll.invoke(currentVisibleItemPosition)
                              lastPosition = currentVisibleItemPosition
                          }
                      }
                  }
              })
          }
          

          你可以在recyclerview上这样使用它。

           campusFeedRv.addScrollListener { position: Int ->
                      Log.d(TAG, "Current Position: $position ")
            }
          

          【讨论】:

            猜你喜欢
            • 2019-05-05
            • 1970-01-01
            • 2013-04-11
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2017-01-12
            相关资源
            最近更新 更多