Android ScrollView嵌套ListView正常分页加载显示解决方案

一般其他组件与ListView嵌合在一起滚动的方案有如下几种:

1.整个页面变为一个ListView,其他组件(如顶部)成为ListView的一个Item或者Header;

2.使用ScrollView嵌套ListView;

 

开发场景

 

某一app在1.0版本ActivityA页面已经包裹了一些内容组件,之后到了2.0版本,需要在当前页面下加一个可以滑动的ListView。这个时候当然首先想到的是,使用ScrollView嵌套ListView来实现。但在ScrollView嵌套ListView一般会有些问题出现,比如无法滑动、ListView只能显示一行等问题。本文将对在已有的页面中添加一个ListView进行分页加载的处理办法来进行阐述。

 

效果示意图

 

 

事例结构图

 

案例解析

根据示意图可以大概推测出实现方案:在一个ScrollView中包裹着ListView,当ListView加载完一组数据后再分页加载另外一组数据。

那么问题来了,为啥将ListView放进ScrollView后却只能显示一行呢?这个就需要在ListView中做些处理,自定义ListView代码如下:

 

public class ImbeddedListView extends ListView {
    private View footer;// 底部布局
    private boolean isLoading;// 判断是否正在加载中


    public ImbeddedListView(Context context) {
        super(context);
        initView(context);
    }

    public ImbeddedListView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initView(context);
    }

    public ImbeddedListView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        initView(context);
    }


    private void initView(Context context) {
        LayoutInflater inflater = LayoutInflater.from(context);
        footer = inflater.inflate(R.layout.listview_footer_view, null);
        footer.findViewById(R.id.loading_layout).setVisibility(View.GONE);
        this.addFooterView(footer);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int expandSpec = MeasureSpec.makeMeasureSpec(Integer.MAX_VALUE >> 2,
                MeasureSpec.AT_MOST);//Measure specification mode: The child can be as large as it wants up to the specified size.——>处理ScrollView嵌套ListView只显示一行的问题,此处让ListView所占的大小与要求的大小一样大
        super.onMeasure(widthMeasureSpec, expandSpec);
    }


    /**
     * 加载完成,1.设置标志;2.隐藏footer
     */
    public void loadComplete() {
        if (footer == null) {
            return;
        }
        isLoading = false;
        footer.findViewById(R.id.loading_layout).setVisibility(View.GONE);
    }

    /**
     * 开始加载,1.设置标志;2.显示footer
     */
    public void startLoading() {
        if (footer == null) {
            return;
        }
        isLoading = true;
        footer.findViewById(R.id.loading_layout).setVisibility(VISIBLE);
    }

    public boolean isLoading() {
        return isLoading;
    }
    
}

 

 

 


要点1:在onMeasure方法中将ListView所占的大小设置为最大。至于为什么是这个值,可以参考:详解嵌套ListView、ScrollView布局显示不全的问题

要点2:分页加载的显示、隐藏的时机。

    a.添加一个footer的布局,通过控制显示与隐藏来表示加载中与否的状态;

    b.提供公开的startLoading与loadComplete方法,供外部在适当时机时调用显示加载与否的状态。

 

所以,问题来了,什么时候是适当时机?

1.开始网络请求时(也就是页面滑动到最底部且还有数据需要加载时),就调用startLoading显示加载中的状态;

2.当网络请求加载完毕时,就调用loadComplete隐藏加载中的状态;

 

额,所以问题又来了,知道数据是否需要加载这个好判断(返回的接口一般会有个数据项的总数,对比下当前已经分页加载的数目就可以知道),但如何知道页面滑动至最底部了呢?

这个时候,就是需要自定义ScrollView来处理事件了:

 

public class PageListScrollView extends ScrollView {
    private OnScrollToBottomListener mOnScrollToBottomListener;


    public PageListScrollView(Context context) {
        super(context);
    }

