问题
ScrollView和ListView都具有滑动的功能,如果在ScrollView中嵌套ListView,就会出现滑动时间冲突,那如果非要在ScrollVIew中嵌套ListView,又该怎么做呢?下文将提出一种解决方案,在ScrollView中嵌套上拉加载的ListView。
解决思路
既然我们知道在ScrollView中嵌套ListView会出现滑动事件冲突,所以解决的办法就是对事件的分发拦截等进行处理。
首先我们先自定义一个上拉加载的DropLoadListView,代码如下:
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.widget.AbsListView;
import android.widget.ListView;
import com.example.mahui.stop.R;
/**
* Created by mahui on 16/12/31.
* 创建上拉加载的listview
*/
public class DropLoadListView extends ListView implements AbsListView.OnScrollListener {
private Context context;
private View footer;
private int totalItemCount;//总数量
private int lastItemCount;//最后一个
private boolean isLoading;//正在加载
private ILoadListener iLoadListener;
public DropLoadListView(Context context) {
super(context);
this.context = context;
initVew();
initEvent();
}
/**
* 事件监听
*/
private void initEvent() {
this.setOnScrollListener(this);
}
public DropLoadListView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
initVew();
initEvent();
}
public DropLoadListView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
this.context = context;
initVew();
initEvent();
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
return super.dispatchTouchEvent(ev);
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
if(!isLoading){
//上拉
this.requestDisallowInterceptTouchEvent(false);
this.getParent().getParent().requestDisallowInterceptTouchEvent(true);
}else{
//下滑
this.requestDisallowInterceptTouchEvent(true);
this.getParent().getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
return super.onTouchEvent(ev);
}
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
if(this.getAdapter().getCount()>1){
if (totalItemCount == lastItemCount && scrollState == SCROLL_STATE_IDLE) {
if (!isLoading) {
isLoading = true;
footer.findViewById(R.id.id_load_progressbar).setVisibility(View.VISIBLE);
footer.findViewById(R.id.id_load_text).setVisibility(View.VISIBLE);
iLoadListener.onLoad();
//加载更多数据
}
}
}
}
/**
* @param view
* @param firstVisibleItem 当前屏幕上显示的第一个item的位置,初始为0
* @param visibleItemCount 当前屏幕上显示的item数
* @param totalItemCount 总共的item数
*/
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
this.lastItemCount = firstVisibleItem + visibleItemCount;
this.totalItemCount = totalItemCount;
}
/**
* 初始化
*/
private void initVew() {
LayoutInflater layoutInflater = LayoutInflater.from(context);
footer = layoutInflater.inflate(R.layout.load, null);
//一开始隐藏
footer.findViewById(R.id.id_load_progressbar).setVisibility(View.GONE);
this.addFooterView(footer);
}
public void setInterface(ILoadListener iLoadListener){
this.iLoadListener=iLoadListener;
}
/**
* 加载更多数据的回调接口
*/
public interface ILoadListener{
public void onLoad();
}
// 加载完成通知隐藏
public void loadComplete() {
isLoading = false;
footer.findViewById(R.id.id_load_progressbar).setVisibility(View.GONE);
footer.findViewById(R.id.id_load_text).setVisibility(View.GONE);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
MeasureSpec.AT_MOST);
super.onMeasure(widthMeasureSpec, expandSpec);
}
}
首先我们得先会自定义上拉加载的ListView,在这里我们不做过多的讲解,我们主要看如下方法:
public boolean onTouchEvent(MotionEvent ev) {
switch (ev.getAction()){
case MotionEvent.ACTION_MOVE:
if(!isLoading){
//上拉
this.requestDisallowInterceptTouchEvent(false);
this.getParent().getParent().requestDisallowInterceptTouchEvent(true);
}else{
//下滑
this.requestDisallowInterceptTouchEvent(true);
this.getParent().getParent().requestDisallowInterceptTouchEvent(false);
}
break;
}
return super.onTouchEvent(ev);
}
该方法是处理触摸事件的,我们通过判断此刻是否是上拉加载,然后进行事件分发处理。在此ListView中,我通过判断totalItemCount == lastItemCount && scrollState == SCROLL_STATE_IDLE是否满足条件,满足则代表我是上拉加载,此时设置isLoading=true,表示要加载,然后在触摸事件中判断是否要加载,如果是,则让当前view对该事件进行拦截,并让ScrollView不要拦截,让当前view消费此事件;如果不是上拉加载,即表示我要滑动整个ScrollView,让当前ListView不拦截,让ScrollView进行拦截,已达到此目的,在该模块处理时,最重要的方法就是requestDisallowInterceptTouchEvent(true),不允许拦截;requestDisallowInterceptTouchEvent(flase),允许拦截;;
效果