玩Android项目开发5------体系页面(使用RecyclerView + FlexBoxLayout实现体系界面)

完成首页的基本功能之后,现在开始着手进入体系页面的功能实现,在体系页面中,主要是对Android整体的体系概括,有大体系下的子体系,我考虑使用RecyclerView + FlexBoxLayout实现。

1、RecyclerView的使用

<androidx.recyclerview.widget.RecyclerView
       android:id="@+id/rv_system"
       android:layout_width="match_parent"
       android:layout_height="match_parent"
       android:layout_marginBottom="70dp"
       ></androidx.recyclerview.widget.RecyclerView>

这应该是我在项目中第一次引进RecyclerView,相对于ListView来说,RecyclerView性能更加高效,关键是支持瀑布流,在后续的功能中,我将会引入这个特性。

在了解RecyclerView的情况下,大家应该都知道它的缓存体系是要比ListView优越的,而且内部封装ViewHolder,不需要我们自行设置,主持多种布局(viewtype)3大缓存区…

使用起来也非常方便,需要设置适配器和布局管理器(不设置没显示)

//设置布局管理器
        rv_system.setLayoutManager(new LinearLayoutManager(getContext()));
        //设置RecyclerView适配器
        rvAdapter = new SystemRecyclerViewAdapter(getContext(),data);
        //设置适配器
        rv_system.setAdapter(rvAdapter);

RecyclerView适配器代码:

public class SystemRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
    private Context context;
    private List<SystemBean.DataBean> data;
    public SystemRecyclerViewAdapter(Context context, List<SystemBean.DataBean> data){
        this.context = context;
        this.data = data;
    }
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_viewpager_tab, null);
        return new TabHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if(holder instanceof TabHolder){
            ((TabHolder) holder).tv_system_title.setText(data.get(position).getName());
            //拿到当前position的child集合
            List<SystemBean.DataBean.ChildrenBean> children = data.get(position).getChildren();
            for (int i = 0; i < children.size(); i++) {
                TextView tv = new TextView(context);
                tv.setText(children.get(i).getName());
                RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
                        ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT
                );
                params.rightMargin = 10;
                params.topMargin = 10;
                params.leftMargin = 10;
                params.bottomMargin = 10;
                tv.setLayoutParams(params);
                ((TabHolder) holder).fl_child.addView(tv);
            }
        }
    }

    @Override
    public int getItemCount() {
        return data.size();
    }

    class TabHolder extends RecyclerView.ViewHolder{
        private TextView tv_system_title;
        private FlexboxLayout fl_child;
        public TabHolder(@NonNull View itemView) {
            super(itemView);
            tv_system_title = itemView.findViewById(R.id.tv_system_title);
            fl_child = itemView.findViewById(R.id.fl_child);
        }
    }

}

完成到这里,显示就算完成了,但是有一个bug,来看一下:
在这里插入图片描述
现在看着没问题,在滑动的时候,再回到顶部看一下:
在这里插入图片描述
数据完全错乱,而且对着滑动的进行,数据更加混乱,已经没有刚开始加载页面时的状态。

这个问题的主要原因就是,前面说到了,RecyclerView的缓存机制,内置ViewHolder,当我们回到顶部,如果缓存中有该Item的ViewHolder缓存,那么就会复用缓存,没找到缓存,那么就重新BindView,这个时候就会出现一个问题,你在重新BindView的时候,所加载的数据,就不一定是当前Item的数据,可能是其他Item的数据,所以会出现数据错乱。

解决方法:设置item缓存数量,确保能存缓存中获取数据。

//设置缓存大小
rv_system.setItemViewCacheSize(data.size());

问题解决,看效果:
在这里插入图片描述
设置一下RecyclerView的item之间的宽度,通过调用RecyclerViewaddItemDecoration方法,设置item之间的间距。

public class RvItemDistance extends RecyclerView.ItemDecoration {
    private final int space;

    public RvItemDistance(int space){
        this.space = space;
    }
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        outRect.left = space;
        outRect.right = space;
        outRect.bottom = space;
        if(parent.getChildLayoutPosition(view) == 0){
            outRect.top = space;
        }
    }

    public static int px2dp(float dpValue) {
        return (int) (0.5f + dpValue * Resources.getSystem().getDisplayMetrics().density);
    }

}

//设置Item之间的间距
rv_system.addItemDecoration(new RvItemDistance(RvItemDistance.px2dp(8)));

在这里插入图片描述
但是在后续的开发过程中,会出现一个问题:随着数据的加载,Item之间的间距逐渐变大;
这个问题还是之前讲过的复用的问题,需要在item加载的时候,处理分割线的问题,因此改进如下:

 if(rv_square.getItemDecorationCount() == 0) {
            rv_square.addItemDecoration(new RvItemDistance(RvItemDistance.px2dp(6)));
        }

2、单击体系下关键字,查看相关文章

加入我点击“开发环境”下“Android Studio”关键字,便可以查看当前该关键字下的相关文章,这和每个关键字携带的id有关,因此当我们点击每个关键字时,要返回它的id值。

在RecyclerView的适配器中,自己写个接口,回调即可。

public interface onTabClickedListener{
        void onClick(int id);
    }
    
    private onTabClickedListener listener;

    public void seOnTabClickedtListener(onTabClickedListener listener) {
        this.listener = listener;
    }
 for (int i = 0; i < ((TabHolder) holder).fl_child.getChildCount(); i++) {
                View childAt = ((TabHolder) holder).fl_child.getChildAt(i);
                int id = children.get(i).getId();
                childAt.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        listener.onClick(id);
                    }
                });
            }

对于接口的回调,点击事件为什么这么写,我来解释一下:

RecyclerViewonBindViewHolder方法中,是给每一个Item绑定数据,假如说position = 0,那么就是给第1个Item绑定数据,实例化Item之后,将TextView添加到FlexBoxLayout中,所以在设置点击事件时,如果给TextView设置单击事件,失效的原因就是,在手机屏幕中,不可能只显示当前一个Item,我们看到的可能有多个,如果给TextView设置单击事件监听,那么我们可能是给第2个Item设置了单击事件监听,也可能是第3个。

因为每个Item,都有一个FlexBoxLayout,所以我们在当前Iposition中,获取FlexBoxLayout中的子View,那么得到的一定是当前position的FlexBoxLayout中的子View,所以应该给这个子View设置单击事件监听。

点击Android Studio相关的回调id
通过接口回调得到的值,当单击每个关键字时,就跳转到对应的关键字界面。

//获取id回调
        rvAdapter.seOnTabClickedtListener(new SystemRecyclerViewAdapter.onTabClickedListener() {
            @Override
            public void onClick(int id,String name) {
                Intent intent = new Intent();
                intent.putExtra("ids",id);
                intent.putExtra("title",name);
                intent.setClass(getContext(), SystemArticleActivity.class);
                startActivity(intent);
        }

在关键字文章界面,我还是用到了RecyclerView,把适配器源码贴出来吧。

public class SystemArticleAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
    private final Context context;
    private final List<SystemArticleBean.DataBean.DatasBean> datas;

    public SystemArticleAdapter(Context context, List<SystemArticleBean.DataBean.DatasBean> datas){
        this.context = context;
        this.datas = datas;
    }
    @NonNull
    @Override
    public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(context).inflate(R.layout.item_system_article,parent,false);
        return new SystemViewHolder(view);
    }

    @Override
    public void onBindViewHolder(@NonNull RecyclerView.ViewHolder holder, int position) {
        if (holder instanceof SystemViewHolder) {
            if(datas.get(position).getShareUser().equals("")){
                ((SystemViewHolder) holder).tv_shareuser.setText("官方分享");
            }else {
                ((SystemViewHolder) holder).tv_shareuser.setText(datas.get(position).getShareUser());
            }
            ((SystemViewHolder) holder).times.setText(datas.get(position).getNiceDate());
            ((SystemViewHolder) holder).tv_sys_title.setText(datas.get(position).getTitle());
        }
    }

    @Override
    public int getItemCount() {
        return datas.size();
    }

    class SystemViewHolder extends RecyclerView.ViewHolder{
        private ImageView iv_heart;
        private TextView tv_shareuser,times,tv_sys_title;

        public SystemViewHolder(@NonNull View itemView) {
            super(itemView);
            iv_heart = itemView.findViewById(R.id.iv_heart);
            tv_shareuser = itemView.findViewById(R.id.tv_shareuser);
            times = itemView.findViewById(R.id.times);
            tv_sys_title = itemView.findViewById(R.id.tv_sys_title);
        }
    }
}

看下效果。
在这里插入图片描述
当然可以根据自己喜欢的形式自己创造,这些还是比较简单的。

单击感兴趣的文章,进入文章详情页面,RecyclerView没有单击事件,同样自己做接口回调。

 public interface onItemClickListener{
        void onClick(int position);
    }
    
    private onItemClickListener listener;

    public void setOnItemClickListener(onItemClickListener listener) {
        this.listener = listener;
    }

通过回调得到当前的Item的position,获取文章的url地址,通过WebView加载url,这个内容在《玩Android项目开发4------首页(ListView实现文章置顶)》一节中有详细介绍。

看效果:
在这里插入图片描述
3、RecyclerView加载分页数据

当RecyclerView往上滑动,到最底层时,这个时候就需要加载显示下一页的数据,并且更新适配器的内容。

首先在适配器中,要判断,如果当前页面至少显示了5条数据,而且已经滑动到该页面的最后一个Item,那么就显示加载的进度条。

 //滑动时
            if(position > 5 && position == datas.size() -1){
                ((SquareViewHolder) holder).pb_load.setVisibility(View.VISIBLE);
            }else{
                ((SquareViewHolder) holder).pb_load.setVisibility(View.GONE);
            }

在这里插入图片描述
接下来监听RecyclerView是否滑动到底部,如果滑动到底部,那么就进行下一页的加载,我这里是自己做的接口回调。

//监听
        aAdapter.setLoadCallback(new SquareAdapter.LoadCallback() {
            @Override
            public void onLoad(boolean lastPosition) {
                if(lastPosition){
//                    Toast.makeText(getContext(),"到底了",Toast.LENGTH_SHORT).show();
                    int curPage = squareBean.getData().getCurPage();
                    Message message = Message.obtain();
                    message.what = LOAD_WHAT;
                    message.obj = curPage;
                    handler.sendMessageDelayed(message,1000);
                }
            }
        });

当页面滑动到底部时,我需要知道当前页面的页数,然后回调去主线程更新适配器数据,加载到下一页。

switch (msg.what){
                case LOAD_WHAT:
                    curPage = (int)msg.obj;
                    fPresenter.getSquareData(curPage);
                    fPresenter.getData().observe(getActivity(), new Observer<SquareBean>() {
                        @Override
                        public void onChanged(SquareBean squareBean) {
                            showSquareList(squareBean);
                        }
                    });
                    Log.e("TAG","curPage==="+curPage);
                    aAdapter.notifyDataSetChanged();
                    break;
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Awesome_lay

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值