TabLayout inside ViewPager prohibits swiping

Viewed 1186

I have the following ViewPager's FINANCIAL fragment page.

enter image description here

It is built using the following mythology

this.detailedStockFragmentPagerAdapter = new DetailedStockFragmentPagerAdapter(this.getSupportFragmentManager(), bundle);
viewPager.setAdapter(detailedStockFragmentPagerAdapter);
viewPager.setOffscreenPageLimit(Math.max(1, detailedStockFragmentPagerAdapter.getCount() - 1));
tabLayout.setupWithViewPager(viewPager);

private static class DetailedStockFragmentPagerAdapter extends FragmentPagerAdapter {
    public DetailedStockFragmentPagerAdapter(FragmentManager fm, Bundle bundle) {
        super(fm);
        this.bundle = bundle;
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public Fragment getItem(int position) {
        switch (position) {
            case 0:
            {
                ...
            }
            case 1:
            {
                ...
            }
            case 2:
            {
                FinancialFragment financialFragment = FinancialFragment.newInstance();
                financialFragment.setArguments(bundle);
                return financialFragment;
            }
            default:
                assert(false);
        }
        return null;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        switch (position) {
            case 0:
                return JStockApplication.instance().getString(R.string.info);
            case 1:
                return JStockApplication.instance().getString(R.string.news);
            case 2:
                return JStockApplication.instance().getString(R.string.financial);
            default:
                return null;
        }
    }
}

In FINANCIAL page, there are 3 components

  1. Button
  2. TextView
  3. inner TabLayout (The bar with 1D, 1W, 1M, 3M, ...)

If I touch any of the following components, and swipe toward right, it works as expected.

  1. Button
  2. TextView
  3. Any empty area of the fragment

enter image description here


However, if my touch area is inner TabLayout, swipe doesn't work. It seems that the TabLayout has "eaten" my swipe event.

Any idea how I can have inner TabLayout in ViewPager fragment, yet doesn't affect my swipe event?

The XML of my ViewPager fragment page is as follow

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <Button
        android:layout_marginBottom="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is button"
        android:textSize="18sp" />

    <TextView
        android:layout_marginBottom="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#ff000000"
        android:textSize="18sp"
        android:text="This fragment is not scrollable. How to make toolbar visible?"/>

    <LinearLayout
        android:id="@+id/period_tablayout_linear_layout"
        android:layout_width="match_parent"
        android:layout_height="38dp"
        android:orientation="vertical">

        <android.support.design.widget.TabLayout
            android:id="@+id/period_tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"

            app:tabTextAppearance="@style/TradingChartPeriodTabTextAppearance"
            app:tabPaddingStart="0dp"
            app:tabPaddingEnd="0dp"
            app:tabIndicatorHeight="2dp"
            app:tabMode="fixed"
            app:tabGravity="fill"
            app:tabBackground="?attr/tradingHistorySummaryChartBackgroundColor"
            app:tabSelectedTextColor="?attr/primaryTextColor"
            app:tabIndicatorColor="?attr/primaryTextColor">

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_1d"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_day_1_short" />

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_1w"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_week_1_short" />

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_1m"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_month_1_short" />

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_3m"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_months_3_short" />

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_6m"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_months_6_short" />

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_1y"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_year_1_short" />

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_5y"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_years_5_short" />

            <android.support.design.widget.TabItem
                android:id="@+id/tab_item_10y"
                android:layout_height="wrap_content"
                android:layout_width="0dp"
                android:layout_weight="1"
                android:text="@string/trading_period_max" />

        </android.support.design.widget.TabLayout>

    </LinearLayout>

</LinearLayout>
3 Answers

TabLayout is a subclass of HorizontalScrollView, which in turn is a subclass of ScrollView. If you disable horizontal scrolling, then the MotionEvent won't be swallowed by TabLayout, instead would be passed higher up the view hierarchy - to ViewPager.

You can subclass TabLayout performing following changes:


    public class MyTabLayout extends TabLayout {

        public MyTabLayout(Context context) {
            super(context);
        }

        public MyTabLayout(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        public MyTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
            super(context, attrs, defStyleAttr);
        }

        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            switch (ev.getAction()) {
                case MotionEvent.ACTION_DOWN:
                    return false;
                default:
                    return super.onTouchEvent(ev);
            }
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            return false;
        }

    }

