【问题标题】:NestedScrollView with wrapped RecyclerView's not scrolling带有包装的 RecyclerView 的 NestedScrollView 不滚动
【发布时间】:2022-09-23 19:29:14
【问题描述】:

我有两个单独的水平 RecyclerView 嵌入在垂直 LinearLayout 中,它本身包含在 NestedScrollView 中 - 顶部和底部视图具有不同的数据集,最终将具有不同的布局。

这是布局..

我目前无法让 NestedScrollView 滚动内容。相反,两个 RecyclerView 都在独立处理自己的滚动行为。所需的行为是滚动 NestedScrollView 会滚动两个 RecyclerView(LinearLayout 的内容),因此它看起来像一个菜单。

我看过许多类似的 SO 线程,但我似乎无法让它工作。我尝试过诸如..

  • 设置填充视口
  • LinearLayout 最初是一个 ConstraintLayout,但是很多帖子建议切换到 LinearLayout,所以坚持使用
  • setNestedScrollingEnabled(在两个 RecyclerViews 的代码和 xml 中都尝试过)
  • 试过 ScrollView 和 NestedScrollView。嵌套似乎是正确的。

这是代码..

MainActivity.java

import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends AppCompatActivity {

@Override
public void onWindowFocusChanged(boolean hasFocus) {
    super.onWindowFocusChanged(hasFocus);
    if (hasFocus) {
        getWindow().getDecorView().setSystemUiVisibility(
                View.SYSTEM_UI_FLAG_LAYOUT_STABLE
                        | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
                        | View.SYSTEM_UI_FLAG_FULLSCREEN
                        | View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY);
    }
}

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

    // remove title
    requestWindowFeature(Window.FEATURE_NO_TITLE);
    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);

                getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_HIDE_NAVIGATION);

    setContentView(R.layout.activity_main);

    if (savedInstanceState == null) {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.content, new SyncedListsActivity())
                .commitNow();
        }
    }
}

SyncedListsActivity.java

import android.graphics.Rect;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import androidx.core.widget.NestedScrollView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;

public class SyncedListsActivity extends Fragment {
View v;
NestedScrollView scrollView;
LinearLayout scrollContent;
RecyclerView topMenu;
TopAdapter topAdapter;
RecyclerView bottomMenu;
BottomAdapter bottomAdapter;

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    Log.d(\"SyncedListsActivity\", \"onCreateView\");
    super.onCreate(savedInstanceState);
    if(v==null) {
        Log.d(\"SyncedListsActivity\", \"v is NULL recreating\");
        v = inflater.inflate(R.layout.activity_synced_scroll, container, false);
        scrollView = v.findViewById(R.id.scrollContainer);
        scrollContent = v.findViewById(R.id.wrapper);

        topMenu = v.findViewById(R.id.topList);
        topMenu.setNestedScrollingEnabled(false);
        bottomMenu = v.findViewById(R.id.bottomList);
        bottomMenu.setNestedScrollingEnabled(false);

        setupTopMenu();
        setupBottomMenu();
    }
    return v;
}

private void setupTopMenu() {
    topMenu.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
    topAdapter = new TopAdapter(getActivity(),this);
    topMenu.setAdapter(topAdapter);
    topAdapter.notifyDataSetChanged();

    SpacesItemDecoration decoration = new SpacesItemDecoration(12);
    topMenu.addItemDecoration(decoration);
}

private void setupBottomMenu() {
    bottomMenu.setLayoutManager(new LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false));
    bottomAdapter = new BottomAdapter(getActivity(),this);
    bottomMenu.setAdapter(bottomAdapter);
    bottomAdapter.notifyDataSetChanged();

    SpacesItemDecoration decoration = new SpacesItemDecoration(12);
    bottomMenu.addItemDecoration(decoration);
}

@Override
public void onResume() {
    super.onResume();
    Log.d(\"SyncedListsActivity\", \"onResume\");
    if(topMenu == null) {
        setupTopMenu();
    }

    if(bottomMenu == null) {
        setupBottomMenu();
    }
}
}

class SpacesItemDecoration extends RecyclerView.ItemDecoration {
private int space;

public SpacesItemDecoration(int space) {
    this.space = space;
}

@Override
public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
    outRect.left = space;
    outRect.right = space;
    outRect.top = space;

