炫酷的Toolbar+Bottom+Fab悬浮按钮显示、隐藏、渐变

前言

由于手机屏幕大小的限制,各种控件需要根据需求进行显示,隐藏,移动等,以增加视觉效果,用户体验。就拿目前市场上常见的APP如知乎、QQ、淘宝、美团等来说,在他们的APP里面随处可见一些比较优美的处理方案。本文主要将一些常见的需要对控件进行显示、隐藏、渐变的场景进行整理了一番。如:点击屏幕Toolbar,bottom的显示隐藏;滑动Scrollview/Webview/Recyclerview显示隐藏,透明度渐变;Fab悬浮按钮滑动缩放显示隐藏(是通过自定义Behavior实现)文章原创,转载请注明地址:小嵩的CSDN博客,地址:http://blog.csdn.net/qq_22393017

由于实现方案比较多,加上篇幅问题。这篇文章重点写了知乎效果的代码,淘宝/QQ空间标题渐变效果的另外写了一篇,地址:精仿淘宝标题栏透明度渐变效果。仿美团的效果demo还在完善,持续更新中,欢迎讨论交流~

效果预览

1.知乎的标题栏和底部栏显示隐藏:

这里写图片描述

2.淘宝、QQ空间标题栏渐变:

3.美团网,大众点评顶部悬浮:

效果演示完了,那么接下来开始分析,讨论实现思路。

思路

第1种-知乎首页的效果

实现方案比较多,这里就讲主要的三种思路:

一、通过监听Srcollview/Recyclerview等控件的滑动,获取Y轴的移动距离,然后判断是上滑还是下滑,对Header 和Footer进行设置显隐动画。
  二、同样是通过监听控件的滑动事件,获取Y轴的移动距离,但在监听回调方法中,则是通过View.setTranslationY()方法动态设置Header和Footer的移动距离,并且添加一个Header和Footer移动距离的阀值,最大移动距离为不可见为止。
  三、通过Behavior 进行嵌套滑动来设置Header 和Footer的显示与隐藏,可用系统预设的,也可自定义,自定义的Behavior文章末尾有demo代码,可自行下载参考。关于Behavior,不了解的话可以搜一下相关方面的知识补习一下推荐链接:Android Behavior的介绍
目前推荐使用这种方案,但某些场景可能用Behavior不方便实现,如涉及到Headview+Viewpager+Fragment切换时,可参考我另一篇文章:
Viewpager+Fragment+Headview的实现

授人予鱼不如授人以渔,思路理解了之后其实代码也很简单,前面两种方案无非就是通过监听屏幕或者控件滑动事件来对View进行处理,而Behavior也是因为基于开发中经常需要处理各种控件的协调,Google才推出了这个协调布局的方案。

第2种-淘宝/QQ空间的效果

即头部渐变的实现方案,其实原理是一样的。两者区别只是一个是平移,一个是透明度变化。平移是通过View.setTranslationY()或者设置平移动画的方法。渐变则是通过 View.setAlpha(alpha)或者设置alpha动画的方法。setAlpha(alpha)这个透明度的alpha值,可以是(头部已滑动距离)/(头部总高度)的百分比,也可以自己根据业务需求改成其他的,百分比计算的参考代码如下:

 float percent = (float)Math.abs(distance)/(float)Math.abs(mMinHeaderTranslation);
    //如果是设置背景透明度,则传入的参数是int类型,取值范围0-255
    //如果设置控件透明度,传入的参数是float类型,取值范围0.0-1.0
    //alpha 值越小越透明
    float falpha = 1-percent;
    int alpha = (int)(falpha * 255);

我单独写了篇文章,仿淘宝的效果,有兴趣的话可移步到此处查看详情:http://blog.csdn.net/qq_22393017/article/details/54602925

第3种-美团/大众点评的效果,有两种实现思路:

第一种是:通过两套布局,一套固定在头部,一套嵌套在Scrollview里面,当滑动到需要悬浮的地方时,通过addview 的方式将需要悬浮的控件添加到固定在头部的容器里面。具体实现代码可参考这篇博客http://blog.csdn.net/xiaanming/article/details/17761431

这里我就把思路说一下,看图:

这里写图片描述

