RecyclerView 中 CardView 项的滚动位置

Posted

技术标签:

【中文标题】RecyclerView 中 CardView 项的滚动位置【英文标题】:Scrolling position of the CardView items in RecyclerView 【发布时间】:2020-07-05 01:14:38 【问题描述】:

在使用这种方法来保存和检索上次查看的项目的位置进行了将近一天的努力之后,我终于找到了一个能够为我提供所需输出的解决方案。在返回 RecyclerView 的途中打开我的一个项目后,我的应用会滚动到该项目,但有一个小错误......我正在使用两种方法 onPause() 和 onResume(),这是我的实现:

@Override
protected void onPause() 
    super.onPause();
    lastFirstVisiblePosition = ((LinearLayoutManager)
            mRecyclerView.getLayoutManager()).findLastVisibleItemPosition();
    Log.d("position", String.valueOf(lastFirstVisiblePosition));


@Override
protected void onResume() 
    super.onResume();

    new Handler().postDelayed(new Runnable() 
        @Override
        public void run() 
            mRecyclerView.scrollToPosition(lastFirstVisiblePosition);
        
    , 200);


    由于我是初学者,我不确定在此目的中使用 Handler 是否明智? 因为我的卡片/项目的尺寸不同,只要项目不在特定位置,我就无法返回上次查看项目的位置,这意味着只要下面的项目不在屏幕上...有什么办法可以考虑我的卡片的尺寸并根据该设置的位置?

还要补充一点我曾尝试过 onSaveInstanceState() 和 onRestoreInstanceState() 方法,但没有成功...其中一种实现:

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) 
    super.onSaveInstanceState(outState);

    lastFirstVisiblePosition = ((LinearLayoutManager)
            mRecyclerView.getLayoutManager()).findLastVisibleItemPosition();
    outState.putString("position", String.valueOf(lastFirstVisiblePosition));
    Log.d("currentPos", String.valueOf(outState));



@Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) 
    super.onRestoreInstanceState(savedInstanceState);

    String position = savedInstanceState.getString("position");
    mRecyclerView.scrollToPosition(Integer.parseInt(position));

这样,我可以得到当前位置但不能恢复它...

【问题讨论】:

【参考方案1】:

创建变量private static int displayedposition = 0;

现在是 RecyclerView 在 Activity 中的位置。

myRecyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() 
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) 
            super.onScrollStateChanged(recyclerView, newState);
        

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) 
            super.onScrolled(recyclerView, dx, dy);

            LinearLayoutManager llm = (LinearLayoutManager) myRecyclerView.getLayoutManager();
            displayedposition = llm.findLastVisibleItemPosition();

        
    );

修改您的 onPause() 方法以添加这些行..

LinearLayoutManager llm = (LinearLayoutManager) mRecyclerView.getLayoutManager();
llm.scrollToPositionWithOffset(displayedposition , youList.size());

如果findLastVisibleItemPosition() 似乎不起作用,请尝试findLastCompletelyVisibleItemPosition()..

它会起作用..尝试并恢复。

【讨论】:

这个youList.size()指的是什么? 你的 RecyclerView 的大小 更多详情请参考此链接***.com/questions/36568168/… 您的回收站视图就是您的列表。 是的,但我现在有点困惑...我需要创建该列表并对其进行初始化吗?【参考方案2】:

我在此展示我的代码,这可能会对您有所帮助.. 我的应用程序link 在 Play 商店中参考..

MainActivity.java

package com.amitabh.dhamma_jaagran;

import android.content.Intent;
import android.os.Bundle;
import android.support.design.widget.AppBarLayout;
import android.support.design.widget.CollapsingToolbarLayout;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.DefaultItemAnimator;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import com.bumptech.glide.Glide;


public class MainActivity extends AppCompatActivity  

private RecyclerView recyclerView;
private AlbumsAdapter adapter;

