我们有一个相当复杂的布局,其中有CollapsingToolbarLayout,底部还有一个RecycerView。
在某些情况下,我们通过调用RecycerView上的setNestedScrollingEnabled(boolean)来临时禁用CollapsingToolbarLayout的展开/折叠。
这通常很好用。
<android.support.design.widget.CoordinatorLayout
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"
android:fitsSystemWindows="true"
tools:context="com.example.user.myapplication.ScrollingActivity">
<android.support.design.widget.AppBarLayout
android:id="@+id/app_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/app_bar_height"
android:fitsSystemWindows="true"
android:theme="@style/AppTheme.AppBarOverlay">
<android.support.design.widget.CollapsingToolbarLayout
android:id="@+id/toolbar_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
app:contentScrim="?attr/colorPrimary"
app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_collapseMode="pin"
app:popupTheme="@style/AppTheme.PopupOverlay"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.RecyclerView
android:id="@+id/nestedView"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior"/>
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal"
app:layout_anchor="@id/app_bar"
app:layout_anchorGravity="bottom|end">
<Button
android:id="@+id/disableNestedScrollingButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="disable"/>
<Button
android:id="@+id/enableNestedScrollingButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="enable"
/>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
ScrollingActivity.java
public class ScrollingActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
final RecyclerView nestedView = (RecyclerView) findViewById(R.id.nestedView);
findViewById(R.id.disableNestedScrollingButton).setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
nestedView.setNestedScrollingEnabled(false);
}
});
findViewById(R.id.enableNestedScrollingButton).setOnClickListener(new OnClickListener() {
@Override
public void onClick(final View v) {
nestedView.setNestedScrollingEnabled(true);
}
});
nestedView.setLayoutManager(new LinearLayoutManager(this));
nestedView.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)) {
};
}
@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;
}
});
}
}
起初我认为这是因为其他原因(我认为这是与Drawer布局的奇怪组合),但后来我找到了一个最小的示例来展示它,这和我想的一样:这都是因为SetNestedScrollingEnabled。
我试图在谷歌的网站(这里)上报道这个问题,希望如果它是一个真正的错误,它会得到修复。如果你想尝试一下,或者看这个问题的视频,去那里,因为我不能在这里上传他们所有(太大和太多的文件)。
有办法克服这一点吗?
是否有其他方法来调用setNestedScrollingEnabled的这个函数?一个没有任何滚动或锁定CollapsingToolbarLayout状态的问题?
这是实现与此答案相同目标的另一种方法。虽然那个答案使用了反射,但这个答案没有,但推理仍然是一样的。
为什么会这样?
问题是RecycerView
有时会对成员变量MSCrolloffSet
使用陈旧值。MScrolloffset
仅设置在RecycerView
中的两个位置:DispatchnestedPrescroll
和DispatchnestedScroll
。我们只关心dispatchnestedprescroll
。此方法在处理MotionEvent.action_move
事件时由RecycerView#OnTouchEvent
调用。
在此视图消耗嵌套滚动的任何部分之前,分派正在进行的嵌套滚动的一个步骤。
嵌套的预滚动事件是嵌套的滚动事件,触摸拦截是触摸。dispatchNestedPreScroll为嵌套滚动操作中的父视图提供了一个机会,可以在子视图使用部分或全部滚动操作之前使用该操作。
...
offsetinwindow
实际上是一个int[2]
,其中第二个索引表示由于嵌套滚动而应用于recycerview
的y移位。
RecycerView#DispatchNestedPrescroll
解析为NestedScrollingChildHelper
中同名的方法。
当RecycerView
调用DispatchnestedPrescroll
时,MScrolloffset
用作offsetinwindow
参数。因此,对offsetinwindow
所做的任何更改都直接更新mscrolloffset
。dispatchnestedprescroll
更新MScrolloffset
只要嵌套滚动有效。如果嵌套滚动无效,则MScrolloffset
不会更新,并且继续使用DispatchNestedPrescroll
上次设置的值。因此,当关闭嵌套滚动时,MScrolloffset
的值立即变得陈旧,但RecycerView
继续使用它。
从DispatchnestedPrescroll
返回时,MScrolloffset[1]
的正确值是为输入坐标跟踪
而调整的量(见上文)。在RecycerView
中,以下行调整y触摸坐标:
mLastTouchY = y - mScrollOffset[1];
假设MScrolloffset[1]
为-30(因为它已经过时,应该为零),那么MlastTouchy
将关闭+30像素(--30=+30)。这种误判的效果是,它将看起来触摸发生在屏幕下更远的地方,而不是真正的触摸。因此,缓慢的向下滚动实际上会向上滚动,向上滚动会更快。(如果向下滚动的速度足以克服这个30px
障碍,那么向下滚动将会发生,但速度会比应该发生的慢。)向上滚动会太快,因为应用程序认为已经覆盖了更多的空间。
MScrolloffset
将继续作为陈旧变量,直到打开嵌套滚动,并且DispatchnestedPrescroll
再次报告MScrolloffset
中的正确值。
方法
由于MScrolloffset[1]
在某些情况下有一个陈旧的值,因此目标是在这些情况下将其设置为正确的值。当没有发生嵌套滚动时,即当AppBar展开或折叠时,此值应为零。不幸的是,MScrolloffset
是RecycerView
本地的,没有设置器。为了在不诉诸反射的情况下获得对MScrolloffset
的访问权限,将创建一个自定义的RecycerView
,该自定义的DispatchnestedPrescroll
将重写。第四个变量是offsetinwindow
,这是我们需要更改的变量。
每当为RecycerView
禁用嵌套滚动时,就会出现陈旧的MScrolloffset
。我们将附加的一个条件是AppBar必须是空闲的,因此我们可以放心地说MScrolloffset[1]
应该为零。这不是问题,因为CollapsingToolbarLayout
在滚动标志中指定Snap
。
下面给出了ScrollingActivity
的更新源文件,以及自定义的RecycerView-MyRecycerView
。必须更改XML布局文件以反映自定义MyRecycerView
。
滚动活动
public class ScrollingActivity extends AppCompatActivity
implements MyRecyclerView.OnClampPrescrollOffsetListener {
private CollapsingToolbarLayout mCollapsingToolbarLayout;
private AppBarLayout mAppBarLayout;
private MyRecyclerView mNestedView;
// This variable will be true when the app bar is completely open or completely collapsed.
private boolean mAppBarIdle = true;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_scrolling);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
mNestedView = (MyRecyclerView) findViewById(R.id.nestedView);
mAppBarLayout = (AppBarLayout) findViewById(R.id.app_bar);
mCollapsingToolbarLayout = (CollapsingToolbarLayout) findViewById(R.id.toolbar_layout);
// Set the listener for the patch code.
mNestedView.setOnClampPrescrollOffsetListener(this);
// Listener to determine when the app bar is collapsed or fully open (idle).
mAppBarLayout.addOnOffsetChangedListener(new AppBarLayout.OnOffsetChangedListener() {
@Override
public final void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
mAppBarIdle = verticalOffset == 0
|| verticalOffset <= appBarLayout.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.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)) {
};
}
@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);
}
// Return "true" when the app bar is idle and nested scrolling is disabled. This is a signal
// to the custom RecyclerView to clamp the y prescroll offset to zero.
@Override
public boolean clampPrescrollOffsetListener() {
return mAppBarIdle && !mNestedView.isNestedScrollingEnabled();
}
private static final String TAG = "ScrollingActivity";
}
MyRecycerView
public class MyRecyclerView extends RecyclerView {
private OnClampPrescrollOffsetListener mPatchListener;
public MyRecyclerView(Context context) {
super(context);
}
public MyRecyclerView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyRecyclerView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// Just a call to super plus code to force offsetInWindow[1] to zero if the patchlistener
// instructs it.
@Override
public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) {
boolean returnValue;
int currentOffset;
returnValue = super.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow);
currentOffset = offsetInWindow[1];
Log.d(TAG, "<<<<dispatchNestedPreScroll: " + currentOffset);
if (mPatchListener.clampPrescrollOffsetListener() && offsetInWindow[1] != 0) {
Log.d(TAG, "<<<<dispatchNestedPreScroll: " + currentOffset + " -> 0");
offsetInWindow[1] = 0;
}
return returnValue;
}
public void setOnClampPrescrollOffsetListener(OnClampPrescrollOffsetListener patchListener) {
mPatchListener = patchListener;
}
public interface OnClampPrescrollOffsetListener {
boolean clampPrescrollOffsetListener();
}
private static final String TAG = "MyRecyclerView";
}
问题内容: 如何保护我们的Web应用程序免受XSS攻击?如果一个应用程序不进行任何特殊字符转换,就很容易受到攻击。 问题答案: 您应该先将所有输入转回HTML,然后再将其输出回给用户。一些参考: OWASP XSS(跨站点脚本)预防备忘单 考虑从Apache Commons Lang使用 或从Spring使用 JSP / Servlet Web应用程序中的XSS预防
问题内容: 现在,我正在尝试对汇编程序进行编码,但我不断收到此错误: 我目前有以下代码: 感谢帮助。 问题答案: 您通常使用默认值 如果key在字典中,则返回key的值,否则返回默认值。如果未提供default,则默认为None,因此此方法永远不会引发KeyError。 因此,当您使用循环时,将如下所示: 打印: 如果要显式检查键是否在字典中,则必须检查键是否在字典中(无索引!)。 例如: 但是该
我使用的是一个包含DATETIME列的SQLite数据库。jOOQ default将日期时间列绑定到java。sql。时间戳。查询包含DATETIME列的表会导致每列出现NumberFormatException(已处理)。 我使用jOOQ 3.11.9。 异常在parse方法,当它第一次尝试将时间戳字符串值转换为数字时。 查看和DefaultTimestampBinding方法时,时间戳总是ge
问题内容: 在我的一个Selenium测试案例中,我有一个问题,我不想拥有MouseOver效果。这是我的工作: 点击“登录”按钮(页面右上方) 等待页面加载 单击搜索结果中的“购买”按钮(页面右中角)。 问题是,在“登录”和“购买”之间的中间存在一个带有MouseOver效果的“购物篮”链接。因此,当我在“登录”按钮上调用Click(),然后在“购买”按钮上调用时,我会触发MouseOver,这
我听说过很多关于在Excel VBA中使用的憎恶是可以理解的,但我不确定如何避免使用它。我发现,如果我能够使用变量而不是函数,那么我的代码将更加可重用。但是,如果不使用,我不确定如何引用东西(如等)。 我已经找到了这篇关于范围的文章和这个关于不使用select的好处的例子,但是我找不到任何关于如何使用的东西。
问题内容: 这看起来很丑。可选选项的优点在这里完全消失了。我读到应该使用或代替。但是,如果我更换每个吸气剂,真的有好处吗? 与 您知道这里的一些常见或最佳做法吗? 问题答案: 您可以使用 关键是仅在可选函数不为空时才评估映射函数,否则结果将保留为空。如果可选为空,将返回。