惯例上图
![](https://img.haomeiwen.com/i1822424/04a50bb5c151f136.gif)
好久没写文章了,最近弄了一个小需求,就是ScrollView里嵌套ListView,当然ListView是固定高度并且要求能上下滑动,而且在滑动到顶部或者底部的时候由ScrollView来拦截滑动事件。这也就是我们说的同向滑动冲突。
什么时候拦截?
上面我们已经说了,滑动到顶部或者底部的时候拦截
如何判断已经滑动到了顶部或者底部?
我们知道无论是ListView还是RecyclerView都是复用机制,啥是复用机制都不知道那我推荐你看看关于这类的文章。由于是复用,那么我们就不能直接用ListView.getChildAt(X)来获取,所以我们要根据系统提供给我们的方法来获取顶部和底部的View,然后通过这两个View来判断是否达到了顶部或者底部
获取顶部和底部的View
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
/**
* 从这里获取最后一个item的View和第一个item的View
*/
lastView = view.getChildAt(totalItemCount - firstVisibleItem - 1);
firstView = view.getChildAt(firstVisibleItem);
}
});
判断是否达到顶端或者底端
int action = motionEvent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
lastY = (int) motionEvent.getY();
} else if (action == MotionEvent.ACTION_MOVE) {
/**
* 这里判断是向下还是向上滑动
*/
if (lastY > motionEvent.getY()) {
isDown = true;
isUp = false;
} else {
isDown = false;
isUp = true;
}
}
if (lv != null) {
if (firstView != null && isUp) {
/**
* 如果ListView已经初始化并且firstView不为空
* 而且不是第一次加载视图(因为第一次加载视图getTop肯定为0)并且向上滑动
*
*/
if (firstView.getTop() == 0) {
flag = true;
} else {
flag = false;
}
}
if (lastView != null && isDown) {
/**
* 这里判断坐标,如果lastView的Bottom等于这个ListView的高度并且是往下滑
* 则把flag设置为true,当然这里还有一个坑,因为ListView是固定高度,所以所有的item高度之和都没有达到ListView的高度,那么这个也需要判断一下
*/
int childHeight = lv.getChildAt(0).getHeight();
int childCount = lv.getChildCount();
int totalChildHeight = childHeight * childCount;
if ((lastView.getBottom()) == view.getHeight() || totalChildHeight < view.getHeight()) {
flag = true;
} else {
flag = false;
}
isFirst = false;
}
}
这里我们还需要判断一下手势,达到了顶端并且是往上滑,达到了底部并且往下滑。
当然这里还有一个坑,那就是我们刚家在布局的时候getTop()是肯定等于0的,所以我们要判断一下是否是第一次加载
最后我们通过flag来判断一下是否让ScrollView来拦截事件
/**
* flag 为 true的时候 ScrollView开始获得事件
*/
if (flag && !isFirst) {
sv.requestDisallowInterceptTouchEvent(false);
} else {
sv.requestDisallowInterceptTouchEvent(true);
}
这样一来也就结束了,放上全部代码
public class MainActivity extends AppCompatActivity {
List<String> mData;
ListView lv;
ArrayAdapter<String> adapter;
ScrollView sv;
boolean flag, isFirst = true;
boolean isUp, isDown;
int lastY;
View lastView, firstView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mData = new ArrayList<>();
for (int i = 0; i < 10; i++) {
mData.add("ad " + i);
}
tv1 = (TextView) findViewById(R.id.tv1);
sv = (ScrollView) findViewById(R.id.sv);
lv = (ListView) findViewById(R.id.lv);
adapter = new ArrayAdapter<>(this, android.R.layout.simple_expandable_list_item_1, mData);
lv.setAdapter(adapter);
lv.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
sv.requestDisallowInterceptTouchEvent(true);
int action = motionEvent.getAction();
if (action == MotionEvent.ACTION_DOWN) {
lastY = (int) motionEvent.getY();
} else if (action == MotionEvent.ACTION_MOVE) {
/**
* 这里判断是向下还是向上滑动
*/
if (lastY > motionEvent.getY()) {
isDown = true;
isUp = false;
} else {
isDown = false;
isUp = true;
}
}
if (lv != null) {
if (firstView != null && isUp) {
/**
* 如果ListView已经初始化并且firstView不为空
* 而且不是第一次加载视图(因为第一次加载视图getTop肯定为0)并且向上滑动
*
*/
if (firstView.getTop() == 0) {
flag = true;
} else {
flag = false;
}
}
if (lastView != null && isDown) {
/**
* 这里判断坐标,如果lastView的Bottom等于这个ListView的高度并且是往下滑
* 则把flag设置为true
*/
int childHeight = lv.getChildAt(0).getHeight();
int childCount = lv.getChildCount();
int totalChildHeight = childHeight * childCount;
if ((lastView.getBottom()) == view.getHeight() || totalChildHeight < view.getHeight()) {
flag = true;
} else {
flag = false;
}
isFirst = false;
}
}
/**
* flag 为 true的时候 ScrollView开始获得事件
*/
if (flag && !isFirst) {
sv.requestDisallowInterceptTouchEvent(false);
} else {
sv.requestDisallowInterceptTouchEvent(true);
}
return false;
}
});
lv.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
/**
* 从这里获取最后一个item的View和第一个item的View
*/
lastView = view.getChildAt(totalItemCount - firstVisibleItem - 1);
firstView = view.getChildAt(firstVisibleItem);
}
});
}
}
最后
爱小丽,爱Android
网友评论