国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 系統(tǒng) > Android > 正文

手勢滑動(dòng)結(jié)束Activity基本功能的實(shí)現(xiàn)(一)

2019-10-23 18:27:11
字體:
供稿:網(wǎng)友

喜歡聽音樂的朋友可能都看過天天動(dòng)聽這款 app, 這款 app 有一個(gè)亮點(diǎn)就是在切換頁面(Fragment)的時(shí)候可以通過手勢滑動(dòng)來結(jié)束當(dāng)前頁面,這里先說一下,我為什么會(huì)這么關(guān)心這個(gè)功能呢,因?yàn)榍皟商?PM說我們即將開始做的這款app 也要實(shí)現(xiàn)頁面能通過手勢滑動(dòng)來結(jié)束的功能,所以我就拿著這款 app 滑了一上午;但是我要實(shí)現(xiàn)的跟天天動(dòng)聽這款 app又有點(diǎn)不同,細(xì)心觀察的朋友可能會(huì)發(fā)現(xiàn),天天動(dòng)聽是 Fragment 之間的切換,而我這里要實(shí)現(xiàn)的是 Activity 之間的切換,不過,不管是哪種,最終效果都是一樣,就是頁面能隨著手勢的滑動(dòng)而滑動(dòng),最終達(dá)到某個(gè)特定條件,結(jié)束此頁面。
要實(shí)現(xiàn)這個(gè)功能其實(shí)也不是特別難,這里我把這個(gè)功能的實(shí)現(xiàn)分為了以下兩個(gè)步驟:

1、識(shí)別手勢滑動(dòng)自定義ViewGroup 的實(shí)現(xiàn)
2、實(shí)現(xiàn)自定義 ViewGroup 和 Activity 綁定

根據(jù)以上兩個(gè)步驟,我們發(fā)現(xiàn),這其中涉及到的知識(shí)點(diǎn)有:Android 事件處理機(jī)制、自定義 View(ViewGroup)的實(shí)現(xiàn),Activity Window的知識(shí),在開發(fā)的過程中還涉及到Activity 主題的配置。Android 事件處理和自定義 View 都在我前面的 blog 中有講到,如果不了解的朋友可以去看看。下面開始按步驟來實(shí)現(xiàn)功能

一、自定義 ViewGroup

這個(gè) ViewGroup 的功能只要是對(duì)事件的攔截,能夠?qū)崿F(xiàn)手勢滑動(dòng)效果;顯示 Activity 的內(nèi)容包括 ActionBar 和內(nèi)容區(qū)。

1、實(shí)現(xiàn)測量和布局

  @Override  protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {    /*獲取默認(rèn)的寬度*/    int width = getDefaultSize(0, widthMeasureSpec);    /*獲取默認(rèn)的高度*/    int height = getDefaultSize(0, heightMeasureSpec);    /*設(shè)置ViewGroup 的寬高*/    setMeasuredDimension(width, height);    /*獲取子 View 的寬度*/    final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width);    /*獲取子View 的高度*/    final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height);    /*設(shè)置子View 的大小*/    mContent.measure(contentWidth, contentHeight);  }
  @Override  protected void onLayout(boolean changed, int l, int t, int r, int b) {    final int width = r - l;    final int height = b - t;    mContent.layout(0, 0, width, height);  }

因?yàn)槊總€(gè) Activity 都只有一個(gè) Layout,所以這里只有一個(gè)子 View,布局和測量就顯得非常簡單。

2、事件攔截

  @Override  public boolean onInterceptTouchEvent(MotionEvent ev) {    if (!isEnable) {      return false;    }    final int action = ev.getAction() & MotionEventCompat.ACTION_MASK;    if (action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP        || action != MotionEvent.ACTION_DOWN && mIsUnableToDrag) {      /*結(jié)束手勢的滑動(dòng),不攔截*/      endToDrag();      return false;    }    switch (action) {      case MotionEvent.ACTION_DOWN:        /*計(jì)算 x,y 的距離*/        int index = MotionEventCompat.getActionIndex(ev);        mActivePointerId = MotionEventCompat.getPointerId(ev, index);        if (mActivePointerId == INVALID_POINTER)          break;        mLastMotionX = mInitialMotionX = MotionEventCompat.getX(ev, index);        mLastMotionY = MotionEventCompat.getY(ev, index);        /*這里判讀,如果這個(gè)觸摸區(qū)域是允許滑動(dòng)攔截的,則攔截事件*/        if (thisTouchAllowed(ev)) {          mIsBeingDragged = false;          mIsUnableToDrag = false;        } else {          mIsUnableToDrag = true;        }        break;      case MotionEvent.ACTION_MOVE:        /*繼續(xù)判斷是否需要攔截*/        determineDrag(ev);        break;      case MotionEvent.ACTION_UP:        break;      case MotionEvent.ACTION_POINTER_UP:        /*這里做了對(duì)多點(diǎn)觸摸的處理,當(dāng)有多個(gè)手指觸摸的時(shí)候依然能正確的滑動(dòng)*/        onSecondaryPointerUp(ev);        break;    }    if (!mIsBeingDragged) {      if (mVelocityTracker == null) {        mVelocityTracker = VelocityTracker.obtain();      }      mVelocityTracker.addMovement(ev);    }    return mIsBeingDragged;  }

