当活动布局具有布局权重或 CardView 位于线性布局内时,RecyclerView smoothScrollToPosition 滚动到不正确的位置

Ben*_*654 0 android android-layout android-recyclerview

当我打电话时,我遇到了意想不到的行为recyclerView.smoothScrollToPosition

当我添加足够的项目并开始离开屏幕时,它无法正确滚动到底部,停留在后面 2-3 处。然后,在添加 15 左右后,当我添加一个项目时,它开始滚动到位置 0,然后下一个项目滚动到底部。添加另一个项目,它会变为 0,然后再次添加,它会变为底部。

您可以在随附的视频中看到发生了什么。一旦滚动条开始离开屏幕,也要注意它。

imgur 上的 Gif,但仅限 15 秒。 http://i.imgur.com/FnjsTY1.gifv]

整个视频可在此处观看 - https://www.dropbox.com/s/k6aaca5ue6lkk21/device-2016-05-27-130928.mp4?dl=0

我把我的应用程序拆开,直到我将其范围缩小到两件事,突然解决了测试应用程序中的问题。

我的活动布局使用布局权重,当我删除它时,它再次开始正确滚动。另外,recyclerView 中显示的 CardView 周围有一个额外的 LinearLayout。删除它也可以解决问题。

这些解决方案在我的应用程序中都不起作用,因为可能存在更多像这两个这样的“陷阱”问题。这也是我将其分解到最小量以显示错误,我的布局要复杂得多。

我不知道该怎么办,因为我什至不完全确定为什么这是错误的。我尝试过使用自定义滚动条的自定义线性布局管理器,但总是遇到同样的问题。

下面是用于重现问题的代码

主要活动

 public class MainActivity extends AppCompatActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        MyFragment fragment = new MyFragment();
        getSupportFragmentManager().beginTransaction()
                .add(R.id.fragment_container, fragment).commit();
    }
}
Run Code Online (Sandbox Code Playgroud)

MyFragment 与 RecyclerView

public class MyFragment extends android.support.v4.app.Fragment {

    RecyclerView recyclerView;
    RecyclerAdapter adapter;
    ViewGroup rootView;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        rootView = (ViewGroup) inflater.inflate(R.layout.fragmentlayout, container, false);

        LinearLayoutManager llm = new LinearLayoutManager(getActivity());

        recyclerView = (RecyclerView) rootView.findViewById(R.id.inventory_recycler);
        recyclerView.setLayoutManager(llm);
        adapter = new RecyclerAdapter(new ArrayList<RecyclerItem>());
        recyclerView.setAdapter(adapter);

        Button button = (Button) rootView.findViewById(R.id.button);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                addItem();
            }
        });

        return rootView;
    }

    public void addItem(){
        adapter.getList().add(new RecyclerItem());
        adapter.notifyItemAdded(adapter.getList().size()-1);
        recyclerView.smoothScrollToPosition(adapter.getList().size()-1);
    }

    public class RecyclerItem {
        public RecyclerItem(){
        }
    }

    public static class RecyclerViewHolder extends RecyclerView.ViewHolder {
        TextView name;
        public RecyclerViewHolder(View v) {
            super(v);
            name = (TextView) v.findViewById(R.id.name);
        }
    }

    public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerViewHolder> {

        private final List<RecyclerItem> items;

        public RecyclerAdapter(List<RecyclerItem> items) {
            this.items = items;
        }

        @Override
        public int getItemCount() {
            return items.size();
        }

        @Override
        public void onBindViewHolder(final RecyclerViewHolder viewHolder, final int position) {
            viewHolder.name.setText("" + position);
        }

        public List<RecyclerItem> getList() {
            return items;
        }

        public void notifyItemAdded(int position) {
            notifyItemInserted(position);
        }

        @Override
        public RecyclerViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
            View itemView = LayoutInflater.
                    from(viewGroup.getContext()).
                    inflate(R.layout.card, viewGroup, false);

            return new RecyclerViewHolder(itemView);
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Activity 布局(删除布局权重并设置值解决问题)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal">

      <FrameLayout
          android:id="@+id/fragment_container"
          android:layout_width="0dp"
          android:layout_weight="1"
          android:layout_height="fill_parent"/>

</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

片段布局

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:id="@+id/button"
        android:layout_width="100dp"
        android:layout_height="50dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentTop="true"
        android:text="add item" />

    <android.support.v7.widget.RecyclerView
        android:id="@+id/inventory_recycler"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_below="@id/button"
        android:scrollbars="vertical" />

</RelativeLayout>
Run Code Online (Sandbox Code Playgroud)

Card Layout (去掉多余的LinearLayout即可解决问题)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:card_view="http://schemas.android.com/apk/res-auto"
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:orientation="vertical">

    <android.support.v7.widget.CardView
        android:id="@+id/card_view"
        android:layout_width="fill_parent"
        android:layout_height="140dp"
        android:layout_margin="4dp"
        app:elevation="3dp"
        card_view:cardCornerRadius="2dp">

        <TextView
            android:id="@+id/name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content" />

    </android.support.v7.widget.CardView>

</LinearLayout>
Run Code Online (Sandbox Code Playgroud)

摇篮

apply plugin: 'com.android.application'

android {
    compileSdkVersion 23
    buildToolsVersion "23.0.2"

    defaultConfig {
        applicationId "com.sample.recyclertest"
        minSdkVersion 16
        targetSdkVersion 23
        versionCode 1
        versionName "1.0"
    }
    buildTypes {
        release {
            minifyEnabled false
            proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
        }
    }
}

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    compile 'com.android.support:appcompat-v7:23.4.0'
    compile 'com.android.support:support-v4:23.4.0'
    compile 'com.android.support:cardview-v7:23.4.0'
    compile 'com.android.support:recyclerview-v7:23.4.0'
}
Run Code Online (Sandbox Code Playgroud)

Ank*_*wal 5

一种直接的解决方案是使用 recyclerview-v7:23.1.1 而不是 recyclerview-v7:23.4.0。

另一种解决方案是使用此方法

/**
     * RecyclerView can perform several optimizations if it can know in advance that RecyclerView's
     * size is not affected by the adapter contents. RecyclerView can still change its size based
     * on other factors (e.g. its parent's size) but this size calculation cannot depend on the
     * size of its children or contents of its adapter (except the number of items in the adapter).
     * <p>
     * If your use of RecyclerView falls into this category, set this to {@code true}. It will allow
     * RecyclerView to avoid invalidating the whole layout when its adapter contents change.
     *
     * @param hasFixedSize true if adapter changes cannot affect the size of the RecyclerView.
     */
    public void setHasFixedSize(boolean hasFixedSize)
Run Code Online (Sandbox Code Playgroud)

recyclerView.setHasFixedSize(true);应该可以解决你的问题。23.4.0

您的代码的问题是,每当您调用notifyItemInserted()时,整个recyclerview都会再次绘制,这会将您的recyclerview移动到第一个位置。最重要的是,您正在调用recyclerView.smoothScrollToPosition(adapter.getList().size() - 1);这意味着视图将到达顶部并同时滚动到最后一个位置。这就是为什么它没有到达最底部的位置。所以只需调用recyclerView.setHasFixedSize(true);以防止其重绘即可。