当 Listview 在 top/bottom 时将运动事件传递给父 Scrollview
Pass motion event to parent Scrollview when Listview at top/bottom
我在 ScrollView
中有一个 ListView
来显示评论,我想执行以下操作:
当用户向下滑动时,首先 ScrollView
应该完全向下滚动,因为列表位于底部。一旦完全向下,Listiew
应该开始滚动。
同样,当用户向上滚动时,首先 ListView
(此处顺序颠倒!)应该在 ScrollView
开始滚动之前向上滚动。
到目前为止,我已经完成了以下工作:
listView.setOnTouchListener(new View.OnTouchListener() {
// Setting on Touch Listener for handling the touch inside ScrollView
@Override
public boolean onTouch(View v, MotionEvent event) {
// If going up but the list is already at up, return false indicating we did not consume it.
if(event.getAction() == MotionEvent.ACTION_UP) {
if (listView.getChildCount() == 0 && listView.getChildAt(0).getTop() == 0) {
Log.e("Listview", "At top!");
return false;
}
}
// Similar behaviour but when going down check if we are at the bottom.
if( event.getAction() == MotionEvent.ACTION_DOWN) {
if (listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1 &&
listView.getChildAt(listView.getChildCount() - 1).getBottom() <= listView.getHeight()) {
Log.e("Listview","At bottom!");
v.getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
}
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
日志在正确的时刻触发,但是 ScrollView
不会移动,即使我 return false。
我也尝试在语句中添加 v.getParent().requestDisallowInterceptTouchEvent(false);
,但这也没有用。
我怎样才能让它发挥作用?
试试这个:
如果到达scrollview Bottom,首先查找。将 onScrollChanged 注册到您的滚动视图
@Override
protected void onScrollChanged(int l, int t, int oldl, int oldt)
{
// Grab the last child placed in the ScrollView, we need it to determinate the bottom position.
View view = (View) getChildAt(getChildCount()-1);
// Calculate the scrolldiff
int diff = (view.getBottom()-(getHeight()+getScrollY()));
// if diff is zero, then the bottom has been reached
if( diff == 0 )
{
// notify that we have reached the bottom
// Add code here to start scrolling the ListView
Log.d(ScrollTest.LOG_TAG, "MyScrollView: Bottom has been reached" );
}
super.onScrollChanged(l, t, oldl, oldt);
}
检查您是否到达了 ListView 的顶部。检查 firstVisibleItem 是否为 0:-
tableListView.setOnScrollListener(new OnScrollListener() {
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
if (visibleItemCount == 0)
// you have reached at top of lustView
{
java.lang.System.out.println("you have reached at top of lustView!");
}
else {
if ((firstVisibleItem + visibleItemCount) == totalItemCount) {
// put your stuff here
java.lang.System.out.println("end of the line reached!");
}
}
}
});
我希望这对你有用。如果遇到任何问题,请告诉我。
你不应该把 ListView 放在 ScrollView 里面,但是你可以使用 ExpandableHeightListView 来实现你的要求。这将使 ScrollView 内的全高 Listview。无需添加 TouchListener。
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<!-- your content -->
</RelativeLayout>
<my.widget.ExpandableHeightListView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
另一种实现您要求的方法是 RecyclerView 和 header。
您可以创建自定义 ScrollView
和 ListView
并覆盖
onInterceptTouchEvent(MotionEvent event)
像这样:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
View view = (View) getChildAt(getChildCount()-1);
int diff = (view.getBottom()-(getHeight()+getScrollY()));
if( event.getAction() == MotionEvent.ACTION_DOWN) {
if (diff ==0) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
这样,当您位于 ScrollView
底部时,在 ListView
上的每次向下触摸都会转到 ListView
。
这只是一个草图实现,但我认为您可以从这里开始,做同样的事情来检测您何时位于 ListView
的顶部
请注意,我会尝试使用 ListView
和您的当前内容 ScrollView
作为 ListView 的 header 添加的 listView.addHeaderView(scrollViewContent)
来实现更简单的实现].不知道合不合你的需求
编辑:
正在检测何时应该开始滚动 ScrollView
。在 ScrollView
中保留对 ListView
的引用。当用户向上滚动并且 ListView
位于顶部时,让 ScrollView
.
使用该事件
private void init(){
ViewConfiguration vc = ViewConfiguration.get(getContext());
mTouchSlop = vc.getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN:{
yPrec = event.getY();
}
case MotionEvent.ACTION_MOVE: {
final float dy = event.getY() - yPrec;
if (dy > mTouchSlop) {
// Start scrolling!
mIsScrolling = true;
}
break;
}
}
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsScrolling = false;
}
// Calculate the scrolldiff
View view = (View) getChildAt(getChildCount()-1);
int diff = (view.getBottom()-(getHeight()+getScrollY()));
if ((!mIsScrolling || !listViewReference.listIsAtTop()) && diff == 0) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsScrolling = false;
}
return super.onTouchEvent(ev);
}
public void setListViewReference(MyListView listViewReference) {
this.listViewReference = listViewReference;
}
ListView 中的方法 listIsAtTop 如下所示:
public boolean listIsAtTop() {
if(getChildCount() == 0) return true;
return (getChildAt(0).getTop() == 0 && getFirstVisiblePosition() ==0);
}
我在 ScrollView
中有一个 ListView
来显示评论,我想执行以下操作:
当用户向下滑动时,首先 ScrollView
应该完全向下滚动,因为列表位于底部。一旦完全向下,Listiew
应该开始滚动。
同样,当用户向上滚动时,首先 ListView
(此处顺序颠倒!)应该在 ScrollView
开始滚动之前向上滚动。
到目前为止,我已经完成了以下工作:
listView.setOnTouchListener(new View.OnTouchListener() {
// Setting on Touch Listener for handling the touch inside ScrollView
@Override
public boolean onTouch(View v, MotionEvent event) {
// If going up but the list is already at up, return false indicating we did not consume it.
if(event.getAction() == MotionEvent.ACTION_UP) {
if (listView.getChildCount() == 0 && listView.getChildAt(0).getTop() == 0) {
Log.e("Listview", "At top!");
return false;
}
}
// Similar behaviour but when going down check if we are at the bottom.
if( event.getAction() == MotionEvent.ACTION_DOWN) {
if (listView.getLastVisiblePosition() == listView.getAdapter().getCount() - 1 &&
listView.getChildAt(listView.getChildCount() - 1).getBottom() <= listView.getHeight()) {
Log.e("Listview","At bottom!");
v.getParent().requestDisallowInterceptTouchEvent(false);
return false;
}
}
v.getParent().requestDisallowInterceptTouchEvent(true);
return false;
}
});
日志在正确的时刻触发,但是 ScrollView
不会移动,即使我 return false。
我也尝试在语句中添加 v.getParent().requestDisallowInterceptTouchEvent(false);
,但这也没有用。
我怎样才能让它发挥作用?
试试这个:
如果到达scrollview Bottom,首先查找。将 onScrollChanged 注册到您的滚动视图
@Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { // Grab the last child placed in the ScrollView, we need it to determinate the bottom position. View view = (View) getChildAt(getChildCount()-1); // Calculate the scrolldiff int diff = (view.getBottom()-(getHeight()+getScrollY())); // if diff is zero, then the bottom has been reached if( diff == 0 ) { // notify that we have reached the bottom // Add code here to start scrolling the ListView Log.d(ScrollTest.LOG_TAG, "MyScrollView: Bottom has been reached" ); } super.onScrollChanged(l, t, oldl, oldt); }
检查您是否到达了 ListView 的顶部。检查 firstVisibleItem 是否为 0:-
tableListView.setOnScrollListener(new OnScrollListener() { public void onScrollStateChanged(AbsListView view, int scrollState) { } public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { if (visibleItemCount == 0) // you have reached at top of lustView { java.lang.System.out.println("you have reached at top of lustView!"); } else { if ((firstVisibleItem + visibleItemCount) == totalItemCount) { // put your stuff here java.lang.System.out.println("end of the line reached!"); } } } });
我希望这对你有用。如果遇到任何问题,请告诉我。
你不应该把 ListView 放在 ScrollView 里面,但是你可以使用 ExpandableHeightListView 来实现你的要求。这将使 ScrollView 内的全高 Listview。无需添加 TouchListener。
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<!-- your content -->
</RelativeLayout>
<my.widget.ExpandableHeightListView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
</ScrollView>
另一种实现您要求的方法是 RecyclerView 和 header。
您可以创建自定义 ScrollView
和 ListView
并覆盖
onInterceptTouchEvent(MotionEvent event)
像这样:
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
View view = (View) getChildAt(getChildCount()-1);
int diff = (view.getBottom()-(getHeight()+getScrollY()));
if( event.getAction() == MotionEvent.ACTION_DOWN) {
if (diff ==0) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
这样,当您位于 ScrollView
底部时,在 ListView
上的每次向下触摸都会转到 ListView
。
这只是一个草图实现,但我认为您可以从这里开始,做同样的事情来检测您何时位于 ListView
请注意,我会尝试使用 ListView
和您的当前内容 ScrollView
作为 ListView 的 header 添加的 listView.addHeaderView(scrollViewContent)
来实现更简单的实现].不知道合不合你的需求
编辑:
正在检测何时应该开始滚动 ScrollView
。在 ScrollView
中保留对 ListView
的引用。当用户向上滚动并且 ListView
位于顶部时,让 ScrollView
.
private void init(){
ViewConfiguration vc = ViewConfiguration.get(getContext());
mTouchSlop = vc.getScaledTouchSlop();
}
@Override
public boolean onInterceptTouchEvent(MotionEvent event) {
final int action = MotionEventCompat.getActionMasked(event);
switch (action) {
case MotionEvent.ACTION_DOWN:{
yPrec = event.getY();
}
case MotionEvent.ACTION_MOVE: {
final float dy = event.getY() - yPrec;
if (dy > mTouchSlop) {
// Start scrolling!
mIsScrolling = true;
}
break;
}
}
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsScrolling = false;
}
// Calculate the scrolldiff
View view = (View) getChildAt(getChildCount()-1);
int diff = (view.getBottom()-(getHeight()+getScrollY()));
if ((!mIsScrolling || !listViewReference.listIsAtTop()) && diff == 0) {
if (event.getAction() == MotionEvent.ACTION_MOVE) {
return false;
}
}
return super.onInterceptTouchEvent(event);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
final int action = MotionEventCompat.getActionMasked(ev);
if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP) {
mIsScrolling = false;
}
return super.onTouchEvent(ev);
}
public void setListViewReference(MyListView listViewReference) {
this.listViewReference = listViewReference;
}
ListView 中的方法 listIsAtTop 如下所示:
public boolean listIsAtTop() {
if(getChildCount() == 0) return true;
return (getChildAt(0).getTop() == 0 && getFirstVisiblePosition() ==0);
}