これは、以下にすべての要素をマッピングして構築しようとしているアプリです。
すべてが機能しますが、内側の水平リサイクラービューで垂直スクロールをキャプチャしないようにします。すべての垂直スクロールは、水平スクロールではなく、外側の垂直リサイクラビューに移動する必要があります。これにより、垂直スクロールにより、scrollFlagに応じてツールバーが非表示になります。
Recyclerviewの「StrawBerry Plant」部分に指を置いて上にスクロールすると、ツールバーがスクロールアウトします。
水平スクロールビューに指を置いて上にスクロールしても、ツールバーはまったくスクロールアウトされません。
これまでの私のxmlレイアウトコードは次のとおりです。
Activityxmlレイアウト:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
xmlns:app="http://schemas.Android.com/apk/res-auto"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:id="@+id/fragment_container"
Android:clipChildren="false">
<Android.support.design.widget.CoordinatorLayout
Android:orientation="vertical"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
Android:id="@+id/container"
>
<Android.support.design.widget.AppBarLayout
Android:id="@+id/appBarLayout"
Android:layout_width="match_parent"
Android:layout_height="wrap_content">
<Android.support.v7.widget.Toolbar
Android:id="@+id/toolbar"
Android:minHeight="?attr/actionBarSize"
Android:background="?attr/colorPrimary"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways">
</Android.support.v7.widget.Toolbar>
<Android.support.design.widget.TabLayout
Android:id="@+id/sliding_tabs"
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:background="?attr/colorPrimary"
style="@style/CustomTabLayout"
/>
</Android.support.design.widget.AppBarLayout>
<Android.support.v4.view.ViewPager
Android:id="@+id/viewPager"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
/>
</Android.support.design.widget.CoordinatorLayout>
</FrameLayout>
"Fruits" fragmentxmlレイアウト(フラグメントのコードです-フラグメントは上の図でラベル付けされています):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:orientation="vertical"
Android:layout_width="match_parent"
Android:layout_height="match_parent">
<ProgressBar
Android:layout_width="wrap_content"
Android:layout_height="wrap_content"
Android:id="@+id/progressBar"
Android:visibility="gone"
Android:layout_centerInParent="true"
Android:indeterminate="true"/>
<!-- <Android.support.v7.widget.RecyclerView-->
<com.example.simon.customshapes.VerticallyScrollRecyclerView
Android:id="@+id/main_recyclerview"
Android:layout_width="match_parent"
Android:layout_height="match_parent"
/>
</RelativeLayout>
ビューグループでタッチイベントを処理するgoogleの例に続くVerticallyScrollRecyclerViewというカスタムクラスを使用しました。その目的は、すべての垂直スクロールイベントをインターセプトして消費し、ツールバーをスクロールイン/アウトすることです。 http://developer.Android.com/training/gestures/viewgroup.html
VerticallyScrollRecyclerViewのコードは次のとおりです。
public class VerticallyScrollRecyclerView extends RecyclerView {
public VerticallyScrollRecyclerView(Context context) {
super(context);
}
public VerticallyScrollRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public VerticallyScrollRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
ViewConfiguration vc = ViewConfiguration.get(this.getContext());
private int mTouchSlop = vc.getScaledTouchSlop();
private boolean mIsScrolling;
private float startY;
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
// Always handle the case of the touch gesture being complete.
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
// Release the scroll.
mIsScrolling = false;
startY = ev.getY();
return super.onInterceptTouchEvent(ev); // Do not intercept touch event, let the child handle it
}
switch (action) {
case MotionEvent.ACTION_MOVE: {
Log.e("VRecView", "its moving");
if (mIsScrolling) {
// We're currently scrolling, so yes, intercept the
// touch event!
return true;
}
// If the user has dragged her finger horizontally more than
// the touch slop, start the scroll
// left as an exercise for the reader
final float yDiff = calculateDistanceY(ev.getY());
Log.e("yDiff ", ""+yDiff);
// Touch slop should be calculated using ViewConfiguration
// constants.
if (Math.abs(yDiff) > 5) {
// Start scrolling!
Log.e("Scroll", "we are scrolling vertically");
mIsScrolling = true;
return true;
}
break;
}
}
return super.onInterceptTouchEvent(ev);
}
private float calculateDistanceY(float endY) {
return startY - endY;
}
}
"Favourite" layoutは、垂直recyclerview内のrecyclerviewです。
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
Android:layout_width="match_parent"
Android:layout_height="match_parent"
xmlns:Android="http://schemas.Android.com/apk/res/Android"
Android:background="@color/white"
xmlns:app="http://schemas.Android.com/apk/res-auto">
<TextView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:text="Favourite"
Android:layout_marginTop="8dp"
Android:layout_marginBottom="8dp"
Android:layout_marginLeft="16dp"
Android:id="@+id/header_fav"/>
<Android.support.v7.widget.RecyclerView
Android:layout_width="match_parent"
Android:layout_height="wrap_content"
Android:orientation="horizontal"
Android:layout_below="@+id/header_fav"
Android:id="@+id/recyclerview_fav">
</Android.support.v7.widget.RecyclerView>
</RelativeLayout>
これはしばらくの間私を悩ませており、私は解決策を思い付くことができませんでした。誰もこの問題を解決する方法を知っていますか?
正しい答えを得るためにGriffindorに5ポイント、そしてもちろんSOの評判ポイント。
テスト済みソリューション:
必要なのは、内部のRecyclerView
sでmInnerRecycler.setNestedScrollingEnabled(false);
を呼び出すことだけです
説明:
RecyclerView
は、NestedScrollingChild
インターフェイスを実装することにより、API 21
で導入されたネストされたスクロールをサポートしています。これは、同じ方向にスクロールする別のビュー内にスクロールビューがあり、フォーカスされているときにのみ内側のView
をスクロールする場合に役立つ機能です。
いずれの場合でも、RecyclerView
はデフォルトで、初期化時にそれ自体でRecyclerView.setNestedScrollingEnabled(true);
を呼び出します。さて、問題に戻ります。両方のRecyclerView
sがViewPager
を持つ同じAppBarBehavior
内にあるため、CoordinateLayout
は、内側のRecyclerView
からスクロールするときに、どのスクロールに応答するかを決定する必要があります。内側のRecyclerView
のネストされたスクロールが有効になっている場合、スクロールフォーカスを取得し、CoordinateLayout
は外側のRecyclerView
のスクロールを介したスクロールに応答することを選択します。つまり、内部のRecyclerView
sは垂直にスクロールしないため、(CoordinateLayout
の観点から)垂直スクロールの変更はなく、変更がなければAppBarLayout
も変更されません。
あなたの場合、内側のRecyclerView
sは異なる方向にスクロールしているため、無効にすることができます。したがって、CoordinateLayout
はそのスクロールを無視し、外側のRecyclerView
のスクロールに応答します。
注意:
Xml属性Android:nestedScrollingEnabled="boolean"
はRecyclerView
とともに使用することを目的としておらず、Android:nestedScrollingEnabled="false"
を使用しようとするとJava.lang.NullPointerException
になります。したがって、少なくとも今のところは、コード。
私は少し遅れていますが、これは同じ問題に直面している他の人にとって間違いなく機能します
mRecyclerView.addOnItemTouchListener(new RecyclerView.OnItemTouchListener() {
@Override
public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) {
int action = e.getAction();
// Toast.makeText(getActivity(),"HERE",Toast.LENGTH_SHORT).show();
switch (action) {
case MotionEvent.ACTION_POINTER_UP:
rv.getParent().requestDisallowInterceptTouchEvent(true);
break;
}
return false;
}
まだ見ている人がいれば、これを試してください:
private val Y_BUFFER = 10
private var preX = 0f
private var preY = 0f
mView.rv.addOnItemTouchListener(object : RecyclerView.OnItemTouchListener {
override fun onTouchEvent(p0: RecyclerView, p1: MotionEvent) {
}
override fun onInterceptTouchEvent(rv: RecyclerView, e: MotionEvent): Boolean {
when (e.action) {
MotionEvent.ACTION_DOWN -> rv.parent.requestDisallowInterceptTouchEvent(true)
MotionEvent.ACTION_MOVE -> {
if (Math.abs(e.x - preX) > Math.abs(e.y - preY)) {
rv.parent.requestDisallowInterceptTouchEvent(true)
} else if (Math.abs(e.y - preY) > Y_BUFFER) {
rv.parent.requestDisallowInterceptTouchEvent(false)
}
}
}
preX = e.x
preY = e.y
return false
}
override fun onRequestDisallowInterceptTouchEvent(p0: Boolean) {
}
})
現在水平にスクロールしているかどうかを確認し、親がイベントを処理できないようにします
テスト済みのソリューション、カスタムNestedScrollView()
を使用します。
コード:
public class CustomNestedScrollView extends NestedScrollView {
public CustomNestedScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
// Explicitly call computeScroll() to make the Scroller compute itself
computeScroll();
}
return super.onInterceptTouchEvent(ev);
}
}
試してみる
public OuterRecyclerViewAdapter(List<Item> items) {
//Constructor stuff
viewPool = new RecyclerView.RecycledViewPool();
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
//Create viewHolder etc
holder.innerRecyclerView.setRecycledViewPool(viewPool);
}
内側のrecylerviewは同じビュープールを使用し、よりスムーズになります
Googleアプリのようなフラグメント内に水平リサイクラービューを追加することをお勧めします