Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> Android Launcher原理分析

Android Launcher原理分析

編輯:關於Android編程

基本概念

本文主要講述Launcher3屏幕滑動過程,首先需要了解Android的觸摸事件分發機制。關於分發機制,可查看文章Android事件分發機制。

常用類介紹

  1. Launcher.java: launcher主要的activity,是launcher桌面第一次啟動的activity.
  2. Workspace.java: 抽象的桌面。由N個cellLayout組成,從cellLayout更高一級的層面上對事件的處理。
  3. PagedView.java: 是workspace的父類,用來桌面的左右滑屏
  4. CellLayout.java: 組成workspace的view,繼承自viewgroup,既是一個dragSource,又是一個dropTarget,可以將它裡面的item拖出去,也可以容納拖動過來的item。在workspace_screen裡面定了一些它的view參數。
  5. LauncherModel.java: 輔助的文件。裡面有許多封裝的對數據庫的操作。包含幾個線程,其中最主要的是ApplicationsLoader和DesktopItemsLoader。ApplicationsLoader在加載所有應用程序時使用,DesktopItemsLoader在加載workspace的時候使用。其他的函數就是對數據庫的封裝,比如在刪除,替換,添加程序的時候做更新數據庫和UI的工作。
  6. LauncherProvider.java: launcher的數據庫,裡面存儲了桌面的item的信息。在創建數據庫的時候會loadFavorites(db)方法,loadFavorites()會解析xml目錄下的default_workspace.xml文件,把其中的內容讀出來寫到數據庫中,這樣就做到了桌面的預制。

初始化參數

PagedView是滑屏最主要的類,下面是init()方法出初始化參數,假設以1080*1440分辨率,即densiy=3為例,來計算各個阈值。

protected void init() {
    mDirtyPageContent = new ArrayList<Boolean>();
    mDirtyPageContent.ensureCapacity(32);
    mScroller = new LauncherScroller(getContext()); //初始化滾動器LauncherScroller
    setDefaultInterpolator(new ScrollInterpolator()); //設置插值器為ScrollInterpolator
    mCurrentPage = 0;
    mCenterPagesVertically = true;

    // 獲取ViewConfiguration
    final ViewConfiguration configuration = ViewConfiguration.get(getContext());

    //mTouchSlop = 16dp;
    mTouchSlop = configuration.getScaledPagingTouchSlop();
    //mPagingTouchSlop = 16dp;
    mPagingTouchSlop = configuration.getScaledPagingTouchSlop();
    //最大速度8000dp/s;
    mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
    //獲取屏幕密度系數mDensity
    mDensity = getResources().getDisplayMetrics().density;

    //用於刪除的拋出阈值: -1440*3
    mFlingToDeleteThresholdVelocity =
            (int) (mFlingToDeleteThresholdVelocity * mDensity);

    //滾動速度阈值 500 *3 px/s
    mFlingThresholdVelocity = (int) (FLING_THRESHOLD_VELOCITY * mDensity);
    //最小滾動速度值 250 *3  px/s
    mMinFlingVelocity = (int) (MIN_FLING_VELOCITY * mDensity);
    //最小snap速度值1500 *3 px/s
    mMinSnapVelocity = (int) (MIN_SNAP_VELOCITY * mDensity);
    setOnHierarchyChangeListener(this);
}

下面主要講述:Workspace,PagedView 這兩個關於滑屏最為核心的類,也是代碼量最大的類。

2. 滑動原理:

先來看看launcher桌面的整體UI布局圖:

launcher ui

1. 手指按下:

當手指按下時,觸發ACTION_DOWN事件,由Activity

launcher_down

當手指按下時,還沒有准備滾動,此時mTouchState = TOUCH_STATE_REST,故Worksapce,PagedView並不會攔截事件,雖然沒有攔截器進行攔截,也沒有onTouchEvent消費,但由於CellLayout的clickable=”true”,故ACTION_DOWN事件仍然是被消費了,具體說明見上一篇文章Android事件分發機制.

2. 手指移動:

 

決定是否屏幕是否開始滑動的阈值計算:

mTouchSlop = configuration.getScaledPagingTouchSlop()
mTouchSlop = res.getDimensionPixelSize(
com.android.internal.R.dimen.config_viewConfigurationTouchSlop)*2; 常量定義在文件`data/res/values/config.xml`中`<dimen name="config_viewConfigurationTouchSlop">8dp</dimen>`。 故mTouchSlop = 8dp * 2 = 16dp,這是系統默認值。

當手指移動距離>mTouchSlop時,Wokespace開始攔截ACTION_MOVE事件,並調用scrollBy(),這是整個過程第一次屏幕移動。每收到一次ACTION_MOVE事件,並執行scrollBy()事件,直到用戶手指離開屏幕。並執行scrollBy()事件,最終調用的還是View.scrollTo()來進行屏幕的滾動操作。

3. 手指離開屏幕

launcher_up