    if (parent.getChildLayoutPosition(view) == 0) {
        outRect.left = 0;
    }
}
}

委托瓦片.java

class DelegateTile {
private int pk; //primary key used by recyclerview DiffUtil to simplify adding/removing items.  Can probably use a CID value for this!
private String title;
private String color;
private int width;
private int viewType;

public DelegateTile(int pk, String title, String color, int width, int viewType) {
    this.pk = pk;
    this.title = title;
    this.color = color;
    this.width = width;
    this.viewType = viewType;
}

public int getPk() { return pk; }
public int getTileType() {
    return viewType;
}
public String getTitle() {
    return title;
}
public String getColor() {
    return color;
}
public int getSize() {
    return width;
}
}

TopAdapter.java

import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class TopAdapter extends RecyclerView.Adapter<TopAdapter.TopAdapterView> {
private Context context;
private Fragment parent;

private List<DelegateTile> tileDefinitions;

public TopAdapter(Context context, SyncedListsActivity parent) {
    this.context = context;
    this.parent = parent;

    //Create tiles
    tileDefinitions = new ArrayList<>();
    tileDefinitions.add(new DelegateTile(0, \"Item 1\", \"9E0026\", 800, 0));
    tileDefinitions.add(new DelegateTile(1, \"Item 2\", \"539E3E\", 400, 1));
    tileDefinitions.add(new DelegateTile(2, \"Item 3\", \"F89B1C\", 800, 0));
    tileDefinitions.add(new DelegateTile(3, \"Item 4\", \"13ADA0\", 400, 1));
    tileDefinitions.add(new DelegateTile(4, \"Item 5\", \"00507E\", 400, 1));
    tileDefinitions.add(new DelegateTile(5, \"Item 6\", \"555555\", 800, 0));
}

@Override
public TopAdapterView onCreateViewHolder(ViewGroup parent, int viewType) {
    TopAdapterView gridAdapterView = null;
    Log.d(\"TOP_RECYCLER\", \"onCreateViewHolder \" + viewType + \" rendering\");
    switch (viewType) {
        case 0: { //Wide tile
            Log.d(\"TOP_RECYCLER\", \"onCreateViewHolder \" + viewType + \" TILE_TYPE_WIDE\");
            View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.tile_wide, parent, false);
            gridAdapterView = new TopAdapterView(layoutView, viewType);
            break;
        }
        case 1: { //normal width tile
            Log.d(\"TOP_RECYCLER\", \"onCreateViewHolder \" + viewType + \" TILE_TYPE_STANDARD\");
            View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.tile_normal, parent, false);
            gridAdapterView = new TopAdapterView(layoutView, viewType);
            break;
        }
    }
    return gridAdapterView;
}

@Override
public void onBindViewHolder(TopAdapterView holder, int position) {
    /*
    different styles for different cells - Slideshow, tiles, Footer
    */
    switch (tileDefinitions.get(position).getTileType()) {
        case 0: { //wide tile
            //Tiles - set tile behavior
            holder.title.setText(tileDefinitions.get(position).getTitle());
            //Dynamic colors for our tiles
            ViewGroup.LayoutParams params = holder.layout.getLayoutParams();
            params.width = tileDefinitions.get(position).getSize(); //setting width
            holder.layout.setLayoutParams(params);
            holder.layout.setBackgroundColor(Color.parseColor(\"#\" + tileDefinitions.get(position).getColor())); //setting color
            holder.layout.setTag(position);
            //margins
            ViewGroup.MarginLayoutParams llparams = (ViewGroup.MarginLayoutParams) holder.layout.getLayoutParams();
            llparams.rightMargin = 5;
        }
        case 1: { //normal width tile
            //Tiles - set tile behavior
            holder.title.setText(tileDefinitions.get(position).getTitle());
            //Dynamic colors for our tiles
            ViewGroup.LayoutParams params = holder.layout.getLayoutParams();
            params.width = tileDefinitions.get(position).getSize(); //setting width
            holder.layout.setLayoutParams(params);
            holder.layout.setBackgroundColor(Color.parseColor(\"#\" + tileDefinitions.get(position).getColor())); //setting color
            holder.layout.setTag(position);
            //margins
            ViewGroup.MarginLayoutParams llparams = (ViewGroup.MarginLayoutParams) holder.layout.getLayoutParams();
            llparams.rightMargin = 5;

            break;
        }
    }
}

@Override
public int getItemCount() {
    return tileDefinitions == null ? 0 : tileDefinitions.size();
}

@Override
public int getItemViewType(int position) {
    // Return type based on position
    return tileDefinitions.get(position).getTileType();
}

class TopAdapterView extends RecyclerView.ViewHolder {
    TextView title;
    LinearLayout layout;
    public TopAdapterView(View itemView, int type) {
        super(itemView);
        switch (type) {
            case 0: { //wide tile
                title = itemView.findViewById(R.id.txtTitle);
                layout = itemView.findViewById(R.id.wide_layout);
                break;
            }
            case 1: { //normal width tile
                title = itemView.findViewById(R.id.txtTitle);
                layout = itemView.findViewById(R.id.normal_layout);
                break;
            }
        }
    }
}
}

底部适配器.java

import android.content.Context;
import android.graphics.Color;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
import java.util.List;

public class BottomAdapter extends RecyclerView.Adapter<BottomAdapter.BottomAdapterView> {
private Context context;
private Fragment parent;

private List<DelegateTile> tileDefinitions;

public BottomAdapter(Context context, SyncedListsActivity parent) {
    this.context = context;
    this.parent = parent;

    //Create tiles
    tileDefinitions = new ArrayList<>();
    tileDefinitions.add(new DelegateTile(0, \"Item 1\", \"A21E23\", 400, 1)); //1
    tileDefinitions.add(new DelegateTile(1, \"Item 2\", \"008EAA\", 800, 0)); //2
    tileDefinitions.add(new DelegateTile(2, \"Item 3\", \"A8CF5A\", 400, 1)); //5
    tileDefinitions.add(new DelegateTile(3, \"Item 4\", \"814199\", 400, 1)); //6
    tileDefinitions.add(new DelegateTile(4, \"Item 5\", \"0093B2\", 800, 0)); //7
    tileDefinitions.add(new DelegateTile(5, \"Item 6\", \"13ADA0\", 400, 1)); //6
    tileDefinitions.add(new DelegateTile(6, \"Item 7\", \"00507E\", 400, 1)); //7
}

@Override
public BottomAdapterView onCreateViewHolder(ViewGroup parent, int viewType) {
    BottomAdapterView gridAdapterView = null;
    Log.d(\"BOTTOM_RECYCLER\", \"onCreateViewHolder \" + viewType + \" rendering\");
    switch (viewType) {
        case 0: { //Wide tile
            Log.d(\"BOTTOM_RECYCLER\", \"onCreateViewHolder \" + viewType + \" TILE_TYPE_WIDE\");
            View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.tile_wide, parent, false);
            gridAdapterView = new BottomAdapterView(layoutView, viewType);
            break;
        }
        case 1: { //normal width tile
            Log.d(\"BOTTOM_RECYCLER\", \"onCreateViewHolder \" + viewType + \" TILE_TYPE_STANDARD\");
            View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.tile_normal, parent, false);
            gridAdapterView = new BottomAdapterView(layoutView, viewType);
            break;
        }
    }
    return gridAdapterView;
}

@Override
public void onBindViewHolder(BottomAdapterView holder, int position) {
    /*
    different styles for different cells - Slideshow, tiles, Footer
    */
    switch (tileDefinitions.get(position).getTileType()) {
        case 0: { //wide tile
            //Tiles - set tile behavior
            holder.title.setText(tileDefinitions.get(position).getTitle());
            //Dynamic colors for our tiles
            ViewGroup.LayoutParams params = holder.layout.getLayoutParams();
            params.width = tileDefinitions.get(position).getSize(); //setting width
            holder.layout.setLayoutParams(params);
            holder.layout.setBackgroundColor(Color.parseColor(\"#\" + tileDefinitions.get(position).getColor())); //setting color
            holder.layout.setTag(position);
            //margins
            ViewGroup.MarginLayoutParams llparams = (ViewGroup.MarginLayoutParams) holder.layout.getLayoutParams();
            llparams.rightMargin = 5;
        }
        case 1: { //normal width tile
            //Tiles - set tile behavior
            holder.title.setText(tileDefinitions.get(position).getTitle());
            //Dynamic colors for our tiles
            ViewGroup.LayoutParams params = holder.layout.getLayoutParams();
            params.width = tileDefinitions.get(position).getSize(); //setting width
            holder.layout.setLayoutParams(params);
            holder.layout.setBackgroundColor(Color.parseColor(\"#\" + tileDefinitions.get(position).getColor())); //setting color
            holder.layout.setTag(position);
            //margins
            ViewGroup.MarginLayoutParams llparams = (ViewGroup.MarginLayoutParams) holder.layout.getLayoutParams();
            llparams.rightMargin = 5;

            break;
        }
    }
}

@Override
public int getItemCount() {
    return tileDefinitions == null ? 0 : tileDefinitions.size();
}

@Override
public int getItemViewType(int position) {
    // Return type based on position
    return tileDefinitions.get(position).getTileType();
}

class BottomAdapterView extends RecyclerView.ViewHolder {
    TextView title;
    LinearLayout layout;
    public BottomAdapterView(View itemView, int type) {
        super(itemView);
        switch (type) {
            case 0: { //wide tile
                title = itemView.findViewById(R.id.txtTitle);
                layout = itemView.findViewById(R.id.wide_layout);
                break;
            }
            case 1: { //normal width tile
                title = itemView.findViewById(R.id.txtTitle);
                layout = itemView.findViewById(R.id.normal_layout);
                break;
            }
        }
    }
}
}

activity_main.xml

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"
xmlns:app=\"http://schemas.android.com/apk/res-auto\"
xmlns:tools=\"http://schemas.android.com/tools\"
android:layout_width=\"match_parent\"
android:layout_height=\"match_parent\"
android:id=\"@+id/main_root\"
android:layout_margin=\"20dp\"
android:clipToPadding=\"false\"
android:clipChildren=\"false\">

<FrameLayout
    android:id=\"@+id/content\"
    android:layout_width=\"0dp\"
    android:layout_height=\"0dp\"
    app:layout_constraintBottom_toBottomOf=\"parent\"
    app:layout_constraintEnd_toEndOf=\"parent\"
    app:layout_constraintStart_toStartOf=\"parent\"
    app:layout_constraintTop_toTopOf=\"parent\"
    android:clipChildren=\"false\"
    android:clipToPadding=\"false\" />
</androidx.constraintlayout.widget.ConstraintLayout>

activity_synced_scroll.xml

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"
xmlns:app=\"http://schemas.android.com/apk/res-auto\"
xmlns:tools=\"http://schemas.android.com/tools\"
android:layout_width=\"match_parent\"
android:layout_height=\"match_parent\"
android:id=\"@+id/main_root\"
android:clipToPadding=\"false\"
android:clipChildren=\"false\">

<androidx.core.widget.NestedScrollView
    android:id=\"@+id/scrollContainer\"
    android:layout_width=\"match_parent\"
    android:layout_height=\"wrap_content\"
    android:orientation=\"horizontal\"
    app:layout_constraintTop_toTopOf=\"parent\"
    app:layout_constraintBottom_toBottomOf=\"parent\">

    <LinearLayout
        android:id=\"@+id/wrapper\"
        android:orientation=\"vertical\"
        android:layout_width=\"match_parent\"
        android:layout_height=\"wrap_content\">
        <androidx.recyclerview.widget.RecyclerView
            android:id=\"@+id/topList\"
            android:layout_width=\"wrap_content\"
            android:layout_height=\"wrap_content\"
            android:orientation=\"horizontal\"
            app:layout_constraintTop_toTopOf=\"parent\"
            app:layout_constraintBottom_toTopOf=\"@id/bottomList\"
            app:layout_constraintStart_toStartOf=\"parent\"/>

        <androidx.recyclerview.widget.RecyclerView
            android:id=\"@+id/bottomList\"
            android:layout_width=\"wrap_content\"
            android:layout_height=\"wrap_content\"
            android:orientation=\"horizontal\"
            app:layout_constraintTop_toBottomOf=\"@id/topList\"
            app:layout_constraintBottom_toBottomOf=\"parent\"
            app:layout_constraintStart_toStartOf=\"parent\"/>
    </LinearLayout>
</androidx.core.widget.NestedScrollView>
</androidx.constraintlayout.widget.ConstraintLayout>

tile_normal.xml

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"
android:id=\"@+id/normal_layout\"
android:layout_width=\"400px\"
android:layout_height=\"400px\"
android:orientation=\"vertical\"
android:gravity=\"center\" >
<TextView
    android:id=\"@+id/txtTitle\"
    android:layout_width=\"wrap_content\"
    android:layout_height=\"wrap_content\"
    android:gravity=\"center\"
    android:textSize=\"25sp\" />
</LinearLayout>

tile_wide.xml

<?xml version=\"1.0\" encoding=\"utf-8\"?>
<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"
android:id=\"@+id/wide_layout\"
android:layout_width=\"800px\"
android:layout_height=\"400px\"
android:orientation=\"vertical\"
android:gravity=\"center\" >
<TextView
    android:id=\"@+id/txtTitle\"
    android:layout_width=\"wrap_content\"
    android:layout_height=\"wrap_content\"
    android:gravity=\"center\"
    android:textSize=\"25sp\" />
</LinearLayout>

    标签: java android android-recyclerview android-nestedscrollview


    【解决方案1】:

    经过长时间的调查,似乎 NestedScrollView 实际上并不支持水平滚动。

    滚动视图仅支持垂直滚动。对于水平滚动,请改用 Horizo​​ntalScrollView。

    对于垂直滚动,请考虑 NestedScrollView而不是滚动视图,它提供了更大的用户界面灵活性并支持材料设计滚动模式。

    通过替换更改我的布局嵌套滚动视图水平滚动视图, 解决问题。修改后的布局是这样的(代码更改为使用类型:NestedScrollView)..

    activity_synced_scroll.xml

    <?xml version="1.0" encoding="utf-8"?>
    <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/main_root"
    android:clipToPadding="false"
    android:clipChildren="false">
    
    <androidx.core.widget.NestedScrollView
    android:id="@+id/scrollContainer"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:orientation="horizontal"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintBottom_toBottomOf="parent">
    
    <LinearLayout
        android:id="@+id/wrapper"
        android:orientation="vertical"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/topList"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintTop_toTopOf="parent"
            app:layout_constraintBottom_toTopOf="@id/bottomList"
            app:layout_constraintStart_toStartOf="parent"/>
    
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/bottomList"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:orientation="horizontal"
            app:layout_constraintTop_toBottomOf="@id/topList"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"/>
        </LinearLayout>
        </androidx.core.widget.NestedScrollView>
    </androidx.constraintlayout.widget.ConstraintLayout>
    

    【讨论】:

      猜你喜欢
      • 2020-06-01
      • 2017-01-16
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-11
      • 2019-05-25
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多