23

リストをスクロールしようとすると、これが正しく機能しないことがあります。BottomSheet がスクロール イベントをインターセプトして非表示にします。

これを再現する方法:

  1. ボトムシートを開く
  2. ViewPager のページを変更する
  3. リストをスクロールしてみてください

結果: BottomSheet が非表示になります。

サンプルコードは次のとおりです。

「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>

スクリーンショット

回避策のアイデアはありますか?

4

9 に答える 9

32

同じ制限に遭遇しましたが、解決できました。

あなたが説明した効果の理由は、BottomSheetBehavior(v24.2.0の時点で)次の方法でレイアウト中に識別される1つのスクロール子のみをサポートするためです。

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;
}

基本的に、DFS を使用して最初のスクロールする子を見つけていることがわかります。

この実装を少し拡張し、小さなライブラリとサンプル アプリを作成しました。ここで見つけることができます: https://github.com/laenger/ViewPagerBottomSheet

build.gradle に maven リポジトリの URL を追加するだけです。

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

ライブラリを依存関係に追加します。

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

ボトムシート ビューに使用ViewPagerBottomSheetBehaviorします。

app:layout_behavior="@string/view_pager_bottom_sheet_behavior"

一番下のシート内にネストされた ViewPager をセットアップします。

BottomSheetUtils.setupViewPager(bottomSheetViewPager)

(これは、ViewPagerボトム シート ビューであり、さらにネストされた ViewPager の場合にも機能します)

サンプル実装

于 2016-07-09T11:42:41.193 に答える
0

このスレッドの Hadi の回答に基づいて、次のように sth を使用できます。

  • これは、「 (requestDisallowInterceptTouchEvent), Called when a child does not want this parent and its祖先が ViewGroup.onInterceptTouchEvent(MotionEvent) でタッチ イベントをインターセプトすることを望んでいないときに呼び出される」をAndroid docから読み取ることができる良い解決策だと思います。

  • 私はいくつかのソリューションをテストしましたが、これは大きなリストで動作するバグやラグのないソリューションです!

  • これは kotlinish の方法であり、ルート ビューを LinearLayout から ConstraintLayout に変更しました。

    import android.content.Context
    import android.util.AttributeSet
    import android.view.MotionEvent
    import androidx.constraintlayout.widget.ConstraintLayout
    
    
    class DisallowInterceptView : ConstraintLayout {
        constructor(context: Context) : super(context) {
            requestDisallowInterceptTouchEvent(true)
        }
    
        constructor(context: Context, attrs: AttributeSet?) : super(context, attrs) {
            requestDisallowInterceptTouchEvent(true)
        }
    
        constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
            requestDisallowInterceptTouchEvent(true)
        }
    
        override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
            parent.requestDisallowInterceptTouchEvent(true)
            return super.dispatchTouchEvent(ev)
        }
    
        override fun onTouchEvent(event: MotionEvent): Boolean {
            when (event.action) {
                MotionEvent.ACTION_MOVE -> requestDisallowInterceptTouchEvent(true)
                MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> requestDisallowInterceptTouchEvent(false)
            }
            return super.onTouchEvent(event)
        }
    }
    
    

次に、ボトム シート コンテナーのレイアウトを変更します。

<com.vgen.vooop.utils.DisallowInterceptView
    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"
    app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior"
>
    .
    .
    .

</com.vgen.vooop.utils.DisallowInterceptView>
于 2021-12-26T11:15:56.727 に答える