diff options
author | Ebrahim Byagowi <ebrahim@gnu.org> | 2020-04-12 18:39:45 +0430 |
---|---|---|
committer | Ebrahim Byagowi <ebrahim@gnu.org> | 2020-04-21 19:30:17 +0430 |
commit | 6f74af75920cabf33c16b08bce1d4297687e8d08 (patch) | |
tree | 322a548bbbfeaa68c264f179940442c27f7e6ac2 | |
parent | 17962b57a052593827628d2db9932318ce2cb514 (diff) | |
download | AntennaPod-6f74af75920cabf33c16b08bce1d4297687e8d08.zip |
Wrap scrollable childs of viewpager in NestedScrollableHost
3 files changed, 158 insertions, 12 deletions
diff --git a/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java new file mode 100644 index 000000000..2be49ada4 --- /dev/null +++ b/app/src/main/java/de/danoeh/antennapod/view/NestedScrollableHost.java @@ -0,0 +1,133 @@ +/* + * Copyright 2019 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * Source: https://github.com/android/views-widgets-samples/blob/87e58d1/ViewPager2/app/src/main/java/androidx/viewpager2/integration/testapp/NestedScrollableHost.kt + */ + +package de.danoeh.antennapod.view; + +import android.content.Context; +import android.util.AttributeSet; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewConfiguration; +import android.widget.FrameLayout; + +import androidx.annotation.NonNull; +import androidx.annotation.Nullable; +import androidx.viewpager2.widget.ViewPager2; + +import static androidx.viewpager2.widget.ViewPager2.ORIENTATION_HORIZONTAL; + +/** + * Layout to wrap a scrollable component inside a ViewPager2. Provided as a solution to the problem + * where pages of ViewPager2 have nested scrollable elements that scroll in the same direction as + * ViewPager2. The scrollable element needs to be the immediate and only child of this host layout. + * + * <p>This solution has limitations when using multiple levels of nested scrollable elements + * (e.g. a horizontal RecyclerView in a vertical RecyclerView in a horizontal ViewPager2).</p> + */ +class NestedScrollableHost extends FrameLayout { + + public NestedScrollableHost(@NonNull Context context) { + super(context); + touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + public NestedScrollableHost(@NonNull Context context, @Nullable AttributeSet attrs) { + super(context, attrs); + touchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); + } + + private int touchSlop = 0; + private float initialX = 0f; + private float initialY = 0f; + + private ViewPager2 getParentViewPager() { + View v = (View) getParent(); + while (v != null && !(v instanceof ViewPager2)) { + v = (View) v.getParent(); + } + return v == null ? null : (ViewPager2) v; + } + + private View getChild() { + return getChildCount() > 0 ? getChildAt(0) : null; + } + + private boolean canChildScroll(int orientation, float delta) { + int direction = (int) -Math.copySign(1, delta); + View child = getChild(); + if (child == null) { + return false; + } + switch (orientation) { + case 0: + return child.canScrollHorizontally(direction); + case 1: + return child.canScrollVertically(direction); + default: + return false; + } + } + + public boolean onInterceptTouchEvent(MotionEvent e) { + handleInterceptTouchEvent(e); + return super.onInterceptTouchEvent(e); + } + + private void handleInterceptTouchEvent(MotionEvent e) { + ViewPager2 parentViewPager = getParentViewPager(); + if (parentViewPager == null) { + return; + } + int orientation = parentViewPager.getOrientation(); + + // Early return if child can't scroll in same direction as parent + if (!canChildScroll(orientation, -1f) && !canChildScroll(orientation, 1f)) { + return; + } + + if (e.getAction() == MotionEvent.ACTION_DOWN) { + initialX = e.getX(); + initialY = e.getY(); + getParent().requestDisallowInterceptTouchEvent(true); + } else if (e.getAction() == MotionEvent.ACTION_MOVE) { + int dx = (int) (e.getX() - initialX); + int dy = (int) (e.getY() - initialY); + boolean isVpHorizontal = orientation == ORIENTATION_HORIZONTAL; + + // assuming ViewPager2 touch-slop is 2x touch-slop of child + float scaledDx = Math.abs(dx) * (isVpHorizontal ? .5f : 1f); + float scaledDy = Math.abs(dy) * (isVpHorizontal ? 1f : .5f); + + if (scaledDx > touchSlop || scaledDy > touchSlop) { + if (isVpHorizontal == (scaledDy > scaledDx)) { + // Gesture is perpendicular, allow all parents to intercept + getParent().requestDisallowInterceptTouchEvent(false); + } else { + // Gesture is parallel, query child if movement in that direction is possible + if (canChildScroll(orientation, isVpHorizontal ? dx : dy)) { + // Child can scroll, disallow all parents to intercept + getParent().requestDisallowInterceptTouchEvent(true); + } else { + // Child cannot scroll, allow all parents to intercept + getParent().requestDisallowInterceptTouchEvent(false); + } + } + } + } + } +} diff --git a/app/src/main/res/layout/feeditem_fragment.xml b/app/src/main/res/layout/feeditem_fragment.xml index fe1063d85..8dfbbe562 100644 --- a/app/src/main/res/layout/feeditem_fragment.xml +++ b/app/src/main/res/layout/feeditem_fragment.xml @@ -171,12 +171,18 @@ </LinearLayout> - <de.danoeh.antennapod.view.ShownotesWebView - android:id="@+id/webvDescription" - android:layout_width="match_parent" + <de.danoeh.antennapod.view.NestedScrollableHost android:layout_below="@id/header" - android:layout_height="match_parent" - android:foreground="?android:windowContentOverlay" /> + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <de.danoeh.antennapod.view.ShownotesWebView + android:id="@+id/webvDescription" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:foreground="?android:windowContentOverlay" /> + + </de.danoeh.antennapod.view.NestedScrollableHost> <FrameLayout android:layout_width="match_parent" diff --git a/app/src/main/res/layout/item_description_fragment.xml b/app/src/main/res/layout/item_description_fragment.xml index 96382eae3..3766cf805 100644 --- a/app/src/main/res/layout/item_description_fragment.xml +++ b/app/src/main/res/layout/item_description_fragment.xml @@ -1,11 +1,18 @@ <?xml version="1.0" encoding="utf-8"?> -<androidx.core.widget.NestedScrollView -xmlns:android="http://schemas.android.com/apk/res/android" -android:layout_width="match_parent" -android:layout_height="match_parent" -android:fillViewport="false"> - <de.danoeh.antennapod.view.ShownotesWebView +<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android" + android:layout_width="match_parent" + android:layout_height="match_parent" + android:fillViewport="false"> + + <de.danoeh.antennapod.view.NestedScrollableHost + android:layout_width="match_parent" + android:layout_height="match_parent"> + + <de.danoeh.antennapod.view.ShownotesWebView android:id="@+id/webview" android:layout_width="match_parent" - android:layout_height="wrap_content"/> + android:layout_height="wrap_content" /> + + </de.danoeh.antennapod.view.NestedScrollableHost> + </androidx.core.widget.NestedScrollView>
\ No newline at end of file |