Android RecyclerView 之 吸顶效果

news2024/11/18 8:47:56

前言

上一篇文章已经实现了列表跟宫格布局的动态切换,这篇文章主要来说通过 CoordinatorLayoutAppbarLayout 的配合,以及 NestedScrollView 来实现吸顶效果 。效果如下。
 

一、CoordinatorLayout 是什么?

CoordinatorLayout 是 Android Support Library (安卓支持库) 中的一个布局容器,用于实现协调子 View 之间的交互和动画效果。它是基于观察者模式设计的,可以根据子 View 之间的关系和事件,实现协调和控制子 View 的行为,它允许你在一个复杂的应用界面中实现各种协调动作和交互效果。它是支持 Material Design 的一个重要组件,可以用于创建各种复杂的布局和动画效果。

二、AppbarLayout 是什么?

AppBarLayout 是 Android Support Library (安卓支持库) 中的一个布局容器,通常结合 Toolbar 和 CollapsingToolbarLayout 使用,用于实现可折叠的应用栏效果。它提供了一种简便的方式来实现应用栏的滚动和折叠效果。它在内部做了很多滚动的事件的封装。CollapsingToolbarLayout 也是安卓支持库中的一个容器,用于实现可折叠的应用栏效果。

三、NestedScrollView 是什么?

NestedScrollView 是 Android Support Library (安卓支持库) 中的一个可嵌套滚动的视图容器,它扩展自 ScrollView,能够在嵌套滚动的情况下,处理多个滚动视图之间的滚动冲突。

四、实践

所以不难理解,当 CoordinatorLayout 和 AppBarLayout 配合使用时,实现吸顶效果。通常的布局结构是:CoordinatorLayout 作为最外层容器,内部包含一个 AppBarLayout,AppBarLayout 内部包含一个可滚动的视图NestedScrollView。通过这样的布局结构,当用户滚动 NestedScrollView 时,AppBarLayout 中的 Toolbar 和 CollapsingToolbarLayout 会根据滚动的位置来调整自身的显示状态。当向上滚动时,AppBarLayout 进行折叠,Toolbar 可以隐藏,当向下滚时,AppBarLayout 展开,Toolbar 显示出来。为了实现吸顶效果,需要在 AppBarLayout 中 CollapsingToolbarLayout 上添加一个属

 app:layout_scrollFlags="scroll|enterAlways|snap"

其中,"scroll" 表示支持滚动,"enterAlways" 表示当向下滚动时,始终进入可见状态,"snap" 表示当滚动事件结束时,自动将 Toolbar 完全显示或隐藏。,为了实现 NestedScrollView 的吸顶效果,可以在NestedScrollView 的父容器上设置属性

app:layout_behavior="@string/appbar_scrolling_view_behavior"

这样可以将 NestedScrollView 与 AppBarLayout 关联起来,以实现滚动时的协调效果。接下来就来一步步实现

1.activity_news_info.xml

新建一个 activity ,命名为 NewsInfoActivity,用作文章详情页展示,我们主要看到吸顶布局结构

CoordinatorLayout 包含着 AppBarLayout ,AppBarLayout 包含着 CollapsingToolbarLayout 可折叠布局 其中包含着自定义 header 头布局,在 CollapsingToolbarLayout外,AppBarLayout 还包含着 自定义 inside_fixed_bar 布局 即固定悬浮框,在之外就是 NestedScrollView 包含 RecyclerView 列表,这样大体的吸顶效果的布局就搭建完毕。可扩展性很强。布局如下,

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <per.goweii.actionbarex.ActionBarEx
        android:id="@+id/abc_main_return"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="#14a4fb"
        app:ab_autoImmersion="false"
        app:ab_bottomLineColor="#f3f3f3"
        app:ab_bottomLineHeight="0dp"
        app:ab_statusBarColor="#00000000"
        app:ab_statusBarMode="dark"
        app:ab_statusBarVisible="true"
        app:ab_titleBarHeight="50dp"
        app:ab_titleBarLayout="@layout/top" />


    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:fitsSystemWindows="false">

        <com.google.android.material.appbar.AppBarLayout
            android:id="@+id/app_bar"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:fitsSystemWindows="false">


            <com.google.android.material.appbar.CollapsingToolbarLayout
                android:id="@+id/toolbar_layout"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:fitsSystemWindows="false"
                app:layout_scrollFlags="scroll|exitUntilCollapsed"
                app:statusBarScrim="@android:color/transparent">

                <include layout="@layout/header" />

            </com.google.android.material.appbar.CollapsingToolbarLayout>


            <include layout="@layout/inside_fixed_bar" />
        </com.google.android.material.appbar.AppBarLayout>

        <androidx.core.widget.NestedScrollView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            app:layout_behavior="@string/appbar_scrolling_view_behavior">

            <androidx.recyclerview.widget.RecyclerView
                android:id="@+id/recycler_view"
                android:layout_width="match_parent"
                android:layout_height="match_parent" />

        </androidx.core.widget.NestedScrollView>
    </androidx.coordinatorlayout.widget.CoordinatorLayout>
