CoordinatorLayout+ToolbarLayout+Behavior实现动态搜索框

2023-10-30


最终效果图(参照京东)

最终效果图

1、实现思路

CoordinatorLayout中可以用Behavior实现特定的布局位置和滑动效果,我们使用Behavior来控制搜索框的变换。

2、具体流程

首先在home_fragment.xml中使用CoordinatorLayout+AppBarLayout实现标题栏:

xml代码如下

<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".ui.home.HomeFragment">

    <android.support.design.widget.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:theme="@style/AppTheme.AppBarOverlay"
        app:elevation="0dp"
        app:layout_behavior="@string/header_logo_behavior"
        tools:ignore="MissingConstraints">

        <android.support.v7.widget.Toolbar
            android:id="@+id/toolbar"
            android:layout_width="match_parent"
            android:layout_height="?attr/actionBarSize"
            app:popupTheme="@style/AppTheme.PopupOverlay">

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

                <TextView
                    android:id="@+id/toolbar_tv"
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:text="@string/app_name"
                    android:textColor="@color/white"
                    android:textSize="20sp"
                    android:textStyle="italic" />
            </RelativeLayout>
        </android.support.v7.widget.Toolbar>
    </android.support.design.widget.AppBarLayout>
</android.support.design.widget.CoordinatorLayout>

不要忘记在HomeFragment类中实现代码:

Toolbar toolbar = (Toolbar) root.findViewById(R.id.toolbar);
        ((AppCompatActivity) getActivity()).setSupportActionBar(toolbar);

接下来我们添加标题栏右侧的菜单,Toolbar的菜单栏添加我们可以先在res/menu文件夹下新建menu_main.xml:

<menu xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools" tools:context=".MainActivity">
    <item android:id="@+id/action_scan"
        android:title="扫一扫"
        app:showAsAction="ifRoom"
        app:actionLayout="@layout/menu_action_scan"/>
    <item android:id="@+id/action_message"
        android:title="消息"
        app:showAsAction="ifRoom"
        app:actionLayout="@layout/menu_action_message"/>
</menu>

同时我们需要在HomeFragment类中复写onCreateOptionsMenu方法:

 @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu_main, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

并且我们需要调用方法setHasOptionsMenu(true),不然菜单无法展示。

接下来我们需要添加RecyclerView和Search搜索框,按照上面我们的实现思路,我们需要重写Behavior,给RecyclerView和Search搜索框分别配置Behavior,从而达到我们要的动态效果。我们先来看一下重写Behavior的流程,继承类CoordinatorLayout.Behavior< view >,重写layoutDependsOn和onDependentViewChanged方法。

  1. layoutDependsOn :是否依赖于特定view
  2. onDependentViewChanged : 依赖于的特定view有变化时触发
    根据以上得知我们需要一个依赖的view,当RecyclerView滑动时view滑动,特定view滑动出发Search搜索框做出变化,完成动态效果。为了不影响页面控件和滑动效果,我们在home_fragment.xml的CoordinatorLayout布局第一个子控件位置新建一个RelativeLayout(按照自己习惯选择),我们让其和标题栏颜色一致
<RelativeLayout
        android:id="@+id/image"
        android:layout_width="match_parent"
        android:layout_height="90dp"
        android:background="@color/colorPrimary" />

同时我们创建Search搜索框,注意其要在AppBarLayout之后避免被AppBarLayout盖住:

 <RelativeLayout
        android:id="@+id/search_rl"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginLeft="30dp"
        android:layout_marginRight="30dp"
        android:layout_marginBottom="10dp"
        android:gravity="right"
        android:visibility="visible"
        app:layout_behavior="@string/header_search_behavior">

        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="@dimen/collapsed_search_height"
            android:layout_alignParentRight="true"
            android:background="@drawable/stroke_corner_search_shape"
            app:layout_behavior=".header_float_behavior">

            <ImageView
                android:layout_width="20dp"
                android:layout_height="20dp"
                android:layout_centerVertical="true"
                android:layout_marginLeft="5dp"
                android:background="@drawable/search" />
        </RelativeLayout>
    </RelativeLayout>

