美文网首页安卓控件Android自定义View
Android手把手教你实现滑动隐藏(GeastureDetec

Android手把手教你实现滑动隐藏(GeastureDetec

作者: Mr云台 | 来源:发表于2016-11-09 20:46 被阅读3419次

    因为移动设备有限的显示屏幕,很多时候都需要在合适的时间去隐藏一些控件,比如滑动隐藏就是一个好的设计方案。本文将实现一个通用性较强的滑动隐藏方案,顺便采用了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,如果觉得本文帮到了你,请留言、点赞,和关注,期待和你一起进步!

    相关文章

      网友评论

      • 希腊明天:博主:发现在快速下滑时,有时button的位置上移,还是会超出之前的边界的。经分析发现,在最后一次移动button的位置时,滑动距离过大的话,会出现此情况。添加如下判断可解决问题:

        //下滑上移Button
        int top = buttonTop - (int) Math.abs(distanceY);
        int bottom = buttonBottom - (int) Math.abs(distanceY);
        if (top <= mOriginButtonTop) {
        top = mOriginButtonTop;
        bottom = mOriginButtonBottom;
        }
        mButton.setTop(top);
        mButton.setBottom(bottom);
        Mr云台:@希腊明天 感觉指出,确实
      • 根艮哏艮根:好东西,已收藏,话说为啥不直接用onTouch呢?他里面的DOWN和UP方法也能判断上下滑啊
        Mr云台:@Android_gen //button不能超出原来的上边界 你看下这行注释的相关代码
        根艮哏艮根:@山中小僧 每次滑动的时候button的位置不一致,一直抖动
        Mr云台:@Android_gen 那也可以,GestureDetector帮你封装了一些事件,不用你去判断,具体你可以看下源码
      • 2c0ee5bd7b20:这个有个问题,button每次向上移动的距离都不一样,怎么解决啊?
        Mr云台: //写一个方法,根据滑动方向和mButton当前的位置,判断按钮是否应该继续滑动
        private boolean ifNeedScroll(boolean isScrollDown) {

        注意这一段注释,我这个demo是ok的
      • 7b19851a4ff4:博主 请问为什么我的mDetectorCompat.onTouchEvent(event)中event为空?
      • 4b46ebdad699:博主 代码共享下吧
        Mr云台:@4b46ebdad699 :sweat: 上面文章里的代码copy一下就好了,不要懒嘛
      • b795ba14cf12:不错,目前也在做类似的功能。可以借鉴一下 @AllenJuns

      本文标题:Android手把手教你实现滑动隐藏(GeastureDetec

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