</LinearLayout>

2.CommentsAdapter

接下来就是吸顶后展示的列表数据适配器

public class CommentsAdapter extends BaseQuickAdapter<NewsCommentBean.Comments, BaseViewHolder> {
    public CommentsAdapter(int layoutResId, @Nullable List<NewsCommentBean.Comments> data) {
        super(layoutResId, data);
    }

    @Override
    protected void convert(@NonNull BaseViewHolder helper, NewsCommentBean.Comments item) {
        try {
            helper.setText(R.id.comment_author_tv, item.getAuthor());
            helper.setText(R.id.comment_info_tv, item.getContent());
            helper.setText(R.id.comment_link_tv, item.getLikes());
            String s = App.dateToStamp(item.getTime());
            helper.setText(R.id.comment_time_tv, s);
            String avatar = item.getAvatar();
            ImageView iconImg = helper.getView(R.id.comment_icon_img);
            //Glide设置圆形图片
            RequestOptions options = new RequestOptions().circleCropTransform();
            Glide.with(mContext).load(avatar).apply(options).into(iconImg);
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

3.comments_item_layout.xml

有适配就需要有子布局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="160dp"
    android:orientation="horizontal">

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="1"
        android:gravity="center_horizontal">

        <ImageView
            android:id="@+id/comment_icon_img"
            android:layout_width="60dp"
            android:layout_height="60dp" />

    </LinearLayout>

    <LinearLayout
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:layout_weight="3"
        android:orientation="vertical">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:orientation="horizontal">

            <TextView
                android:id="@+id/comment_author_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/dp_10"
                android:text="唐吉坷德" />


        </RelativeLayout>


        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content">

            <TextView
                android:id="@+id/comment_info_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_marginLeft="@dimen/dp_10"
                android:text="波兰,尼日利亚都木有劲本届杯赛"
                android:textStyle="bold" />

        </LinearLayout>

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="0dp"
            android:layout_weight="1"
            android:gravity="center_vertical"
            android:orientation="vertical">

            <TextView
                android:id="@+id/comment_time_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_alignParentLeft="true"
                android:layout_centerVertical="true"
                android:layout_marginLeft="@dimen/dp_10"
                android:text="48分钟前" />

            <TextView
                android:id="@+id/comment_link_tv"
                android:layout_width="wrap_content"
                android:layout_height="wrap_content"
                android:layout_centerVertical="true"
                android:layout_marginRight="@dimen/dp_10"
                android:layout_toLeftOf="@+id/zan_img"
                android:text="50" />

            <ImageView
                android:id="@+id/zan_img"
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_alignParentRight="true"
                android:layout_centerVertical="true"
                android:layout_marginRight="10dp"
                android:background="@mipmap/zan" />

            <View
                android:layout_width="match_parent"
                android:layout_height="0.5dp"
                android:layout_alignParentBottom="true"
                android:layout_marginLeft="5dp"
                android:layout_marginTop="5dp"
                android:layout_marginRight="5dp"
                android:background="@color/light_gray" />
        </RelativeLayout>


    </LinearLayout>


</LinearLayout>

4.NewsInfoActivity

 在上一篇文章中 添加个子项点击事件 进入到 NewsInfoActivity 当中,并且传入相关的url以及新闻id以及新闻标题,此处要注意下为什么要把点击事件单独写在一个方法里呢 ,因为我在运行的时候在切换宫格和列表后子项点击事件失效,所以封装在一个方法里后,重新再调用方法即可

    private void adaperChick() {
        adapter.setOnItemChildClickListener(new BaseQuickAdapter.OnItemChildClickListener() {
            @Override
            public void onItemChildClick(BaseQuickAdapter adapter, View view, int position) {
                //新闻ID
                String news_id = mList.get(position).getNews_id();
                //新闻网址
                String url = mList.get(position).getUrl();
                //新闻标题
                String title = mList.get(position).getTitle();
                Intent intent = new Intent(MainActivity.this, NewsInfoActivity.class);
                intent.putExtra("url", url);
                intent.putExtra("news_id", news_id);
                intent.putExtra("title", title);
                startActivity(intent);
            }
        });
    }

public class NewsInfoActivity extends BaseActivity {

    private String news_id;
    private TextView infoTv;
    private RecyclerView recyclerView;
    private LinearLayoutManager linearLayoutManager;
//    private NormalAdapter normalAdapter;
    private TextView titleTv;
    private String title;
    private TextView titleInfoTv;
    private ImageView btnImg;
    private List<NewsCommentBean.Comments> recentList = new ArrayList<>();
    private LinearLayoutManager layoutManager;
    private CommentsAdapter adapter;
    private TextView numTv;
    private LinearLayout backLayoput;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_news_info);


        Intent intent = getIntent();
        news_id = intent.getStringExtra("news_id");
        title = intent.getStringExtra("title");
        infoTv = findViewById(R.id.info_tv);
        initData(news_id);
        initView();
        initComments(news_id);
        titleTv.setText("文章详情");
        titleInfoTv.setText(title);
        btnImg.setVisibility(View.GONE);

        layoutManager = new LinearLayoutManager(NewsInfoActivity.this);
        recyclerView.setLayoutManager(layoutManager);
        recyclerView.setNestedScrollingEnabled(false);
        adapter = new CommentsAdapter(R.layout.comments_item_layout, recentList);
        recyclerView.setAdapter(adapter);

    }


    private void initComments(String id) {
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://news-at.zhihu.com/")
                //设置数据解析器
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiUrl apiUrl = retrofit.create(ApiUrl.class);
        Call<NewsCommentBean> comment = apiUrl.getComment(id);
        comment.enqueue(new Callback<NewsCommentBean>() {
            @Override
            public void onResponse(Call<NewsCommentBean> call, Response<NewsCommentBean> response) {
                NewsCommentBean body = response.body();
                Gson gson = new Gson();
                String s = gson.toJson(body);
                List<NewsCommentBean.Comments> recent = body.getRecent();

                if (recent.size() > 0) {
                    try {
                        recentList.addAll(recent);
                        adapter.setNewData(recentList);

                        runOnUiThread(new Runnable() {
                            @Override
                            public void run() {
                                numTv.setText(recent.size() + "");
                            }
                        });

                    } catch (Exception e) {
                        String message = e.getMessage();
                        e.printStackTrace();
                    }
                }

            }

            @Override
            public void onFailure(Call<NewsCommentBean> call, Throwable t) {

            }
        });

    }

    private void initView() {
        recyclerView = findViewById(R.id.recycler_view);
        titleTv = findViewById(R.id.top_tv_function);
        titleInfoTv = findViewById(R.id.title_tv);
        btnImg = findViewById(R.id.btn_main_menu);
        numTv = findViewById(R.id.num_tv);
        backLayoput = findViewById(R.id.btn_back_layout);
        backLayoput.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                finish();
            }
        });
    }

    private void initData(String news_id) {

        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://news-at.zhihu.com/")
                //设置数据解析器
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        ApiUrl apiUrl = retrofit.create(ApiUrl.class);
        Call<NewsInfoBean> newsInfoBean = apiUrl.getNewsInfoBean(news_id);
        newsInfoBean.enqueue(new Callback<NewsInfoBean>() {
            @Override
            public void onResponse(Call<NewsInfoBean> call, Response<NewsInfoBean> response) {
                NewsInfoBean body = response.body();
                String body1 = body.getBody();
                Document doc = Jsoup.parse(body1);
                Elements elements = doc.select("div.content"); //获取<div class="content">里的内容
                for (Element element : elements) {
                    String text = element.text(); //获取标签内的文本内容
                    infoTv.setText(text); //将解析出来的文本内容设置到TextView上
                }

            }

            @Override
            public void onFailure(Call<NewsInfoBean> call, Throwable t) {

            }
        });

    }

}