@string/header_search_behavior在string.xml中实现:

  <string name="header_search_behavior">com.libinbin.uishow.behavior.HeaderSearchBehavior</string>   

指向HeaderSearchBehavior类,我们来看这个类的一下具体的实现

        @Override
    public boolean layoutDependsOn(CoordinatorLayout parent, View child, View dependency) {
        if (dependency != null && dependency.getId() == R.id.image) {
            return true;
        }
        return false;
    }

    @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, View child, View dependency) {
        if (dependency != null && dependency.getId() == R.id.image) {
            Resources resources = dependency.getResources();
            final float progress = 1.f -
                    Math.abs(dependency.getTranslationY() / (dependency.getHeight() - resources.getDimension(R.dimen.collapsed_header_height)));
            final float header_height = resources.getDimension(R.dimen.collapsed_header_height);
            final float offset_y = resources.getDimension(R.dimen.collapsed_float_offset_y);
            final float child_height = child.getHeight();
            final float child_height_end = (header_height - child_height) / 2 - (header_height + offset_y);
            child.setTranslationY(header_height + resources.getDimension(R.dimen.collapsed_float_offset_y));
            return true;
        }
        return false;

    }

首先我们需要依赖于我们设定的view,通过dependency.getId() == R.id.image判定,在onDependentViewChanged方法中我们给search搜索框(这个child就是我们的搜索框)设定位置,标题栏的高度header_height向上移一点。效果如下:

现在我们创建RecyclerView,我们只需要做个简单实现就可以,在home_fragment.xml的CoordinatorLayout布局的底部添加:

    <android.support.v7.widget.RecyclerView
        android:id="@+id/recyclerView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="#fff"
        app:layoutManager="LinearLayoutManager"
        app:layout_behavior="@string/header_scrolling_behavior" />

HomeFragment类中实现并添加adapter就可以了,这里我们就不做阐述了,我们还是来看@string/header_scrolling_behavior

    <string name="header_scrolling_behavior">com.libinbin.uishow.behavior.HeaderScrollingBehavior</string>

指向类HeaderScrollingBehavior,我们来分析一下这类要怎么实现,首先这个类同样继承自CoordinatorLayout.Behavior< RecyclerView>,view换成了RecyclerView,我们想要根据RecyclerView的滑动来滑动这个特定的view,我们需要复写滑动的相关方法:

- onStartNestedScroll :用户按下手指时触发,询问 NestedScrollParent 是否要处理这次滑动操作
- onNestedScrollAccepted :当 NestedScrollParent 接受要处理本次滑动后,这个回调被调用,
- onNestedPreScroll :当 NestedScrollChild 即将被滑动时调用,在这里你可以做一些处理。
- onNestedScroll : 上一个方法结束后,NestedScrollChild 处理剩下的距离。
- onNestedPreFling :用户松开手指并且会发生惯性滚动之前调用。参数提供了速度信息
- onStopNestedScroll :一切滚动停止后调用,如果不会发生惯性滚动,fling 相关方法不会调用,直接执行到这里。

这几个方法的定义我只做了简单的介绍,具体的方法用途在下边项目代码中都有标注,我们需要结合实际效果才能理清楚。
我们来看一下重点方法,首先我们还是要实现layoutDependsOn和onDependentViewChanged:

@Override
    public boolean layoutDependsOn(CoordinatorLayout parent, RecyclerView child, View dependency) {
        if (dependency != null && dependency.getId() == R.id.image) {
            if(dependentView==null){
                dependentView = new WeakReference<>(dependency);
            }
            return true;
        }
        return false;
    }

  @Override
    public boolean onDependentViewChanged(CoordinatorLayout parent, RecyclerView child, View dependency) {
        child.setTranslationY(dependency.getHeight() + dependency.getTranslationY());
        return true;
    }

