水平 RecyclerView 内的水平 RecyclerView

Posted

技术标签:

【中文标题】水平 RecyclerView 内的水平 RecyclerView【英文标题】:Horizontal RecyclerView inside a Horizontal RecyclerView 【发布时间】:2019-01-20 00:48:31 【问题描述】:

我正在尝试在另一个带有水平 LinearLayoutManager 的 RecyclerView 中使用带有水平 LinearLayoutManager 的 RecyclerView。整个层次结构是这样的:RecyclerView,Recycler的child是一个ScrollView,它包含一个TextView和另一个RecyclerView。为了更好地理解,我希望第一个回收器像 ViewPager 一样工作,但不完全相同(我不想使用 ViewPager)。问题是当我尝试在子回收器上水平滚动时,运动事件被父回收器捕获,导致滚动到下一页而无法滚动子回收器。

MainActivityLayout:

<?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:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    tools:context=".MainActivity">

    <android.support.v7.widget.RecyclerView
        android:id="@+id/RVpage"
        android:descendantFocusability="blocksDescendants"
        android:focusableInTouchMode="false"
        android:focusable="false"
        android:layout_
        android:layout_ />

</LinearLayout>

MainActivity.java:

public class MainActivity extends AppCompatActivity 

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

    private void setRecycler() 
        recyclerView=(RecyclerView) findViewById(R.id.RVpage);
        SnapHelper snapHelper = new LinearSnapHelper();
        snapHelper.attachToRecyclerView(recyclerView);
        BigRecyclerAdapter bigRecyclerAdapter=new BigRecyclerAdapter(this);
        recyclerView.setAdapter(bigRecyclerAdapter);

        //recyclerView.setNestedScrollingEnabled(true);


        recyclerView.setLayoutManager(new LinearLayoutManager(
            this, LinearLayoutManager.HORIZONTAL, false)
        );
        recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() 
            @Override
            public void onScrollStateChanged(RecyclerView recyclerView, int newState) 
                //Toast.makeText(MainActivity.this, "CHANGED BIG", Toast.LENGTH_SHORT).show();
                super.onScrollStateChanged(recyclerView, newState);
            
        );
    

PageRecycler 布局:

<?xml version="1.0" encoding="utf-8"?>
<ScrollView             xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_
    android:layout_
    android:orientation="vertical">

    <LinearLayout
        android:layout_
        android:layout_
        android:orientation="vertical">

    <TextView
        android:layout_
        android:layout_
        android:layout_margin="20dp"
        android:text="nskjdnaskdn \n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak\n sudbadbak
v
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
\n sudbadbak
v
v
\n sudbadbak
\n sudbadbak
v
v
\n sudbadbak
vv
\n sudbadbak
v
v
\n sudbadbak"
        android:textColor="@android:color/black"
        android:textSize="50sp"/>

    <android.support.v7.widget.RecyclerView
        android:id="@+id/photo"
        android:layout_
        android:layout_
        android:clickable="true"
        android:focusableInTouchMode="true"
        android:focusedByDefault="true"
        android:descendantFocusability="afterDescendents"
        android:focusable="true"
        android:padding="20dp" />

    </LinearLayout>

</ScrollView>

在 relativeLayout 中我留下了我尝试过的所有东西,从 clickable=true 到 descendentFocusability。

PageRecycler 适配器:

class BigRecyclerAdapter(private val context: MainActivity) :     RecyclerView.Adapter<BigRecyclerAdapter.ViewHolder>() 
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder 
        val v = LayoutInflater.from(parent?.context)
            .inflate(R.layout.raw, parent, false)

        return ViewHolder(v)
    

/* override fun onViewAttachedToWindow(holder: ViewHolder?) 
    var parent = holder?.recycler?.parent


    val DEBUG_TAG_SCROLL = "DEBUG_TAG_SCROLL"
    Log.d(DEBUG_TAG_SCROLL, "NESTED_SCROLL_VALUE: $holder?.recycler?.hasNestedScrollingParent()")



    if (holder?.recycler?.parent is RecyclerView) 
        Log.d(DEBUG_TAG_SCROLL, "Good parent")
    
    super.onViewAttachedToWindow(holder)
*/

    override fun getItemCount(): Int 
        return 5
    

    override fun onBindViewHolder(holder: ViewHolder?, position: Int) 

        holder?.bind()
//        val DEBUG_TAG_SCROLL = "DEBUG_TAG_SCROLL"
////        Log.d(DEBUG_TAG_SCROLL, "NESTED_SCROLL_VALUE: $holder?.recycler?.hasNestedScrollingParent()")
//
//        if(holder?.recycler?.parent is RecyclerView)
//            Log.d(DEBUG_TAG_SCROLL, "Good parent")
//        
    


    inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) 
        var recycler: RecyclerView

        init 
            recycler = v.findViewById(R.id.photo) as RecyclerView
        

        fun bind() 
            recycler.addOnScrollListener(object : RecyclerView.OnScrollListener() 
                override fun onScrollStateChanged(recyclerView: RecyclerView?, newState: Int) 
                    Toast.makeText(context, "CHANGED SMALL", Toast.LENGTH_SHORT).show()
                    super.onScrollStateChanged(recyclerView, newState)
                

//            override fun onScrolled(recyclerView: RecyclerView?, dx: Int, dy: Int) 
//                super.onScrolled(recyclerView, dx, dy)
//            
            )

            val smallRecyclerAdapter = SmallRecyclerAdapter(context)
            recycler.adapter = smallRecyclerAdapter
            recycler.layoutManager = LinearLayoutManager(
                    context, LinearLayoutManager.HORIZONTAL, false
            )
            recycler.scrollToPosition(5)
        
    