private AlbumsAdapter.AlbumsAdapterListener listener;
private List<Album> albumList;
private long backPressedTime = 0;    // used by onBackPressed()  
private static TextView footerText;

String currentVersion;
View parentLayout;

@Override
protected void onCreate(Bundle savedInstanceState) 
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    Toolbar toolbar = findViewById(R.id.toolbar);
    setSupportActionBar(toolbar);
    initCollapsingToolbar();
    footerText = findViewById(R.id.activity_main_footer);
    parentLayout = findViewById(android.R.id.content);

    recyclerView = findViewById(R.id.recycler_view);

    //  recyclerView.setHasFixedSize(true);

    albumList = new ArrayList<>();
    adapter = new AlbumsAdapter(this, albumList, listener);

    RecyclerView.LayoutManager mLayoutManager = new GridLayoutManager(this, 2);

    ((GridLayoutManager) mLayoutManager).setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() 
        @Override
        public int getSpanSize(int position) 
            return (position % 15 == 0 ? 2 : 1);
        
    );



    recyclerView.setLayoutManager(mLayoutManager);
    recyclerView.setBackgroundColor(getResources().getColor(R.color.white));
 //   recyclerView.setBackgroundResource(R.drawable.white_background);
      recyclerView.setItemAnimator(new DefaultItemAnimator());
    recyclerView.setAdapter(adapter);

    // row click listener
    recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getApplicationContext(), recyclerView, new RecyclerTouchListener.ClickListener() 

        @Override
        public void onClick(View view, int position) 
            Album album = adapter.getAlbumList().get(position);

            String text = album.getName();

          //  Toast.makeText(getApplicationContext(), text + " is selected", Toast.LENGTH_SHORT).show();
            if (text == "तेरापंथ प्रबोध") 
                view.getContext().startActivity(new Intent(view.getContext(), TerapanthPrabodhTabLayoutViewPager.class));
            

            if (text == "संवत्सरी प्रतिक्रमण") 
                view.getContext().startActivity(new Intent(view.getContext(), MangalStuti.class));
            

        

        @Override
        public void onLongClick(View view, int position) 

        
    ));

    prepareAlbums();

    try 
        Glide.with(this).load(R.drawable.cover2).into((ImageView) findViewById(R.id.backdrop));
     catch (Exception e) 
        e.printStackTrace();
               


/**
 * Initializing collapsing toolbar
 * Will show and hide the toolbar title on scroll
 */
private void initCollapsingToolbar() 
    final CollapsingToolbarLayout collapsingToolbar =
            findViewById(R.id.collapsing_toolbar);
    collapsingToolbar.setTitle(" ");
    AppBarLayout appBarLayout = findViewById(R.id.appbar);
    appBarLayout.setExpanded(true);

    // hiding & showing the title when toolbar expanded & collapsed
    appBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() 
        boolean isShow = false;
        int scrollRange = -1;

        @Override
        public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) 
            if (scrollRange == -1) 
                scrollRange = appBarLayout.getTotalScrollRange();
            
            if (scrollRange + verticalOffset == 0) 
                collapsingToolbar.setTitle(getString(R.string.app_name));
                isShow = true;
             else if (isShow) 
                collapsingToolbar.setTitle(" ");
                isShow = false;
            
        
    );


/**
 * Adding few albums for testing
 */
private void prepareAlbums() 
    int[] covers = new int[]

            R.drawable.album000,
            R.drawable.album01,

    ;

    Album a = new Album("तेरापंथ प्रबोध", "Terapanth Prabodh", covers[0]);
    albumList.add(a);

     a = new Album("मंगल स्तुति", "Mangal Stuti", covers[1]);
    albumList.add(a);

    adapter.notifyDataSetChanged();


Album.java

package com.amitabh.dhamma_jaagran;

public class Album

private String name;
private String numOfSongs;
private int thumbnail;

public Album() 

public Album(String name, String numOfSongs, int thumbnail)

this.name = name;
this.numOfSongs = numOfSongs;
this.thumbnail = thumbnail;


