当 recyclerview 为 scrolled/fling 时展开 appbarlayout 到顶部
Expand appbarlayout when recyclerview is scrolled/fling to top
我实现了一个带有 recyclerview 的 collapsingtoolbar 布局,如所附的示例代码所示。我的问题是,当我向下滑动列表时,它并没有一直到顶部。
发生的情况是,滚动恰好在 AppBarLayout 应该结束的位置停止。
我想要的效果是在向下滑动列表时,列表将一直到顶部并且 reveal/expand AppBarLayout
我的 minSdk 是 14。非常感谢任何帮助或建议。
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
app:layout_collapseMode="parallax">
//some elements
</LinearLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"/> //value android.support.design.widget.AppBarLayout$ScrollingViewBehavior
<android.support.v7.widget.Toolbar
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_collapseMode="parallax" />
您可以使用 setExpanded()
方法完全展开或折叠应用栏。一种实现可能涉及覆盖 Activity
class 中的 dispatchTouchEvent()
和 auto-collapsing/expanding 你的应用栏,这取决于它是否折叠超过了中点:
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
float per = Math.abs(mAppBarLayout.getY()) / mAppBarLayout.getTotalScrollRange();
boolean setExpanded = (per <= 0.5F);
mAppBarLayout.setExpanded(setExpanded, true);
}
return super.dispatchTouchEvent(event);
}
关于自动滚动到 fling 的最后位置,我在 GitHub 上放了一些代码,展示了如何 programmatically smooth scroll to a specific location that may help. Calling a scroll to list.size() - 1
on a fling
for instance could replicate the behaviour. Parts of this code by the way are adapted from the StylingAndroid and Novoda 博客:
public class RecyclerLayoutManager extends LinearLayoutManager {
private AppBarManager mAppBarManager;
private int visibleHeightForRecyclerView;
public RecyclerLayoutManager(Context context) {
super(context);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
View firstVisibleChild = recyclerView.getChildAt(0);
final int childHeight = firstVisibleChild.getHeight();
int distanceInPixels = ((findFirstVisibleItemPosition() - position) * childHeight);
if (distanceInPixels == 0) {
distanceInPixels = (int) Math.abs(firstVisibleChild.getY());
}
//Called Once
if (visibleHeightForRecyclerView == 0) {
visibleHeightForRecyclerView = mAppBarManager.getVisibleHeightForRecyclerViewInPx();
}
//Subtract one as adapter position 0 based
final int visibleChildCount = visibleHeightForRecyclerView/childHeight - 1;
if (position <= visibleChildCount) {
//Scroll to the very top and expand the app bar
position = 0;
mAppBarManager.expandAppBar();
} else {
mAppBarManager.collapseAppBar();
}
SmoothScroller smoothScroller = new SmoothScroller(recyclerView.getContext(), Math.abs(distanceInPixels), 1000);
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
public void setAppBarManager(AppBarManager appBarManager) {
mAppBarManager = appBarManager;
}
private class SmoothScroller extends LinearSmoothScroller {
private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
private final float distanceInPixels;
private final float duration;
public SmoothScroller(Context context, int distanceInPixels, int duration) {
super(context);
this.distanceInPixels = distanceInPixels;
float millisecondsPerPx = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
this.duration = distanceInPixels < TARGET_SEEK_SCROLL_DISTANCE_PX ?
(int) (Math.abs(distanceInPixels) * millisecondsPerPx) : duration;
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return RecyclerLayoutManager.this
.computeScrollVectorForPosition(targetPosition);
}
@Override
protected int calculateTimeForScrolling(int dx) {
float proportion = (float) dx / distanceInPixels;
return (int) (duration * proportion);
}
}
}
编辑:
上述代码片段中的AppBarManager
指的是一个interface
,用于与Activity
中的AppBarLayout
通信。 Collapse/expand 应用栏方法就是这样做的,带有动画。最后一种方法用于计算屏幕上可见的 RecyclerView
行数:
public interface AppBarManager {
void collapseAppBar();
void expandAppBar();
int getVisibleHeightForRecyclerViewInPx();
}
public class MainActivity extends AppCompatActivity implements AppBarManager{
@Override
public void collapseAppBar() {
mAppBarLayout.setExpanded(false, true);
}
@Override
public void expandAppBar() {
mAppBarLayout.setExpanded(true, true);
}
@Override
public int getVisibleHeightForRecyclerViewInPx() {
if (mRecyclerFragment == null) mRecyclerFragment =
(RecyclerFragment) getSupportFragmentManager().findFragmentByTag(RecyclerFragment.TAG);
int windowHeight, appBarHeight, headerViewHeight;
windowHeight = getWindow().getDecorView().getHeight();
appBarHeight = mAppBarLayout.getHeight();
headerViewHeight = mRecyclerFragment.getHeaderView().getHeight();
return windowHeight - (appBarHeight + headerViewHeight);
}
我有类似的问题,当 RecyclerView 跳到顶部时,我使用了一个简单的技巧来扩展 AppBarLayout(你需要有支持库 >= 23.x.x)
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int firstVisiblePosition = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
if (firstVisiblePosition == 0) {
mAppBarLayout.setExpanded(true, true);
}
}
}
});
我实现了一个带有 recyclerview 的 collapsingtoolbar 布局,如所附的示例代码所示。我的问题是,当我向下滑动列表时,它并没有一直到顶部。
发生的情况是,滚动恰好在 AppBarLayout 应该结束的位置停止。
我想要的效果是在向下滑动列表时,列表将一直到顶部并且 reveal/expand AppBarLayout
我的 minSdk 是 14。非常感谢任何帮助或建议。
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.AppBarLayout>
<android.support.design.widget.CollapsingToolbarLayout
app:layout_scrollFlags="scroll|exitUntilCollapsed">
<LinearLayout
app:layout_collapseMode="parallax">
//some elements
</LinearLayout>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
app:layout_behavior="@string/appbar_scrolling_view_behavior"/> //value android.support.design.widget.AppBarLayout$ScrollingViewBehavior
<android.support.v7.widget.Toolbar
app:popupTheme="@style/AppTheme.PopupOverlay"
app:layout_collapseMode="parallax" />
您可以使用 setExpanded()
方法完全展开或折叠应用栏。一种实现可能涉及覆盖 Activity
class 中的 dispatchTouchEvent()
和 auto-collapsing/expanding 你的应用栏,这取决于它是否折叠超过了中点:
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_UP) {
float per = Math.abs(mAppBarLayout.getY()) / mAppBarLayout.getTotalScrollRange();
boolean setExpanded = (per <= 0.5F);
mAppBarLayout.setExpanded(setExpanded, true);
}
return super.dispatchTouchEvent(event);
}
关于自动滚动到 fling 的最后位置,我在 GitHub 上放了一些代码,展示了如何 programmatically smooth scroll to a specific location that may help. Calling a scroll to list.size() - 1
on a fling
for instance could replicate the behaviour. Parts of this code by the way are adapted from the StylingAndroid and Novoda 博客:
public class RecyclerLayoutManager extends LinearLayoutManager {
private AppBarManager mAppBarManager;
private int visibleHeightForRecyclerView;
public RecyclerLayoutManager(Context context) {
super(context);
}
@Override
public void smoothScrollToPosition(RecyclerView recyclerView, RecyclerView.State state, int position) {
View firstVisibleChild = recyclerView.getChildAt(0);
final int childHeight = firstVisibleChild.getHeight();
int distanceInPixels = ((findFirstVisibleItemPosition() - position) * childHeight);
if (distanceInPixels == 0) {
distanceInPixels = (int) Math.abs(firstVisibleChild.getY());
}
//Called Once
if (visibleHeightForRecyclerView == 0) {
visibleHeightForRecyclerView = mAppBarManager.getVisibleHeightForRecyclerViewInPx();
}
//Subtract one as adapter position 0 based
final int visibleChildCount = visibleHeightForRecyclerView/childHeight - 1;
if (position <= visibleChildCount) {
//Scroll to the very top and expand the app bar
position = 0;
mAppBarManager.expandAppBar();
} else {
mAppBarManager.collapseAppBar();
}
SmoothScroller smoothScroller = new SmoothScroller(recyclerView.getContext(), Math.abs(distanceInPixels), 1000);
smoothScroller.setTargetPosition(position);
startSmoothScroll(smoothScroller);
}
public void setAppBarManager(AppBarManager appBarManager) {
mAppBarManager = appBarManager;
}
private class SmoothScroller extends LinearSmoothScroller {
private static final int TARGET_SEEK_SCROLL_DISTANCE_PX = 10000;
private final float distanceInPixels;
private final float duration;
public SmoothScroller(Context context, int distanceInPixels, int duration) {
super(context);
this.distanceInPixels = distanceInPixels;
float millisecondsPerPx = calculateSpeedPerPixel(context.getResources().getDisplayMetrics());
this.duration = distanceInPixels < TARGET_SEEK_SCROLL_DISTANCE_PX ?
(int) (Math.abs(distanceInPixels) * millisecondsPerPx) : duration;
}
@Override
public PointF computeScrollVectorForPosition(int targetPosition) {
return RecyclerLayoutManager.this
.computeScrollVectorForPosition(targetPosition);
}
@Override
protected int calculateTimeForScrolling(int dx) {
float proportion = (float) dx / distanceInPixels;
return (int) (duration * proportion);
}
}
}
编辑:
上述代码片段中的AppBarManager
指的是一个interface
,用于与Activity
中的AppBarLayout
通信。 Collapse/expand 应用栏方法就是这样做的,带有动画。最后一种方法用于计算屏幕上可见的 RecyclerView
行数:
public interface AppBarManager {
void collapseAppBar();
void expandAppBar();
int getVisibleHeightForRecyclerViewInPx();
}
public class MainActivity extends AppCompatActivity implements AppBarManager{
@Override
public void collapseAppBar() {
mAppBarLayout.setExpanded(false, true);
}
@Override
public void expandAppBar() {
mAppBarLayout.setExpanded(true, true);
}
@Override
public int getVisibleHeightForRecyclerViewInPx() {
if (mRecyclerFragment == null) mRecyclerFragment =
(RecyclerFragment) getSupportFragmentManager().findFragmentByTag(RecyclerFragment.TAG);
int windowHeight, appBarHeight, headerViewHeight;
windowHeight = getWindow().getDecorView().getHeight();
appBarHeight = mAppBarLayout.getHeight();
headerViewHeight = mRecyclerFragment.getHeaderView().getHeight();
return windowHeight - (appBarHeight + headerViewHeight);
}
我有类似的问题,当 RecyclerView 跳到顶部时,我使用了一个简单的技巧来扩展 AppBarLayout(你需要有支持库 >= 23.x.x)
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
@Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
int firstVisiblePosition = linearLayoutManager.findFirstCompletelyVisibleItemPosition();
if (firstVisiblePosition == 0) {
mAppBarLayout.setExpanded(true, true);
}
}
}
});