1. 程式人生 > >Android手把手教你實現滑動隱藏(GeastureDetector使用)

Android手把手教你實現滑動隱藏(GeastureDetector使用)

因為移動裝置有限的顯示螢幕,很多時候都需要在合適的時間去隱藏一些控制元件,比如滑動隱藏就是一個好的設計方案。本文將實現一個通用性較強的滑動隱藏方案,順便採用了GeastureDetector這個好用的使用者動作檢查工具。

一、本文擬實現的效果圖

最近下載了Now直播APP,發現它實現了一個比較流暢的滑動隱藏效果,具體看下面的GIF圖。


因為在老版本的模擬器上執行,顯得有點卡頓,這不是主要的。介面中有一個綠色的功能按鈕,隨著ListView上滑,它就向下滑動隱藏。能夠看出來它同時還包括了一個變透明的效果。本文將模仿實現一個這樣的滑動隱藏效果,還是按我的老套路,先上最後的效果圖。


二、具體實現。

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;

                //根據滑動方向判斷是否需要移動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);
        }

        //寫一個方法,根據滑動方向,判斷按鈕是否應該繼續滑動
        private boolean ifNeedScroll(boolean isScrollDown) {
            int nowButtonTop = mButton.getTop();

            //手指向下滑動,應該上移Button,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,如果覺得本文幫到了你,請留言、點贊,和關注,期待和你一起進步!