因为在滑动系列方法中会用到这个dependency,所以我们单独定义了一个弱引用去存储它。滑动方法我们重点关注onNestedPreScroll和onNestedScroll

 @Override
    public void onNestedPreScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull RecyclerView child, @NonNull View target, int dx, int dy, @NonNull int[] consumed, int type) {
        if (dy < 0) {
            return;
        }
        View dependentView = getDependentView();
        float newTranslateY = dependentView.getTranslationY() - dy;
        float minHeaderTranslate = -(dependentView.getHeight() - getDependentViewCollapsedHeight());


        if (newTranslateY > minHeaderTranslate) {
            dependentView.setTranslationY(newTranslateY);
            consumed[1] = dy;
        }else{
            dependentView.setTranslationY(minHeaderTranslate);
        }
    }
    
    @Override
    public void onNestedScroll(@NonNull CoordinatorLayout coordinatorLayout, @NonNull RecyclerView child, @NonNull View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
        if (dyUnconsumed > 0) {
            return;
        }
        View dependentView = getDependentView();
        float newTranslateY = dependentView.getTranslationY() - dyUnconsumed;
        final float maxHeaderTranslate = 0;

        if (newTranslateY < maxHeaderTranslate) {
            dependentView.setTranslationY(newTranslateY);
        }else{
            dependentView.setTranslationY(maxHeaderTranslate);
        }
    }

特定view也就是dependentView的Y值取决于dy和dyUnconsumed,为了避免RecyclerView会有一个惯性滚动导致的dependentView突然卡顿的问题,我们需要在newTranslateY 于maxHeaderTranslate/minHeaderTranslate的判定不满足的情况下给定一个定值maxHeaderTranslate/minHeaderTranslate。到这里我们还没有做完,我们还需要在HeaderSearchBehavior的onDependentViewChanged方法中添加特定view和search搜索框的联动效果:

    final float collapsedMargin = resources.getDimension(R.dimen.collapsed_search_margin_right);
            final float zero = resources.getDimension(R.dimen.collapsed_float_zero);
            final float marginLeftRight = resources.getDimension(R.dimen.collapsed_search_margin_left2right);
            final int marginRight = (int) (collapsedMargin + (marginLeftRight - collapsedMargin) * progress);
            final int marginTop = (int) (child_height_end + (zero - child_height_end) * progress);
            final int marginLeft = (int) marginLeftRight;
            CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
            lp.setMargins(marginLeft, marginTop, marginRight, 0);
            child.setLayoutParams(lp);

到这里我们已经实现了RecyclerView和搜索框的联动效果。至于标题栏左侧文字的显隐效果更加简单,这里就不重复描述了,可以到项目中去看。

3、问题解决

在滑动测试的过程中我们发现RecyclerView的底部并不能完全展示,这里存在一个高度问题,解决办法是我们在HeaderScrollingBehavior类中复写方法onLayoutChild,设定其高度,具体实现:

    @Override
    public boolean onLayoutChild(CoordinatorLayout parent, RecyclerView child, int layoutDirection) {
        CoordinatorLayout.LayoutParams lp = (CoordinatorLayout.LayoutParams) child.getLayoutParams();
        if (lp.height == CoordinatorLayout.LayoutParams.MATCH_PARENT) {
            child.layout(0, 0, parent.getWidth(), (int) (parent.getHeight() - getDependentViewCollapsedHeight()));
            return true;
        }
        return super.onLayoutChild(parent, child, layoutDirection);
    }

项目地址

https://github.com/li-21/UIShow

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

