美文网首页Android UI我的Andorid 收藏Android知识
炫酷的Toolbar+Bottom+Fab悬浮按钮显示、隐藏、渐

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

作者: 一枕黄粱终成梦 | 来源:发表于2017-04-25 17:38 被阅读616次

    前言

    由于手机屏幕大小的限制,各种控件需要根据需求进行显示,隐藏,移动等,以增加视觉效果,用户体验。就拿目前市场上常见的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-淘宝购物详情页标题栏渐变

    相关文章

      网友评论

        本文标题:炫酷的Toolbar+Bottom+Fab悬浮按钮显示、隐藏、渐

        本文链接:https://www.haomeiwen.com/subject/gnovzttx.html