安卓——仿饿了么点餐,两个RecyclerView联动效果(一:为rv添加头部)

本文介绍了在Android中如何关联两个RecyclerView,以实现类似饿了么点餐的功能。通过获取数据,设置UI,使用官方的smoothScrollToPosition方法,并自定义ItemDecoration进行数据分组和头部添加。文中提供了关键代码示例,包括数据获取、UI布局和自定义装饰器的创建。
摘要由CSDN通过智能技术生成

1.前言

相信在安卓中,很多人都遇到一个需求,即两个RecyclerView如何相关联,本文进行基于此功能下的仿饿了么点餐的实行。在实行这个需求时,我相信有不少人一脸懵圈。其实官方已经给RecyclerView提供了相关方法——smoothScrollToPosition()提供我们使用,不过在使用这个方法时要考虑多种情况。

模仿饿了吗点餐

2.步骤详情

如图片所示:我们将步骤拆开:

  1. 获取数据:我们需要获取两个RecyclerView的数据,这个我们用上玩安卓的导航接口:https://www.wanandroid.com/navi/json 获取数据即可。我们成左边数据为父数据,右边的数据为子数据。
  2. UI设置:给右边的RecyclerView添加item头部,这部分内容涉及了一点自定义View,因为我对自定义View也不算精通,所以随便画了一下,可能很丑。

2.1 获取数据

老套路,用上okhttp获取数据即可,比较特殊的一点就是需要我们在右边的实体类中填上两个字段isFirst、isLast为第二步铺垫
去掉不必要的字段和注释后,实体类NavigationBean代码如下:

public class NavigationBean implements Serializable {
    private List<DataBean> data;
    public List<DataBean> getData() {
        return data;
    }
    public static class DataBean implements Serializable {
        private String name;
        private List<ArticlesBean> articles;
        public String getName() {
            return name;
        }
        public List<ArticlesBean> getArticles() {
            return articles;
        }
        public static class ArticlesBean implements Serializable {
        	// 新添加的字段、重点
            private boolean isFirst;
            private boolean isLast;
            private String chapterName;
            private String title;
            public String getTitle() {
                return title;
            }
            public String getChapterName() {
                return chapterName;
            }
            public boolean isFirst() {
                return isFirst;
            }
            public boolean isLast() {
                return isLast;
            }
            public void setFirst(boolean first) {
                isFirst = first;
            }
            public void setLast(boolean last) {
                isLast = last;
            }
        }
    }
}

在完成bean类的设计后,我们要在页面的xml里填上两个RecyclerView的UI控件。这里用一个View进行分隔,当然如果你觉得没必要也可以省去。

    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/navigation_recyclerView_left"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"/>
    <View
        android:layout_width="1dp"
        android:layout_height="match_parent"
        android:background="#ff3322"/>
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/navigation_recyclerView_right"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"/>

接下来就是控制器代码,即获取数据。特殊点即在解析返回数据时需要为我们新增的两个字段设置值。
这里老实说应该不能贴上全部代码,但是又怕有新同学看不懂,觉得啰嗦的同学还请见谅。关键代码在parseData()解析数据方法中,我们需要对多个同样父数据的子数据集合始末位置进行标记。

    private List<NavigationBean.DataBean> leftBeans = new ArrayList<>();
    private List<NavigationBean.DataBean.ArticlesBean> rightBeans = new ArrayList<>();
    private NavigationBean navigationBean;
    private RecyclerView rightRv;
    /**
     * 发送请求,获取数据
     */
    private void initSent() {
        OkHttpClient okHttpClient = new OkHttpClient();
        Request request = new Request.Builder()
                .url("https://www.wanandroid.com/navi/json")
                .build();
        okHttpClient.newCall(request).enqueue(new Callback() {
            @Override
            public void onFailure(Call call, IOException e) {
            }
            @Override
            public void onResponse(Call call, Response response) throws IOException {
                String string = response.body().string();
                parseData(string);
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        initView();
                    }
                });
            }
        });
    }

    /**
     * 解析返回数据
     * @param string
     */
    private void parseData(String string){
        Gson gson = new Gson();
        navigationBean = gson.fromJson(string, NavigationBean.class);
        leftBeans = navigationBean.getData();
        for (NavigationBean.DataBean dataBean:navigationBean.getData()){
            for (int i = 0 ;i<dataBean.getArticles().size();i++){
                // 定好始末content的状态
                if (i==0){
                    dataBean.getArticles().get(i).setFirst(true);
                }else if (i==dataBean.getArticles().size()){
                    dataBean.getArticles().get(i).setLast(true);
                }
                rightBeans.add(dataBean.getArticles().get(i));
            }
        }
    }
    /**
     * 设置UI
     */
    private void initView() {
        // 左RV
        RecyclerView leftRv = findViewById(R.id.navigation_recyclerView_left);
        leftRv.setLayoutManager(new LinearLayoutManager(this));
        leftRv.setAdapter(new LeftAdapter(leftBeans));
        // 右RV
        rightRv = findViewById(R.id.navigation_recyclerView_right);
        rightRv.setLayoutManager(new LinearLayoutManager(this));
        rightRv.setAdapter(new RightAdapter(rightBeans));
    }