CoordinatorLayout+ToolbarLayout+Behavior实现动态搜索框 的相关文章

  • 在 Android 中录制、保存和播放视频

    我正在尝试制作一个使用相机应用程序录制视频的应用程序 然后将该视频保存在 SD 卡上 以便我可以播放它 我有一些代码 但我不知道如何继续 因为我是 Android 初学者 我的活动 public class Camcorder extend
  • 如何在没有服务器的情况下通过套接字连接两个Android设备

    我正在尝试开发一个Android应用程序 它可以在没有服务器的情况下与其他设备进行点对点连接交换数据 所以请建议我该怎么做 先感谢您 这是一个无需服务器的 SocketProgramming 聊天的完整代码 在我的应用程序中 首先您是客户端
  • 使用 shouldInterceptRequest 阻止数据加载

    虽然这个问题已经被问过好几次了 但与我的问题真正相关的唯一问题是这个 我可以使用 shouldInterceptRequest 来阻止 Android 中的特定调用吗 https stackoverflow com questions 16
  • 一部又一部的Android动画

    我在 TextView 上有两个 TranslateAnimations 我希望它们一个接一个地执行 但是 通过使用下面的代码 仅执行第二个代码 我该如何解决这个问题 TranslateAnimation animation new Tra
  • MPAndroidChart - 饼图的所有部分都是相同的颜色

    我正在使用 MPAndroidChart https github com PhilJay MPAndroidChart https github com PhilJay MPAndroidChart 库来生成饼图 我遵循了多个教程 包括
  • CollapsingToolbarLayout 无法识别滚动 fling

    我创建了一个简单的折叠工具栏布局它就像一个魅力 我的问题是 如果我尝试在嵌套滚动视图 当我松开手指时它就会停止 正常的滚动就像它应该的那样工作 我的活动代码是不变 gt 自动生成空活动 我只是单击了 android studio 中的 创建
  • 透明模糊视图模糊了下面的布局

    我有一个已设为透明的 Linearlayout 现在我正在寻找一种方法来赋予它模糊效果 因此它下面的内容会变得模糊 就像 Windows 7 Aero 外观一样 参见屏幕截图 我知道你可以做这样的模糊效果 getWindow addFlag
  • Android 应用程序用户尝试更新时收到“应用程序未安装”消息

    UPDATE 对于那些询问用户收到哪些错误代码的人 没有错误代码 它只是打开一个空白的安装后页面 上面写着 该应用程序未安装 旁边有一个大 X 不同版本的 Android 可能有不同的消息 没有任何迹象表明安装过程中出了什么问题 更新2 一
  • 如何在 Google Maps API V2 中获取我的当前位置

    我正在创建一个应用程序 用户需要使用 getMyLocation 查看他 她的地理位置 但这返回 null 有没有解决这个问题的方法 因为我确实读到 getMyLocation 方法总是返回 null 我是 Google 地图新手 因此我们
  • Lombok 如何将代码生成到现有类中? [复制]

    这个问题在这里已经有答案了 我可以使用注释处理器从头开始生成类 但我无法像 lombok 那样修改类 我在 android studio 中搜索了 lombok 生成的类 但是我什么也没找到 然后我通过他们的网站检查了龙目岛概述 还在论坛中
  • FloatingActionButton 不隐藏

    我试图隐藏我的 FloatingActionButtonfabLocation以编程方式 fabLocation setVisibility View GONE 但它不起作用 如果我添加android visibility gone 在我的
  • 如何从内存中清除动态创建的视图?

    我试图从记忆中清除一些观点 情况是这样的 我有一个活动 我将其称为 A 另一个称为 B 现在 我在 Activity A 中按下一个按钮 该按钮调用 Activity B 动态创建大量视图 之后 我按后退按钮返回到活动 A 多次重复这两个步
  • SQLite CursorWindow 限制 - 如何避免崩溃

    我必须执行查询并将结果存储在列表中 我使用的函数如下 List
  • 在 KtorClient 的 DefaultRequest 中声明 ContentType = Application.Json 后,我可以更改特定请求的 ContentType 标头吗

    我正在开发我的 android 项目 我正在创建一个请求函数来上传文件 该函数发出如下请求 httpClient put uri body MultiPartFormDataContent formData append file file
  • android项目中视频文件放在哪里

    我有一个视频 我需要知道放置在哪里以及如何获取该视频的路径 我知道如何从 URL 添加视频 Uri uri Uri parse www abc com myVid mp4 videoView VideoView findViewById R
  • ImageView adjustViewBounds 不适用于相对布局

    我有一个ImageView并已设置其layout height为 100dp 其layout width为 wrap content 使用的绘图具有更大的实际尺寸 为 130dp 宽度 X 215dp 高度 当 的时候ImageView被放
  • ACRA formkey 哪里可以得到?

    所以我尝试按照以下说明进行操作https github com ACRA acra wiki BasicSetup https github com ACRA acra wiki BasicSetup但它太旧了或者什么的 使用我自己的 gm
  • 将文本文件写入 SD 卡失败

    我遇到了一个奇怪的问题 我的应用程序可以将一个简单的文本文件写入 SD 卡 有时它对某些人有效 但对其他人无效 我不知道为什么 对于某些人来说 如果他们输入一些字符 例如 在文件等中 我似乎无法重现它 因为我没有遇到任何麻烦 但这是处理文件
  • 如何从 Activity 的包含类启动 Intent

    我正在寻找从不是 Activity 而是 Activity 类的包含对象的类启动意图的最佳方法 例如活动类 Class MainActivity extends ListActivty TestLauncher tester 以及我想从中开
  • DDMS 未显示来自设备的线程

    我想使用以下命令检查我的 Android 应用程序中是否存在内存泄漏DDMS特色于Eclipse 当我启动模拟设备时 线程会正确显示模拟设备 从 8600 及更高版本开始 然而 当我将 Droid 连接到 PC 时 该设备在 DDMS 中显