第二种是:Headview+Scrollview 的布局,监听Scrollview的滑动,然后用接口回调,在Activity中对Headview设置View.setTranslationY(dy)方法,当滑动到需要悬浮的地方时,即达到阀值时,不再对Headview进行平移。

上滑的时候:


这里写图片描述

下滑时:

这里写图片描述

┈┈┈┈┈┈┈┈┈◆┈┈┈┈┈┈┈┈┈◆┈┈┈┈┈┈┈┈┈◆┈┈┈┈┈┈┈┈分隔线◆┈┈┈┈┈┈┈┈┈◆┈┈┈┈┈┈┈┈┈◆┈┈┈┈┈┈┈┈┈◆┈┈┈┈┈┈┈┈┈◆┈┈┈┈┈┈┈┈

思路讲解完了,没代码说个egg,so...我整合了一下,写了个demo,是关于知乎首页标题栏,底部,Fab显示隐藏的,提供了:屏幕点击、webview、scrollview、recyclerview、MD 包自带behavior,自定义behavior的实现方案,可自行下载查看。

代码部分解读

文章就通过ScrollView的代码,演示一下知乎首页的大致实现步骤,后面两种效果可根据思路实现,操作起来也不难(文章底部有demo下载链接)

Step one
自定义ScrollView,在 onScrollChanged 中添加接口的onScroll方法对其监听:

package com.hideorshowdemo.widget;

import android.content.Context;
import android.util.AttributeSet;
import android.widget.ScrollView;

/**
 * TODO<自定义监听滑动的ScrollView>
 *
 * @author: 小嵩
 * @date: 2017/1/9 11:37
 */

public class ObservableScrollView extends ScrollView {

    private ScrollViewListener scrollViewListener = null;

    public ObservableScrollView(Context context) {
        super(context);
    }

    public ObservableScrollView(Context context, AttributeSet attrs,
                                int defStyle) {
        super(context, attrs, defStyle);
    }

    public ObservableScrollView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public void setOnScrollListener(ScrollViewListener scrollViewListener) {
        this.scrollViewListener = scrollViewListener;
    }

    @Override
    protected void onScrollChanged(int x, int y, int oldx, int oldy) {
        super.onScrollChanged(x, y, oldx, oldy);
        if (scrollViewListener != null) {

            if (oldy < y && ((y - oldy) > 15)) {// 滑动距离超过15像素,翻向底部,控件向上滑动
                scrollViewListener.onScroll(y - oldy);

            } else if (oldy > y && (oldy - y) > 15) {// 滑动距离超过15像素,向下滑动,翻向顶部
                scrollViewListener.onScroll(y - oldy);
            }

        }
    }

    public  interface ScrollViewListener{//dy Y轴滑动距离
         void onScroll(int dy);
    }
}

Step two
XML布局中引用自定义的ScrollView :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">


    <LinearLayout
        android:id="@+id/content"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">


   <com.hideorshowdemo.widget.ObservableScrollView
       android:id="@+id/scrollView"
       android:layout_width="match_parent"
       android:layout_height="match_parent">

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

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

        <TextView
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:padding="10dp"
            android:textSize="20dp"
            android:lineSpacingExtra="10dp"
            android:text="@string/TextContent"
            android:gravity="center"/>
    </LinearLayout>
   </com.hideorshowdemo.widget.ObservableScrollView>
</LinearLayout>

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

    <LinearLayout
        android:id="@+id/lv_bottom"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:orientation="vertical"
        android:layout_alignParentBottom="true">

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

    </LinearLayout>

</RelativeLayout>

Step three:
在Activity中初始化ScrollView,并实现接口回调方法:

package com.hideorshowdemo.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;

import com.hideorshowdemo.R;
import com.hideorshowdemo.utils.HideAnimationUtils;
import com.hideorshowdemo.widget.ObservableScrollView;

import butterknife.Bind;
import butterknife.ButterKnife;
import butterknife.OnClick;

/**
 * TODO<ScrollView示例>
 *
 * @author: 小嵩
 * @date: 2017/1/9 11:24
 * @version: V1.0
 */

public class ScrollViewActivity extends AppCompatActivity {

    @Bind(R.id.iv_back)
    ImageView ivBack;
    @Bind(R.id.toolbar)
    Toolbar toolbar;
    @Bind(R.id.scrollView)
    ObservableScrollView scrollView;
    @Bind(R.id.lv_bottom)
    LinearLayout lvBottom;

