简体   繁体   English

RecyclerView 中 CardView 项的滚动位置

[英]Scrolling position of the CardView items in RecyclerView

After almost a day of struggling with this method to save and retrieve the position of the last viewed item, I finally found a solution that gives me the desired output.在用这种方法保存和检索最后查看项目的位置将近一天后,我终于找到了一个解决方案,可以提供所需的输出。 After opening one of my items on the way back to the RecyclerView my app scrolls to that item but with a small bug... I am using 2 methods onPause() and onResume() and this is my implementation:在返回 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);

}
  1. Since I am a beginner I am not sure if using the Handler in this purpose is a smart idea?由于我是初学者,我不确定为此目的使用 Handler 是否是一个聪明的主意?
  2. Because my cards/items are not the same sizes I am not able to return to the position of the last viewed item as long as the item is not in a certain position meaning as long as the item below is not on the screen... Is there any way to consider the dimension of my card and according to that set the position?因为我的卡片/物品大小不同,所以只要物品不在某个位置,我就无法返回到上次查看物品的位置,这意味着只要下面的物品不在屏幕上......有什么办法可以考虑我的卡的尺寸并根据那个设置位置吗?

one more thing to add I had tried onSaveInstanceState() and onRestoreInstanceState() methods but unsuccessfully... One of the implementations:要添加的另一件事我曾尝试过 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));
}

in this way, I was able to get the current position but not to restore it...通过这种方式,我能够获得当前位置但不能恢复它......

Create a variable private static int displayedposition = 0;创建一个变量private static int displayedposition = 0;

Now for the position of your RecyclerView in your Activity.现在是您的 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();

        }
    });

Modify your onPause() method to add these lines..修改您的onPause()方法以添加这些行..

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

Try findLastCompletelyVisibleItemPosition() if findLastVisibleItemPosition() doesn't seem to work..如果findLastVisibleItemPosition()似乎不起作用,请尝试findLastCompletelyVisibleItemPosition()

It will work.. Try and Revert Back.它会工作.. 尝试并恢复。

I am herewith showing my code which might help you.. My App link on Play Store to refer to..我在此展示我的代码,它可能对你有帮助.. 我在 Play 商店中的应用链接可以参考..

MainActivity.java主活动.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相册.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相册适配器.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();
}

}

To keep things simple i have not used any instances to be saved or scollToPositon.. but it scrolls to the position where it was previously when a user returns to the MainActivity..为了简单起见,我没有使用任何要保存的实例或 scollToPositon .. 但是当用户返回到 MainActivity 时,它会滚动到之前的位置。

This answer is similar to what you have tried i guess..这个答案类似于你尝试过的我猜..

I have created RecyclerView inside mainActivity and I have initialized it.我在 mainActivity 中创建了 RecyclerView 并初始化了它。 Then I have created a ViewHolder class that extends RecyclerView for my items as well as xml file for CardView...然后我创建了一个 ViewHolder 类,它扩展了我的项目的 RecyclerView 以及 CardView 的 xml 文件......

  1. Since I am a beginner I am not sure if using the Handler in this purpose is a smart idea?由于我是初学者,我不确定为此目的使用 Handler 是否是一个聪明的主意?

The handler is not the smartest way indeed because the 200 msec may not sufficient to load the entire data of the RecyclerView , but the reason that you need to use a handler is that you don't know when the RecyclerView is loaded with the data in order to scroll to a certain position just after you know that.处理程序确实不是最聪明的方法,因为 200 毫秒可能不足以加载RecyclerView的整个数据,但是您需要使用处理程序的原因是您不知道RecyclerView何时加载了数据以便在您知道之后滚动到某个位置。

There is a bit better way for your handler, but it function the same as yours as it still suffers the problem of the 200 msec.您的处理程序有更好的方法,但它的功能与您的相同,因为它仍然存在 200 毫秒的问题。

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

The reason that it's better because the handler is coupled to your RecyclerView之所以更好是因为处理程序与您的RecyclerView耦合

But the solution that I recommend to use instead of using a Hanlder is to add a listener to your LinearLayoutManager that is triggered when the list of the RecyclerView is populated by the adapter, typically when the mAdapter.notifyDataSetChanged() is over.但是我建议使用而不是使用Hanlder的解决方案是向LinearLayoutManager添加一个侦听器,当适配器填充RecyclerView列表时触发该侦听器,通常是在mAdapter.notifyDataSetChanged()结束时。 And to do that:要做到这一点:

First第一的

Create a custom LinearLayoutManager and add a listener interface that is triggered whenever the onLayoutCompleted() is invoked.创建一个自定义的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();
    }
}

Second第二

Use the callback when you build your RecyclerView LayoutManager.在构建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);
  1. Because my cards/items are not the same sizes I am not able to return to the position of the last viewed item as long as the item is not in a certain position meaning as long as the item below is not on the screen... Is there any way to consider the dimension of my card and according to that set the position?因为我的卡片/物品大小不同,所以只要物品不在某个位置,我就无法返回到上次查看物品的位置,这意味着只要下面的物品不在屏幕上......有什么办法可以考虑我的卡的尺寸并根据那个设置位置吗?

You need to use the LinearLayoutManager scrollToPositionWithOffset() with some offset according to the dimension of your CardView instead of scrollToPosition()您需要根据CardView的尺寸而不是scrollToPositionWithOffset()使用带有一些偏移量LinearLayoutManager scrollToPosition()

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM