顶部动态伸缩搜索框的简单实现

项目上需要一个点击输入框,然后输入框拉长的需求,自己参考了别人的两个案例简单地实现了一下,作为一个新人开发来说,思路很简单,但是也是相当花时间。本人开发时间不足一年,所以写的代码可能有点烂,如有何处处理不当或者错误的地方请直说。

太年轻走了太多弯路。之后也有遇到更好的方案,但也没更新这篇文。
直接使用ObjectAnimator对view进行控制,不需要啰里啰唆这么多代码。

 ObjectAnimator.ofInt(view, "width", view.getWidth(), maxValue).setDuration(duration).start();

与之前不同的是少了通过view当前的宽度在目标宽度的比例乘以默认持续时长duration得到需要的时长
不加的原因有两个:1、动画时间较短,这部分的时间差基本很难感受到差别。2、增加时间计算的话又会啰里啰唆加很多代码。所以在投入较多收益不高的情况下选择性价比最高的方案。
已更新至Github
============================================================
以下内容过时,暂时留做记录

  日常的基于百度的项目开发与实现 。进入百度搜索能找到类似需求的这两片文章

  第一篇排版有点乱,原谅我没怎么仔细看下去,原理应该是通过ScrollView的滚动监听然后设置搜索框的宽度,稍微仔细阅读过后发现竟然继承了ScrollView自定义了一个,这个处理方法在我觉得不太简单方便。开头的动图发现作者对动画的过度不够平滑,还有就是搜索框展开了之后都突出屏幕了,这些都是细节的问题,虽然是个demo但是看着也有点难受。

  第二篇也差不多,不过在动画方面利用了系统提供的Transition提供了一个不错的效果,就算在Android 5.0以下,只要引入api 'com.android.support:design:25.3.1'就没有问题,但是我就是想自己试试,而且在看到需求的时候就已经有用属性动画的想法了,而Transition刚好是属性动画的封装,所以就尝试着自己写了一下。事实证明,还是调用现有的比较方便,不过自己写一遍也算是有点收获吧。

  先上一张自己做的效果图吧,效果也还行,不过写的过程中遇到的问题挺多的。最后卡了一下是git录制原因。说好的点击展开呢,怎么又变滑动展开了??其实有留点击展开的方法啦,只不过滑动看着更有意思!

示例图

想法

  通过监听ScrollView的滚动触发伸缩动画,通过属性动画产生一个平滑的过度数值,可以说是相当简单了。当然,直接使用Transition,只要设置好开始时的布局、结束的布局以及动画持续的时间就能更快地实现了。

动手

  因为本身需求原因,不是特别需要透明度的变化,所以会主要说明动画方面。代码中很多能用lambda表达式的地方都用了,用多了就会了,虽然可读性变差了,但是真的是简洁。
  首先是用到了属性动画中的ValueAnimator,它能够提供一个平滑的过度数值。没有使用ObjectAnimator是因为刚接触属性动画,对于ObjectAnimator中的拉伸还没有仔细研究,但是看别人的示例图发现ObjectAnimator的拉伸是对view包括其中的字体等都会有拉伸效果,所以暂时先不考虑使用,后续会进行尝试。
  在使用ValueAnimator需要提供开始值和结束值,这就需要对绘制完成的view进行宽度测量,这里采用View.post()的方式,获得view的宽度。给自己的笔记:margin值不会被计入width

subView.post(() -> {
  isSubPost = true;
  subViewWidth = subView.getWidth();
});
parentView.post(() -> {
  isParentPost = true;
  margin = parentView.getPaddingLeft() + parentView.getPaddingRight();
  parentViewWidth = parentView.getWidth();
});

在触发动画的时候,需要注意以下几点:

  • 动画还未开始,非常简单直接从开始到结束
  • 动画进行中,但下一个动画还是当前动画,同上,只要忽略多余的,继续执行当前的动画即可
    例如:搜索框正在展开,然后又触发了展开的操作,这个情况会在SrcollView上滑触发展开动画后
  • 动画进行中,但下一个动画不是当前动画
    例如:展开动画还没有结束时,发生了收缩动画,这时候就要停止展开的动画,从当前位置开始收缩动画
  • 动画完成
    上面需要重点关注的就是第三点
public void expand() {
  //进入此处说明必须展开搜索框
  isExpand = true;
  boolean c; //确定有没有正在进行搜收缩动画
  if (closeAnimator == null) {
    c = false;
  } else {
    c = closeAnimator.isRunning();
  }
  if (subView.getWidth() == subViewWidth || c) {
  //当subView.getWidth() == subViewWidth时说明动画还未开始
    if (openAnimator != null && openAnimator.isRunning()) return; //正在进行当前动画,无需进行下去,直接return
    if (closeAnimator != null) closeAnimator.cancel();//c为true时存在收缩动画,进行cancel
      openAnimator = ValueAnimator.ofFloat(subView.getWidth(), parentViewWidth - margin);
      openAnimator.setDuration(getDuration(duration, true));//获得动画需要的时间
      openAnimator.addUpdateListener(animator -> updata(animator));//动画数值监听
      openAnimator.start();//开始动画
    } else if (openAnimator != null || openAnimator.isRunning()) {
      //这里是动画正在进行中
      //本来可以和上面归并的,但是openAnimator一直要做非空判断,不然可能会出现空指针异常
      //具体还要自己再理理
    }
}

收缩的部分和展开的基本类似,updata(ValueAnimator animator)就是更新view的宽度

private void updata(ValueAnimator animator) {
  float currentValue = (float) animator.getAnimatedValue();
  subView.getLayoutParams().width = (int) currentValue;
  subView.requestLayout();
}

getDuration(long duration, boolean isReverse)计算动画需要的时长,通过view当前的宽度在目标宽度的比例乘以默认持续时长duration得到需要的时长,isReverse用于指明是收缩还是展开,主要用在展开进行一半时被停止,然后接着执行收缩。用Transition实现可没有这个特点哦,也就是说view在展开一半的时候进行收缩的话,使用的可是原来全程的时间哦。(总算自己写的还有点微不足道的用处)

private long getDuration(long duration, boolean isReverse) {
  if (isReverse)
    return duration * (parentViewWidth - margin - subView.getWidth()) / (parentViewWidth - margin - subViewWidth);
  else
    return duration * (subView.getWidth() - subViewWidth) / (parentViewWidth - margin - subViewWidth);
}

setDuration(long duration)设置动画持续时间,不设置时默认300毫秒。

public void setDuration(long duration) {
  this.duration = duration;
}

doAnimat()是为点击展开收缩预留的方法

public void doAnimat() {
  Log.i(tag, "doAnimat");
  if (subView.getWidth() == subViewWidth) {
    Log.i(tag, "doAnimat expand");
    expand();
  } else if (subView.getWidth() == parentViewWidth - margin) {
    Log.i(tag, "doAnimat reduce");
    reduce();
  }
}

最后甩上最后一点东西
方法的调用

telescopicAnimator = new TelescopicAnimator(findViewById(R.id.ll_bg));//实例化,只需要被拉伸的那个view
//在监听中直接食用即可
scrollView.getViewTreeObserver().addOnScrollChangedListener(() -> {
  if (scrollView.getScrollY() >= (imageView.getHeight() - relativeLayout.getHeight())) {
    telescopicAnimator.expand();
  } else if (scrollView.getScrollY() <= 0) {
    telescopicAnimator.reduce();
  }
});

以及activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="match_parent"
  android:layout_height="match_parent">
  <ScrollView
    android:id="@+id/sl_view"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:scrollbars="none">
      <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="2000dp">
      <ImageView
        android:id="@+id/iv_"
        android:layout_width="match_parent"
        android:layout_height="200dp"
        android:scaleType="centerCrop"
        android:src="@mipmap/img" />
      </RelativeLayout>
    </ScrollView>

    <RelativeLayout
        android:id="@+id/rl_bg"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorPrimary"
        android:fitsSystemWindows="true">
        <RelativeLayout
          android:layout_width="match_parent"
          android:layout_height="wrap_content"
          android:background="@null"
          android:paddingBottom="7dp"
          android:paddingLeft="14dp"
          android:paddingRight="14dp"
          android:paddingTop="7dp">
        <LinearLayout
          android:id="@+id/ll_bg"
          android:layout_width="wrap_content"
          android:layout_height="30dp"
          android:layout_alignParentRight="true"
          android:background="@drawable/activity_main_search_bg"
          android:gravity="center"
          android:paddingLeft="30dp"
          android:paddingRight="30dp">
        <TextView
          android:layout_width="wrap_content"
          android:layout_height="wrap_content"
          android:text="搜索"
          android:textColor="@color/searchText" />
      </LinearLayout>
    </RelativeLayout>
  </RelativeLayout>
</RelativeLayout>

总结:

  如果只是使用的话,单个.class文件拉走用就行了,适合对design:25.3.1没有其他需求,不想在项目中额外引入的。如果对design:25.3.1使用较多的话,还是使用封装好的吧,毕竟简单方便。剩下的关于顶部透明度的变化等在开头的两篇文章中有很好地说明了。关于属性动画的话,我是通过 guolinAndroid属性动画完全解析 了解的,大家如果有兴趣的话可以去了解一下。最后demo的代码放在Github了(点击这里)

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • 【Android 动画】 动画分类补间动画(Tween动画)帧动画(Frame 动画)属性动画(Property ...
    Rtia阅读 6,095评论 1 38
  • 在iOS中随处都可以看到绚丽的动画效果,实现这些动画的过程并不复杂,今天将带大家一窥iOS动画全貌。在这里你可以看...
    F麦子阅读 5,092评论 5 13
  • 前言 属性动画的使用 是 Android 开发中常用的知识 本文将献上一份全面 & 详细的属性动画学习指南,将详细...
    Carson带你学安卓阅读 140,754评论 51 505
  • 读经时间: 2017年8月31日 星期四 晴 读经人员: 妈妈。 读经内容: 《易经》序卦传 第二节;《成语接龙...
    161d968e601f阅读 210评论 0 0
  • 鼠爸爸鼠妈妈的女儿长大了,女儿要嫁给谁呢?鼠妈妈问鼠爸爸,鼠爸爸老糊涂了,鼠爸爸说:谁最威风?我就把自己的女儿嫁...
    A诺诺_8715阅读 300评论 0 1