java CollapsingToolbarLayout 无法识别滚动条
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/31795483/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
CollapsingToolbarLayout doesn't recognize scroll fling
提问by user1951516
I have created a simple CollapsingToolbarLayoutand it works like a charm. My problem is, that if I try to use a fling scroll on the nestedscrollview, it just stops when I release my finger. Normal scrolling works like it should.
我创建了一个简单的CollapsingToolbarLayout,它就像一个魅力。我的问题是,如果我尝试在Nestedscrollview上使用滑动滚动,它会在我松开手指时停止。正常滚动应该像它应该的那样工作。
My activities code is unchanged => autogenerated empty activity. (I just clicked on create new empty activity in android studio and edited the XML yet).
我的活动代码未更改 => 自动生成空活动。(我只是点击了在 android studio 中创建新的空活动并编辑了 XML)。
I read here, that scroll gestures on the imageview itself are buggy, but not, that the scrolling itself is buggy: see here.
我在这里读到,图像视图本身的滚动手势有问题,但不是,滚动本身有问题:请参阅此处。
I tried activating "smooth scrolling"through java code. It seems like if I scroll far enough that the imageview isn't visible anymore, fling gestures then are recognized.
我尝试通过 Java 代码激活“平滑滚动”。似乎如果我滚动到足够远的图像视图不再可见,然后就会识别出甩动手势。
TLDR:Why does the fling gesture not work as long as the imageview is visible ? My XML Code looks like this:
TLDR:为什么只要图像视图可见,甩动手势就不起作用?我的 XML 代码如下所示:
<android.support.design.widget.CoordinatorLayout
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:fitsSystemWindows="true">
<android.support.design.widget.AppBarLayout
android:id="@+id/profile_app_bar_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar"
android:fitsSystemWindows="true">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/profile_collapsing_toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:contentScrim="?attr/colorPrimary"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
android:fitsSystemWindows="true">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="420dp"
android:scaleType="centerCrop"
android:fitsSystemWindows="true"
android:src="@drawable/headerbg"
android:maxHeight="192dp"
app:layout_collapseMode="parallax"/>
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
app:layout_collapseMode="pin" />
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
app:layout_anchor="@id/profile_app_bar_layout"
app:layout_anchorGravity="bottom|right|end"
android:layout_height="@dimen/fab_size_normal"
android:layout_width="@dimen/fab_size_normal"
app:elevation="2dp"
app:pressedTranslationZ="12dp"
android:layout_marginRight="8dp"
android:layout_marginEnd="8dp"/>
<android.support.v4.widget.NestedScrollView
android:id="@+id/profile_content_scroll"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
app:layout_behavior="@string/appbar_scrolling_view_behavior"
android:layout_gravity="fill_vertical"
android:minHeight="192dp"
android:overScrollMode="ifContentScrolls"
>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/LoremIpsum"/>
</RelativeLayout>
</android.support.v4.widget.NestedScrollView>
</android.support.design.widget.CoordinatorLayout>
采纳答案by jinang
I had exactly the same issue with CollapsingToolbarLayoutwith ImageViewinside and NestedScrollView. The fling scroll stops when finger is released.
我在使用CollapsingToolbarLayout时遇到了完全相同的问题,其中包含ImageView和NestedScrollView。松开手指时,滑动滚动停止。
However, I've noticed something strange. If you start scrolling with your finger from a view with OnClickListener (e.g. Button), the fling scrolling works perfectly.
然而,我注意到了一些奇怪的事情。如果您从带有 OnClickListener(例如 Button)的视图开始用手指滚动,则可以完美地进行滚动。
Thus I fixed it with a weird solution. Set OnClickListener (that does nothing) on the direct child of NestedScrollView. Then it works perfectly!
因此,我用一个奇怪的解决方案修复了它。设置OnClickListener(什么都不做)上的直接孩子NestedScrollView。然后它完美地工作!
<android.support.v4.widget.NestedScrollView
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="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<!-- Page Content -->
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
Give the direct child (LinearLayout) an id and set OnClickListener in Activity
给直接孩子(LinearLayout)一个id并在Activity中设置OnClickListener
ViewGroup mContentContainer = (ViewGroup) findViewById(R.id.content_container);
mContentContainer.setOnClickListener(this);
@Override
public void onClick(View view) {
int viewId = view.getId();
}
Notes:
笔记:
Tested using Support Design Library 25.0.1
使用支持设计库 25.0.1 进行测试
CollapsingToolbarLayout with scrollFlags="scroll|enterAlwaysCollapsed"
CollapsingToolbarLayout with scrollFlags="scroll|enterAlwaysCollapsed"
回答by wchristiansen
I know this question was asked over a year ago but this issue still doesn't seem to be resolved in the Support/Design libraries. You can star thisissue so it moves farther up in the priority queue.
我知道这个问题是在一年前提出的,但这个问题似乎仍然没有在支持/设计库中得到解决。您可以为这个问题加注星标,使其在优先级队列中更靠前。
That said, I tried most of the posted solutions for this, including the one by patrick-iv with no success. The only way I was able to get to work was to mimic the fling and call it programmatically if a certain set of conditions were detected in onPreNestedScroll()
. In the few hours of my debugging I noticed that the onNestedFling()
was never called on an upward (scroll down) fling and seemed to be consumed prematurely. I can't say with 100% certainty this will work for 100% of implementations but it works well enough for my uses so I ended up settling for this, even though it's pretty hacky and definitely not what I wanted to do.
也就是说,我为此尝试了大多数已发布的解决方案,包括 patrick-iv 的解决方案,但都没有成功。我能够开始工作的唯一方法是模拟投掷,如果在onPreNestedScroll()
. 在我调试的几个小时中,我注意到onNestedFling()
从来没有在向上(向下滚动)投掷时调用并且似乎过早地消耗了。我不能 100% 肯定地说这将适用于 100% 的实现,但它对我的使用来说足够好,所以我最终解决了这个问题,即使它非常hacky而且绝对不是我想要做的。
public class NestedScrollViewBehavior extends AppBarLayout.Behavior {
// Lower value means fling action is more easily triggered
static final int MIN_DY_DELTA = 4;
// Lower values mean less velocity, higher means higher velocity
static final int FLING_FACTOR = 20;
int mTotalDy;
int mPreviousDy;
WeakReference<AppBarLayout> mPreScrollChildRef;
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
// Reset the total fling delta distance if the user starts scrolling back up
if(dy < 0) {
mTotalDy = 0;
}
// Only track move distance if the movement is positive (since the bug is only present
// in upward flings), equal to the consumed value and the move distance is greater
// than the minimum difference value
if(dy > 0 && consumed[1] == dy && MIN_DY_DELTA < Math.abs(mPreviousDy - dy)) {
mPreScrollChildRef = new WeakReference<>(child);
mTotalDy += dy * FLING_FACTOR;
}
mPreviousDy = dy;
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
View directTargetChild, View target, int nestedScrollAxes) {
// Stop any previous fling animations that may be running
onNestedFling(parent, child, target, 0, 0, false);
return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onStopNestedScroll(CoordinatorLayout parent, AppBarLayout abl, View target) {
if(mTotalDy > 0 && mPreScrollChildRef != null && mPreScrollChildRef.get() != null) {
// Programmatically trigger fling if all conditions are met
onNestedFling(parent, mPreScrollChildRef.get(), target, 0, mTotalDy, false);
mTotalDy = 0;
mPreviousDy = 0;
mPreScrollChildRef = null;
}
super.onStopNestedScroll(parent, abl, target);
}
}
And apply it to the AppBar
并将其应用到 AppBar
AppBarLayout scrollView = (AppBarLayout)findViewById(R.id.appbar);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)scrollView.getLayoutParams();
params.setBehavior(new NestedScrollViewBehavior());
回答by Raphael Royer-Rivard
I tried Floofer's solution but it was still not good enough for me. So I came up with a better version of his Behavior. The AppBarLayout now expands and collapses smoothly when flinging.
我尝试了 Floofer 的解决方案,但对我来说仍然不够好。所以我想出了他行为的更好版本。AppBarLayout 现在可以在投掷时平滑地展开和折叠。
Note:I used reflection to hack my way into this, so it may not work for perfectly with a version of Android Design library different from 25.0.0.
注意:我使用反射来解决这个问题,所以它可能无法完美地与不同于 25.0.0 的 Android 设计库版本一起工作。
public class SmoothScrollBehavior extends AppBarLayout.Behavior {
private static final String TAG = "SmoothScrollBehavior";
//The higher this value is, the faster the user must scroll for the AppBarLayout to collapse by itself
private static final int SCROLL_SENSIBILITY = 5;
//The real fling velocity calculation seems complex, in this case it is simplified with a multiplier
private static final int FLING_VELOCITY_MULTIPLIER = 60;
private boolean alreadyFlung = false;
private boolean request = false;
private boolean expand = false;
private int velocity = 0;
private int nestedScrollViewId;
public SmoothScrollBehavior(int nestedScrollViewId) {
this.nestedScrollViewId = nestedScrollViewId;
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child,
View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
if(Math.abs(dy) >= SCROLL_SENSIBILITY) {
request = true;
expand = dy < 0;
velocity = dy * FLING_VELOCITY_MULTIPLIER;
} else {
request = false;
}
}
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child,
View directTargetChild, View target, int nestedScrollAxes) {
request = false;
return super.onStartNestedScroll(parent, child, directTargetChild, target, nestedScrollAxes);
}
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, View target) {
if(request) {
NestedScrollView nestedScrollView = (NestedScrollView) coordinatorLayout.findViewById(nestedScrollViewId);
if (expand) {
//No need to force expand if it is already ready expanding
if (nestedScrollView.getScrollY() > 0) {
int finalY = getPredictedScrollY(nestedScrollView);
if (finalY <= 0) {
//since onNestedFling does not work to expand the AppBarLayout, we need to manually expand it
expandAppBarLayoutWithVelocity(coordinatorLayout, appBarLayout, velocity);
}
}
} else {
//onNestedFling will collapse the AppBarLayout with an animation time relative to the velocity
onNestedFling(coordinatorLayout, appBarLayout, target, 0, velocity, true);
if(!alreadyFlung) {
//TODO wait for AppBarLayout to be collapsed before scrolling for even smoother visual
nestedScrollView.fling(velocity);
}
}
}
alreadyFlung = false;
super.onStopNestedScroll(coordinatorLayout, appBarLayout, target);
}
private int getPredictedScrollY(NestedScrollView nestedScrollView) {
int finalY = 0;
try {
//With reflection, we can get the ScrollerCompat from the NestedScrollView to predict where the scroll will end
Field scrollerField = nestedScrollView.getClass().getDeclaredField("mScroller");
scrollerField.setAccessible(true);
Object object = scrollerField.get(nestedScrollView);
ScrollerCompat scrollerCompat = (ScrollerCompat) object;
finalY = scrollerCompat.getFinalY();
} catch (Exception e ) {
e.printStackTrace();
//If the reflection fails, it will return 0, which means the scroll has reached top
Log.e(TAG, "Failed to get mScroller field from NestedScrollView through reflection. Will assume that the scroll reached the top.");
}
return finalY;
}
private void expandAppBarLayoutWithVelocity(CoordinatorLayout coordinatorLayout, AppBarLayout appBarLayout, float velocity) {
try {
//With reflection, we can call the private method of Behavior that expands the AppBarLayout with specified velocity
Method animateOffsetTo = getClass().getSuperclass().getDeclaredMethod("animateOffsetTo", CoordinatorLayout.class, AppBarLayout.class, int.class, float.class);
animateOffsetTo.setAccessible(true);
animateOffsetTo.invoke(this, coordinatorLayout, appBarLayout, 0, velocity);
} catch (Exception e) {
e.printStackTrace();
//If the reflection fails, we fall back to the public method setExpanded that expands the AppBarLayout with a fixed velocity
Log.e(TAG, "Failed to get animateOffsetTo method from AppBarLayout.Behavior through reflection. Falling back to setExpanded.");
appBarLayout.setExpanded(true, true);
}
}
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {
alreadyFlung = true;
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
}
To use it, set a new Behavior to your AppBarLayout.
要使用它,请为您的 AppBarLayout 设置一个新的 Behavior。
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.app_bar);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
params.setBehavior(new SmoothScrollBehavior(R.id.nested_scroll_view));
回答by patrick.elmquist
This answersolved this problem for me. Create a custom AppBarLayout.Behavior
like this:
这个答案为我解决了这个问题。创建一个AppBarLayout.Behavior
这样的自定义:
public final class FlingBehavior extends AppBarLayout.Behavior {
private static final int TOP_CHILD_FLING_THRESHOLD = 3;
private boolean isPositive;
public FlingBehavior() {
}
public FlingBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
if (velocityY > 0 && !isPositive || velocityY < 0 && isPositive) {
velocityY = velocityY * -1;
}
if (target instanceof RecyclerView && velocityY < 0) {
final RecyclerView recyclerView = (RecyclerView) target;
final View firstChild = recyclerView.getChildAt(0);
final int childAdapterPosition = recyclerView.getChildAdapterPosition(firstChild);
consumed = childAdapterPosition > TOP_CHILD_FLING_THRESHOLD;
}
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed) {
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed);
isPositive = dy > 0;
}
}
and add it to the AppBarLayout
like this:
并将其添加到AppBarLayout
这样的:
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
app:layout_behavior="com.example.test.FlingBehavior">
回答by Kaushik NP
Am just posting this here so that others do not miss it in Comments. The answer by Jinangworks beautifully, but kudos to AntPachonfor pointing out a far simpler method for the same. Instead of implementing an OnClick
method on the Child of the NestedScrollView
programatically, a better way is to set clickable=true
in the xml for the child.
我只是在这里发布这个,这样其他人就不会在评论中错过它。通过回答Jinang工作很漂亮,但荣誉给AntPachon您指出了同样的一个简单得多的方法。而不是以编程方式在 xmlOnClick
上实现方法Child of the NestedScrollView
,更好的方法是clickable=true
在 xml 中为孩子设置。
(Using the same example as Jinang's)
(使用与济南相同的例子)
<android.support.v4.widget.NestedScrollView
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="@string/appbar_scrolling_view_behavior">
<LinearLayout
android:id="@+id/content_container"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:clickable="true" > <!-- new -->
<!-- Page Content -->
</LinearLayout>
</android.support.v4.widget.NestedScrollView>
回答by BIGGGFISH
case MotionEvent.ACTION_UP:
if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker,
mActivePointerId);
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
flingWithNestedDispatch(-initialVelocity);
} else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
getScrollRange())) {
ViewCompat.postInvalidateOnAnimation(this);
}
}
mActivePointerId = INVALID_POINTER;
endDrag();
break;
When i use a fling scroll on the NestedScrollView sometimes "mIsBeingDragged = false", So NestedScrollView dont dispatch fling event.
当我在 NestedScrollView 上使用滑动滚动时,有时会出现“mIsBeingDragged = false”,因此 NestedScrollView 不会调度滑动事件。
When i delete the if (mIsBeingDragged)
statement.
当我删除if (mIsBeingDragged)
语句时。
case MotionEvent.ACTION_UP:
//if (mIsBeingDragged) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
int initialVelocity = (int) VelocityTrackerCompat.getYVelocity(velocityTracker,
mActivePointerId);
if ((Math.abs(initialVelocity) > mMinimumVelocity)) {
flingWithNestedDispatch(-initialVelocity);
} else if (mScroller.springBack(getScrollX(), getScrollY(), 0, 0, 0,
getScrollRange())) {
ViewCompat.postInvalidateOnAnimation(this);
}
//}
mActivePointerId = INVALID_POINTER;
endDrag();
break;
there will be no problem.But I do not know what other serious problems will be caused
不会有问题。但是不知道还会引起什么严重的问题