CollapsingToolbarLayout이 스크롤 플링을 인식하지 않습니다.
심플한 Collapsing Toolbar Layout을 작성했습니다만, 매우 효과적입니다.문제는 네스트스크롤뷰에서 플링스크롤을 사용하려고 하면 손가락을 떼면 정지한다는 것입니다.일반 스크롤은 정상적으로 작동합니다.
활동코드는변경되지않았습니다=>자동생성빈 활동(안드로이드 스튜디오에서 빈 액티비티 새로 만들기를 클릭해서 XML을 편집했습니다).
이미지 뷰의 스크롤 제스처 자체는 버그가 있지만 스크롤 자체가 버그가 있는 것은 아닙니다.여기를 참조해 주십시오.
자바 코드를 통해 "스무스 스크롤"을 활성화하려고 했습니다.이미지 뷰가 보이지 않을 정도로 스크롤하면 슬로 제스처가 인식되는 것 같습니다.
TLDR: 이미지 뷰가 표시되어 있는 한 fling 제스처가 작동하지 않는 이유는 무엇입니까?내 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>
ImageView를 내장한 Collapsing Toolbar Layout과 Nested Scroll View에서도 동일한 문제가 발생하였습니다.손가락을 떼면 플링 스크롤이 정지합니다.
그런데 뭔가 이상한 걸 발견했어요.OnClickListener(예를 들어 버튼)를 사용하여 뷰에서 손가락으로 스크롤을 시작하면 플링 스크롤이 완벽하게 작동합니다.
그래서 이상한 해결책으로 고쳤습니다.Nested Scroll View의 직계 자녀에 OnClickListener(아무것도 하지 않음)를 설정합니다.그럼 완벽하게 작동하겠군!
<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>
직접 자식(LinearLayout)에게 ID를 지정하고 활동에서 OnClickListener를 설정합니다.
ViewGroup mContentContainer = (ViewGroup) findViewById(R.id.content_container);
mContentContainer.setOnClickListener(this);
@Override
public void onClick(View view) {
int viewId = view.getId();
}
주의:
Support Design Library 25.0.1을 사용하여 테스트 완료
CollapsingToolbarLayout with scrollFlags="scroll|enterAlwaysCollapsed"
이 질문은 1년 이상 전에 한 것으로 알고 있습니다만, Support/Design 라이브러리에서는 아직 이 문제가 해결되지 않은 것 같습니다.이 문제는 priority 큐에서 더 위로 이동하도록 스타링할 수 있습니다.
그러나 patrick-iv의 솔루션을 포함하여 대부분의 게시된 솔루션을 시도했지만 성공하지 못했습니다.을 할 수 하여 썸에서 이 검출되었을 이었습니다. 약약면면면면면면면면면면면onPreNestedScroll()
몇 동안 하고 있는 「 」가 「 」라고 하는 을 알게 .onNestedFling()
상승(하강) 튕겨져 나온 적이 없고 너무 일찍 소비된 것 같습니다.이것이 100% 실장에서는 효과가 있다고는 말할 수 없지만, 제 사용법에서는 충분히 효과가 있기 때문에, 꽤 진부하고, 확실히 하고 싶은 것은 아니지만, 결국 이 방법을 택하게 되었습니다.
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);
}
}
앱바에 적용하다
AppBarLayout scrollView = (AppBarLayout)findViewById(R.id.appbar);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams)scrollView.getLayoutParams();
params.setBehavior(new NestedScrollViewBehavior());
Cheese Square 데모:비포 애프터
나는 Floofer의 해결책을 시도했지만 여전히 나에게 충분하지 않았다.그래서 더 나은 버전의 '행동'을 생각해냈죠AppBarLayout은 플립 시 부드럽게 확장 및 축소됩니다.
주의: 리플렉션으로 해킹을 했기 때문에 25.0.0과는 다른 버전의 Android Design 라이브러리에서 완벽하게 동작하지 않을 수 있습니다.
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);
}
}
이를 사용하려면 AppBarLayout에 새 동작을 설정하십시오.
AppBarLayout appBarLayout = (AppBarLayout) findViewById(R.id.app_bar);
CoordinatorLayout.LayoutParams params = (CoordinatorLayout.LayoutParams) appBarLayout.getLayoutParams();
params.setBehavior(new SmoothScrollBehavior(R.id.nested_scroll_view));
이 답변으로 이 문제가 해결되었습니다.커스텀 작성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;
}
}
에 추가합니다.AppBarLayout
다음과 같습니다.
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
...
app:layout_behavior="com.example.test.FlingBehavior">
다른 사람들이 댓글에서 놓치지 않도록 여기에 게시합니다.Jinang의 답변은 훌륭하게 먹혀들었지만, AntPachon은 같은 방법을 훨씬 더 간단하게 지적해 준 것에 찬사를 보냈다.실장하는 대신OnClick
의 메서드Child of the NestedScrollView
프로그램적으로, 더 나은 방법은,clickable=true
xml로 입력합니다.
(Jinang의 예와 같은 예)
<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>
코드 : https://android.googlesource.com/platform/frameworks/support/+/master/core-ui/java/android/support/v4/widget/NestedScrollView.java#834
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;
NestedScrollView에서 플링 스크롤을 사용하는 경우 "mIsBeingDragged = false"이므로 So NestedScrollView는 플링 이벤트를 디스패치하지 않습니다.
삭제 시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;
아무 문제 없을 거야.하지만 나는 어떤 다른 심각한 문제가 발생할지 모른다.
언급URL : https://stackoverflow.com/questions/31795483/collapsingtoolbarlayout-doesnt-recognize-scroll-fling
'programing' 카테고리의 다른 글
자 컴포넌트 내의 이미지의 :src를 바인드하려면 어떻게 해야 합니까? (0) | 2023.01.27 |
---|---|
마리아DB 10센트OS 7 데이터 이동 문제 (0) | 2023.01.27 |
버튼을 클릭하면 열리는 새 브라우저 창으로 전환하는 방법 (0) | 2023.01.27 |
Importorr: libmariadbclient.so18: 공유 개체 파일을 열 수 없습니다.해당 파일 또는 디렉터리가 없습니다. (0) | 2023.01.27 |
이름이 없는 로더 'app' 모듈에 있으므로 클래스에 캐스팅할 수 없습니다. (0) | 2023.01.27 |