随机推荐

  • 汽车部件IPX9K/IP69K、IP66K等ip防护等级测试的应用

    汽车部件IPX9K IP69K IP66K等ip防护等级测试的应用 汽车传感器 连接器 水泵 灯具等部件的ip防护等级测试 以IPX9K IP69K IPX5 IPX6 IPX6K IPX7 IPX8 IP5X IP6X测试为主流 其中高等
  • Flutter——最详细(GridView)使用教程

    GridView简介 可以创建网格列表视图 主要通过Count extent custom builder构造列表 有内边距 是否反向 滑动控制器等属性 四个属性使用场景 Count extent custom适用于子布局较少时使用 可能会
  • 【区块链共识算法】-PoW算法

    极客时间 工作量证明 比如小李来 BAT 面试 说自己的编程能力很强 那么他需要做一定难度的工作 比如做个编程题 根据做题结果 面试官可以判断他是否适合这个岗位 工作对于请求方是有难度的 对于验证方则是比较简单的 易于验证的 Pow算法 计
  • vue如何制作动态效果的进度条

    vue如何制作动态效果的进度条 先看效果图 制作这样动效的进度条其实很简单 我们只需要将进度条原本的背景通过transparent画出如下图片的效果 之后我们通过keyframes设置背景的动画效果就做成了 下面上代码 首先进度条的进度提示
  • 使用Python爬取实时天气信息: 如何构建自己的气象观察站

    目录 步骤1 选择天气网站 步骤2 发送HTTP请求 步骤3 解析HTML内容 步骤4 提取天气数据
  • 1. CUDA安装失败解决方法

    CUDA安装失败原因 一般CUDA安装失败都是由于其中Visual Studio VS Intergration无法安装导致的 当然可以通过自定义的方式取消Visual Studio Intergration进行安装 然后再重新用CUDA安
  • zookeeper最新版3.6.2单机、集群

    Linux安装zookeeper3 6 2单机 集群 注意 需要先安装JDK 可以参考这里 Linux 安装JDK1 8 1 下载 wget http mirror bit edu cn apache zookeeper zookeeper
  • 学习Node.js的基础知识和核心概念(全面)

    Node js 这个神奇的技术 融合了前端与后端的力量 让JavaScript在服务器端发挥了异乎寻常的魔力 本文将通过代码和文字解释 全面介绍Node js的特点 从异步非阻塞I O到强大的模块系统 再到丰富的包管理和事件驱动编程 一步步
  • JAVA同步代码块 & 同步方法

    JAVA同步代码块 同步方法 为了解决多线程操作共享数据时产生的安全问题 例如以下代码 if ticket lt 0 卖完了 break else ticket System out println Thread currentThread
  • dss_linkis(datasphere studio-1.1.1、linkis-1.1.1)基础框架安装

    目录 一 基础框架安装 1 1 所需的环境 1 2 环境部署 1 3 dss linkis安装 一 基础框架安装 1 1 所需的环境 我的安装环境如下 与官网给出的相差一点点 CentOS7 DataSphere Studio1 1 1 J
  • 线程的局部变量——ThreadLocal

    ThreadLocal是什么 对这个词语分解 将其分为Thread和Local 顾名思义便是本线程的变量 既然是当前线程的变量 那么就意味着这个变量对于其他线程来说就是隔离的 也就是不可见的 ThreadLocal对每一个线程都有一个副本
  • Eclipse汉化教程

    前言 首次使用Eclipse时 我们对那些不知道的英语都感到迷惑 很多人都会上X度查 那么 如果不用X度 我们该如何进行汉化呢 1 Babel链接获取 到Eclipse Babel Project Downloads获取Babel链接 如图
  • TCP的四个拥塞控制算法

    目录 假定 慢开始 拥塞避免算法 快重传 快恢复 假定 cwnd 拥塞窗口 swnd 发送窗口 swnd cwnd ssthresh 门限值 发送方维护一个叫做拥塞窗口cwnd的状态变量 其值取决于网络的拥塞程度 并且动态变化 拥塞窗口cw
  • 【C语言进阶】⑥函数指针详解

    一 函数指针 1 概念 函数指针 首先它是一个指针 一个指向函数的指针 在内存空间中存放的是函数的地址 请看示例 int main int a 10 int pa a char ch c char pc ch int arr 10 0 in
  • java调用C或者C++动态库dll

    java调用C或者C 动态库dll 本文章使用的是IntelliJ IDEA Community Edition 2021 2 3版本测试的 1 新建项目 linjie demo 添加类HelloLinjie 2 选择项目 新建 目录 输入
  • redis 5 HyperLogLog 布隆过滤器 GeoHash 和 scan

    空闲的时候可以用root登录服务器 玩下左轮手枪 RANDOM 6 0 rm rf echo Clicks 这次我们一起来看下redis的HyperLogLog 布隆过滤器 GeoHash 和 scan HyperLogLog 先看个场景
  • 第1章 前 言

    来源 我是码农 转载请保留出处和链接 本文链接 http www 54manong com id 1258 1 1 问题的背景 1 1 1 RFID技术 RFID即无线射频识别技术 Radio Frequency Identificatio
  • 一维动态规划-拾取硬币

    1 问题引入 假如有n个硬币排在一行 如 c 0 c 1
  • opengl测试操作

    深度测试 深度缓冲 Depth Buffer 来防止被阻挡的面渲染到其它面的前面 在这一节中 我们将会更加深入地讨论这些储存在深度缓冲 或z缓冲 z buffer 中的深度值 Depth Value 以及它们是如何确定一个片段是处于其它片段
  • CoordinatorLayout+ToolbarLayout+Behavior实现动态搜索框

    文章目录 最终效果图 参照京东 1 实现思路 2 具体流程 3 问题解决 项目地址 最终效果图 参照京东 1 实现思路 CoordinatorLayout中可以用Behavior实现特定的布局位置和滑动效果 我们使用Behavior来控制搜