前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >基于SwipeRefreshLayout的上拉加载控件

基于SwipeRefreshLayout的上拉加载控件

作者头像
全栈程序员站长
发布于 2022-09-12 12:06:34
发布于 2022-09-12 12:06:34
95400
代码可运行
举报
运行总次数:0
代码可运行

大家好,又见面了,我是你们的朋友全栈君。

距离上一篇博客,居然已经过了大半年的时间,时间过得真快啊!CSDN最近大改版,各种用户体验也是被无数人吐槽,让人提不起任何写博客的兴趣,不过,该写的博客还是必须得写,话不多话,直接进入正题。 现在项目中用列表来展示数据比比皆是,ListView和RecyclerView大家也是耳熟能详。实际项目中,后台肯定的接口肯定都是分页的,那么,分页加载也是自然而然的事,下面基于Google原生的下拉刷新控件SwipeRefreshLayout,实现上拉加载更多的功能。直接上代码: CustomSwipeRefreshLayout.java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.jackie.sample.custom_view;

import android.content.Context;
import android.support.v4.widget.SwipeRefreshLayout;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.ListView;

import com.jackie.sample.R;
import com.jackie.sample.utils.LogUtils;

/** * Created by Jackie on 2018/1/11 * 自定义下拉刷新上拉加载控件 目前只支持ListView */
public class CustomSwipeRefreshLayout extends SwipeRefreshLayout implements OnScrollListener { 
   
    private Context mContext;

    // 滑动到最下面时的上拉操作
    private int mTouchSlop;

    // ListView
    private ListView mListView;

    // 上拉监听器, 到了最底部的上拉加载操作
    private OnLoadListener mOnLoadListener;
    private OnScrollListener mOnScrollListener;
    private OnScrollStateChangeListener mOnScrollStateChangeListener;

    // ListView的加载中FooterView
    private View mFooterView;

    // 按下时的y坐标
    private int mDownY;
    // 移动时的y坐标, 与mDownY一起用于滑动到底部时判断是上拉还是下拉
    private int mMoveY;
    // 是否在加载中 (上拉加载更多)
    private boolean mIsLoading = false;

    private int mCanLoadCount = 6;
    private int mStartLoadCount = 3;

    public CustomSwipeRefreshLayout(Context context) {
        this(context, null);
    }

