zoukankan      html  css  js  c++  java
  • Android实现RecyclerView的下拉刷新和上拉载入很多其它

    需求

    先上效果图, Material Design风格的下拉刷新和上拉载入很多其它。

    blog.csdn.net/never_cxb

    源代码地址(欢迎star) https://github.com/studychen/SeeNewsV2

    假设对于RecyclerView还不熟悉,參见这篇 Android Material Design学习之RecyclerView取代 ListView

    本文链接 http://blog.csdn.net/never_cxb/article/details/50759109 转载请注明出处

    下拉刷新

    效果图

    上拉时候会有一个圆形动画。刷新载入数据。

    http://blog.csdn.net/never_cxb/

    思路

    使用Google官方的android.support.v4.widget.SwipeRefreshLayout

    列表RecyclerView的xml布局

    给原来的RecyclerView添加一个SwipeRefreshLayout的父布局

    <?xml version="1.0" encoding="utf-8"?

    > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:background="?

    attr/listBackground" android:orientation="vertical"> <android.support.v4.widget.SwipeRefreshLayout android:id="@+id/swiperefreshlayout" android:layout_width="match_parent" android:layout_height="wrap_content"> <!-- 新闻列表展示--> <android.support.v7.widget.RecyclerView android:id="@+id/rcv_article_origin" android:layout_width="match_parent" android:layout_height="match_parent" /> </android.support.v4.widget.SwipeRefreshLayout> </LinearLayout>

    java代码

    这儿有几个注意点:
    setColorSchemeColors()能够控制圆形动画的颜色,最多设置4个。

    setOnRefreshListener 设置下拉刷新的回调事件。

    下拉刷新后。使用 AsyncTask 依据当前RecyclerView中首个Item的id来载入很多其它数据。

    数据载入完成后,使用setRefreshing(false);取消动画。

    假设刷新后得到0条记录,提示没有数据更新。若得到>0条数据。把数据加到RecyclerView中

    mSwipeRefreshLayout.setColorSchemeColors(Color.RED, Color.BLUE);
    mSwipeRefreshLayout.setOnRefreshListener(new SwipeRefreshLayout.OnRefreshListener() {
        @Override
        public void onRefresh() {
            new MoreArticleTask().execute(mAdapter.getTopArticleId());
        }
    });
    
    // Integer 是输入參数
    // 得到比某个id大的新闻数组
    class MoreArticleTask extends AsyncTask<Integer, Void, List<SimpleArticleItem>> {
        @Override
        protected List<SimpleArticleItem> doInBackground(Integer... params) {
            try {
                Thread.sleep(1500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            return getMoreById(mColumn, params[0]);
        }
    
        @Override
        protected void onPostExecute(List<SimpleArticleItem> simpleArticleItems) {
            super.onPostExecute(simpleArticleItems);
    
            if (mSwipeRefreshLayout != null) {
                mSwipeRefreshLayout.setRefreshing(false);
            }
            //没有新的数据。提示消息
            if (simpleArticleItems == null || simpleArticleItems.size() == 0) {
                Snackbar.with(mActivity.getApplicationContext()) // context
                        .text(mActivity.getResources().getString(R.string.list_more_data)) // text to display
                        .duration(Snackbar.SnackbarDuration.LENGTH_SHORT) // make it shorter
                        .show(mActivity); // activity where it is displayed
            } else {
                mArticleList.addAll(simpleArticleItems);
                mAdapter.notifyDataSetChanged();
            }
        }
    
    }
    
    

    首次进入页面就显示载入ing的动画

    效果例如以下:

    http://blog.csdn.net/never_cxb/

    直接使用 mSwipeRefreshLayout.setRefreshing(true);载入动画初始状态并不显示。

    看了 http://stackoverflow.com/questions/26858692/swiperefreshlayout-setrefreshing-not-showing-indicator-initially 的解答。
    改用以下的代码,初始状态就有载入动画显示。

    mSwipeRefreshLayout.post(new Runnable() {
        @Override
        public void run() {
            mSwipeRefreshLayout.setRefreshing(true);
            new MoreArticleTask().execute(mAdapter.getTopArticleId());
        }
    });

    上拉载入很多其它

    RecyclerView 展示列表是个通用需求。那么当数据较多时候。怎样实现分页载入呢?

    比方笔者的项目中,先载入15条新闻,当滑动究竟部时候,再载入15条。

    效果图

    http://blog.csdn.net/never_cxb/

    思路

    在RecyclerView底部添加一个Footer的ViewHolder。数据载入完成取出底部的ViewHolder。

    底部Footer的xml布局文件

    非常easy,就是一个ProgressBar。本项目为了Material Design。使用了一个开源ProgressBar https://github.com/Todd-Davies/ProgressWheel,你也能够使用原生的ProgressBar。

    <?

    xml version="1.0" encoding="utf-8"?><!--上拉载入很多其它 RecyclerView 底部--> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:wheel="http://schemas.android.com/apk/res-auto" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical"> <com.pnikosis.materialishprogress.ProgressWheel android:id="@+id/rcv_load_more" android:layout_width="40dp" android:layout_height="40dp" android:layout_gravity="center_horizontal" wheel:matProg_barColor="@color/accent" wheel:matProg_progressIndeterminate="true" /> </LinearLayout>

    底部Footer的ViewHolder

    /**
     * 底部载入很多其它
     */
    class FooterViewHolder extends RecyclerView.ViewHolder {
        @InjectView(R.id.rcv_load_more)
        ProgressWheel rcvLoadMore;
    
        public FooterViewHolder(View itemView) {
            super(itemView);
            ButterKnife.inject(this, itemView);
        }
    }

    在Fragment中给RecyclerView添加滑动监听

    layoutManager.getItemCount() 能够得到当前RecyclerView中Item的总数目

    layoutManager.findLastVisibleItemPosition() 得到最后一个可见的Item的位置position

    假设 totalItemCount < (lastVisibleItem + Constant.VISIBLE_THRESHOLD)

    比方一共15个Item,当前到达第13个, Constant.VISIBLE_THRESHOLD设为3
    总数小于最后一个+阈值,就载入很多其它新闻数据。同一时候把loading标记为 true 。

    private boolean loading = false;
    
    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
    
        mRecyclerView.setLayoutManager(new LinearLayoutManager(mActivity));
    
        mAdapter = new OriginArticleAdapter(mActivity, mArticleList);
    
        mRecyclerView.setAdapter(mAdapter);
    
        mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
    
            @Override
            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
    
                super.onScrolled(recyclerView, dx, dy);
    
                LinearLayoutManager layoutManager = (LinearLayoutManager) recyclerView.getLayoutManager();
    
                int totalItemCount = layoutManager.getItemCount();
    
                int lastVisibleItem = layoutManager.findLastVisibleItemPosition();
    
                if (!loading && totalItemCount < (lastVisibleItem + Constant.VISIBLE_THRESHOLD)) {
                    new ArticleTask(mActivity).execute(mAdapter.getBottomArticleId());
                    loading = true;
                }
            }
        });
    }
    

    在Fragment中控制底部Footer

    我们依旧使用AsyncTask。

    注意 onPreExecute() 给 mArticleList 添加了一个null标记Footer。假设是第一次进入页面(mArticleList为空)不须要加Footer。

    当数据载入完成后。用 mArticleList.remove(mArticleList.size() - 1);把最以下的Footer删除。

    再用 mArticleList.addAll(moreArticles); 添加新增的新闻数据。

    并用 mAdapter.notifyDataSetChanged(); 通知 RecyclerView.Adapter 有数据改变。

    private List<SimpleArticleItem> mArticleList = new ArrayList<SimpleArticleItem>();
    
    class ArticleTask extends AsyncTask<Integer, Void, List<SimpleArticleItem>> {
    
        private Context mContext;
    
        public ArticleTask(Context context) {
            mContext = context;
        }
    
        /**
         * Runs on the UI thread before {@link #doInBackground}.
         */
        @Override
        protected void onPreExecute() {
            super.onPreExecute();
            if (mArticleList != null && mArticleList.size() > 0) {
                mArticleList.add(null);
    
                // notifyItemInserted(int position)。这种方法是在第position位置
                // 被插入了一条数据的时候能够使用这种方法刷新,
                // 注意这种方法调用后会有插入的动画,这个动画能够使用默认的,也能够自定义。

    mAdapter.notifyItemInserted(mArticleList.size() - 1); } } /** * @param params 偏移量 aid * @return */ @Override protected List<SimpleArticleItem> doInBackground(Integer... params) { try { Thread.sleep(1500); } catch (InterruptedException e) { e.printStackTrace(); } return getArticleList(mColumn, params[0]); } @Override protected void onPostExecute(final List<SimpleArticleItem> moreArticles) { // 新增新闻数据 super.onPostExecute(moreArticles); if (mArticleList.size() == 0) { mArticleList.addAll(moreArticles); mAdapter.notifyDataSetChanged(); } else { //删除 footer mArticleList.remove(mArticleList.size() - 1); mArticleList.addAll(moreArticles); mAdapter.notifyDataSetChanged(); loading = false; } } }

    Override RecyclerView.Adapter 中的 getItemViewType

    extends RecyclerView.Adapter<RecyclerView.ViewHolder> 假设是null,返回Footer的Type;否则。返回正常新闻的Type。

    本文,为了UI美观,把新闻分为两种:大于3幅图片 、小于3幅图片,对应返回不同的Type和ViewHolder。

    public final static int TYPE_MULTI_IMAGES = 2; // 多个图片的文章
    public final static int TYPE_FOOTER = 3;//底部--往往是loading_more
    public final static int TYPE_NORMAL = 1; // 正常的一条文章
    @Override
    public int getItemViewType(int position) {
    
        SimpleArticleItem article = articleList.get(position);
        if (article == null) {
            return TYPE_FOOTER;
        } else if (article.getImageUrls().length >= 3) {
            return TYPE_MULTI_IMAGES;
        } else {
            return TYPE_NORMAL;
        }
    
    }

    Override RecyclerView.Adapter 中的 onCreateViewHolder

    在该方法中利用 switch (viewType) 返回不同的xml布局文件及ViewHolder

    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    
        RecyclerView.ViewHolder vh;
        View view;
        switch (viewType) {
            //其它无法处理的情况使用viewholder_article_simple
            default:
            case TYPE_NORMAL:
                view = mLayoutInflater.inflate(
                        R.layout.item_article_normal, parent, false);
                vh = new ItemArticleViewHolder(view);
                return vh;
            case TYPE_FOOTER:
                view = mLayoutInflater.inflate(
                        R.layout.recyclerview_footer, parent, false);
                vh = new FooterViewHolder(view);
                return vh;
            case TYPE_MULTI_IMAGES:
                view = mLayoutInflater.inflate(
                        R.layout.item_article_multi_images, parent, false);
                vh = new MultiImagesViewHolder(view);
                return vh;
        }
    }
    

    Override RecyclerView.Adapter 中的 onBindViewHolder

    利用 instanceof 推断是何种类型的ViewHolder。来给控件赋值、绑定数据。

    注意:由于Footer是用null表示的,为了防止NullPointerException,
    我们先用if把FooterViewHolder的情况处理了。

    @Override
    public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
    
        //这时候 article是 null。先把 footer 处理了
        if (holder instanceof FooterViewHolder) {
            ((FooterViewHolder) holder).rcvLoadMore.spin();
            return;
        }
    
        SimpleArticleItem article = articleList.get(position);
        String[] imageUrls = article.getImageUrls();
    
        if (holder instanceof ItemArticleViewHolder) {
            ItemArticleViewHolder newHolder = (ItemArticleViewHolder) holder;
            newHolder.rcvArticleTitle.setText(article.getTitle());
            newHolder.rcvArticleDate.setText(article.getPublishDate());
            //当图片小于3张时候 选取第1张图片
            if (imageUrls.length > 0) {
                newHolder.rcvArticlePhoto.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME
                        + imageUrls[0]));
            } else {
                newHolder.rcvArticlePhoto.setImageURI(Uri.parse(ApiUrl.randomImageUrl(article.getId())));
            }
            //注意这个阅读次数是 int 类型。须要转化为 String 类型
            newHolder.rcvArticleReadtimes.setText("浏览: " + article.getReadTimes());
    
            newHolder.rcvArticleSummary.setText(article.getSummary());
        } else {
            MultiImagesViewHolder newHolder = (MultiImagesViewHolder) holder;
            newHolder.articleTitle.setText(article.getTitle());
            newHolder.articlePic1.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[0]));
            newHolder.articlePic2.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[1]));
            newHolder.articlePic3.setImageURI(Uri.parse(Constant.BUCKET_HOST_NAME + imageUrls[2]));
            newHolder.countPics.setText("图片: " + imageUrls.length);
            newHolder.countRead.setText("浏览: " + article.getReadTimes());
        }
    }
    

    下拉刷新和上拉载入很多其它的源代码地址(欢迎star) https://github.com/studychen/SeeNewsV2

    本文链接 http://blog.csdn.net/never_cxb/article/details/50759109 转载请注明出处

    一些坑

    注意给RecyclerView setLayoutManager,不然RecyclerView可能不显示

    // 1. get a reference to recyclerView
    RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
    
    // 2. set layoutManger
    recyclerView.setLayoutManager(new LinearLayoutManager(this));

    參考文章

  • 相关阅读:
    模仿outlook快捷方式栏的一个控件
    买了一本书《Programming pearls》编程珠玑(88上的数学题目(1))
    一个IE动画图标的小例子
    对水波特效实现原理的解释
    向量空间的几何变换
    一个简单Led控件
    【转载】配置(visual studio.net已检测到指定的web服务器运行的不是asp.net1.1版)
    最近学习ASP2.0相关的几个小问题(非创新性文章)
    一道c的面试题,大数相乘。
    Led控件(2)——Led显示屏模拟
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7220338.html
Copyright © 2011-2022 走看看