总结

一个小小的很实用的功能就完成了,下一篇文章会接着实现RecyclerView 多布局效果,后续还会加上列表本地缓存等功能,Demo 在此系列文章完结附上,不妨点赞收藏哦~

青山不改,绿水长流 有缘江湖再见 ~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/956457.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

pnpm快速创建 Vue.js 项目(npm类似)

目录 pnpm 创建一个 Vue.js 项目 前提准备&#xff1a; 运行创建命令&#xff1a; 选择项目配置&#xff1a;&#xff08;按需选择&#xff09; cd 项目名&#xff1a;&#xff08;进入项目终端&#xff09; 安装项目依赖&#xff1a; 运行项目&#xff1a; pnpm 创建一…

文献速读|5分的生信+免疫组化:单细胞测序转录组联合bulk转录组肿瘤预后模型

今天给大家分享一篇IF5.8的纯生信单细胞联合Bulk转录组构建预后模型的文章&#xff0c;于2023年3月19日发表在Cancer Immunology Immunotherapy上&#xff1a;Integrative analyses of bulk and single-cell RNA-seq identified cancer-associated fibroblasts-related signatu…

Docker安装详细步骤

Docker安装详细步骤 1、安装环境准备 主机&#xff1a;192.168.40.5 zch01 设置主机名 # hostnamectl set-hostname zch01 && bash 配置hosts文件 [root ~]# vi /etc/hosts 添加如下内容&#xff1a; 192.168.40.5 zch01 关闭防火墙 [rootzch01 ~]# systemct…

