使现代化
我稍微修改了代码以解决剩余的问题-至少是我可以复制的问题。关键的更新是处理
dy
仅当AppBar展开或折叠时。在第一次迭代中,
dispatchNestedPreScroll()
在未检查AppBar的状态是否为折叠状态的情况下处理滚动。
这个答案解决了关于以下方面的问题:
RecyclerView
我给出的另一个答案仍然适用于这里。
回收视图
NestedScrollView
在支持库的26.0.0-beta2中引入了。
以下代码基于
this answer
但包括对AppBar不稳定行为的修复。我已经删除了修复奇怪滚动的代码,因为它似乎不再需要了。
AppBarTracking.java
public interface AppBarTracking {
boolean isAppBarIdle();
boolean isAppBarExpanded();
}
MyRecyclerView.java
public class MyRecyclerView extends RecyclerView {
public MyRecyclerView(Context context) {
this(context, null);
}
public MyRecyclerView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public MyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
private AppBarTracking mAppBarTracking;
private View mView;
private int mTopPos;
private LinearLayoutManager mLayoutManager;
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow,
int type) {
// App bar latching trouble is only with this type of movement when app bar is expanded
// or collapsed. In touch mode, everything is OK regardless of the open/closed status
// of the app bar.
if (type == ViewCompat.TYPE_NON_TOUCH && mAppBarTracking.isAppBarIdle()
&& isNestedScrollingEnabled()) {
// Make sure the AppBar stays expanded when it should.
if (dy > 0) { // swiped up
if (mAppBarTracking.isAppBarExpanded()) {
// Appbar can only leave its expanded state under the power of touch...
consumed[1] = dy;
return true;
}
} else { // swiped down (or no change)
// Make sure the AppBar stays collapsed when it should.
// Only dy < 0 will open the AppBar. Stop it from opening by consuming dy if needed.
mTopPos = mLayoutManager.findFirstVisibleItemPosition();
if (mTopPos == 0) {
mView = mLayoutManager.findViewByPosition(mTopPos);
if (-mView.getTop() + dy <= 0) {
// Scroll until scroll position = 0 and AppBar is still collapsed.
consumed[1] = dy - mView.getTop();
return true;
}
}
}
}
boolean returnValue = super.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow, type);
// Fix the scrolling problems when scrolling is disabled. This issue existed prior
// to 26.0.0-beta2.
if (offsetInWindow != null && !isNestedScrollingEnabled() && offsetInWindow[1] != 0) {
offsetInWindow[1] = 0;
}
return returnValue;
}
@Override
public void setLayoutManager(RecyclerView.LayoutManager layout) {
super.setLayoutManager(layout);
mLayoutManager = (LinearLayoutManager) getLayoutManager();
}
public void setAppBarTracking(AppBarTracking appBarTracking) {
mAppBarTracking = appBarTracking;
}
@SuppressWarnings("unused")
private static final String TAG = "MyRecyclerView";
}
public class ScrollingActivity extends AppCompatActivity
implements AppBarTracking {
private MyRecyclerView mNestedView;
private int mAppBarOffset;
private boolean mAppBarIdle = false;
private int mAppBarMaxOffset;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mNestedView = findViewById(R.id.nestedView);
final AppBarLayout appBar = findViewById(R.id.app_bar);
appBar.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public final void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
mAppBarOffset = verticalOffset;
// mAppBarOffset = 0 if app bar is expanded; If app bar is collapsed then
// mAppBarOffset = mAppBarMaxOffset
// mAppBarMaxOffset is always <=0 (-AppBarLayout.getTotalScrollRange())
// mAppBarOffset should never be > zero or less than mAppBarMaxOffset
mAppBarIdle = (mAppBarOffset >= 0) || (mAppBarOffset <= mAppBarMaxOffset);
}
});
appBar.post(new Runnable() {
@Override
public void run() {
mAppBarMaxOffset = -appBar.getTotalScrollRange();
}
});
findViewById(R.id.disableNestedScrollingButton).setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
// If the AppBar is fully expanded or fully collapsed (idle), then disable
// expansion and apply the patch; otherwise, set a flag to disable the expansion
// and apply the patch when the AppBar is idle.
setExpandEnabled(false);
}
});
findViewById(R.id.enableNestedScrollingButton).setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
setExpandEnabled(true);
}
});
mNestedView.setAppBarTracking(this);
mNestedView.setLayoutManager(new LinearLayoutManager(this));
mNestedView.setAdapter(new Adapter() {
@Override
public ViewHolder onCreateViewHolder(final ViewGroup parent, final int viewType) {
return new ViewHolder(LayoutInflater.from(parent.getContext()).inflate(
android.R.layout.simple_list_item_1,
parent,
false)) {
};
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
((TextView) holder.itemView.findViewById(android.R.id.text1)).setText("item " + position);
}
@Override
public int getItemCount() {
return 100;
}
});
}
private void setExpandEnabled(boolean enabled) {
mNestedView.setNestedScrollingEnabled(enabled);
}
@Override
public boolean isAppBarExpanded() {
return mAppBarOffset == 0;
}
@Override
public boolean isAppBarIdle() {
return mAppBarIdle;
}
@SuppressWarnings("unused")
private static final String TAG = "ScrollingActivity";
}
这里发生了什么?
从问题中可以明显看出,当用户的手指不在屏幕上时,布局无法按应有的方式关闭或打开应用程序栏。拖动时,应用程序栏的行为与它应该的一样。
dispatchNestedPreScroll()
有一个新的
type
论点这个
类型
dx
和
dy公司
是由于用户触摸屏幕
ViewCompat.TYPE_TOUCH
ViewCompat.TYPE_NON_TOUCH
.
虽然没有确定导致问题的具体代码,但解决方法是消除垂直移动
dy公司
)当需要时,不要让垂直运动传播。实际上,应用程序栏在扩展时将锁定到位,并且在通过触摸手势关闭之前,不允许开始关闭。应用程序栏也将在关闭时锁定,直到
位于其最顶端,并且有足够的
在执行触摸手势时打开应用程序栏。
因此,这与其说是一种修复,不如说是一种对问题条件的劝阻。
MyRecyclerView
该代码处理在本文中识别的问题
question
当嵌套滚动被禁用时,处理不正确的滚动移动。这是在调用
dispatchNestedPreScroll()
这改变了
offsetInWindow[1]
offsetInWindow
有时为空。幸运的是,当它重要时,它似乎是非空的,所以最后一部分继续工作。
需要注意的是,这个“修复”非常特定于所提出的问题,不是一个通用的解决方案。修复可能会有很短的保质期,因为我预计这样一个明显的问题很快就会得到解决。