達到下面兩種情況之一,都可以觸發launcher滑屏事件:

  • 滑動距離超過屏幕寬度的0.4倍,SIGNIFICANT_MOVE_THRESHOLD = 0.4 boolean isSignificantMove = Math.abs(deltaX) > pageWidth * SIGNIFICANT_MOVE_THRESHOLD;

  • 速度 大於500dp/s, 且滑動距離大於 25px。 boolean isFling = mTotalMotionX > MIN_LENGTH_FOR_FLING && Math.abs(velocityX) > mFlingThresholdVelocity;

當滿足屏幕翻頁的條件時,開始執行翻頁動畫,通過插值器ScrollInterpolator來計算每一步需要移動的距離。 插值函數為:f(t)=1+(t-1)^5。不斷循環,直到timePassed > mDuration時,動畫結束。

部分源碼

1. determineScrollingStart滑動檢測

用於判斷是否進行滑動操作

protected void determineScrollingStart(MotionEvent ev, float touchSlopScale) {
    // Disallow scrolling if we don't have a valid pointer index
    final int pointerIndex = ev.findPointerIndex(mActivePointerId);
    if (pointerIndex == -1) return;

    // Disallow scrolling if we started the gesture from outside the viewport
    final float x = ev.getX(pointerIndex);
    final float y = ev.getY(pointerIndex);
    if (!isTouchPointInViewportWithBuffer((int) x, (int) y)) return;

    final int xDiff = (int) Math.abs(x - mLastMotionX);
    final int yDiff = (int) Math.abs(y - mLastMotionY);

    final int touchSlop = Math.round(touchSlopScale * mTouchSlop);
    boolean xPaged = xDiff > mPagingTouchSlop;
    boolean xMoved = xDiff > touchSlop;
    boolean yMoved = yDiff > touchSlop;

    //當滑動距離足夠時,才開始滑動
    if (xMoved || xPaged || yMoved) {
        if (mUsePagingTouchSlop ? xPaged : xMoved) {
            // Scroll if the user moved far enough along the X axis
            mTouchState = TOUCH_STATE_SCROLLING;
            mTotalMotionX += Math.abs(mLastMotionX - x);
            mLastMotionX = x;
            mLastMotionXRemainder = 0;
            mTouchX = getViewportOffsetX() + getScrollX();
            mSmoothingTime = System.nanoTime() / NANOTIME_DIV;
            onScrollInteractionBegin();
            pageBeginMoving();
        }
    }
}

2. snapToPage滑屏方法

手指離開屏幕後,調用的滑動動畫的方法

protected void snapToPage(int whichPage, int delta, int duration, boolean immediate,
                          TimeInterpolator interpolator) {

    whichPage = validateNewPage(whichPage);

    mNextPage = whichPage;
    View focusedChild = getFocusedChild();
    if (focusedChild != null && whichPage != mCurrentPage &&
            focusedChild == getPageAt(mCurrentPage)) {
        focusedChild.clearFocus();
    }

    sendScrollAccessibilityEvent();

    pageBeginMoving();
    awakenScrollBars(duration);
    if (immediate) {
        duration = 0;
    } else if (duration == 0) {
        duration = Math.abs(delta);
    }

    if (!mScroller.isFinished()) {
        abortScrollerAnimation(false);
    }

    if (interpolator != null) {
        mScroller.setInterpolator(interpolator);
    } else {
        // 插值器為ScrollInterpolator
        mScroller.setInterpolator(mDefaultInterpolator);
    }

    //這個整個滑動的開始點
    mScroller.startScroll(mUnboundedScrollX, 0, delta, 0, duration);

    updatePageIndicator();

    // Trigger a compute() to finish switching pages if necessary
    if (immediate) {
        computeScroll();
    }

    // Defer loading associated pages until the scroll settles
    mDeferLoadAssociatedPagesUntilScrollCompletes = true;

    mForceScreenScrolled = true;
    invalidate();
}

3.ScrollInterpolator插值器

滑屏時的插值器

private static class ScrollInterpolator implements Interpolator {
    public ScrollInterpolator() {
    }

    public float getInterpolation(float t) {
        t -= 1.0f;
        return t * t * t * t * t + 1;
    }
}

4. LauncherScroller滑動器

launcher桌面的滑動器 mDeceleration = computeDeceleration(ViewConfiguration.getScrollFriction()); mPhysicalCoeff = computeDeceleration(0.84f); // look and feel tuning

//計算 pixels/(s^2)
private float computeDeceleration(float friction) {
    return SensorManager.GRAVITY_EARTH   // g (m/s^2)
                  * 39.37f               // inch/meter
                  * mPpi                 // pixels per inch
                  * friction;
}

//獲取速度
public float getCurrVelocity() {
    return mMode == FLING_MODE ?
            mCurrVelocity : mVelocity - mDeceleration * timePassed() / 2000.0f;
}

5.computeScroll

computeScroll():重寫了父類的computeScroll();主要功能是計算拖動的位移量、更新背景、設置要顯示的屏幕

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved