編輯:關於Android編程
把我實現的一個源碼中桌面左右滑動實現循環小功能分享出來:
Android 的原生代碼中的Launch2的一大功能就是支持左右滑動,但是好像它不能支持循環滑動,初想一下好像比較簡單,那就是在獲取第幾屏的時候
取余,差不多就可以實現,但是事實上源碼裡面會有很多誤導,那麼我就分享一下我分析Android2.3.3的Launch2源碼並實現可以循環滑動桌面:
首先我們去找到代碼的位置:。。。。/package/app/Launch2/src/com/android/Lanucher2下是整個桌面的源代碼,那麼我們要找到桌面滑動的代碼,由於
實現桌面左右滑動是由workspace來實現的,那麼我們可以找到Workspace.java文件,Workspace中有五個CellLayout,分別代表五個分屏。
當左右拖動CellLayout時,就能實現滑動的效果。那麼我們就開始看一下Workspace.java:
首先你可以看到:
private int mCurrentScreen;
private int mNextScreen = INVALID_SCREEN;
有這兩個變量的定義可能我們會想到一個是當前的屏值一個是下一屏的值,因為我就是這麼想的(見笑了!!!),然而實際上mNextScreen卻並不是下一屏的值,當然後面會 www.2cto.com
有講到,這也是看源碼難的地方,沒有注釋,一下子無從下手。
回到我們的源碼來,首先我們習慣性的想到先看OnCreate,但是Workspace.java繼承的是ViewGroup,不是Activity,但是我們可以找到開始的地方,從其構造函數看,在構造
函數裡面我們可以看到 initWorkspace(),這個很明顯了就是初始化Workspace,在接下來的initWorkspace()函數中我們可以看到:
mCurrentScreen = mDefaultScreen;
Launcher.setScreen(mCurrentScreen);
也就是初始化為第2屏(0~4),也就是中間那一屏:
起初在文件中看到很多地方有mCurrentScreen和mNextScreen,覺的只要找到怎麼獲得是第幾屏幕就可以,根據是否滿足滑動條件就到下一屏就可以了,但是經過
一番分析與修改沒能成功.如:else if (mNextScreen != INVALID_SCREEN)結果發現mNextScreen的值根本就是當前的屏值,這玩笑開得,走了個大彎,而且還是錯的。
因此調整了一下心態,要一步一步來:
首先了解到:
Android對touch事件的攔截制度。而攔截發生在ViewGroup的onInterceptTouchEvent()和onTouchEvent()以及View的onTouchEvent中。
當發生touch事件時,系統會產生一個MotionEvent並且沿著View Tree開始傳遞。首先獲取MotionEvent是View Tree的根節點,根節點通常是一個ViewGroup,
ViewGroup將在onInterceptTouchEvent()中獲取MotionEvent並決定是否繼續向下傳遞。當在ViewGroup.onInterceptEvent()中返回true時,將截獲MotionEvent,
View Tree下面的View將無法獲得MotionEvent,轉而交給當前ViewGroup的onTouchEvent()方法。如果onTouchEvent中返回false,那麼MotionEvent將沿著View Tree向上傳
給上一層。
滑動功能主要分兩步:
1、在onInterceptTouchEvent中進行攔截。
2、在onTouchEvent中進行滑動。
因此就找到onInterceptTouchEvent函數,這個函數主要就是決定什麼時候截獲MotionEvent來實現滑動,避免了子View的其他事件的影響(如點擊事件)。
public boolean onInterceptTouchEvent(MotionEvent ev) {
.......
//用戶已經進入滑動狀態,並且正在滑動手指。對這種情況直接進行攔截,執行onTouchEvent()繼續執行滑動操作
final int action = ev.getAction();
if ((action == MotionEvent.ACTION_MOVE) && (mTouchState != TOUCH_STATE_REST)) {
return true;
}
//獲取速度跟蹤器,記錄各個時刻的速度。並且添加當前的MotionEvent以記錄更行速度值
acquireVelocityTrackerAndAddMovement(ev);
......................
在接受到ACTION_MOVE時,
switch (action & MotionEvent.ACTION_MASK) {
case MotionEvent.ACTION_MOVE: {
....................
這裡面的那些代碼還沒完全理解,不過在其它地方有看到:
/**
* mIsBeingDragged == false, otherwise the shortcut would have caught it. Check
* whether the user has moved far enough from his original down touch.
*/
/**
* 當在這裡接受到ACTION_MOVE時,說明mTouchState!=TOUCH_STATE_SCROLLING並且mIsBeingDragged的值應該為false,
* 否則DragLayer就應該截獲了MotionEvent用於實現拖拽。
* 此時還沒有進入滑動狀態,當mActivePointerId == INVALID_POINTER時,也就是在此之前沒有接收到任何touch事件。
* 這種情況發生在Workspace變小時,也就是之前Workspace處於SPRING_LOADED狀態。當出現這種情況時直接把當前的事件當作ACTION_DOWN進行處理。
* 反之,則通過determineScrollingStart()嘗試能夠進入滑動狀態。
*/
..............
.............................
case MotionEvent.ACTION_DOWN: {
final float x = ev.getX();
final float y = ev.getY();
// Remember location of down touch
mLastMotionX = x;//記下上一次touch的坐標
mLastMotionY = y;
mAllowLongPress = true;//長按可以實現監聽
.......................
...................
}
好了onInterceptTouchEvent就講到這裡,感興趣的可以深入分析一下,接下來看執行各種關於滑動的工作的計算,界面的刷新等工作的函數:
public boolean onTouchEvent(MotionEvent ev) {
。。。。。
主要看:case MotionEvent.ACTION_UP:
if (mTouchState == TOUCH_STATE_SCROLLING) {
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
final int velocityX = (int) velocityTracker.getXVelocity(mActivePointerId);
final int screenWidth = getWidth();
final int whichScreen = (mScrollX + (screenWidth / 2)) / screenWidth;
final float scrolledPos = (float) mScrollX / screenWidth;
Log.i("velocityX","velocityX="+velocityX+"whichScreen="+whichScreen);
if (velocityX > SNAP_VELOCITY && mCurrentScreen > 0) {//當velocityX的值大於SNAP_VELOCITY且當前屏值大於0時就向左滑動到下一屏。
// Fling hard enough to move left.
// Don't fling across more than one screen at a time.
final int bound = scrolledPos < whichScreen ?
mCurrentScreen - 1 : mCurrentScreen;
snapToScreen(Math.min(whichScreen, bound), velocityX, true);//向左的下一屏
} else if (velocityX < -SNAP_VELOCITY && mCurrentScreen < getChildCount() - 1) {當velocityX的值大於SNAP_VELOCITY且當前屏值大於0時就向右滑動到下一屏。
// Fling hard enough to move right
// Don't fling across more than one screen at a time.
final int bound = scrolledPos > whichScreen ?
mCurrentScreen + 1 : mCurrentScreen;
snapToScreen(Math.max(whichScreen, bound), velocityX, true);//向右的下一屏
} else {
snapToScreen(whichScreen, 0, true);
}
}
mTouchState = TOUCH_STATE_REST;
mActivePointerId = INVALID_POINTER;
releaseVelocityTracker();
break;
那麼我們要修改的就是判斷條件了:
/***********************************************/
//modifided by xxnan 2012-12-10
if (velocityX > SNAP_VELOCITY) {//取消掉判斷是否屏值小於0
// Fling hard enough to move left.
// Don't fling across more than one screen at a time.
Log.i("numscreen","numscreen="+mCurrentScreen);
/* final int bound = scrolledPos < whichScreen ?
( (mCurrentScreen+ getChildCount()) - 1 )% getChildCount(): mCurrentScreen;*/
//取消判斷是否小於whichScreen 也就是當前屏值(如果whichScreen ==0就不做),因此我們要注釋掉
final int bound =( (mCurrentScreen+ getChildCount()) - 1 )% getChildCount() ;
Log.i("numscreen","bound="+bound);
snapToScreen( bound, velocityX, true);
} else if (velocityX < -SNAP_VELOCITY ) {//取消掉判斷是否屏值大於於getChildCount()-1
// Fling hard enough to move right
// Don't fling across more than one screen at a time.
/*final int bound = scrolledPos > whichScreen ?
( mCurrentScreen + 1 )% getChildCount(): mCurrentScreen;*/
final int bound = ( mCurrentScreen + 1 )% getChildCount() ;
snapToScreen(bound, velocityX, true);
} else {
snapToScreen(whichScreen, 0, true);
}
/***********************************************/
大概修改就是如此了。雖然之前修改了一些代碼,大多是當屏值小於0就加getChildCount()取余,以及大於etChildCount()-1時加1取余:如:
public void scrollLeft() {
clearVacantCache();
/*if (mScroller.isFinished()) {
if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
} else {
if (mNextScreen > 0) snapToScreen(mNextScreen - 1);
}*/
if (mScroller.isFinished()) {
if (mCurrentScreen > 0) snapToScreen(mCurrentScreen - 1);
else
snapToScreen(getChildCount() -1);
} else {
if (mNextScreen > 0) snapToScreen(mNextScreen - 1);
}
}
public void scrollRight() {
//modified by xxnan
clearVacantCache();
/* if (mScroller.isFinished()) {
if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
} else {
if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);
}*/
if (mScroller.isFinished()) {
if (mCurrentScreen < getChildCount() -1) snapToScreen(mCurrentScreen + 1);
else
snapToScreen(0);
} else {
if (mNextScreen < getChildCount() -1) snapToScreen(mNextScreen + 1);
}
}
但都無關緊要,主要還是在計算滑動和界面的刷新的函數onTouchEvent中的修改。
本文實例為大家分享了Android仿美團下拉菜單的實現代碼,分類進行選擇,供大家參考,具體內容如下效果圖操作平台AS2.0第三方框架:butterknifebuild.g
通過兩張圖對比,,不難發現布局異常!看代碼 android:layout_hei
項目中需要在應用從後台切換到前台時做操作,自己實現了功能,但對這塊的機制不太了解,So.找了相關的資料來學習總結下。!!!部分資料來源https://github.com
咱們在做Android APP開發的時候經常碰到有下拉刷新和上拉加載跟多的需求,這篇文章咱們先說說下來刷新,咱們就以google的原生的下拉刷新控件SwipeRefres