精华内容
下载资源
问答
  • 主要为大家详细介绍了基于jQuery实现点击列表加载更多效果的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
  • 原理:页面载入时,jQuery向后台请求数据,ASP程序通过查询数据库将最新的几条记录显示在列表页,在列表页的底部有个“更多”链接,通过触发该链接,向服务端发送Ajax请求,后台ASP程序接收请求参数,并作出相应,...
  • 先给大家展示效果图: 效果演示 本例是分页的另外一种显示方式,并不是隐藏未显示的内容 数据库结构与《ajax 翻页》是一样的 JavaScript 代码 [removed] $(document).ready(function() { var track_click = ;...
  • 我们在一些网站评论栏目上可以看到这样的应用,列表没有使用分页条,而是一次加载一定数量的记录显示在列表页,当用户浏览到列表页底部时,可以通过单击“加载更多”来查看更多记录。 原理:页面载入时,jQuery向...
  • js点击加载更多

    2018-08-19 18:21:52
    js点击加载更多,点击加载功能.
  • jquery 滚动到底部时自动加载更多。简单思路仅供参考,不足之处请多包涵,相互学习共同进步。参考文章:http://blog.csdn.net/djk8888/article/details/53781452。我的腾讯微博:http://t.qq.com/djk8888
  • ExpandSwipeRefreshLayout扩展了SwipeRefreshLayout的功能,使得同时具有下拉刷新与上拉加载更多
  • IOS上拉刷新及自动加载更多,不需要上拉加载更多,直接就翻到下面就自动加载更多! 喜欢的话到我博客留个言吧,感谢:http://blog.csdn.net/xiaoyuan511 新手,大神勿喷。求共同进步!
  • 预先声明:下拉刷新是网上另一位大神发出来的源码,本彩笔只是仿照大神的源码自己写了上拉加载更多。然后把侧滑删除也揉了进去。 最后请各位注意ListView的上拉,下拉和侧滑都是经过测试可用的。但是...
  • 这篇文章主要为大家详细介绍了Android RecyclerView实现下拉刷新和上拉加载更多,具有一定的参考价值,感兴趣的小伙伴们可以下载参考一下! (Android)五分钟让你轻松学会下拉刷新和上拉加载更多。文章地址 ...
  • Jquery实现手机端上拉滚动加载更多数据功能手指滑动,完美实现多数据分布加载效果。数据再多页面流畅展示。
  • 我们在一些微博网站上可以碰到这样的应用,微博内容列表没有使用分页条,而是一次加载一定数量的记录显示在列表页,当用户浏览到列表页底部时,可以通过单击“查看更多”来加载更多记录。本文我将结合jQuery和PHP给...
  • 通过多次爬坑,发现了这些监听滚动来加载更多的组件的共同点, 因为这些加载更多的方法是绑定在需要加载更多的内容的元素上的, 所以是进入页面则直接触发一次,当监听到滚动事件之后,继续加载更多, 所以对于无限...
  • RecyclerView下拉刷新和加载更多 博客:http://blog.csdn.net/meijian531161724
  • PHP+Ajax点击加载更多内容

    热门讨论 2015-08-09 11:54:17
    PHP+Ajax点击加载更多内容,适用于手机端和web端。
  • 一个基于jquery库的点击加载更多实现(可在一个页面实现多个)。 真实的案例。 当初进行项目的时候没有找到合适的代码(找到的很多都是滚动条拖到底自动出现,没有点击出现的),并且要实现一个页面多个点击加载,...
  • RecyclerView系列之加载更多

    千次阅读 2019-06-29 14:49:02
    很久很久以前简单封装过一次RecyclerView,后来一直开发TV端,列表的需求花样不是很,主要是解决TV端各种兼容问题,也没怎么琢磨这个美丽的控件了,现在打算重新整理一下,其实网上已经有很优秀的开源项目了,...

    一、背景
    很久很久以前简单封装过一次RecyclerView,后来一直开发TV端,列表的需求花样不是很多,主要是解决TV端各种兼容问题,也没怎么琢磨这个美丽的控件了,现在打算重新整理一下,其实网上已经有很多优秀的开源项目了,涵盖功能多,但是就因为功能太多,用起来反而有一些不方便的地方,例如用在TV上或者别的什么地方,有的地方得根据需求和兼容问题作出修改,这样改起来就麻烦了,看的头皮发麻呀,而且很多功能用不到,用第三方库时,你们是不是也是多一个功能都不想要,所以还是自己搞一下,可以方便的扩展和修改,最终效果:
    list

    grid
    先说下之前的,RecyclerView下拉刷新很简单,直接通过其外层包裹的SwipeRefreshLayout提供的接口即可实现,然后我就自然而然的像自定义其它控件一样把这两个控件给自定义成了一个组合控件,使用时如下:

        <com.wasu.tvfc.widget.ARecycleView
            android:id="@+id/ac_recycler"
            android:layout_width="wrap_content"
            android:layout_height="match_parent"
            app:EmptyView="@layout/empty_view"
            app:layout_moreProgress="@layout/view_more_progress" />
    
    mAdapter = new WechatAdapter(mContext, mList);
    recyclerview.setLayoutManager(new LinearLayoutManager(mContext,LinearLayoutM
    recyclerview.setAdapter(mAdapter);
    recyclerview.setOnLoadMoreListener(new ARecycleView.OnLoadMoreListener() {
        @Override
        public void loadMore() {
            mPresenter.getMoreWechatData();
        }
    });
    recyclerview.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener()
        @Override
        public void onRefresh() {
            mPresenter.getWechatData();
        }
    });
    

    之前感觉这样用起来很方便了,但是随着时代的进步,祖国的发展,人民生活水平不断提高,房价也越来越贵了,想生活的简单点的我,越来越觉得之前那种写法用起来不是很舒服,因此对自己提出了几点疑问:
    Q:为何要把 SwipeRefreshLayout 和 RecyclerView 封装到一起?
    A:当时需求不是很多,在布局的写法也基本一样,所以这种一堆控件不如写成一个组合控件用的方便;
    Q:这其实是一种惯性思维,为何要把数据为空的展示逻辑也封装到这个组合控件里了?
    A:之前用 ListView 时也经常有列表为空的展示需求,现在自定义 RecyclerVIew 就干脆把这个功能也一起封装到这个组合控件里,让其功能尽量完善;
    Q:这样一来其它不包含列表的页面数据为空时如果也要显示一个特殊页,就得再写一套空展示逻辑,这就和 RecyclerView 里的重复了,这不成了形而上学了,现在我想要一个纯净的写法,还要能实现上拉加载更多和下拉刷新,就像这样:

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".activity.ListMoreActivity">
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swiperefreshlayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
            </android.support.v7.widget.RecyclerView>
        </android.support.v4.widget.SwipeRefreshLayout>
    </android.support.constraint.ConstraintLayout>
    

    总结:
    1.别的控件为了使用方便可以自定义成一个组合控件,但是 SwipeRefreshLayout 和 RecyclerView 定义到一起感觉有点不爽;
    2.empty 页面直接定义到 RecyclerView 里其实不是一种完美的做法,会有很多局限,例如其它没有用到 RecyclerView 的页面如果也要在无数据时展示 empty 页面,这时又得重新搞一套逻辑,所以这种特殊页面应该单独搞一个模块,本文暂不涉及;
    3.要在布局里不改变原生写法的情况下实现加载更多和下拉刷新逻辑。

    二.方案
    既然不能在封装 RecyclerView 和 SwipeRefreshLayout 上做文章了,那么就只好去找 Adapter 看看了,思索了许久终于想到了两个方案;(什么?你说我是看了github 上的开源项目才想到的?就你知道的多)
    2.1方案一:Adapter 和布局都以原生的方式来写,然后把原生的 Adapter 装饰一把,增加上拉加载更多的逻辑,加载判断等逻辑放到一个单独工具类里;
    思路如下:
    1.布局及 adapter 都以原生的方式来写;
    2.装饰 adapter 的功能,通过 itemType 区分使用哪种 ViewHolder,从而区分展示内容布局,加载更多布局什么的;
    3.再用一个单独的工具类来处理加载更多的监听判断逻辑;
    Activity的xml:

    <android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        tools:context=".activity.ListMoreActivity">
        <android.support.v4.widget.SwipeRefreshLayout
            android:id="@+id/swiperefreshlayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
            <android.support.v7.widget.RecyclerView
                android:id="@+id/recyclerview"
                android:layout_width="match_parent"
                android:layout_height="match_parent">
    
            </android.support.v7.widget.RecyclerView>
        </android.support.v4.widget.SwipeRefreshLayout>
    </android.support.constraint.ConstraintLayout>
    

    adapter:

    public class ListMoreAdapter extends RecyclerView.Adapter<ListMoreAdapter.MyViewHolder> {
        private List<String> mList;
    
        public ListMoreAdapter(List<String> list) {
            this.mList = list;
        }
    
        @NonNull
        @Override
        public MyViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            return new MyViewHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list, parent, false));
        }
    
        @Override
        public void onBindViewHolder(@NonNull MyViewHolder holder, int position) {
            holder.tv.setText(mList.get(position));
        }
    
        @Override
        public int getItemCount() {
            return mList.size();
        }
    
        class MyViewHolder extends RecyclerView.ViewHolder {
    
            private TextView tv;
    
            private MyViewHolder(View itemView) {
                super(itemView);
                tv = itemView.findViewById(R.id.textview);
            }
        }
    }
    

    Activity里使用:

    public class ListMoreActivity extends BaseActivity {
    
    
        private static Handler mHandler = new Handler();
        private ALoadMoreAdapterWrapper aLoadMoreAdapterWrapper;
    
        @Override
        protected void init() {
            initData();
        }
    
        private void initData() {
            ListMoreAdapter listMoreAdapter = new ListMoreAdapter(mDatas);
            aLoadMoreAdapterWrapper = new ALoadMoreAdapterWrapper(listMoreAdapter);//装饰Adapter
            recyclerView.setAdapter(aLoadMoreAdapterWrapper);
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
            ARecyclerViewHelper aRecyclerViewHelper = new ARecyclerViewHelper(recyclerView, aLoadMoreAdapterWrapper);//工具类
    
            aRecyclerViewHelper.setOnLoadMoreListener(new ARecyclerViewHelper.OnLoadMoreListener() {
                @Override
                public void loadMore() {
                    
                }
            });
    
            swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    
                }
            });
        }
    
    }
    

    去掉 Activity 里的 14 和 18 行,就完全是没有任何杂质的原生写法了,包括布局 xml 和 Adapter,是不是很舒服 ,还有一个优点就是在 TV 或者别的什么一体机设备上万一出现问题,可以快速判断出是 RecyclerView 本身的兼容问题还是自定义 RecyclerView 里哪一段逻辑导致的问题,根据不同情况进行不同处理;
    就通过这两行就可以增加上拉加载更多的功能,然后来看看这两行干了什么:
    ALoadMoreAdapterWrapper.java

    public class ALoadMoreAdapterWrapper extends RecyclerView.Adapter {
        private static final int ITEM_TYPE_LOADMOTE = 100001;
    
        private boolean enableLoadMore = true;
        private LoadMoreHolder mLoadMoreHolder;
        private RecyclerView.Adapter mAdapter;
    
        public ALoadMoreAdapterWrapper(RecyclerView.Adapter adapter) {
            this.mAdapter = adapter;
        }
    
        @NonNull
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            if (viewType == ITEM_TYPE_LOADMOTE) {
                if (null == mLoadMoreHolder) {
                    mLoadMoreHolder = new LoadMoreHolder(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_load_more, parent, false));
                    setLoadMoreVisible(true);
                }
                return mLoadMoreHolder;
            }
            return mAdapter.onCreateViewHolder(parent, viewType);
        }
    
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            if (getItemViewType(position) != ITEM_TYPE_LOADMOTE) {
                mAdapter.onBindViewHolder(holder, position);
            }
        }
    
        @Override
        public int getItemCount() {
            if (!enableLoadMore) {
                return mAdapter.getItemCount();
            }
            return mAdapter.getItemCount() + 1;
        }
    
        @Override
        public int getItemViewType(int position) {
            if (!enableLoadMore) {
                return mAdapter.getItemViewType(position);
            }
            if (position == getItemCount() - 1) {
                return ITEM_TYPE_LOADMOTE;
            }
            return mAdapter.getItemViewType(position);
        }
    
        public void setLoadMoreEnable(boolean enable) {
            enableLoadMore = enable;
            setLoadMoreVisible(enable);
            notifyItemChanged(getLoadMoreViewPosition());
        }
    
        private int getLoadMoreViewPosition() {
            return getItemCount();
        }
    
        public boolean isEnableLoadMore() {
            return enableLoadMore;
        }
    
        private void setLoadMoreVisible(boolean flag) {
            if (null == mLoadMoreHolder) {
                return;
            }
            mLoadMoreHolder.setLoadMoreViewVisible(flag);
        }
    
        public void setLoadMoreState(boolean isLoadMore, boolean isFinish) {
            if (isFinish) {
                mLoadMoreHolder.setPbVisible(true);
                mLoadMoreHolder.setTv("-- 到底啦 --");
                mLoadMoreHolder.setBGColor();
                return;
            }
            if (isLoadMore) {
                mLoadMoreHolder.setTv("正在加载...");
            } else {
                mLoadMoreHolder.setTv("上拉加载更多...");
            }
        }
    
        class LoadMoreHolder extends RecyclerView.ViewHolder {
    
            private TextView tv;
            private View pb;
    
            LoadMoreHolder(View itemView) {
                super(itemView);
                tv = itemView.findViewById(R.id.ilm_tv);
                pb = itemView.findViewById(R.id.ilm_pb);
                itemView.setVisibility(View.GONE);
            }
    
            private void setTv(CharSequence txt) {
                tv.setText(txt);
            }
    
            private void setPbVisible(boolean isFinish) {
                pb.setVisibility(isFinish ? View.GONE : View.VISIBLE);
            }
    
            private void setLoadMoreViewVisible(boolean flag) {
                itemView.setVisibility(flag ? View.VISIBLE : View.GONE);
            }
    
            private void setBGColor() {
                itemView.setBackgroundColor(Color.parseColor("#FFF1EFF0"));
            }
        }
    }
    

    主要逻辑处理在于这几个方法:
    getItemCount(),如果没开启加载更多功能就返回实际 count,否则返回实际的 count 加 1,这个1就是留着放加载更多布局的;
    getItemViewType(),如果没开启加载更多功能就返回实际 adapter 里返回 type,否则最后一个 item 返回 TYPE_LOADMORE;
    onCreateViewHolder(),如果 itemType 是 TYPE_LOADMORE,就创建 LoadMoreViewHolder,否则创建实际 adapter的ViewHolder;
    onBindViewHolder(),如果 itemType 不是 TYPE_LOADMORE,就执行实际 adapter的onBindViewHolder。

    ARecyclerViewHelper.java

    public class ARecyclerViewHelper extends RecyclerView.OnScrollListener {
        private RecyclerView mRecyclerView;
        private RecyclerView.LayoutManager mLayoutManager;
        private ALoadMoreAdapterWrapper mAdapterWrapper;
        private boolean isLoading;
        private OnLoadMoreListener onLoadMoreListener;
        private boolean isFinish;
        private int lastItemCount;
    
        public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
            this.onLoadMoreListener = onLoadMoreListener;
            mAdapterWrapper.setLoadMoreEnable(true);
        }
    
        public ARecyclerViewHelper(RecyclerView recyclerView, ALoadMoreAdapterWrapper adapterWrapper) {
            this.mRecyclerView = recyclerView;
            this.mAdapterWrapper = adapterWrapper;
            mLayoutManager = mRecyclerView.getLayoutManager();
            checkFullScreen(mRecyclerView);
            init();
        }
    
        private void init() {
            mRecyclerView.addOnScrollListener(this);
        }
    
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if (!mAdapterWrapper.isEnableLoadMore()) {
                return;
            }
            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                if (isLoading) {
                    return;
                }
                if (mLayoutManager instanceof GridLayoutManager) {
                    final GridLayoutManager gridLayoutManager = (GridLayoutManager) mLayoutManager;
                    gridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
                        @Override
                        public int getSpanSize(int position) {
                            if (position == mLayoutManager.getItemCount() - 1) {
                                return gridLayoutManager.getSpanCount();
                            } else {
                                return 1;
                            }
                        }
                    });
                }
                int lastCompletelyVisibleItemPosition = 0;
                if (mLayoutManager instanceof LinearLayoutManager) {
                    lastCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastCompletelyVisibleItemPosition();
                    lastItemCount = mLayoutManager.getItemCount();
                    if (!isFinish && lastCompletelyVisibleItemPosition == lastItemCount - 2) {//回弹效果
                        int firstCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();
                        View viewByPosition = mLayoutManager.findViewByPosition(lastCompletelyVisibleItemPosition);
                        if (null == viewByPosition) {
                            return;
                        }
                        int i = recyclerView.getBottom() - recyclerView.getPaddingBottom() - viewByPosition.getBottom();
                        if (i > 0 && firstCompletelyVisibleItemPosition != 0) {
                            recyclerView.smoothScrollBy(0, -i);
                        }
                    } else if (!isFinish && lastCompletelyVisibleItemPosition == lastItemCount - 1) {
                        isLoading = true;
                        mAdapterWrapper.setLoadMoreState(true, false);
                        if (null != onLoadMoreListener) {
                            onLoadMoreListener.loadMore();
                        }
                    }
                }
            }
        }
    
        /**
         * 加载完了调用此方法来关闭加载更多View或设置没有更多数据的View
         * @param isFinish
         */
        public void setLoadMoreComplete(boolean isFinish) {
            isLoading = false;
            this.isFinish = isFinish;
            if (mLayoutManager.getItemCount() == lastItemCount) {//处理加载更多时没数据增加的情况,隐藏加载更多条目
                int lastCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findLastCompletelyVisibleItemPosition();
                if (lastCompletelyVisibleItemPosition < lastItemCount - 2) {
                    return;
                }
                int firstCompletelyVisibleItemPosition = ((LinearLayoutManager) mLayoutManager).findFirstCompletelyVisibleItemPosition();
                View viewByPosition = mLayoutManager.findViewByPosition(lastItemCount - 2);
                int i = mRecyclerView.getBottom() - mRecyclerView.getPaddingBottom() - viewByPosition.getBottom();
                if (i > 0 && firstCompletelyVisibleItemPosition != 0) {
                    mRecyclerView.smoothScrollBy(0, -i);
                }
            }
            mAdapterWrapper.setLoadMoreState(false, isFinish);
        }
    
        /**
         * 检测数据是否满屏
         * @param recyclerView
         */
        private void checkFullScreen(RecyclerView recyclerView) {
            mAdapterWrapper.setLoadMoreEnable(false);
            if (recyclerView == null) return;
            final RecyclerView.LayoutManager manager = recyclerView.getLayoutManager();
            if (manager == null) return;
            recyclerView.postDelayed(new Runnable() {
                @Override
                public void run() {
                    if (manager instanceof LinearLayoutManager) {
                        LinearLayoutManager linearLayoutManager = (LinearLayoutManager) manager;
                        mAdapterWrapper.setLoadMoreEnable(isFullScreen(linearLayoutManager));
                    } else if (manager instanceof StaggeredGridLayoutManager) {
                        StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) manager;
                        int[] positions = new int[staggeredGridLayoutManager.getSpanCount()];
                        staggeredGridLayoutManager.findLastCompletelyVisibleItemPositions(positions);
                        int pos = getTheBiggestNumber(positions) + 1;
                        mAdapterWrapper.setLoadMoreEnable(pos != mAdapterWrapper.getItemCount());
                    }
                }
            }, 50);
        }
    
        private boolean isFullScreen(LinearLayoutManager layoutManager) {
            return (layoutManager.findLastCompletelyVisibleItemPosition() + 1) != mAdapterWrapper.getItemCount() ||
                    layoutManager.findFirstCompletelyVisibleItemPosition() != 0;
        }
    
        private int getTheBiggestNumber(int[] numbers) {
            int tmp = -1;
            if (numbers == null || numbers.length == 0) {
                return tmp;
            }
            for (int num : numbers) {
                if (num > tmp) {
                    tmp = num;
                }
            }
            return tmp;
        }
    
        public interface OnLoadMoreListener {
            void loadMore();
        }
    }
    

    这个类的逻辑也不多,思路如下:
    1.检测一下数据是否满屏了,如果没满就禁止加载更多,这个逻辑如果不处理,会出现只有一条数据时也显示加载更多 item 的情况,那就尴尬了;
    2.设置 RecyclerView 的滑动监听,在 onScrollStateChanged() 方法里处理是否要加载更多的逻辑;
    3.加载更多数据完成后,调用 setLoadMoreComplete() 方法处理 View 状态;
    至此就已经实现了加载更多功能了。
    list1

    有句话不知当讲不当讲,说了半天,其实我不喜欢这种方式,所以加载更多出错、点击重新加载什么的功能我也没在方案一代码里加。(什么?二狗子说要顺着网线来打我?且慢,让我说完方案二再来不迟,我还有更好的方案。)

    2.2方案二:把控制上拉加载的逻辑放到一个 BaseAdapter 中,实际的 adapter 直接继承这个 BaseAdapter 就拥有了上拉加载功能;

    public abstract class BaseAdapter<T> extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
        ...
    
        public void setOnLoadMoreListener(OnLoadMoreListener onLoadMoreListener) {
            this.onLoadMoreListener = onLoadMoreListener;
            isOpenLoadMore = true;
        }
    
        public BaseAdapter(List<T> mDatas) {
            this.mDatas = mDatas;
        }
    
        protected abstract int getLayoutId();
    
        protected abstract void convert(BaseViewHolder viewHolder, T item);
    
        @NonNull
        @Override
        public BaseViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
            BaseViewHolder baseViewHolder = null;
            switch (viewType) {
                case TYPE_CONTENT_VIEW:
                    baseViewHolder = BaseViewHolder.create(getLayoutId(), parent);
                    break;
                case TYPE_FOOTER_VIEW:
                    if (mFooterLayout == null) {
                        mFooterLayout = new RelativeLayout(parent.getContext());
                    }
                    baseViewHolder = BaseViewHolder.create(mFooterLayout);
                    break;
            }
            return baseViewHolder;
        }
    
        @Override
        public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
            switch (holder.getItemViewType()) {
                case TYPE_CONTENT_VIEW:
                    BaseViewHolder viewHolder = (BaseViewHolder) holder;
                    convert(viewHolder, mDatas.get(position));
                    break;
            }
        }
    
        @Override
        public int getItemCount() {
            return mDatas.size() + getFooterViewCount();
        }
    
        @Override
        public int getItemViewType(int position) {
            if (isFooterView(position)) {
                return TYPE_FOOTER_VIEW;
            }
            return TYPE_CONTENT_VIEW;
        }
        
        protected int getFooterViewCount() {
            return isOpenLoadMore && !mDatas.isEmpty() ? 1 : 0;
        }
        
        ...
    }
    

    这是BaseAdapter里处理上拉加载的主要方法,也是通过 itemType 来区分加载什么 item,通过是否开启加载更多的 flag 来控制 itemCount,里面多处理了一个 ViewHolder 的逻辑,搞了一个 BaseViewHolder:

    public class BaseViewHolder extends RecyclerView.ViewHolder {
        private final SparseArray<View> views;
    
        public BaseViewHolder(View itemView) {
            super(itemView);
            this.views = new SparseArray<>();
        }
    
        public static BaseViewHolder create(int layoutId, ViewGroup parent) {
            View itemView = LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false);
            return new BaseViewHolder(itemView);
        }
    
        public static BaseViewHolder create(View itemView) {
            return new BaseViewHolder(itemView);
        }
    
        @SuppressWarnings("unchecked")
        public <T extends View> T getView(@IdRes int viewId) {
            View view = views.get(viewId);
            if (view == null) {
                view = itemView.findViewById(viewId);
                views.put(viewId, view);
            }
            return (T) view;
        }
    
        public BaseViewHolder setText(@IdRes int viewId, CharSequence value) {
            TextView view = getView(viewId);
            view.setText(value);
            return this;
        }
    
        public BaseViewHolder setText(@IdRes int viewId, @StringRes int strId) {
            TextView view = getView(viewId);
            view.setText(strId);
            return this;
        }
    }
    

    示例代码只加了个 setText() 方法,增加其它方法和此方法基本相同,例如 setImg() 什么的。
    实际使用:
    ListMore2Adapter.java

    public class ListMore2Adapter extends BaseAdapter<String> {
        public ListMore2Adapter(List<String> mDatas) {
            super(mDatas);
        }
    
        @Override
        protected int getLayoutId() {
            return R.layout.item_list;
        }
    
        @Override
        protected void convert(BaseViewHolder viewHolder, String item) {
            viewHolder.setText(R.id.textview, item);
        }
    }
    

    ListMore2Activity.java

    private void initData() {
            listMoreAdapter = new ListMore2Adapter(mDatas);
            listMoreAdapter.setOnLoadMoreListener(new OnLoadMoreListener() {
                @Override
                public void onLoadMore(boolean isReload) {
                    loadMore();
                }
            });
            //要在setAdapter之前设置OnLoadMoreListener,因为要通过设置Listener来标记加载更多功能开启,如果不这样做,就得在Adapter的构造函数里把标记传过去
            recyclerView.setLayoutManager(new LinearLayoutManager(this));
            recyclerView.setAdapter(listMoreAdapter);
            recyclerView.addItemDecoration(new DividerItemDecoration(this, DividerItemDecoration.VERTICAL));
            swipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
                @Override
                public void onRefresh() {
                    
                }
            });
        }
    

    这个方案就是文章最开始展示的效果了,其中有两点要注意的地方,GridView布局时,要自己写个ItemDecoration,需要对加载更多的item处理一下,否则会出现下面的情况
    在这里插入图片描述
    加载更多的layout右边被加上了空隙。

    /**
     * Created by Aislli on 2018/9/10 0010.
     */
    public class SpaceDecoration extends RecyclerView.ItemDecoration {
        private int space;
    
        public SpaceDecoration(int space) {
            this.space = space;
        }
    
        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
            super.getItemOffsets(outRect, view, parent, state);
    
            RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
            int spanCount;
            int orientation;
            int itemCount = layoutManager.getItemCount();
            boolean isLoadMore;
            if (layoutManager instanceof GridLayoutManager) {
                GridLayoutManager gridLayoutManager = (GridLayoutManager) layoutManager;
                spanCount = gridLayoutManager.getSpanCount();
                orientation = gridLayoutManager.getOrientation();
    
                GridLayoutManager.LayoutParams lp = (GridLayoutManager.LayoutParams) view.getLayoutParams();
                isLoadMore = lp.getSpanSize() == spanCount;
            } else {
                StaggeredGridLayoutManager staggeredGridLayoutManager = (StaggeredGridLayoutManager) layoutManager;
                spanCount = staggeredGridLayoutManager.getSpanCount();
                orientation = staggeredGridLayoutManager.getOrientation();
    
                StaggeredGridLayoutManager.LayoutParams lp = (StaggeredGridLayoutManager.LayoutParams) view.getLayoutParams();
                isLoadMore = lp.isFullSpan();
            }
            int childAdapterPosition = parent.getChildAdapterPosition(view);
    
            if (orientation == GridLayoutManager.VERTICAL) {
                outRect.top = childAdapterPosition < spanCount ? 0 : space;// the first row
                outRect.bottom = 0;
                outRect.left = 0;
                if (childAdapterPosition % spanCount < (spanCount - 1)) {// except the last column
                    outRect.right = space;
                }
                if (childAdapterPosition == itemCount - 1 && isLoadMore) {// load more view
                    outRect.right = 0;
                }
            }
        }
    }
    

    第 44 行这里处理一下最后一个 item 的 right 值就 OK 了。
    第二个需要注意的是,如果一行有 4 个 item,结果产品要求上拉加载时每页数据少于 4 条,例如要求一页加载 3 条数据,这样会出现一个问题
    在这里插入图片描述
    在这里插入图片描述
    当加载后数据正好充满一行时,这个加载更多的 View 不会触发 onScrollStateChanged() 方法,那么就不会执行弹回判断操作,从而导致加载更多 View 回不去了,不过理论上不会有这样的需求的,一行 4 条数据,加载更多时一页最少也要 4 条数据才有意义,所以正常情况下是不会出现这种情况的。但是如果真的有这种需求也没关系,加个判断处理一下就是

        /**
         * 重置成正常状态
         */
        private void stateDefault() {
            currentState = STATE_DEFAULT;
            mLoadingViewTv.setText("上拉加载更多");
            mLoadingViewPb.setVisibility(View.GONE);
    //        mRecyclerView.postDelayed(new Runnable() {
    //            @Override
    //            public void run() {
    //                hiddenFooterView();
    //            }
    //        }, 200);
        }
    
        private void hiddenFooterView() {
            RecyclerView.LayoutManager layoutManager = mRecyclerView.getLayoutManager();
            int lastVisibleItemPosition = findLastVisibleItemPosition(layoutManager);
            if (lastVisibleItemPosition >= getItemCount() - 2 && currentState != STATE_END) {
                if (findFirstVisibleItemPosition(layoutManager) == 0) return;
                View viewByPosition = layoutManager.findViewByPosition(getItemCount() - 2);
                if (null == viewByPosition) return;
                int i = mRecyclerView.getBottom() - mRecyclerView.getPaddingBottom() - viewByPosition.getBottom();
                if (i > 0) {
                    mRecyclerView.smoothScrollBy(0, -i);
                }
            }
        }
    

    正常加载完都会触发 stateDefault() 方法重置状态,如果有上面所说需求,就把上面的注释放开,在重置完状态后执行 hiddenFooterView() 就 OK 了,没这种需求就不用加这个检测了,一般不会出现这种情况的。
    后面的同学突然大吼一声:嘿!!同学,你仄条嗦的有问题吧,“一行 4 条数据,加载更多时一页最少也要 4 条数据才有意义”,什么叫赠藏情况下不会粗现仄种情况,如果最后一页就剩下 2 条数据怎么办,每次都可能粗现呀!你嗦怎么办?
    真是的…吼的我心里一凉,还以为大清要亡了,允许加载更多的接口数据中,一般都会有个页数和总数,到了最后一页时就会调用 adapter.loadEnd() 方法,调完之后下面的加载更多View就变成类似“–到底了–”的 endView,完全是正常流程,无任何问题嘛。
    我自己在项目中用的是方案二的方式,有兴趣的可看完整代码。
    源码

    展开全文
  • 仿微信、QQ聊天界面, 并在listview实现了上拉加载更多,下拉刷新功能
  • Jetpack-复合分页 Jetpack组成分页。 使用Jetpack Compose列出分页 LazyColumn加载更多。 LazyColumn分页。 载入第一页 第一页 正在载入下一页
  • 仿淘宝上拉加载更多

    热门讨论 2015-06-04 10:13:47
    仿淘宝上拉加载更多,仿淘宝上拉加载更多,仿淘宝上拉加载更多
  • SwipeRefreshLayout 下拉刷新、加载更多、分页索引功能; SwipeRefreshLayout 下拉刷新、加载更多、分页索引功能;
  • jquery 点击加载更多 (可在一个页面实现多个)
  • H5基于iScroll实现下拉刷新,上拉加载更多
  • 上一篇文章已经介绍了如何为RecyclerView添加FootView,在此基础上,要添加分页加载的功能其实已经很简单了。 上一篇文章地址:[为RecyclerView添加FootView和HeadView]...
  • 微信小程序加载更多,是将之前的数据和点击加载后请求的数据用concat拼接在一起并执行setData, 下面是一个简单的栗子: index.wxml代码如下 ...加载更多按钮绑定setLoading index.js文件代码如下

    微信小程序加载更多,是将之前的数据和点击加载后请求的数据用concat拼接在一起并执行setData,

    下面是一个简单的栗子:

    index.wxml代码如下

    <view wx:for="{{duanziInfo}}" wx:for-item="name" wx:for-index="id"> 
        <view class="duanzi-view">
          <view class="duanzi-content">
            <text class="dz-content">{{name.content}}</text>
          </view>
        </view>
    </view>
    <view class="button-wrapper">
      <button type="default" size="default" loading="{{loading}}" disabled="{{disabled}}" bindtap="setLoading">
        {{loadText}}
      </button>
    </view>
    加载更多按钮绑定setLoading

    index.js文件代码如下

    Page({
            data: {
                loadText:'加载更多',
                duanziInfo:[]
            },
    	//初始化请求
    	onLoad: function (res) {
    		var that = this
    		//内容
    		wx.request({
    			url: 'http://xxxxx.com/index.php?m=Industry&a=getDuanziInfo',
    			data: {token:token},
    			method: 'GET', 
    			success: function(res){
    			console.log(res.data.result) //打印初始化数据
    				that.setData({
    				duanziInfo:res.data.result
    				})
    			}
    		})
    	  },
    	  //加载更多
    	setLoading: function(e) {
    		var duanziInfoBefore = this.data.duanziInfo
    		var that = this
    		wx.showToast({ //期间为了显示效果可以添加一个过度的弹出框提示“加载中”
    			title: '加载中',
    			icon: 'loading',
    			duration: 200
    		  })
    		wx.request({
    			url: 'http://xxxxx.com/index.php?m=Industry&a=getDuanziInfo',
    			data: {token:token},
    			method: 'GET', 
    			success: function(res){
    				console.log(duanziInfoBefore.concat(res.data.result)) //打印拼接之后数据
    			    that.setData({
    				loadText:"数据请求中",
    				loading:true,
    				duanziInfo:duanziInfoBefore.concat(res.data.result),
    				loadText:"加载更多",
    				loading:false,
    			  })
    			}
    		})
    	}
    })

    初始化和加载更多中的打印数据如下


    (以上是点击查看更多,还可以根据距离视图区域的距离来加载更多,具体实现是将视图用<scroll-view>标签,并给其一个固定高度,在给定参数中设置距离像素触发事件。具体文档:https://mp.weixin.qq.com/debug/wxadoc/dev/component/scroll-view.html?t=20161122)


    展开全文
  • 上拉加载更多

    2014-11-27 14:18:30
    1.在listview中加入20条载入中的这个布局并隐藏 2.加入OnScrollListener监听,通过监听滚动事件,当滚动到最低端的时候,...3.通过接口回调实现加载更多的功能 4.加载完数据时,通知listview加载结束,隐藏上面的布局文件
  • 一种Flutter加载更多的实现方法

    千次阅读 2019-05-24 17:22:07
    本文介绍了一种Flutter加载更多的实现方法。可以统一封装SliverListview、SliverGridView、SliverStaggeredGrid几种控件,并提供了默认的加载动画、emptyview、errorview等。

    转载注明出处:https://blog.csdn.net/skysukai

    1、why flutter?

    我们在进行Android开发的时候,比如布局文件,会创建一个xml来存放布局。写熟悉了觉得没什么,可是,用xml来存放布局文件是十年前的技术了。在十年过后,再用xml来写布局文件运行时在由系统负责渲染看起来有些过时。
    关于为什么是flutter,网上有很多讨论,在我看来最重要的应该是大前端的思想。作为一个Android开发者,我们也应该去学习这种先进的思想。

    2、背景

    项目中有很多ListView、GridView的场景,通常来说,从服务器获取数据都会分页获取。而flutter官方并没有提供一个loadmore控件。这就需要开发者自己实现。先贴出一张效果图:
    在这里插入图片描述
    网上有一些关于ListView加载更多的实现,无外乎都是判断是否滚动到底,滚动到底之后再加载更多。但是在我们的项目中,不仅有ListView还有GridView、StaggeredGrid(瀑布流)。作为一个懒程序员,我更愿意用一套代码解决这三个控件的加载更多 ,而不愿意分别为他们都写一套代码。

    3、实现

    站在巨人的肩膀上才能看得更远,这篇blog给了我莫大的启示,感谢作者。我的加载更多控件姑且叫做LoadMoreIndicator吧。

    3.1 总体思路

    3.1.1 状态定义

    总体来说,我们还是需要判断是否滚动到底。如果滚动到底且还有数据,则加载更多;否则,无更多数据。所以,我们的LoadMoreIndicator至少应该包含IDLE、NOMORE这两个状态,除此之外,应该还有FAIL、LOADING两个状态,分别对应加载失败、正在加载。

    3.1.2 监听滚动事件

    作为加载更多的实现,如何判断滚动到底?flutter提供了ScrollController来监听滚动类控件,而我最开始也是使用ScrollController来做的,不过后面还是换成了Notification,其中遇到过一个坑,后边再详细说明。有关flutter的notification机制网上有很多介绍,总的来说就是一个flutter的事件分发机制。

    3.1.3 如何统一封装

    上面提到过,项目里面用到的滚动控件包括ListView、GridView、StaggeredGrid,那这三个不同的控件该如何封装到一起呢?单论这个问题似乎有很多解。再仔细分析项目需求,除了那三个滚动控件之外,可能还需要Appbar用于定义title,也还需要pinnedHeader。能把三个滚动控件统一起来,且还支持Appbar、pinnedHeader的控件只有CustomScrollView了。CustomScrollView包含多个滚动模型,能够处理许多个滚动控件带来的滑动冲突。
    那么,LoadMoreIndicator的主体也很清晰了——通过CustomScrollView封装不同的滚动控件,并且处理各种业务场景。

    3.2 主体框架

    给出一小段代码,说明LoadMoreIndicator的主体:

    class LoadMoreIndicator<T extends Widget, K extends Widget>
        extends StatefulWidget {
    
      /// the Sliver header
      final K header;
    
      /// the Sliver body
      final T child;
    
      /// callback to loading more
      final LoadMoreFunction onLoadMore;
    
      /// footer delegate
      final LoadMoreDelegate delegate;
    
      /// whether to load when empty
      final bool whenEmptyLoad;
    
      ///define emptyview or use default emptyview
      final Widget emptyView;
      
      const LoadMoreIndicator({
        Key key,
        @required this.child,
        @required this.onLoadMore,
        this.header,
        this.delegate,
        this.whenEmptyLoad = true,
        this.controller,
        this.emptyView
      }) : super(key: key);
    
      @override
      _LoadMoreIndicatorState createState() => _LoadMoreIndicatorState();
      
      ……
    }
    class _LoadMoreIndicatorState extends State<LoadMoreIndicator> {
    	……
    
    	/// original widget need to be wrapped by CustomScrollView
    	final List<Widget> _components = [];
    	
    	  @override
    	  Widget build(BuildContext context) {
    	    /// build header
    	    if (childHeader != null) {
    	      _components.add(SliverToBoxAdapter(
    	        child: childHeader,
    	      ));
    	    }
    	
    	    /// add body
    	    _components.add(childBody);
    	
    	    /// build footer
    	    _components.add(SliverToBoxAdapter(
    	      child: _buildFooter(),
    	    ));
    	
    	    return _rebuildConcrete();
    	  }
    	  /// build actual Sliver Body
    	  Widget _rebuildConcrete() {
    	    return NotificationListener<ScrollNotification>(
    	      onNotification: _onScrollToBottom,
    	      child: CustomScrollView(
    	        slivers: _components,
    	      ),
    	    );
    	  }
      bool _onScrollToBottom(ScrollNotification scrollInfo) {
        /// if is loading return
        if (_status == LoadMoreStatus.LOADING) {
          return true;
        }
        /// scroll to bottom
        if (scrollInfo.metrics.extentAfter == 0.0 &&
            scrollInfo.metrics.pixels >= scrollInfo.metrics.maxScrollExtent * 0.8) {
          if (loadMoreDelegate is DefaultLoadMoreDelegate) {
            /// if scroll to bottom and there has more data then load
            if (_status != LoadMoreStatus.NOMORE && _status != LoadMoreStatus.FAIL) {
              loadData();
            }
          }
        }
    
        return false;
      }
    	……
     }
    

    以上这一小段代码就是LoadMoreIndicator最核心代码了,非常简单。只需要把需要封装的控件传递过来,添加header、footer即可。有一个问题是,这样封装的话,滚动控件必须是sliver的实现,如:SliverGrid、SliverList、SliverStaggeredGrid,目前没有想到其他更好的解决办法。loadData()就是加载更多的实现,一般是连接到服务器获取数据。

    3.3 构造footer

    LoadMoreIndicator中,封装完滚动控件之后,最重要的工作就是构造footer了。选中了LoadMoreIndicator代码的主体是Customscrollview之后,其实构造footer也很简单了。SliverToBoxAdapter就是flutter提供的用于封装的其他Widget的控件,只需要把构造的footer用SliverToBoxAdapter再包装一层即可大功告成。给出代码片段:

      Widget _buildFooter() {
        return NotificationListener<_RetryNotify>(
          child: NotificationListener<_AutoLoadNotify>(
            child: DefaultLoadMoreView(
              status: _status,
              delegate: loadMoreDelegate,
            ),
            onNotification: _onAutoLoad,
          ),
          onNotification: _onRetry,
        );
      }
    

    DefaultLoadMoreView用于设置默认的加载更多动画,如果用户没有设置,则使用这个加载效果;否则使用定义过的加载效果。

    /// if don't define loadmoreview use default
    class DefaultLoadMoreView extends StatefulWidget {
      final LoadMoreStatus status;
      final LoadMoreDelegate delegate;
    
      const DefaultLoadMoreView({
        Key key,
        this.status = LoadMoreStatus.IDLE,
        @required this.delegate,
      }) : super(key: key);
    
      @override
      DefaultLoadMoreViewState createState() => DefaultLoadMoreViewState();
    }
    
    class DefaultLoadMoreViewState extends State<DefaultLoadMoreView> {
      LoadMoreDelegate get delegate => widget.delegate;
    
      @override
      Widget build(BuildContext context) {
        notify();
        return GestureDetector(
          behavior: HitTestBehavior.translucent,
          onTap: () {
            if (widget.status == LoadMoreStatus.FAIL ||
                widget.status == LoadMoreStatus.IDLE) {
              /// tap to load
              _RetryNotify().dispatch(context);
            }
          },
          child: Container(
            alignment: Alignment.center,
            child: delegate.buildChild(
              context,
              widget.status,
            ),
          ),
        );
      }
     ……
    }
    

    加载动画的实现在DefaultLoadMoreDelegate中,通过代理的模式来设置默认的加载动画:

    ///default LoadMoreView delegate
    class DefaultLoadMoreDelegate extends LoadMoreDelegate {
      @override
      Widget buildChild(BuildContext context, LoadMoreStatus status) {
        switch (status) {
          case LoadMoreStatus.IDLE:
          case LoadMoreStatus.LOADING:
            return LoadingAnimation(blockBackKey: false);
            break;
          case LoadMoreStatus.NOMORE:
            return Center(
              child: Padding(
                padding: EdgeInsets.all(10.0),
                child: Row(
                  mainAxisAlignment: MainAxisAlignment.center,
                  crossAxisAlignment: CrossAxisAlignment.center,
                  children: <Widget>[
                    Text(
                      S.of(context).loadMore_Nomore,
                      style: TextStyle(color: Colors.white),
                    ),
                  ],
                ),
              ),
            );
    
            break;
          case LoadMoreStatus.FAIL:
            return Text(
              S.of(context).loadMore_Fail,
              style: TextStyle(color: Colors.white),
            );
            break;
        }
    
        return null;
      }
    }
    

    3.4 其他问题

    到这里,基本讲清楚了LoadMoreIndicator的实现思路,还有很多细节问题需要花功夫完善,如:怎么判断是否加载完,没有更多数据?是否可以提供默认的EmptyView?

    3.4.1 loadData()的实现

    前面已经提到过当判断滚动到底的时候需要触发加载更多,loadData()这个函数怎么实现呢?

      /// notify UI to load more data and receive result
      void loadData() {
        if (_status == LoadMoreStatus.LOADING) {
          return;
        }
    
        if(mounted) {
          setState(() {
            _updateStatus(LoadMoreStatus.LOADING);
          });
        }
    
        widget.onLoadMore((int count, int pageNum, int pageSize) {
    
          if (pageNum * pageSize >= count) {
            _updateStatus(LoadMoreStatus.NOMORE);
          } else {
            _updateStatus(LoadMoreStatus.IDLE);
          }
    
          if(mounted) {
            setState(() {
              _isEmpty = count == 0;
            });
          }
        }, (int errorCode) {
          _updateStatus(LoadMoreStatus.FAIL);
          if (mounted) {setState(() {});}
        });
      }
    

    LoadMoreIndicator中滚动到底之后,需要触发真实的页面去请求数据,而不可能在控件里边去完成业务逻辑。在java中可以使用回调接口来完成,再把请求结果传回LoadMoreIndicator,用于更新footer状态。在dart中可以使用typedef来完成相同的功能,即用方法来代替回调接口,这部分不是本文的重点,在此略过。
    来看一下LoadMoreIndicator中回调方法的定义:

    typedef void LoadMoreOnSuccess(int totalCount, int pageNum, int pageSize);
    typedef void LoadMoreOnFailure(int errorCode);
    typedef void LoadMoreFunction(LoadMoreOnSuccess success, LoadMoreOnFailure failure);
    

    LoadMoreFunction作为LoadMoreIndicator的一个成员变量,它的实现在具体业务逻辑中。LoadMoreOnSuccessLoadMoreOnFailure是业务逻辑加载失败或成功的回调,用于通知LoadMoreIndicator更新footer状态。

    3.3.2 为什么不能用ScrollController

    LoadMoreIndicator完成之后,能够满足项目中大部门场景,但是在一个场景中,页面不能滚动了。先来看下设计图:
    在这里插入图片描述
    在这个界面中,有三个页签,每一个页签都要求能够加载更多。flutter提供了NestedScrollView来实现一个滑动头部折叠的动画效果。在NestedScrollView的body中设置TabBarView,即可达到效果。
    之前提到,为了监听滚动,一般来说得给控件设置ScrollController来监听,但是NestedScrollView本身自带一个监听,用于处理滚动冲突,并且在NestedScrollView有一段注释:

     // The "controller" and "primary" members should be left
     // unset, so that the NestedScrollView can control this
     // inner scroll view.
     // If the "controller" property is set, then this scroll
     // view will not be associated with the NestedScrollView.
     // The PageStorageKey should be unique to this ScrollView;
     // it allows the list to remember its scroll position when
     // the tab view is not on the screen.
    

    所以,在LoadMoreIndicator只能使用ScrollNotification来监听滚动到底,但是在这样修改之后,理论上能够监听tabbarview的滚动了,实际上,tabbarview还是不能滚动到底,头像依然不能被收起。来看下那个包裹头像的appbar是怎么写的吧:

    SliverAppBar(
      pinned: true,
      expandedHeight: ScreenUtils.px2dp(1206),
      forceElevated: innerBoxIsScrolled,
      bottom: PreferredSize(
        child: Container(
        child: TabBar(
          indicatorColor: Colors.red,
          indicatorWeight: ScreenUtils.px2dp(12),
          indicatorPadding: EdgeInsets.only(top: 10.0),
          indicatorSize: TabBarIndicatorSize.label,
          labelColor: Colors.red,
          labelStyle: _tabBarTextStyle(),
          unselectedLabelColor: Colors.white,
          unselectedLabelStyle: _tabBarTextStyle(),
          tabs: _tabTagMap.keys
           .map(
            (String tag) => Tab(
              child: Tab(text: tag),
            ),
           ).toList(),
          ),
          color: Colors.black,
        ),
      preferredSize:
        Size(double.infinity, ScreenUtils.px2dp(192))),
      flexibleSpace: Container(
        child: Column(
          children: <Widget>[
            AppBar(
              backgroundColor: Colors.black,
            ),
            Expanded(
              child: _userInfoHeadWidget(
                context, _userInfo, UserInfoType.my),
              ),
           ],
         ),
       ),
      ),
    ),
    

    看上去没有什么问题,但是tabbar无论如何不能被收起,后来无意在github上发现,改为以下可实现:

    SliverAppBar(
      expandedHeight: ScreenUtils.px2dp(1206),
      flexibleSpace: SingleChildScrollView(
        physics: NeverScrollableScrollPhysics(),
        child: Container(
          child: Column(
            children: <Widget>[
              AppBar(
                backgroundColor: Colors.black,
              ),
              _userInfoHeadWidget(
                context, _userInfo, UserInfoType.my
              ),
            ],
          ),
        ),
      ),
    

    其实思想就是把用户头像appbar的flexiblespace里,同时设置flexiblespace可滚动。这样,tarbar就可以收起了。

    4、结语

    经过一些踩坑,一个在flutter下的加载更多就完成了。总体来说,flutter的开发是比Android开发效率高。不过,目前还是很不成熟,在Android中一句话可以搞定的事情,在flutter中确不一定。能够做出这个加载更多,也是站在巨人的肩膀上,感谢以下作者给予的启发。
    相关参考:https://blog.csdn.net/qq_28478281/article/details/83827699
    相关参考:https://juejin.im/post/5bfb9cb7e51d45592b766769
    相关参考:https://stackoverflow.com/questions/48035594/flutter-notificationlistener-with-scrollnotification-vs-scrollcontroller
    相关参考:https://github.com/xuelongqy/flutter_easyrefresh/blob/master/README.md

    展开全文
  • 小程序下拉加载更多数据

    千次阅读 2020-04-02 19:11:45
    功能描述:拖动下拉条,可以加载更多内容 实现步骤 配置.json文件 在app.json页面配置"enablePullDownRefresh": true,这会让下拉刷新效果适用于项目的每一个页面;或者在对应需要下拉刷新功能的...

    1    功能介绍
    1.1    简单列表分页
    功能描述:拖动下拉条,可以加载更多内容
    1.1.1    实现步骤
    1.1.1.1    配置.json文件
    1)    在app.json页面配置"enablePullDownRefresh": true,这会让下拉刷新效果适用于项目的每一个页面;或者在对应需要下拉刷新功能的.json文件加上这句配置。
    2)    修改app.json文件如下值

    1.1.1.2    在js中加上事件
     
    1.1.1.3    请求数据示例
     
    1.2    Swiper组件下拉获取更多
    如果页面是有swipper组件的,如下图效果,页面有两个tab页的。据我从网上查找的资料,swipper组件会屏弊onReachBottom事件,所以需要想别的办法实现下拉条获取更多内容
     
    1.2.1    实现步骤
    1.2.1.1    配合scroll-view组件
    在scroll-view上增加监听bindscrolltolower
     
    1.2.1.2    LoadMore事件
     
    1.2.1.3    注意点
    注意多个列表的区分,定义不同的变量来控制 
            


     

    展开全文
  • SwipeRefreshLayout配合RecyclerView实现下拉刷新和上拉加载更多以及没有数据的显示这个demo的源码,详情请参看:http://blog.csdn.net/lovexieyuan520/article/details/50589412
  • 自定义SwipeRefreshLayout(进入页面自动刷新,下拉刷新,点击加载更多) 此Demo博客地址:http://blog.csdn.net/u012814441/article/details/49253761

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,334,198
精华内容 533,679
关键字:

加载更多