    private boolean isShowing = true;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_scrollview);
        ButterKnife.bind(this);
        initView();
    }

    private void initView() {
        scrollView.setOnScrollListener(new ObservableScrollView.ScrollViewListener() {//滑动事件回调监听(一次滑动的过程会触发多次,通过isShowing来防止对头部和底部多次设置显示与隐藏)
            @Override
            public void onScroll(int dy) {
                if (dy > 0 && isShowing) {//手指往上滑,并且标题栏已经显示,则隐藏底部栏
                    isShowing = false;
                    new HideAnimationUtils(false, toolbar,lvBottom);
                } else if (dy <= 0 && !isShowing) {//往下滑,已隐藏,则显示
                    isShowing = true;
                    new HideAnimationUtils(true, toolbar,lvBottom);
                }
            }
        });
    }

    @OnClick(R.id.iv_back)
    public void OnClick(View v) {
        switch (v.getId()) {
            case R.id.iv_back:
                finish();
                break;
        }
    }


    @Override
    protected void onDestroy() {
        ButterKnife.unbind(this);
        super.onDestroy();
    }
}

其中HideAnimationUtils是我封装的动画工具类,由于动画效果是统一的,在各个Activity中调用,能减少代码冗余,可自行根据需求更改或舍弃。

package com.hideorshowdemo.utils;

import android.view.View;
import android.view.animation.TranslateAnimation;


/**
 * TODO<标题栏动画显示隐藏的工具类>
 *
 * @author: 小嵩
 * @date: 2017/1/9 11:16
 * @version: V1.0
 */

public class HideAnimationUtils {

    private Boolean Show;
    private View view_title;
    private View view_bottom;

    public HideAnimationUtils(Boolean show, View title, View bottom) {
        this.Show = show;
        this.view_title = title;
        this.view_bottom = bottom;
        ShowOrHideTitle();
        ShowOrHideBottom();
    }

    private void ShowOrHideTitle(){//标题栏
        int fromY;//0表示控件Y轴起点
        int toY;//正值表示下移,负值上移
        if (Show) {//显示
            fromY = -view_title.getHeight();
            toY = 0;
        } else {//隐藏
            fromY = 0;
            toY = -view_title.getHeight();
        }
        final TranslateAnimation animation;//平移动画
        animation = new TranslateAnimation(0, 0, fromY, toY);
        animation.setDuration(400);//设置动画持续毫秒
        animation.setFillAfter(true);//动画执行完后是否停留在执行完的状态
        view_title.startAnimation(animation);
    }

    private void ShowOrHideBottom(){//底部栏
        int fromY;//0表示控件Y轴起点
        int toY;//正值表示下移,负值上移
        if (Show) {//显示
            fromY = view_bottom.getHeight();
            toY = 0;
        } else {//隐藏
            fromY = 0;
            toY = view_bottom.getHeight();
        }
        final TranslateAnimation animation;//平移动画
        animation = new TranslateAnimation(0, 0, fromY, toY);
        animation.setDuration(400);//设置动画持续毫秒
        animation.setFillAfter(true);//动画执行完后是否停留在执行完的状态
        view_bottom.startAnimation(animation);
    }
}

大功告成,效果如下:

效果图

点击屏幕显示隐藏效果:

这里写图片描述

自定义Behavior,实现滑动时fab悬浮按钮缩放的效果:

这里写图片描述

GitHub地址:仿知乎首页标题显示隐藏demo
GitHub地址:TitlebarGradient-淘宝购物详情页标题栏渐变

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,067评论 25 707
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,093评论 4 62
  • 内容抽屉菜单ListViewWebViewSwitchButton按钮点赞按钮进度条TabLayout图标下拉刷新...
    皇小弟阅读 46,754评论 22 665
  • 午后下了一场大雨,不过我并没有亲眼见到。当时我和同事正在办公室享用午餐。大概是因为昨晚没有睡好吧,感觉办公室的灯光...
    过路的蜻蜓阅读 252评论 0 1
  • 几年前我们都是懵懂的青年,不懂烦恼肆无忌惮的每天快快乐乐!可自从与你相识,相知,相爱,相濡以沫。我的世界全都变了,...
    倾禾阅读 203评论 0 0