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));

    參考文章

  • 相关阅读:
    C++笔记(2018/2/6)
    2017级面向对象程序设计寒假作业1
    谁是你的潜在朋友
    A1095 Cars on Campus (30)(30 分)
    A1083 List Grades (25)(25 分)
    A1075 PAT Judge (25)(25 分)
    A1012 The Best Rank (25)(25 分)
    1009 说反话 (20)(20 分)
    A1055 The World's Richest(25 分)
    A1025 PAT Ranking (25)(25 分)
  • 原文地址:https://www.cnblogs.com/blfbuaa/p/7220338.html
Copyright © 2011-2022 走看看