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

jacky2012 8年前
   <p>因为移动设备有限的显示屏幕,很多时候都需要在合适的时间去隐藏一些控件,比如滑动隐藏就是一个好的设计方案。本文将实现一个通用性较强的滑动隐藏方案,顺便采用了GeastureDetector这个好用的用户动作检查工具。</p>    <h3><strong>一、本文拟实现的效果图</strong></h3>    <p>最近下载了Now直播APP,发现它实现了一个比较流畅的滑动隐藏效果,具体看下面的GIF图。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/501972bd69db7418160abe314d04207c.gif"></p>    <p style="text-align:center">image</p>    <p>因为在老版本的模拟器上运行,显得有点卡顿,这不是主要的。界面中有一个绿色的功能按钮,随着ListView上滑,它就向下滑动隐藏。能够看出来它同时还包括了一个变透明的效果。本文将模仿实现一个这样的滑动隐藏效果,还是按我的老套路,先上最后的效果图。</p>    <p style="text-align:center"><img src="https://simg.open-open.com/show/0bb5038e3d0be89d4e3aeb1678317e22.gif"></p>    <p style="text-align:center">image</p>    <h3><strong>二、具体实现。</strong></h3>    <p>1. 布局文件,主要是一个ScrollView,里面包含了一个textView,另外就是一个功能按钮。</p>    <pre>  <code class="language-java"><?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></code></pre>    <p>2. MainActivity中的主体内容,主要干了如下几件事。</p>    <ul>     <li>其中给ScrollView添加了一个touch监听器</li>     <li>处理用户滑动事件采用了GestureDector</li>     <li>初始化和获取一些关键的成员变量</li>    </ul>    <p>下面代码中有一个小细节,就是用了View.post()方法去获取mButton的原始top值,为什么要这么干呢?</p>    <p>读者可以试一试直接获取,这时候你调试发现获取的值是0;</p>    <p>因为在onCreate的时候,view的绘制可能还没有完成,采取view.post()的方法可以解决这个问题。</p>    <pre>  <code class="language-java">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;              }          });      }  }</code></pre>    <p>3. 第2步中的MyGestureListener 的实现。</p>    <p>使用GeastrueDetector必须要传入一个具体的监听器实现,这个很好理解,因此继承了GestureDetector.SimpleOnGestureListener来实现具体的滑动处理逻辑,在它的onScroll()方法中主要干了如下几件事:</p>    <ul>     <li>首先判断是不是竖直方向的滑动</li>     <li>判断是向上滑动还是向下滑动</li>     <li>根据不同的滑动方向动态改变功能按钮的top和bottom的值,实现一个移动的效果。</li>     <li>因为是滑动多少就移动多少,所以这个效果的连续性和流畅性还是不错的。</li>     <li>最后注意一些限制,功能按钮不能向上滑出原本的边界;向下移动,如果离开了屏幕的可见范围,就不再移动它。</li>    </ul>    <pre>  <code class="language-java">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;          }        }</code></pre>    <p>4. 上一步中注意判断View是否在屏幕可见范围内的一个方法。</p>    <pre>  <code class="language-java">//判断一个控件是否在屏幕范围内      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;          }</code></pre>    <h3><strong>总结</strong></h3>    <p>最后运行的效果图,已经在最前面展示过了。本文的滑动隐藏的原理实现的关键点有如下几点:</p>    <ul>     <li> <p>滑动的隐藏的原理是移动需要隐藏的控件,直至滑出屏幕外。</p> </li>     <li> <p>本文采取的移动方式是动态改变控件的top和bottom值,当然还有别的方式,比如scrollBy(),setTranslateY(),setPadding(),但是他们有各自的应用场景。比如scrollBy()实际上没有移动View只是移动了View的内容,如果你不希望引起其他View的位置变化,可以采用scrollBy()方法。</p> </li>     <li> <p>本文通过在onScroll()方法里采取滑动多少距离就移动功能按钮多少距离,保证功能按钮移动的连续性,避免一个突兀跳变的问题。</p> </li>     <li> <p>最后,就是注意滑动边界的判断,在上述步骤中已经说明了。读者可以不加滑动边界判断,试一试是什么效果。</p> </li>    </ul>    <p>ok,如果觉得本文帮到了你,请留言、点赞,和关注,期待和你一起进步!</p>    <p> </p>    <p>来自:http://www.jianshu.com/p/bd2d3436d77b</p>    <p> </p>