sna*_*msm 1 android android-webview android-scroll android-toolbar android-appbarlayout
我有两个活动,一个应用程序扩展相同的abstract-一个与WebView另一个具有ViewPager与TabLayout和RecyclerView的S -和在这个层面上,我实现“共享”显示/隐藏工具栏与滚动/一扔功能:
app:layout_scrollFlags="scroll|snap|enterAlways"
WebViewToolbar就像RecyclerView在ViewPager(保持TabLayout可见)中的任何选项卡下一样“推出” ,在这两种情况下,任何滚动到顶部都会带回Toolbar. 就像在这个例子中一样:
现在我必须改变这种行为 -Toolbar当用户不在页面顶部时应该隐藏,所以:Toolbar如果存在任何滚动到底部应该隐藏并且只有(滚动到顶部 && getScrollY()<=0)应该Toolbar输入:
我从本指南中借用了 GIF ,这表明(所有其他的)我想要的行为是默认的,只有scroll标志(可能还有snap)。所以我已经删除了enterAlways,我的“本机”Activity开始按预期开箱即用。WebView- 根本没有变化......
我怀疑这个错误是我造成的NestedWebView,我目前使用的这一个。所以我试图用这些来替换这个类:
marshi - 不可滚动的 webview 内容(工具栏显示/隐藏)
takahirom - 即使在 1px 几乎立即向上滚动时,工具栏也会进入(一种捕捉行为,但在触摸期间)
hanks - 没有嵌套滚动(固定工具栏),还有未注释的setNestedScrollingEnabled(true);行
如何实现Toolbar与GIF合作的第二个 GIF行为WebView?
阅读底部的编辑和其他答案/评论!不要使用这个类,WebView如果可以,也不要以任何形式使用。你迟早会惹上麻烦,维护WebView就是地狱……
我发现很多不同的实现NestedWebView,并注意到他们都是老农具NestedScrollingChild,当目前我们有这个接口的第三版本:NestedScrollingChild3(androidx)。所以我已经深入了解了当前版本NestedSrcollView并添加了一些新的方法处理,现在它可以工作了。确切地说 - 我试图在整个屏幕上实现平滑滚动,就像什么时候WebView会填满整个屏幕并且Toolbar是网页内容的一部分(总是在顶部)。也在snap工作。明智地使用
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.VelocityTracker;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewParent;
import android.webkit.WebView;
import android.widget.OverScroller;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.NestedScrollingChild3;
import androidx.core.view.NestedScrollingChildHelper;
import androidx.core.view.ViewCompat;
/**
* WebView compatible with CoordinatorLayout by snachmsm
* The implementation based on NestedScrollView of design library androidx v1.0.1
*/
public class NestedWebView extends WebView implements NestedScrollingChild3 {
private static final String TAG = "NestedWebView";
private static final int INVALID_POINTER = -1;
private final int[] mScrollOffset = new int[2];
private final int[] mScrollConsumed = new int[2];
private int mLastMotionY;
private NestedScrollingChildHelper mChildHelper;
private boolean mIsBeingDragged = false;
private VelocityTracker mVelocityTracker;
private int mTouchSlop;
private int mActivePointerId = INVALID_POINTER;
private int mNestedYOffset;
private OverScroller mScroller;
private int mMinimumVelocity;
private int mMaximumVelocity;
private int mLastScrollerY;
public NestedWebView(Context context) {
this(context, null);
}
public NestedWebView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.webViewStyle);
}
public NestedWebView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setOverScrollMode(WebView.OVER_SCROLL_NEVER);
initScrollView();
mChildHelper = new NestedScrollingChildHelper(this);
setNestedScrollingEnabled(true);
}
private void initScrollView() {
mScroller = new OverScroller(getContext());
final ViewConfiguration configuration = ViewConfiguration.get(getContext());
mTouchSlop = configuration.getScaledTouchSlop();
mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mIsBeingDragged)) { // most common
return true;
}
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE:
final int activePointerId = mActivePointerId;
if (activePointerId == INVALID_POINTER) {
break;
}
final int pointerIndex = ev.findPointerIndex(activePointerId);
if (pointerIndex == -1) {
Log.e(TAG, "Invalid pointerId=" + activePointerId
+ " in onInterceptTouchEvent");
break;
}
final int y = (int) ev.getY(pointerIndex);
final int yDiff = Math.abs(y - mLastMotionY);
if (yDiff > mTouchSlop
&& (getNestedScrollAxes() & ViewCompat.SCROLL_AXIS_VERTICAL) == 0) {
mIsBeingDragged = true;
mLastMotionY = y;
initVelocityTrackerIfNotExists();
mVelocityTracker.addMovement(ev);
mNestedYOffset = 0;
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
break;
case MotionEvent.ACTION_DOWN:
mLastMotionY = (int) ev.getY();
mActivePointerId = ev.getPointerId(0);
initOrResetVelocityTracker();
mVelocityTracker.addMovement(ev);
mScroller.computeScrollOffset();
mIsBeingDragged = !mScroller.isFinished();
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL);
break;
case MotionEvent.ACTION_CANCEL:
case MotionEvent.ACTION_UP:
mIsBeingDragged = false;
mActivePointerId = INVALID_POINTER;
recycleVelocityTracker();
if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0, getScrollRange())) {
ViewCompat.postInvalidateOnAnimation(this);
}
stopNestedScroll();
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
break;
}
return mIsBeingDragged;
}
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onTouchEvent(MotionEvent ev) {
initVelocityTrackerIfNotExists();
MotionEvent vtev = MotionEvent.obtain(ev);
final int actionMasked = ev.getActionMasked();
if (actionMasked == MotionEvent.ACTION_DOWN) {
mNestedYOffset = 0;
}
vtev.offsetLocation(0, mNestedYOffset);
switch (actionMasked) {
case MotionEvent.ACTION_DOWN:
if ((mIsBeingDragged = !mScroller.isFinished())) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
}
if (!mScroller.isFinished()) {
abortAnimatedScroll();
}
mLastMotionY = (int) ev.getY();
mActivePointerId = ev.getPointerId(0);
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_TOUCH);
break;
case MotionEvent.ACTION_MOVE:
final int activePointerIndex = ev.findPointerIndex(mActivePointerId);
if (activePointerIndex == -1) {
Log.e(TAG, "Invalid pointerId=" + mActivePointerId + " in onTouchEvent");
break;
}
final int y = (int) ev.getY(activePointerIndex);
int deltaY = mLastMotionY - y;
if (dispatchNestedPreScroll(0, deltaY, mScrollConsumed, mScrollOffset,
ViewCompat.TYPE_TOUCH)) {
deltaY -= mScrollConsumed[1];
mNestedYOffset += mScrollOffset[1];
}
if (!mIsBeingDragged && Math.abs(deltaY) > mTouchSlop) {
final ViewParent parent = getParent();
if (parent != null) {
parent.requestDisallowInterceptTouchEvent(true);
}
mIsBeingDragged = true;
if (deltaY > 0) {
deltaY -= mTouchSlop;
} else {
deltaY += mTouchSlop;
}
}
if (mIsBeingDragged) {
mLastMotionY = y - mScrollOffset[1];
final int oldY = getScrollY();
final int range = getScrollRange();
// Calling overScrollByCompat will call onOverScrolled, which
// calls onScrollChanged if applicable.
if (overScrollByCompat(0, deltaY, 0, oldY, 0, range, 0,
0, true) && !hasNestedScrollingParent(ViewCompat.TYPE_TOUCH)) {
mVelocityTracker.clear();
}
final int scrolledDeltaY = getScrollY() - oldY;
final int unconsumedY = deltaY - scrolledDeltaY;
mScrollConsumed[1] = 0;
dispatchNestedScroll(0, scrolledDeltaY, 0, unconsumedY, mScrollOffset,
ViewCompat.TYPE_TOUCH, mScrollConsumed);
mLastMotionY -= mScrollOffset[1];
mNestedYOffset += mScrollOffset[1];
}
break;
case MotionEvent.ACTION_UP:
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) velocityTracker.getYVelocity(mActivePointerId);
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
if (!dispatchNestedPreFling(0, -initialVelocity)) {
dispatchNestedFling(0, -initialVelocity, true);
fling(-initialVelocity);
}
} else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
getScrollRange())) {
ViewCompat.postInvalidateOnAnimation(this);
}
mActivePointerId = INVALID_POINTER;
endDrag();
break;
case MotionEvent.ACTION_CANCEL:
if (mIsBeingDragged) {
if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
getScrollRange())) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
mActivePointerId = INVALID_POINTER;
endDrag();
break;
case MotionEvent.ACTION_POINTER_DOWN:
final int index = ev.getActionIndex();
mLastMotionY = (int) ev.getY(index);
mActivePointerId = ev.getPointerId(index);
break;
case MotionEvent.ACTION_POINTER_UP:
onSecondaryPointerUp(ev);
mLastMotionY = (int) ev.getY(ev.findPointerIndex(mActivePointerId));
break;
}
if (mVelocityTracker != null) {
mVelocityTracker.addMovement(vtev);
}
vtev.recycle();
return super.onTouchEvent(ev);
}
private void abortAnimatedScroll() {
mScroller.abortAnimation();
stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
}
private void endDrag() {
mIsBeingDragged = false;
recycleVelocityTracker();
stopNestedScroll();
}
private void onSecondaryPointerUp(MotionEvent ev) {
final int pointerIndex = (ev.getAction() & MotionEvent.ACTION_POINTER_INDEX_MASK)
>> MotionEvent.ACTION_POINTER_INDEX_SHIFT;
final int pointerId = ev.getPointerId(pointerIndex);
if (pointerId == mActivePointerId) {
final int newPointerIndex = pointerIndex == 0 ? 1 : 0;
mLastMotionY = (int) ev.getY(newPointerIndex);
mActivePointerId = ev.getPointerId(newPointerIndex);
if (mVelocityTracker != null) {
mVelocityTracker.clear();
}
}
}
private void fling(int velocityY) {
int height = getHeight();
mScroller.fling(getScrollX(), getScrollY(), // start
0, velocityY, // velocities
0, 0, // x
Integer.MIN_VALUE, Integer.MAX_VALUE, // y
0, height / 2);
runAnimatedScroll(true);
}
private void runAnimatedScroll(boolean participateInNestedScrolling) {
if (participateInNestedScrolling) {
startNestedScroll(ViewCompat.SCROLL_AXIS_VERTICAL, ViewCompat.TYPE_NON_TOUCH);
} else {
stopNestedScroll(ViewCompat.TYPE_NON_TOUCH);
}
mLastScrollerY = getScrollY();
ViewCompat.postInvalidateOnAnimation(this);
}
@Override
public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
if (disallowIntercept) {
recycleVelocityTracker();
}
super.requestDisallowInterceptTouchEvent(disallowIntercept);
}
private void initOrResetVelocityTracker() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
} else {
mVelocityTracker.clear();
}
}
private void initVelocityTrackerIfNotExists() {
if (mVelocityTracker == null) {
mVelocityTracker = VelocityTracker.obtain();
}
}
private void recycleVelocityTracker() {
if (mVelocityTracker != null) {
mVelocityTracker.recycle();
mVelocityTracker = null;
}
}
@Override
protected boolean overScrollBy(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
// this is causing double scroll call (doubled speed), but this WebView isn't overscrollable
// all overscrolls are passed to appbar, so commenting this out during drag
if (!mIsBeingDragged)
overScrollByCompat(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY,
maxOverScrollX, maxOverScrollY, isTouchEvent);
// without this call webview won't scroll to top when url change or when user pick input
// (webview should move a bit making input still in viewport when "adjustResize")
return true;
}
int getScrollRange() {
//Using scroll range of webview instead of childs as NestedScrollView does.
return computeVerticalScrollRange();
}
@Override
public boolean isNestedScrollingEnabled() {
return mChildHelper.isNestedScrollingEnabled();
}
@Override
public void setNestedScrollingEnabled(boolean enabled) {
mChildHelper.setNestedScrollingEnabled(enabled);
}
@Override
public boolean startNestedScroll(int axes, int type) {
return mChildHelper.startNestedScroll(axes, type);
}
@Override
public boolean startNestedScroll(int axes) {
return startNestedScroll(axes, ViewCompat.TYPE_TOUCH);
}
@Override
public void stopNestedScroll(int type) {
mChildHelper.stopNestedScroll(type);
}
@Override
public void stopNestedScroll() {
stopNestedScroll(ViewCompat.TYPE_TOUCH);
}
@Override
public boolean hasNestedScrollingParent(int type) {
return mChildHelper.hasNestedScrollingParent(type);
}
@Override
public boolean hasNestedScrollingParent() {
return hasNestedScrollingParent(ViewCompat.TYPE_TOUCH);
}
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
int[] offsetInWindow) {
return dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
offsetInWindow, ViewCompat.TYPE_TOUCH);
}
@Override
public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
int[] offsetInWindow, int type) {
return mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
offsetInWindow, type);
}
@Override
public void dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed,
@Nullable int[] offsetInWindow, int type, @NonNull int[] consumed) {
mChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed,
offsetInWindow, type, consumed);
}
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
return dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, ViewCompat.TYPE_TOUCH);
}
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow, int type) {
return mChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
}
@Override
public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) {
return mChildHelper.dispatchNestedFling(velocityX, velocityY, false);
}
@Override
public boolean dispatchNestedPreFling(float velocityX, float velocityY) {
return mChildHelper.dispatchNestedPreFling(velocityX, velocityY);
}
@Override
public int getNestedScrollAxes() {
return ViewCompat.SCROLL_AXIS_VERTICAL;
}
@Override
public void computeScroll() {
if (mScroller.isFinished()) {
return;
}
mScroller.computeScrollOffset();
final int y = mScroller.getCurrY();
int unconsumed = y - mLastScrollerY;
mLastScrollerY = y;
// Nested Scrolling Pre Pass
mScrollConsumed[1] = 0;
dispatchNestedPreScroll(0, unconsumed, mScrollConsumed, null,
ViewCompat.TYPE_NON_TOUCH);
unconsumed -= mScrollConsumed[1];
if (unconsumed != 0) {
// Internal Scroll
final int oldScrollY = getScrollY();
overScrollByCompat(0, unconsumed, getScrollX(), oldScrollY, 0, getScrollRange(),
0, 0, false);
final int scrolledByMe = getScrollY() - oldScrollY;
unconsumed -= scrolledByMe;
// Nested Scrolling Post Pass
mScrollConsumed[1] = 0;
dispatchNestedScroll(0, 0, 0, unconsumed, mScrollOffset,
ViewCompat.TYPE_NON_TOUCH, mScrollConsumed);
unconsumed -= mScrollConsumed[1];
}
if (unconsumed != 0) {
abortAnimatedScroll();
}
if (!mScroller.isFinished()) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
// copied from NestedScrollView exacly as it looks, leaving overscroll related code, maybe future use
private boolean overScrollByCompat(int deltaX, int deltaY,
int scrollX, int scrollY,
int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY,
boolean isTouchEvent) {
final int overScrollMode = getOverScrollMode();
final boolean canScrollHorizontal =
computeHorizontalScrollRange() > computeHorizontalScrollExtent();
final boolean canScrollVertical =
computeVerticalScrollRange() > computeVerticalScrollExtent();
final boolean overScrollHorizontal = overScrollMode == View.OVER_SCROLL_ALWAYS
|| (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollHorizontal);
final boolean overScrollVertical = overScrollMode == View.OVER_SCROLL_ALWAYS
|| (overScrollMode == View.OVER_SCROLL_IF_CONTENT_SCROLLS && canScrollVertical);
int newScrollX = scrollX + deltaX;
if (!overScrollHorizontal) {
maxOverScrollX = 0;
}
int newScrollY = scrollY + deltaY;
if (!overScrollVertical) {
maxOverScrollY = 0;
}
/
| 归档时间: |
|
| 查看次数: |
1071 次 |
| 最近记录: |