事件攔截,是攔截而是其不會(huì)向子 View 分發(fā),直接執(zhí)行本級(jí) View的 onTouchEvent方法;

3、事件處理

  @Override  public boolean onTouchEvent(MotionEvent event) {    if (!isEnable) {      return false;    }    if (!mIsBeingDragged && !thisTouchAllowed(event))      return false;    final int action = event.getAction();    if (mVelocityTracker == null) {      mVelocityTracker = VelocityTracker.obtain();    }    mVelocityTracker.addMovement(event);    switch (action & MotionEventCompat.ACTION_MASK) {      case MotionEvent.ACTION_DOWN:        /*按下則結(jié)束滾動(dòng)*/        completeScroll();        int index = MotionEventCompat.getActionIndex(event);        mActivePointerId = MotionEventCompat.getPointerId(event, index);        mLastMotionX = mInitialMotionX = event.getX();        break;      case MotionEventCompat.ACTION_POINTER_DOWN: {        /*有多個(gè)點(diǎn)按下的時(shí)候,取最后一個(gè)按下的點(diǎn)為有效點(diǎn)*/        final int indexx = MotionEventCompat.getActionIndex(event);        mLastMotionX = MotionEventCompat.getX(event, indexx);        mActivePointerId = MotionEventCompat.getPointerId(event, indexx);        break;      }      case MotionEvent.ACTION_MOVE:        if (!mIsBeingDragged) {          determineDrag(event);          if (mIsUnableToDrag)            return false;        }        /*如果已經(jīng)是滑動(dòng)狀態(tài),則根據(jù)手勢滑動(dòng),而改變View 的位置*/        if (mIsBeingDragged) {          // 以下代碼用來判斷和執(zhí)行View 的滑動(dòng)          final int activePointerIndex = getPointerIndex(event, mActivePointerId);          if (mActivePointerId == INVALID_POINTER)            break;          final float x = MotionEventCompat.getX(event, activePointerIndex);          final float deltaX = mLastMotionX - x;          mLastMotionX = x;          float oldScrollX = getScrollX();          float scrollX = oldScrollX + deltaX;          final float leftBound = getLeftBound();          final float rightBound = getRightBound();          if (scrollX < leftBound) {            scrollX = leftBound;          } else if (scrollX > rightBound) {            scrollX = rightBound;          }          mLastMotionX += scrollX - (int) scrollX;          scrollTo((int) scrollX, getScrollY());        }        break;      case MotionEvent.ACTION_UP:        /*如果已經(jīng)是滑動(dòng)狀態(tài),抬起手指,需要判斷滾動(dòng)的位置*/        if (mIsBeingDragged) {          final VelocityTracker velocityTracker = mVelocityTracker;          velocityTracker.computeCurrentVelocity(1000, mMaxMunVelocity);          int initialVelocity = (int) VelocityTrackerCompat.getXVelocity(              velocityTracker, mActivePointerId);          final int scrollX = getScrollX();          final float pageOffset = (float) (-scrollX) / getContentWidth();          final int activePointerIndex = getPointerIndex(event, mActivePointerId);          if (mActivePointerId != INVALID_POINTER) {            final float x = MotionEventCompat.getX(event, activePointerIndex);            final int totalDelta = (int) (x - mInitialMotionX);            /*這里判斷是否滾動(dòng)到下一頁,還是滾回原位置*/            int nextPage = determineTargetPage(pageOffset, initialVelocity, totalDelta);            setCurrentItemInternal(nextPage, true, initialVelocity);          } else {            setCurrentItemInternal(mCurItem, true, initialVelocity);          }          mActivePointerId = INVALID_POINTER;          endToDrag();        } else {//          setCurrentItemInternal(0, true, 0);          endToDrag();        }        break;      case MotionEventCompat.ACTION_POINTER_UP:        /*這里有事多點(diǎn)處理*/        onSecondaryPointerUp(event);        int pointerIndex = getPointerIndex(event, mActivePointerId);        if (mActivePointerId == INVALID_POINTER)          break;        mLastMotionX = MotionEventCompat.getX(event, pointerIndex);        break;    }    return true;  }

因?yàn)檫@里加入了多點(diǎn)控制,所以代碼看起來有點(diǎn)復(fù)雜,其實(shí)原理很簡單,就是不斷的判斷是否符合滑動(dòng)的條件。其他就不細(xì)講了,來看看這個(gè)自定義 ViewGroup 的效果

手勢,滑動(dòng),Activity

可以看到,這里我們已經(jīng)實(shí)現(xiàn)了手勢識(shí)別的 ViewGroup,其實(shí)這個(gè)ViewGroup如果發(fā)揮想象,它能實(shí)現(xiàn)很多效果,不單單是我今天要講的效果,還可以用作側(cè)拉菜單,或者是做 QQ5.0版本側(cè)滑效果都可以實(shí)現(xiàn)的。

二、側(cè)滑 View綁定 Activity

這里為了代碼的簡潔,還是通過一個(gè) ViewGroup 來封裝了一層。

/** * Created by moon.zhong on 2015/3/13. */public class SlidingLayout extends FrameLayout {  /*側(cè)滑View*/  private SlidingView mSlidingView ;  /*需要側(cè)滑結(jié)束的Activity*/  private Activity mActivity ;  public SlidingLayout(Context context) {    this(context, null);  }  public SlidingLayout(Context context, AttributeSet attrs) {    this(context, attrs, 0);  }  public SlidingLayout(Context context, AttributeSet attrs, int defStyleAttr) {    super(context, attrs, defStyleAttr);    mSlidingView = new SlidingView(context) ;    addView(mSlidingView);    mSlidingView.setOnPageChangeListener(new SlidingView.OnPageChangeListener() {      @Override      public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {        if (position == 1){          Log.v("zgy","========position=========") ;          mActivity.finish();        }      }      @Override      public void onPageSelected(int position) {      }    });    mActivity = (Activity) context;    bindActivity(mActivity) ;  }  /**   * 側(cè)滑View 和Activity 綁定   * @param activity   */  private void bindActivity(Activity activity){    /*獲取Activity 的最頂級(jí)ViewGroup*/    ViewGroup root = (ViewGroup) activity.getWindow().getDecorView();    /*獲取Activity 顯示內(nèi)容區(qū)域的ViewGroup,包行ActionBar*/    ViewGroup child = (ViewGroup) root.getChildAt(0);    root.removeView(child);    mSlidingView.setContent(child);    root.addView(this);  }}

測試 Activity 這事就變的非常簡單了

public class SecondActivity extends ActionBarActivity {  @Override  protected void onCreate(Bundle savedInstanceState) {    super.onCreate(savedInstanceState);    setContentView(R.layout.activity_second);    /*綁定Activity*/    new SlidingLayout(this) ;  }}

來看看效果怎么樣:

手勢,滑動(dòng),Activity

咦!能滑動(dòng)結(jié)束頁面,但為什么邊滑走的同時(shí)看不到第一個(gè) Acitivity,而是要等結(jié)束了才能看到呢?我們猜測,應(yīng)該是滑動(dòng)的時(shí)候,這個(gè) Activity 還有哪里把第一個(gè) Activity 覆蓋了,每個(gè) Activity 都是附在一個(gè) Window 上面,所以這里就涉及到一個(gè) Activity 的 window背景顏色問題, OK,把第二個(gè) Activity 的 window 背景設(shè)為透明

<style name="TranslucentTheme" parent="AppTheme"> <item name="android:windowIsTranslucent">true</item> <item name="android:windowBackground">@android:color/transparent</item> <item name="android:windowContentOverlay">@null</item></style>
<activity android:name=".SecondActivity"  android:label="SecondActivity"  android:screenOrientation="portrait"  android:theme="@style/TranslucentTheme" />

再來看看效果,效果圖:

手勢,滑動(dòng),Activity

完美實(shí)現(xiàn)!

好了,今天就到這里,下期文章就是對(duì)這個(gè)功能的進(jìn)一步優(yōu)化和改善,如果感興趣,可以繼續(xù)關(guān)注我!

以上就是本文的全部內(nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持VEVB武林網(wǎng)。


注:相關(guān)教程知識(shí)閱讀請(qǐng)移步到Android開發(fā)頻道。
發(fā)表評(píng)論 共有條評(píng)論
用戶名: 密碼:
驗(yàn)證碼: 匿名發(fā)表
主站蜘蛛池模板: 连平县| 镇雄县| 九台市| 磐石市| 晋州市| 黄陵县| 五原县| 怀来县| 双桥区| 西充县| 锡林浩特市| 丰城市| 开平市| 都江堰市| 嘉善县| 铁力市| 星座| 万州区| 土默特左旗| 丰城市| 彭水| 伊宁市| 芦山县| 佛学| 哈尔滨市| 遵义市| 金昌市| 宁武县| 浦江县| 浦北县| 凤庆县| 石楼县| 英吉沙县| 团风县| 进贤县| 澎湖县| 丹巴县| 延寿县| 道孚县| 万宁市| 拜泉县|