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,如果覺得本文幫到了你,請留言、點贊,和關注,期待和你一起進步!