public String getName()

 return this.name;


public String getNumOfSongs()

return this.numOfSongs;


public int getThumbnail()

 return this.thumbnail;


public void setName(String paramString)

 this.name = name;


public void setNumOfSongs(String paramString)

 this.numOfSongs = numOfSongs;


public void setThumbnail(int paramInt)

 this.thumbnail = thumbnail;


AlbumsAdapter.java

package com.amitabh.dhamma_jaagran;

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.CardView;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.ImageView;
import android.widget.TextView;
import com.bumptech.glide.Glide;
import java.util.ArrayList;
import java.util.List;



public class AlbumsAdapter extends RecyclerView.Adapter<AlbumsAdapter.MyViewHolder> 

private Context mContext;
private List<Album> albumList;
private AlbumsAdapterListener listener;
private int lastPosition = -1;

public class MyViewHolder extends RecyclerView.ViewHolder 
    public TextView title, count;
    public ImageView thumbnail, overflow;
    public CardView cardView;

    public MyViewHolder(View view) 
        super(view);
        title =  view.findViewById(R.id.title);
        count = view.findViewById(R.id.count);
        thumbnail =  view.findViewById(R.id.thumbnail);
        overflow =  view.findViewById(R.id.overflow);
        cardView =  view.findViewById(R.id.card_view);
    



public AlbumsAdapter(Context mContext, List<Album> albumList, AlbumsAdapterListener 
listener) 
    this.mContext = mContext;
    this.albumList = albumList;
    this.listener = listener;


@Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) 
    View itemView = LayoutInflater.from(parent.getContext())
            .inflate(R.layout.album_card, parent, false);

    return new MyViewHolder(itemView);


@Override
public void onViewDetachedFromWindow(@NonNull AlbumsAdapter.MyViewHolder holder) 
    super.onViewDetachedFromWindow(holder);
    holder.itemView.clearAnimation();


@Override
public void onBindViewHolder(final MyViewHolder holder, final int position) 
    Album album = albumList.get(position);
    holder.title.setText(album.getName());
    holder.count.setText(album.getNumOfSongs());

    /*loading album cover using Glide library*/
    Glide.with(mContext).load(album.getThumbnail()).into(holder.thumbnail);

    Animation animation = AnimationUtils.loadAnimation(mContext,
            (position > lastPosition) ? R.anim.up_from_bottom
                    : R.anim.down_from_top);
    holder.itemView.startAnimation(animation);
    lastPosition = position;




@Override
public int getItemCount() 
    return albumList.size();



public interface AlbumsAdapterListener 
    void onAddToFavoriteSelected(int position);

    void onPlayNextSelected(int position);

    void onCardSelected(int position, ImageView thumbnail);

public List<Album> getAlbumList()
    return albumList;


public void filter(ArrayList<Album> newList)

    albumList=new ArrayList<>();
    albumList.addAll(newList);
    notifyDataSetChanged();



为了简单起见,我没有使用任何要保存的实例或 scollToPositon.. 但是当用户返回 MainActivity 时它会滚动到之前的位置..

这个答案与我猜你尝试过的类似..

我在 mainActivity 中创建了 RecyclerView 并对其进行了初始化。然后我创建了一个 ViewHolder 类,它为我的项目扩展 RecyclerView 以及 CardView 的 xml 文件......

【讨论】:

非常感谢你,我的朋友,为此,我真的很感激。我想它会帮助我! 请考虑解释你的代码是如何工作的,而不是用勺子喂代码。 考虑到提问者相当新,我以最简单的方式回答了问题,关于解释,我尝试遵循与提问者相同的模式..即“在里面创建一个 RecyclerView mainActivity 并对其进行初始化。然后为项目创建一个扩展 RecyclerView 的 Adapter 和 ViewHolder 类.."【参考方案3】:
    由于我是初学者,我不确定在此目的中使用 Handler 是否明智?

