Non-Scrolling Fragment in a ViewPager inside Coord

2019-03-15 05:42发布

问题:

I am using a ViewPager in a CoordinatorLayout (from latest version of Design Library) in an Activity. Some fragments for this ViewPager have layouts such as RecyclerView or NestedScrollView, but some just cannot scroll given their small content.

    <android.support.design.widget.AppBarLayout
        android:id="@+id/tabanim_appbar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/MyTheme">

        <android.support.v7.widget.Toolbar
            android:id="@+id/tabanim_toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            android:background="?attr/colorPrimary"
            app:layout_scrollFlags="scroll|enterAlways"
            app:popupTheme="@style/ThemeOverlay.AppCompat.Light"/>


        <android.support.design.widget.TabLayout
            android:id="@+id/tabanim_tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content" />

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

    <android.support.v4.view.ViewPager
        android:id="@+id/tabanim_viewpager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

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

But in one Fragment with a FrameLayout as the root view, I need to have a button that is anchored to the bottom, but it appears to be drawn off-screen. To be able to see it, I need to add a bottom padding equals to the height of the Toolbar.

  <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:background="@android:color/white">

    <TextView
        android:id="@+id/textView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_gravity="center"
        android:text="Home screen"
        android:textAppearance="?android:attr/textAppearanceLarge" />

    <TextView
        android:layout_width="match_parent"
        android:layout_height="70dp"
        android:layout_gravity="bottom"
        android:gravity="center"
        android:text="@string/brand"
        android:textColor="@color/brandColor"
        android:textSize="20sp" />
</FrameLayout>

Likewise, a layout_gravity set to 'center' on an element does not appear to be in the center of the visible area for this fragment.

My understanding is that CoordinatorLayout is only intented to work with scrolling contents, is that correct ? So that using only regular ViewGroup such as FrameLayout, RelativeLayout, LinearLayout for the ViewPager fragments will have their bottom part drawn off-screen ?

In that case, do I need to remove this button from this fragment layout and move it to the activity layout containing the CoordinatorLayout ? It needs to be shown only on the first fragment.

回答1:

From reference to this answer The below worked for me

Extend scrollview behavior

public class MyBehavior extends AppBarLayout.ScrollingViewBehavior {

private View layout;

public MyBehavior() {
}

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

@Override
public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
    boolean result = super.onDependentViewChanged(parent, child, dependency);
    if (layout != null) {
        layout.setPadding(layout.getPaddingLeft(), layout.getPaddingTop(), layout
                .getPaddingRight(), layout.getTop());
    }
    return result;
}

public void setLayout(View layout) {
    this.layout = layout;
}
}

Specify that to viewpager

<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/main_content"
android:layout_width="match_parent"
android:layout_height="match_parent">

<android.support.design.widget.AppBarLayout
    android:id="@+id/appbar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:background="?attr/colorPrimary"
        app:layout_scrollFlags="scroll|enterAlways"
        app:popupTheme="@style/ThemeOverlay.AppCompat.Light" />
</android.support.design.widget.AppBarLayout>
<org.nsu.myapplication.VerticalViewPager
    android:id="@+id/viewpager"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    app:layout_behavior="your.domain.name.MyBehavior"
    />
  </android.support.design.widget.CoordinatorLayout>

and in onCreate of activity

CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) viewPager.getLayoutParams();
    MyBehavior behavior = (MyBehavior) lp.getBehavior();
    behavior.setLayout(viewPager);


回答2:

The issue is caused by intercepting of touch events from ViewPager by parent container. Parent scroll container listens for scroll touch events and if there vertical shift it steals the event from it's child views. It just thinks that user wants to scroll the parent container vertically

Below is the code resolve it

import android.content.Context;
import android.util.AttributeSet;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ScrollView;

public class CustomScrollView extends ScrollView {

    private GestureDetector mGestureDetector;
    private View.OnTouchListener mGestureListener;

    public CustomScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
        mGestureDetector = new GestureDetector(context, new YScrollDetector());
        setFadingEdgeLength(0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        return super.onInterceptTouchEvent(ev) && mGestureDetector.onTouchEvent(ev);
    }

    class YScrollDetector extends GestureDetector.SimpleOnGestureListener {
        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {
            return Math.abs(distanceY) > Math.abs(distanceX);
        }
    }

}

Use this class instead of your ViewPager

   <(your packagename).CustomScrollView
    android:id="@+id/tabanim_viewpager"
    android:layout_width="match_parent"
    android:layout_height="match_parent"/>