Output:

enter image description here

I think the following works:

public class CustomTabLayout extends TabLayout {
    private PointF touchStartPoint;
    private int mTouchSlop;
    private boolean mIsScrolling;

    public CustomTabLayout(Context context) {
        super(context);
        config();

    }

    public CustomTabLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        config();
    }

    public CustomTabLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        config();

    }

    private void config() {
        ViewConfiguration vc = ViewConfiguration.get(getContext());
        mTouchSlop = vc.getScaledTouchSlop();
    }


    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        final int action = ev.getAction();

        if (action == MotionEvent.ACTION_DOWN) {
            touchStartPoint = new PointF(ev.getX(), ev.getY());

            return super.onInterceptTouchEvent(ev);
        }

        if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {

            mIsScrolling = false;
            return false;
        }

        switch (action) {
            case MotionEvent.ACTION_MOVE: {
                if (mIsScrolling) {
                    return false;
                }
                final int xDiff = calculateDistanceX(ev);
                if (xDiff > mTouchSlop) {
                    mIsScrolling = true;
                    return true;
                }
                break;
            }
        }
        return super.onInterceptTouchEvent(ev);
    }

    private int calculateDistanceX(MotionEvent ev) {
        return (int) Math.abs(ev.getX() - touchStartPoint.x);
    }
}

you can use Viewpager inside your FinantialFragment After Changing Your FinancialFragment should be like below :

public class FinancialFragment extends Fragment {

    private ViewPager viewPager;
    private View rootView;
    private TabLayout tabLayout;

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

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment

        rootView = inflater.inflate(R.layout.fragment_three, container, false);
        viewPager = (ViewPager) rootView.findViewById(R.id.viewpager);
        tabLayout = (TabLayout) rootView.findViewById(R.id.period_tab_layout);

        return rootView;
    }

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);


        setupViewPager(viewPager);

        tabLayout.setupWithViewPager(viewPager);

    }

    private void setupViewPager(ViewPager viewPager) {
        ViewPagerAdapters adapter = new ViewPagerAdapters(getChildFragmentManager() );
        adapter.addFragment(new OneDayFragment(), "1D");
        adapter.addFragment(new OneWeekFragment(), "1W");
        adapter.addFragment(new OneMonthFragment(), "1M");
        adapter.addFragment(new ThreeMonthFragment(), "3M");
        adapter.addFragment(new SixMonthFragment(), "6M");
        adapter.addFragment(new OneYearFragment(), "1Y");
        adapter.addFragment(new FiveYearFragment(), "5Y");
        viewPager.setAdapter(adapter);
        viewPager.setOffscreenPageLimit(Math.max(1, adapter.getCount() - 1));
    }

    class ViewPagerAdapters extends FragmentPagerAdapter {
        private final List<Fragment> mFragmentList = new ArrayList<>();
        private final List<String> mFragmentTitleList = new ArrayList<>();

        public ViewPagerAdapters(FragmentManager fm) {
            super(fm);
        }

        @Override
        public Fragment getItem(int position) {
            return mFragmentList.get(position);
        }

        @Override
        public int getCount() {
            return mFragmentList.size();
        }

        public void addFragment(Fragment fragment, String title) {
            mFragmentList.add(fragment);
            mFragmentTitleList.add(title);
        }

        @Override
        public CharSequence getPageTitle(int position) {
            return mFragmentTitleList.get(position);
        }
    }
}

And Yout layout should be like this :

<LinearLayout 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:orientation="vertical">

    <Button
        android:layout_marginBottom="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="This is button"
        android:textSize="18sp" />

    <TextView
        android:layout_marginBottom="20dp"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textColor="#ff000000"
        android:textSize="18sp"
        android:text="This fragment is not scrollable. How to make toolbar visible?"/>

    <LinearLayout
        android:id="@+id/period_tablayout_linear_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">

        <android.support.design.widget.TabLayout
            android:id="@+id/period_tab_layout"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_marginLeft="5dp"
            android:layout_marginRight="5dp"
            app:tabPaddingStart="0dp"
            app:tabPaddingEnd="0dp"
            app:tabIndicatorHeight="2dp"
            app:tabMode="fixed"
            app:tabGravity="fill">

        </android.support.design.widget.TabLayout>

        <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"  />

    </LinearLayout>

</LinearLayout>
Related