SwipeRefreshLayout与RecyclerView的巧夺天工
发布日期:2021-06-30 21:26:48 浏览次数:2 分类:技术文章

本文共 12631 字,大约阅读时间需要 42 分钟。

平常开发我们需要使用ListView下拉刷新或者其上滑加载的时候,不是自己写就是用别人写好了,但是编程中有一点是不变的,就是一般封装好的东西,其扩展性极低,比如你使用xutils,imageloader等开源框架的时候,它允许你扩展吗?答案当然是否,那我想要实现自己非常酷酷的ListView时候,只有自己动手实现。不过,谷歌在2015在v4开发包加入 豪华套餐SwipeRefreshLayout供你享用。

1.SwipeRefreshLayout使用注意说明

SwipeRefreshLayout默认只能包含一个滑动控件,比如本文使用的RecyclerView。

㈡一般使用ListView组件都有一个需求,那么就是没有网络的情况下,将显示其他控件提示用户加载失败或者需要联网。那么,SwipeRefreshLayout可以包含有且仅有一个布局,布局里面可以添加你需要的控件。

㈢如果你按㈡这样做,那么SwipeRefreshLayout默认只会监听一个滑动控件,当你有多个控件的时候会使其找不到监听的滑动控件。这样SwipeRefreshLayout功能就不复存在了。

㈣那么今天我们将实现的下拉刷新和上滑加载该怎么办呢?答案就是重写SwipeRefreshLayout。

2.重写SwipeRefreshLayout

当我们重写SwipeRefreshLayout,需要使用到如下一个方法:

㈠canChildScrollUp

我们来看看其文档说明:

public boolean canChildScrollUp ()

Returns
Whether it is possible for the child view of this layout to scroll up. Override this if the child view is a custom view.

如果子视图为自定义视图那么必须重写该方法。同理,当你的子视图用布局包裹的时候,其就是你自定义的,除非你的子视图只有ListView,当有多个控件时候,其默认找不到ListView监听其滑动事件,必须重写该方法。

㈡重写SwipeRefreshLayout

既然找不到该子视图,那么就必须传入子视图的控件,以监听其滑动状态,也就是自定义一个属性:

下面源码是怎么写的canChildScrollUp:

public boolean canChildScrollUp() {    if (android.os.Build.VERSION.SDK_INT < 14) {        if (mTarget instanceof AbsListView) {            final AbsListView absListView = (AbsListView) mTarget;            return absListView.getChildCount() > 0                    && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)                            .getTop() < absListView.getPaddingTop());        } else {            return ViewCompat.canScrollVertically(mTarget, -1) || mTarget.getScrollY() > 0;        }    } else {        return ViewCompat.canScrollVertically(mTarget, -1);    }}

下面我们来依葫芦画瓢重写SwipeRefreshLayout:

