Android ViewPager con RecyclerView funciona incorrectamente dentro de BottomSheet

Cuando intento a la lista de desplazamiento, a veces esto funciona incorrecto – BottomSheet intercepta el evento de desplazamiento y se esconde.

Cómo reproducir esto:

  1. Hoja inferior abierta
  2. Cambiar una página de ViewPager
  3. Trate de desplazarse por la lista

Resultado: BottomSheet se ocultará.

Aquí está el ejemplo de código:

Compile 'com.android.support:design:23.4.0'

MainActivity.java

package com.nkdroid.bottomsheetsample; import android.os.Bundle; import android.support.design.widget.BottomSheetBehavior; import android.support.design.widget.TabLayout; import android.support.v4.view.PagerAdapter; import android.support.v4.view.ViewPager; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.TextView; public class MainActivity extends AppCompatActivity { private BottomSheetBehavior behavior; @Override protected void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); final Button btnView = (Button) findViewById(R.id.btnView); btnView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(final View v) { behavior.setState(BottomSheetBehavior.STATE_EXPANDED); } }); final View bottomSheet = findViewById(R.id.bottom_sheet); behavior = BottomSheetBehavior.from(bottomSheet); final ViewPager viewPager = (ViewPager) findViewById(R.id.viewPager); viewPager.setAdapter(new MyPagerAdapter()); final TabLayout tabLayout = (TabLayout) findViewById(R.id.tabs); tabLayout.setupWithViewPager(viewPager); } private class MyPagerAdapter extends PagerAdapter { @Override public int getCount() { return 15; } @Override public Object instantiateItem(final ViewGroup container, final int position) { final RecyclerView recyclerView = new RecyclerView(MainActivity.this); recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this)); recyclerView.setAdapter(new ItemAdapter()); container.addView(recyclerView); return recyclerView; } @Override public boolean isViewFromObject(final View view, final Object object) { return view.equals(object); } @Override public void destroyItem(final ViewGroup container, final int position, final Object object) { container.removeView((View) object); } @Override public CharSequence getPageTitle(final int position) { return String.valueOf(position); } } public class ItemAdapter extends RecyclerView.Adapter<ItemAdapter.ViewHolder> { @Override public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) { return new ViewHolder(new TextView(MainActivity.this)); } @Override public void onBindViewHolder(final ViewHolder holder, final int position) { } @Override public int getItemCount() { return 100; } public class ViewHolder extends RecyclerView.ViewHolder { public TextView textView; public ViewHolder(final View itemView) { super(itemView); textView = (TextView) itemView; } } } } 

Activity_main.xml

 <?xml version="1.0" encoding="utf-8"?> <android.support.design.widget.CoordinatorLayout android:id = "@+id/coordinatorLayout" 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:background = "#a3b1ef" android:fitsSystemWindows = "true" tools:context = ".ui.MainActivity" > <Button android:id = "@+id/btnView" android:layout_width = "match_parent" android:layout_height = "wrap_content" android:text = "Show view" app:layout_behavior = "@string/appbar_scrolling_view_behavior" /> <LinearLayout android:id = "@+id/bottom_sheet" android:layout_width = "match_parent" android:layout_height = "400dp" android:background = "#fff" android:gravity = "center" android:orientation = "vertical" app:layout_behavior = "@string/bottom_sheet_behavior" > <android.support.design.widget.TabLayout android:id = "@+id/tabs" android:layout_width = "match_parent" android:layout_height = "wrap_content" app:tabMode = "scrollable" /> <android.support.v4.view.ViewPager android:id = "@+id/viewPager" android:layout_width = "match_parent" android:layout_height = "match_parent" /> </LinearLayout> </android.support.design.widget.CoordinatorLayout> 

Captura de pantalla

¿Alguna idea para una solución?

Me encontré con la misma limitación, pero fueron capaces de resolverlo.

La razón del efecto que describió es que BottomSheetBehavior (a partir de v24.2.0) sólo admite un niño desplazable que se identifica durante el diseño de la siguiente manera:

 private View findScrollingChild(View view) { if (view instanceof NestedScrollingChild) { return view; } if (view instanceof ViewGroup) { ViewGroup group = (ViewGroup) view; for (int i = 0, count = group.getChildCount(); i < count; i++) { View scrollingChild = findScrollingChild(group.getChildAt(i)); if (scrollingChild != null) { return scrollingChild; } } } return null; } 

Puede ver que encuentra esencialmente el primer niño que se desplaza con DFS.

He mejorado ligeramente esta implementación y montado una pequeña biblioteca , así como una aplicación de ejemplo. Puede encontrarlo aquí: https://github.com/laenger/ViewPagerBottomSheet

Simplemente agregue la URL de repo de maven a su build.gradle:

 repositories { maven { url "https://raw.github.com/laenger/maven-releases/master/releases" } } 

Agregue la biblioteca a las dependencias:

 dependencies { compile "biz.laenger.android:vpbs:0.0.2" } 

Use ViewPagerBottomSheetBehavior para la vista inferior de la hoja:

 app:layout_behavior="@string/view_pager_bottom_sheet_behavior" 

Configurar cualquier ViewPager anidado dentro de la hoja inferior:

 BottomSheetUtils.setupViewPager(bottomSheetViewPager) 

(Esto también funciona cuando ViewPager es la vista inferior de la hoja y para otras ViewPagers anidadas)

Implementación de muestra

  • RecyclerView Varias vistas de disposición en una clase de adaptador
  • Cómo obligar a RecyclerView a desplazarse cuando no hay suficientes elementos para llenar la altura de la sceen
  • Expanda CardView dentro de RecyclerView como nueva actividad como Inbox de Google Android
  • ScrollBy no funciona correctamente en recyclerview anidado
  • ¿Hay una manera de compartir un mismo LayoutManager entre múltiples RecyclerViews anidados
  • Lista 2D con RecyclerView en HorizontalScrollView
  • RecyclerView con fragmentos
  • RecyclerView oculta la barra de acción cuando se abre SoftKeyboard
  • RecyclerView con GridLayoutManager y Picasso mostrando imagen incorrecta
  • RecyclerView ItemTouchHelper Action_Drag_Ended?
  • Android: entradas en recyclerview
  • FlipAndroid es un fan de Google para Android, Todo sobre Android Phones, Android Wear, Android Dev y Aplicaciones para Android Aplicaciones.