PhotoRecycler raw(子recyclerView):

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_
    android:layout_>
    <ImageView
        android:layout_
        android:layout_
        android:id="@+id/photoView"/>

</LinearLayout>

PhotoRecycler 适配器:

class SmallRecyclerAdapter(private val context: MainActivity): RecyclerView.Adapter<SmallRecyclerAdapter.ViewHolder>()
    override fun onCreateViewHolder(parent: ViewGroup?, viewType: Int): ViewHolder 
        val v = LayoutInflater.from(parent?.context)
            .inflate(R.layout.raw_small, parent, false)
        // set the view's size, margins, paddings and layout parameters

        return ViewHolder(v)
    

    override fun getItemCount(): Int 
        return 10
    

    override fun onBindViewHolder(holder: ViewHolder?, position: Int) 
        //TODO here

        var drawableName="p"+((position%4)+1)
        val drawable =  context.getResources().getDrawable(context.getResources()
            .getIdentifier(drawableName, "drawable", context.getPackageName()))
        holder?.imageView?.setImageDrawable(drawable)
    


    inner class ViewHolder(v: View) : RecyclerView.ViewHolder(v) 
        var imageView: ImageView

        init 
            imageView=v.findViewById(R.id.photoView) as ImageView
        
    

如果您需要更多详细信息,请告诉我,我会更新。

【问题讨论】:

你为什么要这个?在同一方向上的嵌套滚动通常会出现问题。应用程序应该如何知道它应该滚动哪个元素? 为什么要问问题?它应该基本上可以工作,它可以与 ViewPager 一起工作,并且是相同的想法。我猜它知道在水平回收器内滚动垂直回收器的方式相同。我可以通过扩展 Recycler 或 LayoutManager 让它在很长一段时间内工作,但它是矫枉过正的。在尝试之前我想我应该问一下是否存在更简单的解决方案 它知道在水平回收器中滚动垂直回收器 - 这很容易区分,因为它的滚动方向不同。我的 2 美分是更简单的解决方案是制作不需要嵌套水平滚动的不同设计 设计不是我可以改变的。容易或不区分它应该。它只是一个滚动监听器,应该传播到视图的子视图 如果滚动事件传播给孩子会发生什么?他们都滚动?还是只有孩子?只有父母?它是怎么知道的 【参考方案1】:

之前我实现了一个嵌套的ScrollView,我以为子滚动视图总是在消耗触摸,而父滚动视图永远不会听他们的

方法的作用来了 onInterceptTouchEvent 控制视图是使用触摸还是将其传递给其父级。

所以计划是计算滚动的方向,我是否到达了子 ScrollView 的滚动结束。

此代码是 2 年前编写的,因此如果不推荐使用,请原谅。

public class CustomScrollView extends ScrollView 

private boolean bottomReached = false;
private boolean topReached = true;
private float startTouch = -1;
private float distance = -1;

public CustomScrollView(Context context) 
    super(context);


public CustomScrollView(Context context, AttributeSet attrs) 
    super(context, attrs);


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


@Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) 
    super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);


@Override
public boolean onInterceptTouchEvent(MotionEvent ev) 
    switch (ev.getAction()) 
        case MotionEvent.ACTION_DOWN:
            startTouch = ev.getY();
            break;
        case MotionEvent.ACTION_UP:
            startTouch = -1;
            break;
        case MotionEvent.ACTION_MOVE:
            distance = ev.getY() - startTouch;
                if (Math.abs(distance) < 10) 
                    boolean onIntercept = super.onInterceptTouchEvent(ev);
                    return onIntercept;
                 else 
                    if (!bottomReached && !topReached) 
                        return true;
                     else 
                        if (distance > 0) 
                            // Scrolling Up
                            return bottomReached;
                         else 
                            // Scrolling Down
                            return topReached;
                        
                    
                
    
    return super.onInterceptTouchEvent(ev);


@Override
public boolean onTouchEvent(MotionEvent ev) 
    switch (ev.getAction()) 
        case MotionEvent.ACTION_DOWN:
            startTouch = ev.getY();
            break;
        case MotionEvent.ACTION_UP:


            startTouch = -1;
            break;
        case MotionEvent.ACTION_MOVE:
            distance = ev.getY() - startTouch;
    
    return super.onTouchEvent(ev);


【讨论】:

【参考方案2】:

看看这个网站和他在 GitHub 上的代码 Nested Recyclerview

【讨论】:

以上是关于水平 RecyclerView 内的水平 RecyclerView的主要内容,如果未能解决你的问题,请参考以下文章

Android 布局:具有滚动行为的 Viewpager 内的垂直 Recyclerview 内的水平 Recyclerview

NestedScrollView 内的 RecyclerView :使水平滚动更容易

垂直 RecyclerView 内的水平 ViewPager2

如何修复垂直 RecyclerView 内的水平 ViewPager2 和 RecyclerView 的滚动问题?

RecyclerView smoothScroll位于中心位置。安卓

在垂直 recyclerView 中带有 wrap_content 的水平 recyclerView