处理程序确实不是最聪明的方式,因为 200 毫秒可能不足以加载 RecyclerView 的全部数据,但是您需要使用处理程序的原因是您不知道 @987654324 何时@ 加载了数据,以便在您知道之后滚动到某个位置。

您的处理程序有更好的方法,但它的功能与您的相同,因为它仍然存在 200 毫秒的问题。

mRecyclerView.postDelayed(new Runnable() 
    @Override
    public void run() 
        mRecyclerView.scrollToPosition(lastFirstVisiblePosition);
        mRecyclerView.removeCallbacks(this);
    
, 200);

之所以更好,是因为处理程序与您的RecyclerView 耦合

但我建议使用而不是使用Hanlder 的解决方案是向您的LinearLayoutManager 添加一个侦听器,该侦听器在RecyclerView 的列表由适配器填充时触发,通常是在@987654330 @ 结束了。为此:

第一

创建一个自定义LinearLayoutManager 并添加一个侦听器接口,该接口在onLayoutCompleted() 被调用时触发。

public class LayoutCompletionLinearLayoutManager extends LinearLayoutManager 

    public void setCallback(OnLayoutCompleteCallback callback) 
        mCallback = callback;
    

    private OnLayoutCompleteCallback mCallback = null;

    public LayoutCompletionLinearLayoutManager(Context context) 
        super(context);
    

    public LayoutCompletionLinearLayoutManager(Context context, int orientation, boolean reverseLayout) 
        super(context, orientation, reverseLayout);
    

    public LayoutCompletionLinearLayoutManager(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) 
        super(context, attrs, defStyleAttr, defStyleRes);
    

    public void onLayoutCompleted(RecyclerView.State state) 
        super.onLayoutCompleted(state);
        if (mCallback != null)
            mCallback.onLayoutComplete();
    

    public interface OnLayoutCompleteCallback 
        void onLayoutComplete();
    

第二

在构建 RecyclerView LayoutManager 时使用回调。

final LayoutCompletionLinearLayoutManager layoutMgr = 
    new LayoutCompletionLinearLayoutManager(getApplicationContext(), 
        LinearLayoutManager.HORIZONTAL, false); // change orientation according to your RecyclerView

layoutMgr.setCallback(new LayoutCompletionLinearLayoutManager.OnLayoutCompleteCallback() 
    @Override
    public void onLayoutComplete() 
        mRecyclerView.scrollToPosition(lastFirstVisiblePosition);
        layoutMgr.setCallback(null);
    
);

mRecyclerView.setLayoutManager(layoutMgr);
    因为我的卡片/项目的尺寸不同,只要项目不在特定位置,我就无法返回上次查看项目的位置,这意味着只要下面的项目不在屏幕上...有什么办法可以考虑我的卡片的尺寸并根据该设置的位置?

您需要根据CardView 的尺寸使用LinearLayoutManager scrollToPositionWithOffset() 和一些偏移,而不是scrollToPosition()

((LayoutCompletionLinearLayoutManager) mRecyclerView.getLayoutManager())
    .scrollToPositionWithOffset(lastFirstVisiblePosition, offset);

【讨论】:

谢谢你,尤其是第一部分,因为我可以更好地理解处理程序。但是关于我的第二个问题......我无法启动 scrollToPositionWithOffset() 到 RecyclerView,只是到 LayoutManager 你是个了不起的人!让我尝试实现你向我展示的所有内容,我会让你知道它对我有用 不幸的是,它对我不起作用。无论如何,非常感谢您的帮助!

以上是关于RecyclerView 中 CardView 项的滚动位置的主要内容,如果未能解决你的问题,请参考以下文章

如何使 Recyclerview 和 Cardview 透明

翻译Android RecyclerView CardView

在RecyclerView中翻转CardView

RecyclerView中的CardView垂直滚动

RecyclerView 结合 CardView 使用

为啥滚动时 RecyclerView (在我的情况下)中 CardView 的内容会发生变化?