Android手把手教你实现滑动隐藏(GeastureDetector使用)

因为移动设备有限的显示屏幕,很多时候都需要在合适的时间去隐藏一些控件,比如滑动隐藏就是一个好的设计方案。本文将实现一个通用性较强的滑动隐藏方案,顺便采用了GeastureDetector这个好用的用户动作检查工具。

一、本文拟实现的效果图

最近下载了Now直播APP,发现它实现了一个比较流畅的滑动隐藏效果,具体看下面的GIF图。

image

因为在老版本的模拟器上运行,显得有点卡顿,这不是主要的。界面中有一个绿色的功能按钮,随着ListView上滑,它就向下滑动隐藏。能够看出来它同时还包括了一个变透明的效果。本文将模仿实现一个这样的滑动隐藏效果,还是按我的老套路,先上最后的效果图。

image

二、具体实现。

1. 布局文件,主要是一个ScrollView,里面包含了一个textView,另外就是一个功能按钮。

<?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/scrollView"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        >
        <TextView
            android:id="@+id/textview1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:gravity="center"
            android:paddingBottom="1000dp"
            android:paddingTop="250dp"
            android:text="可滑动的ScrollView"
            />
    </ScrollView>
    <Button
        android:id="@+id/button2"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_alignParentBottom="true"
        android:layout_marginBottom="20dp"
        android:text="功能滑动按钮"
        />
</RelativeLayout>

2. MainActivity中的主体内容,主要干了如下几件事。

  • 其中给ScrollView添加了一个touch监听器
  • 处理用户滑动事件采用了GestureDector
  • 初始化和获取一些关键的成员变量

下面代码中有一个小细节,就是用了View.post()方法去获取mButton的原始top值,为什么要这么干呢?

读者可以试一试直接获取,这时候你调试发现获取的值是0;

因为在onCreate的时候,view的绘制可能还没有完成,采取view.post()的方法可以解决这个问题。

package com.example.administrator.myapplication;

import android.graphics.Point;
import android.graphics.Rect;
import android.os.Bundle;
import android.support.v4.view.GestureDetectorCompat;
import android.support.v7.app.AppCompatActivity;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.ScrollView;

public class MainActivity extends AppCompatActivity {

    private GestureDetectorCompat mDetectorCompat;
    private Button mButton;
    private ScrollView mScrollView;
    private int mOriginButtonTop;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        mButton = (Button) findViewById(R.id.button2);

        mButton.post(new Runnable() {//post一个线程去获取button的原始top值
            @Override
            public void run() {
                mOriginButtonTop = mButton.getTop();
            }
        });

        mScrollView = (ScrollView) findViewById(R.id.scrollView);
        mDetectorCompat = new GestureDetectorCompat(this, new MyGestureListener());

        mScrollView.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View v, MotionEvent event) {
                mDetectorCompat.onTouchEvent(event);
                return false;
            }
        });
    }
}

3. 第2步中的MyGestureListener 的实现。

使用GeastrueDetector必须要传入一个具体的监听器实现,这个很好理解,因此继承了GestureDetector.SimpleOnGestureListener来实现具体的滑动处理逻辑,在它的onScroll()方法中主要干了如下几件事:

  • 首先判断是不是竖直方向的滑动
  • 判断是向上滑动还是向下滑动
  • 根据不同的滑动方向动态改变功能按钮的top和bottom的值,实现一个移动的效果。
  • 因为是滑动多少就移动多少,所以这个效果的连续性和流畅性还是不错的。
  • 最后注意一些限制,功能按钮不能向上滑出原本的边界;向下移动,如果离开了屏幕的可见范围,就不再移动它。
 class MyGestureListener extends GestureDetector.SimpleOnGestureListener {

        @Override
        public boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY) {

            if (Math.abs(distanceY) > Math.abs(distanceX)) {//判断是否竖直滑动
                int buttonTop = mButton.getTop();
                int buttonBottom = mButton.getBottom();

                //是否向下滑动
                boolean isScrollDown = e1.getRawY() < e2.getRawY() ? true : false;

                //根据滑动方向和mButton当前的位置判断是否需要移动Button的位置
                if (!ifNeedScroll(isScrollDown)) return false;

                if (isScrollDown) {
                    //下滑上移Button
                    mButton.setTop(buttonTop - (int) Math.abs(distanceY));
                    mButton.setBottom(buttonBottom - (int) Math.abs(distanceY));
                } else if (!isScrollDown) {
                    //上滑下移Button
                    mButton.setTop(buttonTop + (int) Math.abs(distanceY));
                    mButton.setBottom(buttonBottom + (int) Math.abs(distanceY));
                }
            }

            return super.onScroll(e1, e2, distanceX, distanceY);
        }

        //写一个方法,根据滑动方向和mButton当前的位置,判断按钮是否应该继续滑动
        private boolean ifNeedScroll(boolean isScrollDown) {
            int nowButtonTop = mButton.getTop();

            //button不能超出原来的上边界
            if (isScrollDown && nowButtonTop <= mOriginButtonTop) return false;

            //判断按钮是否在屏幕范围内,如果不在,则不需要再移动位置
            if (!isScrollDown) {
                return isInScreen(mButton);
            }

            return true;
        }

    }

4. 上一步中注意判断View是否在屏幕可见范围内的一个方法。


        //判断一个控件是否在屏幕范围内
    private boolean isInScreen(View view) {
            int width, height;
            Point p = new Point();
            getWindowManager().getDefaultDisplay().getSize(p);
            width = p.x;
            height = p.y;

            Rect rect = new Rect(0, 0, width, height);

            if (!view.getLocalVisibleRect(rect)) return false;

            return true;
        }

总结

最后运行的效果图,已经在最前面展示过了。本文的滑动隐藏的原理实现的关键点有如下几点:

  • 滑动的隐藏的原理是移动需要隐藏的控件,直至滑出屏幕外。

  • 本文采取的移动方式是动态改变控件的top和bottom值,当然还有别的方式,比如scrollBy(),setTranslateY(),setPadding(),但是他们有各自的应用场景。比如scrollBy()实际上没有移动View只是移动了View的内容,如果你不希望引起其他View的位置变化,可以采用scrollBy()方法。

  • 本文通过在onScroll()方法里采取滑动多少距离就移动功能按钮多少距离,保证功能按钮移动的连续性,避免一个突兀跳变的问题。

  • 最后,就是注意滑动边界的判断,在上述步骤中已经说明了。读者可以不加滑动边界判断,试一试是什么效果。

ok,如果觉得本文帮到了你,请留言、点赞,和关注,期待和你一起进步!

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

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 172,206评论 25 707
  • 内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出。 当了解了Android坐标系和触...
    Blankj阅读 6,641评论 3 61
  • 发现 关注 消息 iOS 第三方库、插件、知名博客总结 作者大灰狼的小绵羊哥哥关注 2017.06.26 09:4...
    肇东周阅读 12,109评论 4 62
  • 孙悟空的五指山,五指山的符文 不能玩多余一把 2个小时有点多啊,上班才上8小时班,占比太重了
    mimikatz阅读 153评论 0 0
  • 贺先生今天穿的黑色无帽卫衣和白色内衬,走路带风,有点帅,我真幸福
    莫淮阅读 224评论 0 0