    public CustomSwipeRefreshLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        initView(context);
    }

    private void initView(Context context) {
        mContext = context;

        // 表示滑动的时候,手的移动要大于这个距离才开始移动控件。如果小于这个距离就不触发移动控件,如ViewPager就是用这个距离来判断用户是否翻页
        mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        // 设置下拉progress的开始位置和结束位置
// setProgressViewOffset(false, DensityUtils.dp2px(context, 20), DensityUtils.dp2px(context, 80));
    }

    @Override  
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {  
        super.onLayout(changed, left, top, right, bottom);

        // 初始化ListView对象 
        if (mListView == null) {
            getListView();
        }
    }  

    /** * 获取ListView对象 */  
    private void getListView() {  
        int childCount = getChildCount();

        if (childCount > 0) {
            for (int i = 0; i < childCount; i++) {
                View childView = getChildAt(i);

                if (childView instanceof ListView) {
                    mListView = (ListView) childView;

                    // 设置滚动监听器给ListView, 使得滚动的情况下也可以自动加载
                    mListView.setOnScrollListener(this);
                }
            }
        }  
    }

    /** * @param event * @return */
    @Override
    public boolean dispatchTouchEvent(MotionEvent event) {  
        final int action = event.getAction();  

        switch (action) {  
            case MotionEvent.ACTION_DOWN:
                // 按下
                mDownY = (int) event.getRawY();

                break;
            case MotionEvent.ACTION_MOVE:
                // 移动
                mMoveY = (int) event.getRawY();

                break;
            case MotionEvent.ACTION_UP:  
                // 抬起
                if (canLoad()) {  
                    loadData();  
                }

                break;  
            default:  
                break;  
        }  

        return super.dispatchTouchEvent(event);  
    }  

    // 是否可以加载更多, 条件是到了最底部, ListView不在加载中, 且为上拉操作.
    private boolean canLoad() {
        if (mListView == null) {
            LogUtils.showLog("不能加载");
            return false;
        }

        return isBottom() && !mIsLoading && isPullUp() && mListView.getAdapter().getCount() > mCanLoadCount && !isRefreshing();
    }

    /** * 设置列表的数目达到多少条才能上拉加载更多 * @param canLoadCount */
    public void setCanLoadCount(int canLoadCount) {
        this.mCanLoadCount = canLoadCount;
    }

    // 判断是否到了最底部
    private boolean isBottom() {
        if (mListView != null && mListView.getAdapter() != null) {  
            return mListView.getLastVisiblePosition() >= (mListView.getAdapter().getCount() - mStartLoadCount);
        }

        return false;  
    }

    /** * 设置上拉到列表倒数第几条的时候开始加载更多 * @param startLoadCount */
    public void setStartLoadCount(int startLoadCount) {
        this.mStartLoadCount = startLoadCount;
    }

    // 是否是上拉操作
    private boolean isPullUp() {  
        return (mDownY - mMoveY) >= mTouchSlop;
    }  

    // 如果到了最底部,而且是上拉操作.那么执行onLoad方法
    private void loadData() {  
        if (mOnLoadListener != null) {  
            // 设置状态 
            setLoading(true);

            mOnLoadListener.onLoad();
        }  
    }  

    private void setLoading(boolean loading) {
        mIsLoading = loading;

        if (mIsLoading) {
            mFooterView = LayoutInflater.from(mContext).inflate(R.layout.listview_footer, null, false);

// if (mListView.getFooterViewsCount() <= 0) { 
   
// mListView.addFooterView(mFooterView);
// }
//
// mFooterView.setVisibility(View.VISIBLE);

            mListView.addFooterView(mFooterView);
        } else {
            if (mListView == null || mListView.getFooterViewsCount() <= 0 || mListView.getAdapter() == null || mFooterView == null) {
                return;
            }

// mFooterView.setVisibility(View.GONE);

            mListView.removeFooterView(mFooterView);

// ObjectAnimator animation = ObjectAnimator.ofFloat(mFooterView, "scaleY", 1, 0);
// animation.setDuration(100);
// animation.start();
// animation.addListener(new Animator.AnimatorListener() { 
   
// public void onAnimationStart(Animator arg0) { 
   
//
// }
//
// public void onAnimationRepeat(Animator arg0) { 
   
//
// }
//
// public void onAnimationEnd(Animator arg0) { 
   
// mListView.removeFooterView(mFooterView);
//
// }
//
// public void onAnimationCancel(Animator arg0) { 
   
//
// }
// });

            mDownY = 0;
            mMoveY = 0;
        }  
    }  

    // 设置上拉加载的监听
    public void setOnLoadListener(OnLoadListener loadListener) {  
        mOnLoadListener = loadListener;  
    }

    // 滑动状态改变的代理
    @Override  
    public void onScrollStateChanged(AbsListView view, int scrollState) {
        if (mOnScrollStateChangeListener != null) {
            mOnScrollStateChangeListener.onScrollStateChanged(view, scrollState);
        }
    }  

    @Override  
    public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
        if (mOnScrollListener != null) {
            mOnScrollListener.onScroll(view, firstVisibleItem, visibleItemCount, totalItemCount);
        }

        // 滚动时到了最底部也可以加载更多 
        if (canLoad()) {  
            loadData();  
        }
    }  

    // 加载更多的监听器
    public interface OnLoadListener { 
   
        void onLoad();
    }

    public interface OnScrollListener { 
   
        void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount);
    }

    public interface OnScrollStateChangeListener { 
   
        void onScrollStateChanged(AbsListView listView, int state);
    }

    public void onRefreshComplete() {
        setLoading(false);
        setRefreshing(false);

// if (isPullUp()) { 
   
// setLoading(false);
// } else { 
   
// setRefreshing(false);
// }
    }

    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.mOnScrollListener = onScrollListener;
    }

    public void setOnScrollStateChangeListener(OnScrollStateChangeListener onScrollStateChangeListener) {
        this.mOnScrollStateChangeListener = onScrollStateChangeListener;
    }
}

listview_footer.xml

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<?xml version="1.0" encoding="utf-8"?>  
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" android:gravity="center" android:paddingBottom="8dp" android:paddingTop="5dp" >

    <ProgressBar android:layout_width="wrap_content" android:layout_height="wrap_content" style="?android:attr/progressBarStyleSmall" android:layout_centerVertical="true" android:layout_centerHorizontal="true" android:paddingRight="100dp" android:indeterminate="true" />  

    <TextView android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_gravity="center" android:gravity="center" android:paddingTop="5dip" android:text="加载更多..." android:textAppearance="?android:attr/textAppearanceMedium" android:textColor="@android:color/darker_gray" android:textSize="14sp" android:textStyle="bold" />  

</RelativeLayout>  