W5500-EVB-PICO通过SNTP获取网络时间(十一)

前言 上一章我们用W5500_EVB_PICO 开发板做Ping数据测试IP检测连通性&#xff0c;那么本章我们进行W5500_EVB_PICO SNTP的测试。 什么是NTP&#xff1f; NTP(Network Time Protocol&#xff09;网络时间协议基于UDP&#xff0c;用于网络时间同步的协议&#xff0c;使网络中的计…

如何高效进行测试用例评审

1.用例评审的目的 为了减少测试人员执行阶段做无效工作&#xff0c;执行无效case&#xff0c;提交无效缺陷&#xff08;可以友情提醒研发同学&#xff0c;讲到自己负责的相关模块时&#xff0c;注意下是否存在异议点&#xff09;为了避免三方&#xff08;产品、研发、测试&…

多项式求逆

已知 F F F&#xff0c;求 G G G 考虑倍增 F ( x ) ∗ H ( x ) ≡ 1 ( m o d x n / 2 ) F(x) * H(x) \equiv 1 \pmod{x^{n/2}} F(x)∗H(x)≡1(modxn/2) F ( x ) ∗ G ( x ) ≡ 1 ( m o d x n / 2 ) F(x) * G(x) \equiv 1 \pmod{x^{n/2}} F(x)∗G(x)≡1(modxn/2) 假设 H H…

噪声的产生机理和来源

引言&#xff1a;噪声广泛存在于自然界&#xff0c;上节揭示了噪声的本质&#xff0c;噪声按照噪声携带能量的强弱分为功率型噪声和信号型噪声&#xff0c;功率型噪声持续时间短&#xff0c;能量强&#xff0c;对设备的寿命具有很大的影响&#xff0c;而信号型噪声顾名思义来源…

不想出门?那就把“自然”搬进家里吧!

为自然主题房间寻找灵感&#xff1a; 简单创意 以自然为主题的房间将自然灵感的色调&#xff08;棕色、灰色、米色、白色、蓝色和绿色&#xff09;与皮革、木材、黄麻和藤条等纹理相结合。就像真实的户外一样&#xff0c;它也懂得平衡的力量。户外&#xff1a; 每一片树丛&…

云备份——第三方库简单介绍并使用(上)

目录 一&#xff0c;Jsoncpp库序列化和反序列化 二&#xff0c;bundle文件压缩库 2.1 文件压缩 2.2 文件解压 一&#xff0c;Jsoncpp库序列化和反序列化 首先我们需要先了解一下json是什么&#xff0c;json是一种数据交换格式&#xff0c;采用完全独立于编程语言的文本格式来…

开学有哪些电容笔值得买?平价电容笔排行榜

