手勢滑動結束Activity基本功能的實現(xiàn)(一)
喜歡聽音樂的朋友可能都看過天天動聽這款 app, 這款 app 有一個亮點就是在切換頁面(Fragment)的時候可以通過手勢滑動來結束當前頁面,這里先說一下,我為什么會這么關心這個功能呢,因為前兩天 PM說我們即將開始做的這款app 也要實現(xiàn)頁面能通過手勢滑動來結束的功能,所以我就拿著這款 app 滑了一上午;但是我要實現(xiàn)的跟天天動聽這款 app又有點不同,細心觀察的朋友可能會發(fā)現(xiàn),天天動聽是 Fragment 之間的切換,而我這里要實現(xiàn)的是 Activity 之間的切換,不過,不管是哪種,最終效果都是一樣,就是頁面能隨著手勢的滑動而滑動,最終達到某個特定條件,結束此頁面。
要實現(xiàn)這個功能其實也不是特別難,這里我把這個功能的實現(xiàn)分為了以下兩個步驟:
1、識別手勢滑動自定義ViewGroup 的實現(xiàn)
2、實現(xiàn)自定義 ViewGroup 和 Activity 綁定
根據(jù)以上兩個步驟,我們發(fā)現(xiàn),這其中涉及到的知識點有:Android 事件處理機制、自定義 View(ViewGroup)的實現(xiàn),Activity Window的知識,在開發(fā)的過程中還涉及到Activity 主題的配置。Android 事件處理和自定義 View 都在我前面的 blog 中有講到,如果不了解的朋友可以去看看。下面開始按步驟來實現(xiàn)功能
一、自定義 ViewGroup
這個 ViewGroup 的功能只要是對事件的攔截,能夠實現(xiàn)手勢滑動效果;顯示 Activity 的內容包括 ActionBar 和內容區(qū)。
1、實現(xiàn)測量和布局
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { /*獲取默認的寬度*/ int width = getDefaultSize(0, widthMeasureSpec); /*獲取默認的高度*/ int height = getDefaultSize(0, heightMeasureSpec); /*設置ViewGroup 的寬高*/ setMeasuredDimension(width, height); /*獲取子 View 的寬度*/ final int contentWidth = getChildMeasureSpec(widthMeasureSpec, 0, width); /*獲取子View 的高度*/ final int contentHeight = getChildMeasureSpec(heightMeasureSpec, 0, height); /*設置子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); }
因為每個 Activity 都只有一個 Layout,所以這里只有一個子 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) { /*結束手勢的滑動,不攔截*/ endToDrag(); return false; } switch (action) { case MotionEvent.ACTION_DOWN: /*計算 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); /*這里判讀,如果這個觸摸區(qū)域是允許滑動攔截的,則攔截事件*/ 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: /*這里做了對多點觸摸的處理,當有多個手指觸摸的時候依然能正確的滑動*/ onSecondaryPointerUp(ev); break; } if (!mIsBeingDragged) { if (mVelocityTracker == null) { mVelocityTracker = VelocityTracker.obtain(); } mVelocityTracker.addMovement(ev); } return mIsBeingDragged; }
事件攔截,是攔截而是其不會向子 View 分發(fā),直接執(zhí)行本級 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: /*按下則結束滾動*/ completeScroll(); int index = MotionEventCompat.getActionIndex(event); mActivePointerId = MotionEventCompat.getPointerId(event, index); mLastMotionX = mInitialMotionX = event.getX(); break; case MotionEventCompat.ACTION_POINTER_DOWN: { /*有多個點按下的時候,取最后一個按下的點為有效點*/ 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)是滑動狀態(tài),則根據(jù)手勢滑動,而改變View 的位置*/ if (mIsBeingDragged) { // 以下代碼用來判斷和執(zhí)行View 的滑動 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)是滑動狀態(tài),抬起手指,需要判斷滾動的位置*/ 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); /*這里判斷是否滾動到下一頁,還是滾回原位置*/ 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: /*這里有事多點處理*/ onSecondaryPointerUp(event); int pointerIndex = getPointerIndex(event, mActivePointerId); if (mActivePointerId == INVALID_POINTER) break; mLastMotionX = MotionEventCompat.getX(event, pointerIndex); break; } return true; }
因為這里加入了多點控制,所以代碼看起來有點復雜,其實原理很簡單,就是不斷的判斷是否符合滑動的條件。其他就不細講了,來看看這個自定義 ViewGroup 的效果
可以看到,這里我們已經(jīng)實現(xiàn)了手勢識別的 ViewGroup,其實這個ViewGroup如果發(fā)揮想象,它能實現(xiàn)很多效果,不單單是我今天要講的效果,還可以用作側拉菜單,或者是做 QQ5.0版本側滑效果都可以實現(xiàn)的。
二、側滑 View綁定 Activity
這里為了代碼的簡潔,還是通過一個 ViewGroup 來封裝了一層。
/** * Created by moon.zhong on 2015/3/13. */ public class SlidingLayout extends FrameLayout { /*側滑View*/ private SlidingView mSlidingView ; /*需要側滑結束的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) ; } /** * 側滑View 和Activity 綁定 * @param activity */ private void bindActivity(Activity activity){ /*獲取Activity 的最頂級ViewGroup*/ ViewGroup root = (ViewGroup) activity.getWindow().getDecorView(); /*獲取Activity 顯示內容區(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) ; } }
來看看效果怎么樣:
咦!能滑動結束頁面,但為什么邊滑走的同時看不到第一個 Acitivity,而是要等結束了才能看到呢?我們猜測,應該是滑動的時候,這個 Activity 還有哪里把第一個 Activity 覆蓋了,每個 Activity 都是附在一個 Window 上面,所以這里就涉及到一個 Activity 的 window背景顏色問題, OK,把第二個 Activity 的 window 背景設為透明
<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" />
再來看看效果,效果圖:
完美實現(xiàn)!
好了,今天就到這里,下期文章就是對這個功能的進一步優(yōu)化和改善,如果感興趣,可以繼續(xù)關注我!
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
- Android通過滑動實現(xiàn)Activity跳轉(手勢識別器應用)
- Android實現(xiàn)手勢滑動多點觸摸放大縮小圖片效果
- Android GestureDetector手勢滑動使用實例講解
- Android手勢滑動實現(xiàn)ImageView縮放圖片大小
- Android實現(xiàn)手勢滑動多點觸摸縮放平移圖片效果
- Android實現(xiàn)圖片自動輪播并且支持手勢左右無限滑動
- Android中Activity滑動關閉的效果
- Android仿微信activity滑動關閉效果
- Android仿微信滑動退出Activity
- android中使用Activity實現(xiàn)監(jiān)聽手指上下左右滑動
相關文章
Android 獲取未安裝的APK圖標、版本號、包名等信息方法
下面小編就為大家分享一篇Android 獲取未安裝的APK圖標、版本號、包名等信息方法,具有很好的參考價值,希望對大家有所幫助。2018-01-01深入理解Android中的Window和WindowManager
這篇文章給大家介紹了Window和WindowManager知識,非常不錯,具有參考借鑒價值,需要的朋友參考下吧2017-02-02