可以看出代码很简单,注释很清楚,这里就不作赘述。由于SwipeRefreshLayout本身就是下拉加载控件,这样封装后,该控件就具备了下拉刷新上拉加载更多的功能,用法没有任务变化:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
<com.jackie.sample.custom_view.CustomSwipeRefreshLayout
        android:id="@+id/swipe_refresh_layout"
        android:layout_width="match_parent"
        android:layout_height="match_parent">

    <ListView
        android:id="@+id/list_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:scrollbars="none" />
</com.jackie.sample.custom_view.CustomSwipeRefreshLayout>

唯一的不足之处,该控件只支持ListView,不支持RecyclerView,有兴趣的同学可以自行优化。 SwipeRefreshLayoutActivity .java

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
package com.jackie.sample.material_design;

import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AppCompatActivity;
import android.widget.ArrayAdapter;
import android.widget.ListView;

import com.jackie.sample.R;
import com.jackie.sample.custom_view.CustomSwipeRefreshLayout;
import com.jackie.sample.utils.ThreadUtils;

import java.util.ArrayList;
import java.util.List;

/** * Created by Jackie on 2018/1/11. * 自定下拉刷新上拉加载控件 */

public class SwipeRefreshLayoutActivity extends AppCompatActivity { 
   
    private CustomSwipeRefreshLayout mSwipeRefreshLayout;
    private ListView mListView;

    private ArrayAdapter mAdapter;

    private List<String> mList;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        setContentView(R.layout.activity_swipe_refresh_layout);

        initView();
        initData();
        initEvent();
    }

    private void initView() {
        mSwipeRefreshLayout = (CustomSwipeRefreshLayout) findViewById(R.id.swipe_refresh_layout);
        mListView = (ListView) findViewById(R.id.list_view);

        mList = new ArrayList<>();
    }

    private void initData() {
        for (int i = 0; i < 20; i++) {
            mList.add("第" + (i + 1) + "条数据");
        }

        mAdapter = new ArrayAdapter<>(this, android.R.layout.simple_list_item_1, mList);
        mListView.setAdapter(mAdapter);
    }

    private void refreshData() {
        ThreadUtils.newThread(new Runnable() {
            @Override
            public void run() {
                try {
                    Thread.sleep(2000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        for (int i = 0; i < 5; i++) {
                            mList.add("第" + (i + 1) + "条数据");
                        }

                        mAdapter.notifyDataSetChanged();

                        mSwipeRefreshLayout.onRefreshComplete();
                    }
                });
            }
        });
    }

    private void initEvent() {
        mSwipeRefreshLayout.setColorSchemeResources(R.color.colorPrimary);

        /** * 下拉刷新 */
        mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
            @Override
            public void onRefresh() {
               refreshData();
            }
        });

        /** * 上拉加载更多 */
        mSwipeRefreshLayout.setOnLoadListener(new CustomSwipeRefreshLayout.OnLoadListener() {
            @Override
            public void onLoad() {
                refreshData();
            }
        });
    }
}

效果图如下:

