美文网首页
笔记20170420--用PullRefreshLayout+R

笔记20170420--用PullRefreshLayout+R

作者: hello我的world | 来源:发表于2017-04-20 21:32 被阅读0次

    需求:

    达到像LoadMoreListView那样的效果,实现分页时上拉加载等待时间有个footerView

    要达到的效果

    上拉加载.jpg 下拉刷新.jpg

    PullRefreshLayout+RecyclerView相关代码:

    xml--Activity的布局:
    <?xml version="1.0" encoding="utf-8"?>
    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical"
        tools:context="xxx.xxx.xxx.xxx.activity.XXXActivity">
    
        <com.baoyz.widget.PullRefreshLayout
            android:id="@+id/pullRefreshLayout"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
    
            <android.support.v7.widget.RecyclerView
                android:id="@+id/listView"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:overScrollMode="never" />
        </com.baoyz.widget.PullRefreshLayout>
    </LinearLayout>
    
    
    xml--上拉加载的footerView的布局(R.layout.load_more_footer):
    <?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="46dp"
        android:background="@color/globalBackGround"
        android:gravity="center"
        android:id="@+id/loadmore"
        android:orientation="horizontal">
    
        <ProgressBar
            style="?android:attr/progressBarStyleSmall"
            android:layout_width="35dp"
            android:layout_height="35dp" />
    
        <TextView
            android:layout_width="wrap_content"
            android:layout_height="46dp"
            android:gravity="center"
            android:text="正在加载..." />
    </LinearLayout>
    
    xml--adapter用到的item布局
    <?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="wrap_content"
        android:background="@drawable/touch_bg"
        android:clickable="true"
        android:id="@+id/msg_layout"
        android:orientation="vertical">
    
        <RelativeLayout
            android:layout_width="fill_parent"
            android:layout_height="wrap_content"
            android:layout_marginTop="1dip">
    
            <ImageView
                android:id="@+id/bell_logo"
                android:layout_width="50dp"
                android:src="@drawable/xtxx_icon"
                android:layout_height="50dp"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:layout_gravity="center"
                android:layout_marginBottom="10dp"
                android:layout_marginLeft="15dp"
                android:layout_marginRight="15dp"
                android:layout_marginTop="10dp" />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignTop="@id/bell_logo"
                android:layout_toRightOf="@id/bell_logo"
                android:ellipsize="end"
                android:gravity="left|center_vertical"
                android:lines="1"
                android:text="系统消息"
                android:textColor="#1e1e1e"
                android:textSize="16sp" />
    
            <TextView
                android:id="@+id/msg_title"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignBottom="@id/bell_logo"
                android:layout_toLeftOf="@+id/red_point"
                android:layout_toRightOf="@id/bell_logo"
                android:ellipsize="end"
                android:gravity="left|center_vertical"
                android:lines="1"
                android:text="content content......." />
    
            <TextView
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentRight="true"
                android:layout_alignTop="@id/bell_logo"
                android:layout_marginRight="10dp"
                android:text="2017-04-20"
                android:id="@+id/msg_time"
                android:textColor="@color/color_aaaaaa_content_text"
                android:textSize="10sp" />
    
    </LinearLayout>
    
    网上找的HeaderViewRecyclerAdapter:
    package xxx.xxx.xxx.xxx.adapter;
    
    /*
     * Copyright (C) 2014 darnmason
     *
     * Licensed under the Apache License, Version 2.0 (the "License");
     * you may not use this file except in compliance with the License.
     * You may obtain a copy of the License at
     *
     *      http://www.apache.org/licenses/LICENSE-2.0
     *
     * Unless required by applicable law or agreed to in writing, software
     * distributed under the License is distributed on an "AS IS" BASIS,
     * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     * See the License for the specific language governing permissions and
     * limitations under the License.
     */
    
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.view.ViewGroup;
    
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * <p>
     * RecyclerView adapter designed to wrap an existing adapter allowing the addition of
     * header views and footer views.
     * </p>
     * <p>
     * I implemented it to aid with the transition from ListView to RecyclerView where the ListView's
     * addHeaderView and addFooterView methods were used. Using this class you may initialize your
     * header views in the Fragment/Activity and add them to the adapter in the same way you used to
     * add them to a ListView.
     * </p>
     * <p>
     * I also required to be able to swap out multiple adapters with different content, therefore
     * setAdapter may be called multiple times.
     * </p>
     * Created by darnmason on 07/11/2014.
     */
    public class HeaderViewRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    
        private static final int HEADERS_START = Integer.MIN_VALUE;
        private static final int FOOTERS_START = Integer.MIN_VALUE + 10;
        private static final int ITEMS_START = Integer.MIN_VALUE + 20;
        private static final int ADAPTER_MAX_TYPES = 100;
    
        private RecyclerView.Adapter mWrappedAdapter;
        private List<View> mHeaderViews, mFooterViews;
        private Map<Class, Integer> mItemTypesOffset;
    
        /**
         * Construct a new header view recycler adapter
         * @param adapter The underlying adapter to wrap
         */
        public HeaderViewRecyclerAdapter(RecyclerView.Adapter adapter) {
            mHeaderViews = new ArrayList<View>();
            mFooterViews = new ArrayList<View>();
            mItemTypesOffset = new HashMap<Class, Integer>();
            setWrappedAdapter(adapter);
        }
    
        /**
         * Replaces the underlying adapter, notifying RecyclerView of changes
         * @param adapter The new adapter to wrap
         */
        public void setAdapter(RecyclerView.Adapter adapter) {
            if(mWrappedAdapter != null && mWrappedAdapter.getItemCount() > 0) {
                notifyItemRangeRemoved(getHeaderCount(), mWrappedAdapter.getItemCount());
            }
            setWrappedAdapter(adapter);
            notifyItemRangeInserted(getHeaderCount(), mWrappedAdapter.getItemCount());
        }
    
        @Override
        public int getItemViewType(int position) {
            int hCount = getHeaderCount();
            if (position < hCount) return HEADERS_START + position;
            else {
                int itemCount = mWrappedAdapter.getItemCount();
                if (position < hCount + itemCount) {
                    return getAdapterTypeOffset() + mWrappedAdapter.getItemViewType(position - hCount);
                }
                else return FOOTERS_START  + position - hCount - itemCount;
            }
        }
    
        @Override
        public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
            if (viewType < HEADERS_START + getHeaderCount())
                return new StaticViewHolder(mHeaderViews.get(viewType - HEADERS_START));
            else if (viewType < FOOTERS_START + getFooterCount())
                return new StaticViewHolder(mFooterViews.get(viewType - FOOTERS_START));
            else {
                return mWrappedAdapter.onCreateViewHolder(viewGroup, viewType - getAdapterTypeOffset());
            }
        }
    
        @Override
        public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position) {
            int hCount = getHeaderCount();
            if (position >= hCount && position < hCount + mWrappedAdapter.getItemCount())
                mWrappedAdapter.onBindViewHolder(viewHolder, position - hCount);
        }
    
        /**
         * Add a static view to appear at the start of the RecyclerView. Headers are displayed in the
         * order they were added.
         * @param view The header view to add
         */
        public void addHeaderView(View view) {
            mHeaderViews.add(view);
        }
    
        /**
         * Add a static view to appear at the end of the RecyclerView. Footers are displayed in the
         * order they were added.
         * @param view The footer view to add
         */
        public void addFooterView(View view) {
            mFooterViews.add(view);
        }
    
        @Override
        public int getItemCount() {
            return getHeaderCount() + getFooterCount() + getWrappedItemCount();
        }
    
        /**
         * @return The item count in the underlying adapter
         */
        public int getWrappedItemCount() {
            return mWrappedAdapter.getItemCount();
        }
    
        /**
         * @return The number of header views added
         */
        public int getHeaderCount() {
            return mHeaderViews.size();
        }
    
        /**
         * @return The number of footer views added
         */
        public int getFooterCount() {
            return mFooterViews.size();
        }
    
        private void setWrappedAdapter(RecyclerView.Adapter adapter) {
            if (mWrappedAdapter != null) mWrappedAdapter.unregisterAdapterDataObserver(mDataObserver);
            mWrappedAdapter = adapter;
            Class adapterClass = mWrappedAdapter.getClass();
            if(!mItemTypesOffset.containsKey(adapterClass)) putAdapterTypeOffset(adapterClass);
            mWrappedAdapter.registerAdapterDataObserver(mDataObserver);
        }
    
        private void putAdapterTypeOffset(Class adapterClass) {
            mItemTypesOffset.put(adapterClass, ITEMS_START + mItemTypesOffset.size() * ADAPTER_MAX_TYPES);
        }
    
        private int getAdapterTypeOffset() {
            return mItemTypesOffset.get(mWrappedAdapter.getClass());
        }
    
        private RecyclerView.AdapterDataObserver mDataObserver = new RecyclerView.AdapterDataObserver() {
            @Override
            public void onChanged() {
                super.onChanged();
                notifyDataSetChanged();
            }
    
            @Override
            public void onItemRangeChanged(int positionStart, int itemCount) {
                super.onItemRangeChanged(positionStart, itemCount);
                notifyItemRangeChanged(positionStart + getHeaderCount(), itemCount);
            }
    
            @Override
            public void onItemRangeInserted(int positionStart, int itemCount) {
                super.onItemRangeInserted(positionStart, itemCount);
                notifyItemRangeInserted(positionStart + getHeaderCount(), itemCount);
            }
    
            @Override
            public void onItemRangeRemoved(int positionStart, int itemCount) {
                super.onItemRangeRemoved(positionStart, itemCount);
                notifyItemRangeRemoved(positionStart + getHeaderCount(), itemCount);
            }
    
            @Override
            public void onItemRangeMoved(int fromPosition, int toPosition, int itemCount) {
                super.onItemRangeMoved(fromPosition, toPosition, itemCount);
                int hCount = getHeaderCount();
                // TODO: No notifyItemRangeMoved method?
                notifyItemRangeChanged(fromPosition + hCount, toPosition + hCount + itemCount);
            }
        };
    
        private static class StaticViewHolder extends RecyclerView.ViewHolder {
    
            public StaticViewHolder(View itemView) {
                super(itemView);
            }
        }
        //下边这个方法是我自己拷贝另一个网上的adpter的,作用是获得footerview,好在加载完的时候直接隐藏掉footerview
        public View getFooterView() {
            return  getFooterCount()>0 ? mFooterViews.get(0) : null;
        }
    
    }
    
    
    网上找的自定义onScrollListener:
    package xxx.xxx.xxx.common.listener;
    
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    
    
    public abstract class SysMsgListScrollListener  extends RecyclerView.OnScrollListener{
    
        private LinearLayoutManager mLinearLayoutManager;
        int lastVisibleItemPosition;
    
        public SysMsgListScrollListener(LinearLayoutManager linearLayoutManager) {
            this.mLinearLayoutManager = linearLayoutManager;
        }
    
        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            //获取最后一个显示 的item的位置
            lastVisibleItemPosition = mLinearLayoutManager.findLastVisibleItemPosition();
        }
    
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            //这一段是关键,如果显示的item总数大于0,并且滑动状态符合,并且最后一个位置大于等于item总数量-1的话(代表最后一个) 就执行加载方法onLoadMore();
            int currentScrollState = newState;
            int visibleItemCount = mLinearLayoutManager.getChildCount();
            int totalItemCount = mLinearLayoutManager.getItemCount();
            if ((visibleItemCount > 0 && currentScrollState == RecyclerView.SCROLL_STATE_IDLE && (lastVisibleItemPosition) >= totalItemCount - 1)) {
                onLoadMore();
            }
        }
    
        public abstract void onLoadMore();
    
    }
    
    
    自己写的一个接口,用作item点击监听:
    package xxx.xxx.xxx.common.listener;
    
    /**
     * Created by Hello我的World on 2017/4/20.
     */
    
    public interface SysMsgOnItemClickListener {
         void sysMsgItemClick(long id);
    }
    
    
    ViewHolder:
    package xxx.xxx.xxx.xxx.adapter;
    
    import android.support.v7.widget.RecyclerView;
    import android.view.View;
    import android.widget.LinearLayout;
    import android.widget.TextView;
    
    import xxx.xxx.xxx.R;
    import xxx.xxx.xxx.common.bean.SysMsgInfo;
    import xxx.xxx.xxx.common.listener.SysMsgOnItemClickListener;
    
    import java.util.List;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    import butterknife.OnClick;
    
    /**
     * Created by Hello我的World on 2017/4/20.
     */
    
    public class ViewHolder extends RecyclerView.ViewHolder  {
    
        @BindView(R.id.msg_layout)
        LinearLayout msg_layout;
        @BindView(R.id.msg_title)
        TextView msg_title;
        @BindView(R.id.msg_time)
        TextView msg_time;
        
        SysMsgOnItemClickListener listener;
        List<SysMsgInfo> data;
    
        public ViewHolder(View itemView, SysMsgOnItemClickListener listener, List<SysMsgInfo>data) {
            super(itemView);
            ButterKnife.bind(this,itemView);
            this.listener = listener;
            this.data = data;
        }
    
        @OnClick(R.id.msg_layout)
        public void onClick(){
            listener.sysMsgItemClick(data.get(getLayoutPosition()).getId());
        }
    
        public void setContent(SysMsgInfo info){
            msg_title.setText(info.getTitle());
            msg_time.setText(info.getCreateTime());
        }
    
    }
    
    真正加载数据的Adapter(用来放到上边那个“网上找的adapter”里):
    package xxx.xxx.xxx.common.adapter;
    
    import android.content.Context;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.view.ViewGroup;
    
    import xxx.xxx.xxx.R;
    import xxx.xxx.xxx.common.bean.SysMsgInfo;
    import xxx.xxx.xxx.common.listener.SysMsgOnItemClickListener;
    
    import java.util.List;
    
    /**
     * Created by Hello我的World on 2017/4/20.
     */
    
    public class SysMsgListAdapter extends RecyclerView.Adapter<ViewHolder> {
    
        Context context;
        List<SysMsgInfo> data;//这个list里的对象,是自己写的数据databean,按自己实际需求实现
        SysMsgOnItemClickListener onItemClickListener;//这个监听器是自己写的接口,就一个方法,让item收到click事件时可以回调
    
        public SysMsgListAdapter(Context context,List<SysMsgInfo>data,SysMsgOnItemClickListener listener){
            this.context = context;
            this.data = data;
            this.onItemClickListener = listener;
        }
    
        @Override
        public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(context).inflate(R.layout.sysmsg_item, parent, false);
            return new ViewHolder(view,onItemClickListener,data);
        }
    
        @Override
        public void onBindViewHolder(ViewHolder holder, int position) {
            holder.setContent(data.get(position));
        }
    
        @Override
        public int getItemCount() {
            return data.size();
        }
    
        public List<SysMsgInfo> getData() {
            return data;
        }
    }
    
    使用:
    package xxx.xxx.xxx.common.activity;
    
    import android.content.Intent;
    import android.os.Bundle;
    import android.support.v7.widget.LinearLayoutManager;
    import android.support.v7.widget.RecyclerView;
    import android.view.LayoutInflater;
    import android.view.View;
    import android.widget.TextView;
    
    import com.baoyz.widget.PullRefreshLayout;
    import xxx.xxx.xxx.R;
    import xxx.xxx.xxx.base.BaseActivity;
    import xxx.xxx.xxx.base.utils.CommonUtil;
    import xxx.xxx.xxx.common.adapter.HeaderViewRecyclerAdapter;
    import xxx.xxx.xxx.common.adapter.SysMsgListAdapter;
    import xxx.xxx.xxx.common.bean.GetSysMessageListReturnInfo;
    import xxx.xxx.xxx.common.contract.SysMsgListContract;
    import xxx.xxx.xxx.common.listener.SysMsgListScrollListener;
    import xxx.xxx.xxx.common.presenter.SysMsgListPresenter;
    
    import butterknife.BindView;
    import butterknife.ButterKnife;
    
    public class SysMsgListActivity extends BaseActivity implements SysMsgListContract.View {
        @BindView(R.id.pullRefreshLayout)
        PullRefreshLayout pullRefreshLayout;
        @BindView(R.id.tv_title)
        TextView tv_title;
        @BindView(R.id.listView)
        RecyclerView listView;
    
        SysMsgListContract.Presenter presenter;
        HeaderViewRecyclerAdapter hAdapter;
        SysMsgListAdapter adapter;
        View footer;
        int pageIndex = 1;
        int pageSize = 20;
    
        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
            setContentView(R.layout.activity_sys_msg_list);
            ButterKnife.bind(this);
            tv_title.setText("系统消息中心");
            //这是这个activity的presenter,loadSysMsgList方法是加载数据的方法
            presenter = new SysMsgListPresenter(this);
            presenter.loadSysMsgList(pageIndex, pageSize);
            //布局管理器
            LinearLayoutManager l = new LinearLayoutManager(this);
            listView.setLayoutManager(l);
            listView.addOnScrollListener(new SysMsgListScrollListener(l) {
                @Override
                public void onLoadMore() {
                    //这个是RecyclerView的滑动监听,如果滑动到最后  就重新加载,先把footerView显示出来
                    if(footer!=null) footer.setVisibility(View.VISIBLE);
                    //执行加载
                    presenter.loadSysMsgList(pageIndex, pageSize);
                }
            });
            pullRefreshLayout.setOnRefreshListener(() -> {
                //下拉刷新操作,直接将pageIndex还原
                pageIndex = 1;
                //如果adapter有数据,直接清掉,并且将“网上找来的adapter”更新
                if (adapter != null) {
                    adapter.getData().clear();
                    if (hAdapter != null) {
                        hAdapter.notifyDataSetChanged();
                    }
                }
                //加载数据
                presenter.loadSysMsgList(pageIndex, pageSize);
            });
        }
    
        @Override
        public void showProgress(String message) {
    
        }
    
        @Override
        public void dismissProgress() {
    
        }
        //下边这个方法是Adapter的关键代码段
        @Override
        public void showMsgList(GetSysMessageListReturnInfo data, int pageIndex) {
            pullRefreshLayout.setRefreshing(false);//数据返回后,将下拉刷新状态设置为false
            if (data.getMessageList().size() == 0) return;//如果数据为0就不用显示了
            this.pageIndex = pageIndex;//分页加载的页码
            //如果adapter为空,就新建adapter,如果不为空代表之前有过加载,直接在adapter的数组上addAll好了
            if (adapter == null) {
                adapter = new SysMsgListAdapter(this, data.getMessageList(), id -> {
                    //这个就是SysMsgOnItemClickListener的回调,然后做出自己需要的逻辑处理即可 
                    Intent intent = new Intent(SysMsgListActivity.this,SysMsgDetailActivity.class);
                    intent.putExtra("MsgId",id);
                    startActivity(intent);
                });
            } else {
                adapter.getData().addAll(data.getMessageList());
                if (hAdapter != null) hAdapter.notifyDataSetChanged();
            }
            //如果“在网上找的Adapter”为空,就新创建一个,新建对象时将我们自己展示数据的adapter传进去
            if (hAdapter == null) {
                hAdapter = new HeaderViewRecyclerAdapter(adapter);
                listView.setAdapter(hAdapter);
                //isLastPage==0代表不是最后一页,isLastPage==1代表最后一页
                if (data.getIsLastPage() != 1) {
                    //如果加载返回的数据中isLastPage表示不是最后一页,就要添加footerView
                    footer = LayoutInflater.from(this).inflate(R.layout.load_more_footer, listView, false);
                    hAdapter.addFooterView(footer);
                }
            }
            //数据加载完成时将footerView隐藏
            if (footer != null) footer.setVisibility(View.GONE);
        }
    
        @Override
        public void loadDone() {
            //加载到最后数据,当服务器返回已经没有数据了,presenter中会回调这个方法,在这里将footerView隐藏掉
            CommonUtil.showToast("数据已加载到最后...");
            if (footer != null) footer.setVisibility(View.GONE);
        }
    
        @Override
        protected void onDestroy() {
            super.onDestroy();
            presenter.onDestroy();
        }
    
    
    }
    
    

    实现思路:

    • Activity的布局(要包含PullRefreshLayout和RecyclerView)
      这里用的是com.baoyz.widget.PullRefreshLayout,然后这个布局中包一个RecyclerView
    • 创建footerView的布局
    • 创建adapter加载数据要用到的item
    • 要有一个可以加footerView的adapter,网上找的HeaderViewRecyclerAdapter 点击这里看这个adapter的源码
    • 实现onItemClick的监听器(其实就是个接口,在item监听到点击着自己了就回调这个接口的方法)
    • 要知道RecyclerView滑动了没呀,要根据滑动状态判断加载不加载啊(onScrollListener RecyclerView滑动监听,ps.这个监听的源码地址找不到了)
    • 要有一个ViewHolder
    • 要有一个放数据的Adapter
    • Activity里的一系列使用

    以上是笔记,其实去年已经写过类似需求,也找到一套方法,没有记笔记,现在用到时发现全忘了,重新找资料看的,所以这次一定要记录好备用!
    BTW:方法千千万,还有很多更简洁更快的方法----->不信就去看啊
    再看啊

    相关文章

      网友评论

          本文标题:笔记20170420--用PullRefreshLayout+R

          本文链接:https://www.haomeiwen.com/subject/uqzlzttx.html