public class LYJSwipeRefreshLayout extends SwipeRefreshLayout {    private static final String TAG = LYJSwipeRefreshLayout.class.getCanonicalName();    private int mScrollableChildId;//控件ID    private View mScrollableChild;//子控件    public LYJSwipeRefreshLayout(Context context) {        this(context, null);    }    public LYJSwipeRefreshLayout(Context context, AttributeSet attrs) {        super(context, attrs);        //获取监听子控件的ID        TypedArray a = context.obtainStyledAttributes(                attrs, R.styleable.LYJSwipeLayoutAttrs);        mScrollableChildId = a.getResourceId(R.styleable.LYJSwipeLayoutAttrs_scrollableChildId, 0);        mScrollableChild = findViewById(mScrollableChildId);        a.recycle();    }    @Override    public boolean canChildScrollUp() {        //判断有没有传入子控件        ensureScrollableChild();        if (android.os.Build.VERSION.SDK_INT < 14) {            if (mScrollableChild instanceof AbsListView) {                final AbsListView absListView = (AbsListView) mScrollableChild;                return absListView.getChildCount() > 0                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)                        .getTop() < absListView.getPaddingTop());            } else {                return mScrollableChild.getScrollY() > 0;            }        } else {            return ViewCompat.canScrollVertically(mScrollableChild, -1);        }    }    private void ensureScrollableChild() {        if (mScrollableChild == null) {            mScrollableChild = findViewById(mScrollableChildId);        }    }}

布局文件如下:

红色标记的为传入子控件ID的属性。这样SwipeRefreshLayout就可以监听recyclerview了。

3.实现下拉刷新,上滑加载

为了代码的重用效率高,我写了一个基类BaseActivity:

public abstract class BaseActivity extends AppCompatActivity implements SwipeRefreshLayout.OnRefreshListener {    /***     * 处理下拉和刷新滴     */    protected SwipeRefreshLayout swipeRefreshLayout;    /***     * 进化的ListView     */    protected RecyclerView recyclerView;    /***     * 该布局在没有网络的时候,显示的布局     */    protected LinearLayout linearLayout;    /***     * RecyclerView的样式(网格,瀑布,线性)     */    protected LinearLayoutManager mLayoutManager;    /***     * 记录最后一项的位置     */    protected int lastVisibleItem=0;    @Override    protected void onCreate(Bundle savedInstanceState) {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        this.swipeRefreshLayout=(SwipeRefreshLayout)findViewById(R.id.activity_main_swipe);        this.recyclerView=(RecyclerView)findViewById(R.id.activity_main_recyclerview);        this.linearLayout=(LinearLayout)findViewById(R.id.activity_main_linearlayout);        initView();        recyclerView.setOnScrollListener(new RecyclerView.OnScrollListener() {            @Override            public void onScrolled(RecyclerView recyclerView, int dx, int dy) {                super.onScrolled(recyclerView, dx, dy);                lastVisibleItem = mLayoutManager.findLastVisibleItemPosition();            }            @Override            public void onScrollStateChanged(RecyclerView recyclerView, int newState) {                super.onScrollStateChanged(recyclerView, newState);                onRecyclerViewStateChanged(newState);            }        });    }    @Override    public void onRefresh() {        onRecyclerViewRefresh();    }    /***     * 初始化界面     */    public abstract void initView();    /***     * 监听RecyclerView滑动事件     * @param newState 滑动状态     */    public abstract void onRecyclerViewStateChanged(int newState);    /***     * 下拉刷新处理     */    public abstract void onRecyclerViewRefresh();}

注释非常明确,布局中有一个linearlayout其中无任何控件,是为了扩展任何你需要的无网络时显示的界面的。你只需要继承该类实现这几个抽象方法。

㈠自定义适配器

代码如下:

public class LYJRecyclerViewAdapter extends RecyclerView.Adapter
{ /*** * 每项的数据集合 */ private List
messageItems; /*** * 监听item点击事件。 */ private LYJItemClickListener mItemClickListener; /*** * 一共显示多少条数据 */ private int totalSize; public LYJRecyclerViewAdapter(List
messageItems,int size){ this.messageItems=messageItems; this.totalSize=size; } /*** * 监听点击事件接口 */ public interface LYJItemClickListener { public void onItemClick(View view, int postion); } /*** * 设置item点击事件 * @param listener */ public void setOnItemClickListener(LYJItemClickListener listener) { this.mItemClickListener = listener; } @Override public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) { if (i == Constants.TYPE_ITEM) { View view = LayoutInflater.from(viewGroup.getContext()).inflate( R.layout.activity_main_adapter_item, null); view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); return new ItemViewHolder(view,this.mItemClickListener); } //滑动到底部返回footview else if (i == Constants.TYPE_FOOTER) { View view = LayoutInflater.from(viewGroup.getContext()).inflate( R.layout.activity_main_adapter_footview, null); view.setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)); return new FooterViewHolder(view); } return null; } @Override public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int i) { if(viewHolder instanceof ItemViewHolder){ ((ItemViewHolder) viewHolder).name.setText(messageItems.get(i)); }else{ if(this.totalSize==(getItemCount()-1)){ ((FooterViewHolder)viewHolder).flagTxt.setText("已经加载完全部内容"); }else{ ((FooterViewHolder)viewHolder).flagTxt.setText("正在加载中........"); } } } @Override public int getItemCount() { return messageItems.size()+1;//加1是多的footview那一项,也就是滑动到footview就加载,而不是最后数据项。 } @Override public int getItemViewType(int position) { if (position + 1 == getItemCount()) { return Constants.TYPE_FOOTER; } else { return Constants.TYPE_ITEM; } } /*** * 底部布局 */ public class FooterViewHolder extends RecyclerView.ViewHolder { private TextView flagTxt; public FooterViewHolder(View itemView) { super(itemView); this.flagTxt=(TextView)itemView.findViewById(R.id.activity_main_adapter_footview_txt); } } /*** * 数据项布局 */ class ItemViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { private TextView name; private LYJItemClickListener mListener;//设置点击事件 public ItemViewHolder(View itemView, LYJItemClickListener listener) { super(itemView); this.name = (TextView) itemView.findViewById(R.id.activity_main_adapter_item_name); this.mListener = listener; itemView.setOnClickListener(this);//设置点击事件 } @Override public void onClick(View v) { if (mListener != null) { mListener.onItemClick(v, getPosition()); } } }}

㈡MainActivity的实现

其继承自BaseActivity,并且实现item点击事件接口LYJRecyclerViewAdapter.LYJItemClickListener:

public class MainActivity extends BaseActivity implements LYJRecyclerViewAdapter.LYJItemClickListener{    /***     * 数据项     */    private List
messageItems=new ArrayList<>(); /*** * 自定义adapter */ private LYJRecyclerViewAdapter adapter; /*** * 获取资源文件字符串中间转换集合 */ private List
strFlag; @Override public void initView() { Toolbar toolbar=(Toolbar)findViewById(R.id.activity_main_toolbar); toolbar.setTitle(""); setSupportActionBar(toolbar); this.swipeRefreshLayout.setColorSchemeColors(Color.RED);//设置加载内圈颜色 this.swipeRefreshLayout.setOnRefreshListener(this);//设置下拉刷新事件 this.swipeRefreshLayout.setProgressBackgroundColorSchemeColor(getResources().getColor(R.color.activity_main_tablayout_bg));//设置加载外圈颜色 // 这句话是为了,第一次进入页面的时候显示加载进度条 swipeRefreshLayout.setProgressViewOffset(false, 0, (int) TypedValue .applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24, getResources() .getDisplayMetrics())); mLayoutManager=new LinearLayoutManager(this,LinearLayoutManager.VERTICAL,false);//设置布局样式 recyclerView.setHasFixedSize(true); recyclerView.setLayoutManager(mLayoutManager); loadingRecyclerView(Constants.LISTVIEW_INIT);//初始化RecyclerView } @Override public void onRecyclerViewStateChanged(int newState) { if (messageItems == null || messageItems.size() <= 0) { Snackbar.make(swipeRefreshLayout, "没有数据得先下拉刷新", Snackbar.LENGTH_SHORT).show(); return; } //滚动事件结束并且到达最底端 if (newState == RecyclerView.SCROLL_STATE_IDLE && lastVisibleItem + 1 == adapter.getItemCount()) { loadingRecyclerView(Constants.LISTVIEW_DOWNLOAD);//下滑RecyclerView } } @Override public void onRecyclerViewRefresh() { loadingRecyclerView(Constants.LISTVIEW_REFRESH);//下拉刷新RecyclerView } public void loadingRecyclerView(int recyclerViewState){ swipeRefreshLayout.setRefreshing(true);//打开加载动画 if (!LYJNetwork.isNetworkAvailable(MainActivity.this)) { Snackbar.make(swipeRefreshLayout, "没有网络你逗我玩啊?", Snackbar.LENGTH_SHORT).show(); swipeRefreshLayout.setRefreshing(false);//没有网络时候直接关闭加载动画 return; } //当为初始化的时候 if(recyclerViewState==Constants.LISTVIEW_INIT){ addStringToList(); adapter=new LYJRecyclerViewAdapter(messageItems,100); recyclerView.setAdapter(adapter); }else if(recyclerViewState==Constants.LISTVIEW_REFRESH){ //当为下拉刷新的时候 messageItems.clear(); addStringToList(); recyclerView.setAdapter(null); adapter = new LYJRecyclerViewAdapter(messageItems,100); adapter.setOnItemClickListener(MainActivity.this); recyclerView.setAdapter(adapter); }else{ //当为下滑加载的时候 if(messageItems.size()!=100){ addStringToList(); adapter.notifyDataSetChanged(); } } swipeRefreshLayout.setRefreshing(false);//执行完成也要关闭加载动画 } @Override public void onItemClick(View view, int postion) { //每项的点击事件 } //模拟增加数据 public void addStringToList(){ strFlag= Arrays.asList(getResources().getStringArray(R.array.welltest_array_string)); for(int i=0;i

这样谷歌官方控件的下拉刷新,上滑动加载就完成了。

从这里可以看到,虽然说ListView有点击事件,有许多扩展,但你想扩展ListView就必须重构很多地方。而RecyclerView,虽然什么都没有,但你扩展起来要方便的多。这就是从0开始的优势。当一个框架继承了很多很多东西,那么你要修改其中的东西,那么就是牵一发而动全身。没有最适合的框架,只有最优解。

本文源码:(MainActivity为本文源码,自行修改)

看看最后实现的效果:

转载地址:https://liyuanjinglyj.blog.csdn.net/article/details/50569814 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:仿百度福袋红包界面
下一篇:Android Support Design Library之TextInputLayout

发表评论

最新留言

路过,博主的博客真漂亮。。
[***.116.15.85]2024年04月25日 07时48分55秒