发布者:全栈程序员栈长,转载请注明出处:https://javaforall.cn/152910.html原文链接:https://javaforall.cn

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
SwipeRefreshLayout实现上滑加载更多[通俗易懂]
在我们的项目中,需要用到许多下拉刷新和上滑加载的操作,不说什么没用的,直接来介绍SwipeRefreshLayout的扩展用法。
全栈程序员站长
2022/09/12
3.6K0
SwipeRefreshLayout实现上滑加载更多[通俗易懂]
自定义SwipeRefreshLayout实现ListView上拉加载下拉刷新
说实话现在大部分人都不在用ListView了,不过说实话如果仅仅是普通的列表其实用哪个都无所谓的。 可能有人会说有好多第三方的下拉刷新上拉加载的框架,但是我觉得吧,有些东西自己能实现的就还是用自己写的好。 不罗嗦了,直接上代码,注释都已写好
longzeqiu
2019/08/14
1.4K0
Android利用V4包中的SwipeRefreshLayout实现上拉加载
基本原理 上拉加载或者说滚动到底部时自动加载,都是通过判断是否滚动到了ListView或者其他View的底部,然后触发相应的操作,这里我们以 ListView来说明。因此我们需要在监听ListView的滚动事件,当ListView滚动到底部时自动触发加载操作;但是当用户支持手指滑动屏 幕,没有滚动时,我们也需要让它加载,因此这种情形就是上拉加载更多。所以我们需要在触摸事件里面进行判断,如果到了底部,且用户是上拉操作,那么执行加 载更多。 时间有限,直接上代码吧。 实现代码 /** * 继承自SwipeRe
庞小明
2018/03/07
8570
Android利用V4包中的SwipeRefreshLayout实现上拉加载
定制SwipeRefreshLayout
SwipeRefreshLayout大家都用过,google推出的,亲生儿子,兼容性自然最好!可是SwipeRefreshLayout只支持下拉刷新,没有上拉加载更多,这样是没办法满足我们的需要的,所以本文就对它进行一下定制,加上下拉刷新。
全栈程序员站长
2022/09/12
1.2K0
SwipeRefreshLayout实现上拉下拉刷新
1:在布局中添加SwipeRefreshLayout和Listview组件 [html] view plain copy <?xml version="1.0" encoding="utf-8
xiangzhihong
2018/02/02
1.1K0
1、创建刷新控件
 一些控件我定义成全局的是因为下面会用到。setPadding可以设置显示位置,左上右下,上为负数就是隐藏在顶部。我们需要手指下拉去控制他缓慢显示,就用到了OnTouchListener,我们实现OnTouch方法做一些事件的分发处理。
cMusketeer
2022/10/27
5870
1、创建刷新控件
Android自定义组合控件---教你如何自定义下拉刷新和左滑删除
版权声明:本文为博主原创文章,转载请标明出处。 https://blog.csdn.net/lyhhj/article/details/80122918
Hankkin
2018/09/06
1.4K0
Android自定义组合控件---教你如何自定义下拉刷新和左滑删除
Android——RecyclerView自定义OnScrollListener实现下拉刷新监听,上拉加载更多功能
针对RecyclerView有很多开源的框架可以直接拿来用,比较有代表性的是BaseRecyclerViewAdapterHelper,功能很强大,具备上拉监听的功能。使用开源的东西固然省时省力,但是前期自定义的Adapter使用较多,不想再更改了,且总使用开源的东西慢慢人都变傻了,这个功能不算复杂,就自己查找相关资料参照实现了。
Winter_world
2020/09/25
3.8K0
SwipeRefreshLayout下拉刷新组件
在实际开发中,经常都会遇到下拉刷新、上拉加载更多的情形,这一期就一起来学习Android系统的SwipeRefreshLayout下拉刷新组件。 一、SwipeRefreshLayout简介 SwipeRefrshLayout是Google官方更新的一个控件,可以实现下拉刷新的效果,该控件集成自ViewGroup在support-v4兼容包下。 SwipeRefrshLayout常用的几个方法如下: isRefreshing():判断当前的状态是否是刷新状态。 setColorSc
分享达人秀
2018/02/05
3.5K0
SwipeRefreshLayout下拉刷新组件
Android ListView下拉/上拉刷新:设计原理与实现「建议收藏」
Android上ListView的第三方开源的下拉刷新框架很多,应用场景很多很普遍,几乎成为现在APP的通用设计典范,甚至谷歌官方都索性在Android SDK层面支持下拉刷新,我之前写了一篇文章《Android SwipeRefreshLayout:谷歌官方SDK包中的下拉刷新》专门介绍过(链接地址:http://blog.csdn.net/zhangphil/article/details/46965377 )。 每一种ListView下拉刷新的开源框架,基本功能相同,设计原理大同小异,下拉刷新的功能实现,其中一个设计实现的的方案核心要点大多集中在ListView的OnScrollListener()等事件的重写上。但是,常见的一些下拉刷新开源框架中,有些缺乏上拉刷新的功能。上拉刷新的功能在一些应用场景中也是需要的,比如,当用户的设备屏幕由于数据需要从网络中加载,但一次网络请求根本不可能把全部数据都加载完,因此在初始化阶段只喂全部数据中的一部分数据。当用户在一个ListView中翻到最底时候,“加载更多”,注意!此处出现另外一种设计方案,比如在ListView的footer view中设计一个按钮,假设按钮就叫做“加载更多”,当用户翻到ListView最后见底时候,点击该按钮后才“加载更多”再次发起数据请求加载更多数据,然后刷新ListView,这种设计方案也比较常见。本文则介绍一个可以自动感知ListView下拉到底、然后可自动加载更多的支持下拉/上拉刷新的ListView。
全栈程序员站长
2022/09/15
1.1K0
Android ListView下拉/上拉刷新:设计原理与实现「建议收藏」
优雅地实现RecyclerView的上拉加载
这篇博客是承接上一篇博客--探索Android架构的DataLayer层(DataManager方式)具体实现,其实是上篇博客的一个使用比较普遍的例子,当然如果把上一篇博客设计的数据加载回调接口提炼出来也是可以做一篇单独的文章。
1025645
2018/08/23
9950
android上拉下拉加载更多数据
最近项目中用到了ListView的下拉刷新的功能,总结了一下前辈们的代码,单独抽取出来写了一个demo作为示例。 效果图 下拉刷新: 加载更多: CustomListView.java packag
xiangzhihong
2018/01/29
2.6K0
SwipeRefreshLayout与RecyclerView的巧夺天工
平常开发我们需要使用ListView下拉刷新或者其上滑加载的时候,不是自己写就是用别人写好了,但是编程中有一点是不变的,就是一般封装好的东西,其扩展性极低,比如你使用xutils,imageloader等开源框架的时候,它允许你扩展吗?答案当然是否,那我想要实现自己非常酷酷的ListView时候,只有自己动手实现。不过,谷歌在2015在v4开发包加入 豪华套餐SwipeRefreshLayout供你享用。
全栈程序员站长
2022/09/12
1.5K0
SwipeRefreshLayout与RecyclerView的巧夺天工
Flutter ListView 下拉刷新,上拉加载更多
正常项目中使用ListView一定会涉及到分页加载的问题,此时无法避免地需要用到下拉刷新和上拉加载更多的功能。
毛大姑娘
2020/09/10
3.8K0
Flutter ListView 下拉刷新,上拉加载更多
【Flutter】ListView 列表高级功能 ( ScrollController 上拉加载更多 )
在 FLutter 中 , 所有的列表都支持设置一个 ScrollController 类型的参数 ,
韩曙亮
2023/03/29
2.1K0
【Flutter】ListView 列表高级功能 ( ScrollController 上拉加载更多 )
GridView 设置选中状态
设置原理 是将bean中的数据加入一个字段isSelect  默认填充数据的时候 ,默认选中的第一个值 设置为true,其余的都设置为false.
再见孙悟空_
2023/02/10
1K0
Android开发笔记(一百二十三)下拉刷新布局SwipeRefreshLayout
下拉刷新布局SwipeRefreshLayout是Android又一与时俱进的控件,顾名思义它随着用户手势向下滑动就会触发刷新操作。从实际的下拉效果来看,SwipeRefreshLayout秉承了Android一贯的简洁界面,可定制性并不太好,远不如开源的下拉刷新框架PullToRefresh,但毕竟是原生的控件,用起来比较方便,所以我们还是好好了解了解它。 SwipeRefreshLayout最早在19.1的support-v4库中引入,所以要先确保sdk的“Android Support Library”版本不低于19.1。另外,SwipeRefreshLayout的源码多次升级,因此有新版与旧版之分,两版之间不但支持的方法有区别,而且界面效果也有差异。 下面是SwipeRefreshLayout的常用方法说明: setColorScheme : 设置进度条/圆圈的颜色。(该方法在新版中已被废弃) setOnRefreshListener : 设置刷新监听器。在下拉松开时触发该监听器,需要重写该监听器的onRefresh方法。 setRefreshing : 设置刷新的状态。true表示正在刷新,false表示结束刷新。 isRefreshing : 判断是否正在刷新。 下面是新版增加的方法说明: setColorSchemeColors : 设置进度圆圈的圆环颜色。 setProgressBackgroundColorSchemeColor : 设置进度圆圈的背景颜色。 setProgressViewOffset : 设置进度圆圈的偏移量。第一个参数表示进度圈是否缩放,第二个参数表示进度圈开始出现时距顶端的偏移,第三个参数表示进度圈拉到最大时距顶端的偏移。 setDistanceToTriggerSync : 设置手势向下滑动多少距离才会触发刷新操作。 SwipeRefreshLayout的旧版与新版之间的界面区别主要有: 1、旧版的进度条是布局顶部的一条横线,而新版的布局顶部的一个圆圈。 2、旧版在下拉时,进度条不动,页面会随着向下滑动;而新版在下拉时,页面不再向下滑动,进度圆圈会向下滑动。 这两种显示效果各有千秋,开发者可按照个人喜好决定采用哪种效果。需要注意的是,想要旧版的效果,就得使用旧版的android-support-v4.jar;想要新版的效果,就得使用新版的android-support-v4.jar。新旧两版的v4包见本文末尾的代码工程。 下面是旧版SwipeRefreshLayout的下拉刷新效果截图:
aqi00
2019/01/18
2.3K0
推荐阅读
相关推荐
SwipeRefreshLayout实现上滑加载更多[通俗易懂]
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档