    public PageListScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public PageListScrollView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }


    //滚动到底部时,clampedY变为true,此时将回调将状态传出去
    @Override
    protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
        super.onOverScrolled(scrollX, scrollY, clampedX, clampedY);
        if (scrollY != 0 && mOnScrollToBottomListener != null) {
            mOnScrollToBottomListener.onScrollBottomListener(clampedY);
        }
    }


    public void setOnScrollToBottomListener(OnScrollToBottomListener listener) {
        mOnScrollToBottomListener = listener;
    }

    public interface OnScrollToBottomListener {
        void onScrollBottomListener(boolean isBottom);
    }

}


从以上可以看出,通过在方法onOverScrolled方法中获取参数clampedY来判断当前ScrollView是否滑动至底部,然后借用自定义的接口将该状态传递出去(之后用于处理网络数据请求等等)。

 

 

主界面的代码

 

public class MainActivity extends AppCompatActivity implements PageListScrollView.OnScrollToBottomListener {
    private PageListScrollView scrollView;
    private ImbeddedListView commentLv;
    private CommentListViewAdapter commentListViewAdapter;
    private ArrayList<CommentEntity> commentEntityList;
    private int pagesize = 10;
    private int currentpage = 0;
    private boolean judgeCanLoadMore = true;
    private int totalCount = 50;//设置本次加载的数据的总数


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Fresco.initialize(this);
        setContentView(R.layout.activity_main);
        initViews();
    }

    private void initViews() {
        scrollView = (PageListScrollView) findViewById(R.id.scrollView);
        commentLv = (ImbeddedListView) findViewById(R.id.commentLv);
        commentLv.setFocusable(false);
        scrollView.setOnScrollToBottomListener(this);

        initData();
    }

    private void initData() {
        initAdapter(getCommentList());
    }

    private void initAdapter(ArrayList<CommentEntity> dataList) {
        if (commentListViewAdapter == null) {
            if (commentEntityList == null) {
                commentEntityList = new ArrayList<>();
            }
            commentEntityList.addAll(dataList);
            commentListViewAdapter = new CommentListViewAdapter(this, commentEntityList);
            commentLv.setAdapter(commentListViewAdapter);
            return;
        }

        if (dataList == null || dataList.size() == 0) {
            return;
        }
        commentEntityList.addAll(dataList);
        if (commentListViewAdapter != null) {
            commentListViewAdapter.notifyDataSetChanged();
        }
    }

    private void initLoadMoreTagOp() {
        if (totalCount == 0 || totalCount <= currentpage * pagesize) {//当前获取的数目大于等于总共的数目时表示数据加载完毕,禁止滑动
            judgeCanLoadMore = false;
            commentLv.loadComplete();
            return;
        }
    }


    //当ScrollView滑动至底部后,会回调此方法
    @Override
    public void onScrollBottomListener(boolean isBottom) {
        //模拟进行数据的分页加载
        if (judgeCanLoadMore && isBottom && !commentLv.isLoading()) {
            commentLv.startLoading();
            new Handler().postDelayed(new Runnable() {
                @Override
                public void run() {
                    initAdapter(getCommentList());
                    commentLv.loadComplete();
                    initLoadMoreTagOp();
                }
            }, 2000);//模拟网络请求,延缓2秒钟
        }
    }


    /**
     * 模拟网络请求后返回的数据
     * @return
     */
    private ArrayList<CommentEntity> getCommentList() {
        int currentpageCount = currentpage * pagesize;
        if (currentpageCount >= totalCount) {
            return null;
        }
        ArrayList<CommentEntity> list = new ArrayList<>();
        for (int i = currentpageCount + 1; i <= pagesize + currentpageCount; i++) {
            CommentEntity commentEntity = new CommentEntity(i + "", i + "位", "", "这是内容" + i, "2017年7月12日19:20", "2017年7月12日19:20");
            list.add(commentEntity);
        }
        currentpage++;
        return list;
    }
}

 

 

 

最后,GitHub地址>>

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ganshenml

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值