苹果的原装Pencil&#xff0c;无疑是一款性能出色的电容笔&#xff0c;但它的价格也很高&#xff0c;如果不小心弄丢了或者弄坏了&#xff0c;那就太让人心痛。再说了&#xff0c;一支价值不菲的电容笔&#xff0c;要是不是用于专业绘画&#xff0c;实在是大材小用。不过&#…

01. 二进制原理

道家阴阳启发了莱布尼茨提出二进制&#xff01; 1. 为什么是二进制&#xff1f; 1.1 二进制与硬盘&#xff08;磁盘&#xff09; 硬盘也叫磁盘&#xff0c;它内部是排列整齐的一个个小磁体。 磁盘上面有“电刷”&#xff0c;磁盘可以转动&#xff0c;“电刷”也可以上下移动…

Python 带参数的装饰器

首先我们定义一个可以打印日志的装饰器&#xff1a; def log(func):def wrapper(*args, **kwargs):print(call %s(): % func.__name__)return func(*args, **kw)return wrapper它接受一个函数作为输入&#xff0c;再返回一个函数。我们使用一下这个装饰器 log def now():prin…

【esp32】解决以太网+mqtt堆栈溢出问题 报错 no mem for receive buffer

本文主要记录了 esp32 + 以太网 +mqtt 功能时遇到的堆栈溢出的情况,千里之堤毁于蚁穴,开发过程的不细心导致多付出了一天多的时间,记录于此,共勉 📋 个人简介 💖 作者简介:大家好,我是喜欢记录零碎知识点的小菜鸟。😎📝 个人主页:欢迎访问我的 Ethernet_Comm 博…

一文看懂DETR(二)

训练流程 1.输入图像经过CNN的backbone获得32倍下采样的深度特征&#xff1b; 2.将图片给拉直形成token&#xff0c;并添加位置编码送入encoder中&#xff1b; 3.将encoder的输出以及Object Query作为decoder的输入得到解码特征&#xff1b; 4.将解码后的特征传入FFN得到预测特…

Rethinking the Role of Demonstrations: What Makes In-Context LearningWork?

背景 这篇论文主要验证了prompt demos对模型的作用是什么&#xff0c;主要几个方面的影响&#xff1a; 1.prompt demo的input和label的映射关系对结果影响非常小&#xff0c;模型没有从mapping关系中学到太多信息。这是比较反直觉的 2.模型从input输入的数据空间和label输出的…

VR全景与家居行业碰撞在一起,会迸发怎样的火花?

元宇宙的热度一直在持续&#xff0c;由于前几年疫情反复&#xff0c;很多人只能居家办公&#xff0c;这也让很多线下实体行业非常难做&#xff0c;元宇宙的兴起可以带动线下行业把生意做起来&#xff0c;那么家居行业、元宇宙、VR全景这些元素碰撞在一起&#xff0c;会迸发出怎…

从Instagram到TikTok:利用社交媒体平台实现业务成功

自 2000年代初成立和随后兴起以来&#xff0c;社交媒体一直被大大小小的品牌用作高度针对性的营销工具&#xff0c;自 Facebook推出近二十年以来&#xff0c;这些网站继续彻底改变企业处理广告的方式。 在这篇博文中&#xff0c;我们将讨论订阅企业应该如何从整体上对待社交媒…

秋云qiun chart 使用echart配置格式化柱形图数据

dataLabel数据文字格式化 <!-- seriesTemplate是config-echarts.js中对应图表类型定义好的series模板 &#xff0c;如果每个series的formatter都不一样&#xff0c;则format需要定义在chartData.series中&#xff0c;不能使用seriesTemplate --> <!-- formatter…

Java 面试 - Redis

Redis Redis 是基于键值对的非关系型数据库。Redis 拥有string、hash、list、set、zset等多种数据结构, redis具有惊人的读写性能, 其优秀的持久化机制是的它在断电和机械故障时也不会发生数据丢失, 可以用于热点数据存放, 还提供了键过期、发布订阅、食物、流水线、LUA脚本等多…

在node项目中通过jsencrypt实现内容加密

上来肯定先引入第三方依赖 npm install jsencrypt然后 我们创建一个js文件 叫 rsaEncrypt.js 参考代码如下 import JSEncrypt from jsencrypt/bin/jsencrypt.min// 密钥对生成 http://web.chacuo.net/netrsakeypairconst publicKey MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBANL378k…