2.2 设置ItemDecoration

在获取完数据后,我们得对右边的列表进行分类,即添加对应的父数据(ItemDecoration)。
需要我们对右rv数据进行分组,根据左rv数据(父数据)进行归类。
这便需要用到ItemDecoration这个抽象类,我们需要自定义类继承该类。

public class DemoItemDecoration extends RecyclerView.ItemDecoration {
    private int mSectionHeight = 40;
    private Context context;
    // 头部列表集合
    private List<NavigationBean.DataBean.ArticlesBean> titles ;
    // 头部背景画笔
    private Paint bPaint;
    // 头部文字画笔
    private Paint TextPaint;
    public DemoItemDecoration(Context context, List<NavigationBean.DataBean.ArticlesBean> titles) {
        this.context = context;
        this.titles = titles;
        // 设置画笔
        bPaint = new Paint();
        bPaint.setColor(Color.GRAY);
        // 文字笔
        TextPaint = new Paint();
        // 颜色
        TextPaint.setColor(Color.WHITE);
        // 字体大小
        TextPaint.setTextSize(40);
    }
    @Override
    public void getItemOffsets(@NonNull Rect outRect, @NonNull View view, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.getItemOffsets(outRect, view, parent, state);
        // 获取列表的位置
        int position = parent.getChildAdapterPosition(view);
        // 在每个组上面空出一定距离 目前为40
        if(titles.get(position).isFirst()){
            outRect.top = mSectionHeight;
        }
    }
    @Override
    public void onDraw(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDraw(c, parent, state);
    }
    @Override
    public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
        super.onDrawOver(c, parent, state);
        // 获取列表长度
        int childCount = parent.getChildCount();
        for(int i = 0;i<childCount;i++){
            // child 为列表的view
            View child = parent.getChildAt(i);
            //获取列表位置
            int position = parent.getChildAdapterPosition(child);
            NavigationBean.DataBean.ArticlesBean app = titles.get(position);
            // 设置位于头部的文字
            if (app.isFirst()){
                // 获取四个方向的距离,来布置画布
                float sectionLeft = parent.getLeft();
                float sectionTop = child.getTop() - mSectionHeight;
                float sectionRight = parent.getWidth();
                float sectionBottom = child.getTop();
                c.drawRect(0,sectionTop,sectionRight,sectionBottom,bPaint);
                // 文字笔居中
                c.drawText(app.getChapterName(),sectionLeft,child.getTop()-5,TextPaint);
            }
        }
    }
}

在控制器的回调UI线程方法中调用DemoItemDecoration 类,将父数据作为参数传入。

    private void initSent() {
        ...
                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        initView();
                         // 增加头部。参数:context、右列表实体集合
                        DemoItemDecoration decoration = new DemoItemDecoration(MainActivity.this,rightBeans);
                        rightRv.addItemDecoration(decoration);
                    }
                });
		...
    }

3.最后

到此就为左右两个rv设置数据,同时添加给右边的Rv数据进行分组和添加每组头部。之后关于联动的代码逻辑和实现效果就在二为大家展现。
目前效果如下:
在这里插入图片描述

评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

薪火_

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

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

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

打赏作者

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

抵扣说明:

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

余额充值