1. 程式人生 > >[Android例項] Scroll原理-附ScrollView原始碼分析

[Android例項] Scroll原理-附ScrollView原始碼分析

想象一下你拿著放大鏡貼很近的看一副巨大的清明上河圖, 那放大鏡裡可以看到的內容是很有限的,

而隨著放大鏡的上下左右移動,就可以看到不同的內容了
android中手機螢幕就相當於這個放大鏡, 而看到的內容是畫在一個無限大的畫布上~ 
畫的內容有限, 而手機螢幕可以看到的東西更有限~ 但是背景畫布是無限的
如果把放大鏡的移動比作scroll操作,那麼可以理解,這個scroll的距離是無限制的~ 
只不過scroll到有圖的地方才能看到內容

參考ScrollView理解, 當child內容過長時,有一部分內容是"看不到"的,相當於"在螢幕之外",
而隨著我們的拖動滾動,則慢慢看到剩下的內容,相當於我們拿著放大鏡向下移動~


而程式碼中的這個scroll方法系統提供了兩個:
scrollTo和scrollBy

原始碼如下
/**      * Set the scrolled position of your view. This will cause a call to      * {@link #onScrollChanged(int, int, int, int)} and the view will be      * invalidated.      * @param x the x position to scroll to      * @param y the y position to scroll to
     */  public void scrollTo (int x, int y) { if (mScrollX != x || mScrollY != y) {  int oldX = mScrollX;  int oldY = mScrollY;  mScrollX = x;  mScrollY = y;             invalidateParentCaches();             onScrollChanged( mScrollX, mScrollY, oldX, oldY);  if (!awakenScrollBars()) {
                postInvalidateOnAnimation();             }         }     }  /**      * Move the scrolled position of your view. This will cause a call to      * {@link #onScrollChanged(int, int, int, int)} and the view will be      * invalidated.      * @param x the amount of pixels to scroll by horizontally      * @param y the amount of pixels to scroll by vertically      */  public void scrollBy( int x, int y) { scrollTo (mScrollX + x, mScrollY + y);     }
mScrollX 表示離檢視起始位置的x水平方向的偏移量
mScrollY表示離檢視起始位置的y垂直方向的偏移量
可以通過getScrollX() 和getScrollY()方法分別獲得

兩個方法的區別就是to引數是絕對值,by是相對於當前滾動到的位置的增量值

比如:
mScrollX=100, mScrollY=100
scrollTo(20, 20) -> mScrollX=20, mScrollY=20;
scrollBy(20, 20) -> mScrollX=120,mScrollY=120;

注意:
這裡mScrollX和mScrollY的值是偏移量,是相對於檢視起始位置的偏移量~
所以任何view,無論佈局是怎麼樣的,只要是剛初始化未經過scroll的,偏移量都是0~
即mScrollX/Y是相對於自己初始位置的偏移量,而不是相對於其容器的位置座標

-----------------------------------------------------------------------------

下面是就ScrollView的原始碼拆開了的分析,並加入了一些補充擴充套件,
主要內容包括
1.最基本的隨著touch滾動的效果
2.fling效果,即滑動後擡起手後繼續關心滾動的效果
3.over scroll效果,即拖動超出邊界的處理

上述123系統都有提供相關實現方法,但是ScrollView預設只有1,2的實現效果,
over scroll需要我們自行進行一定處理後才可以看到~
下面就ScrollView的原始碼進行分析,且提供三個自定義ScrollView(難度依次遞進)實現上面的三種效果,已打包成demo

後面原始碼分析時,系統是亂七八糟直接寫一起時,分析的被比較細也比較亂,
demo中三個自定義ScrollView相當於按照難度梯度抽取出來的,
即view2是在view1基礎上修改新增功能的,view3是在view2基礎上修改新增功能的
可以從demo下手幫助理解其中原理-----------------------------------------------------------------------------

scroll相當於一個拖動,我們可以用scrollTo/By控制其滾動到某個位置,
那一般ScrollView控制元件這種都是隨著我們的手勢生效的,內部原理是如何的呢~
下面來研究下系統ScrollView控制元件原始碼裡面的具體實現~
系統考慮的東西比較多,研究起來較為複雜,所以先就核心部分拆開一點點研究~
手的拖動肯定是跟touch即觸控事件掛鉤了~直接定位到ScrollView中的該方法
(onTouchEvent幹什麼用的就不掃盲了)
首先是ACTION_DOWN
case MotionEvent.ACTION_DOWN: {     mIsBeingDragged = getChildCount() != 0;     if (!mIsBeingDragged) {         return false;     }     /*      * If being flinged and user touches, stop the fling. isFinished      * will be false if being flinged.      */     if (!mScroller.isFinished()) {         mScroller.abortAnimation();         if (mFlingStrictSpan != null) {             mFlingStrictSpan.finish();             mFlingStrictSpan = null;         }     }     // Remember where the motion event started     mLastMotionY = ev.getY();     mActivePointerId = ev.getPointerId(0);     break; } 有三段程式碼,第二三段帶註釋,下面是介紹:
1.ScrollView沒有child則不做處理,這個不解釋,都沒child滾個蛋啊
如果有child則設定標誌位mIsBeingDragged即"開始拖動"(看英文就可以理解了)
2.看註釋理解~如果還在滑動使用者觸碰了螢幕,則立刻停止滑動
mScroller是一個OverScroller物件,是處理滾動的,
類介紹裡提到這個功能和Scroller類差不多,大部分情況下可以替代之,
區別可以簡單的理解為OverScroller允許超出邊界,後面會介紹Scroller類~
至於mFlingStrictSpan無視之
3.看註釋理解~記住點選的位置
scrollerView,處理垂直滾動,這裡就只記錄Y座標了
mActivePointerId是用來處理多點觸控時的穩定性的,這裡先記住作用就行了
-----------------------------------------------------------------------------
然後是ACTION_MOVE,這個是重點,隨著move我們希望控制元件也能隨著我們的手的拖動滾動到所需位置
case MotionEvent.ACTION_MOVE:     if (mIsBeingDragged ) {         // Scroll to follow the motion event         final int activePointerIndex = ev.findPointerIndex(mActivePointerId);         final float y = ev.getY(activePointerIndex);         final int deltaY = ( int) ( mLastMotionY - y);         mLastMotionY = y;         final int oldX = mScrollX;         final int oldY = mScrollY;         final int range = getScrollRange();         final int overscrollMode = getOverScrollMode();         final boolean canOverscroll = overscrollMode == OVER_SCROLL_ALWAYS ||                 (overscrollMode == OVER_SCROLL_IF_CONTENT_SCROLLS && range > 0);         if (overScrollBy(0, deltaY, 0, mScrollY,                 0, range, 0, mOverscrollDistance, true)) {             // Break our velocity if we hit a scroll barrier.             mVelocityTracker.clear();         }         onScrollChanged( mScrollX, mScrollY , oldX, oldY);         if (canOverscroll) {             final int pulledToY = oldY + deltaY;             if (pulledToY < 0) {                 mEdgeGlowTop.onPull((float) deltaY / getHeight());                 if (! mEdgeGlowBottom.isFinished()) {                     mEdgeGlowBottom.onRelease();                 }             } else if (pulledToY > range) {                 mEdgeGlowBottom.onPull((float) deltaY / getHeight());                 if (! mEdgeGlowTop.isFinished()) {                     mEdgeGlowTop.onRelease();                 }             }             if ( mEdgeGlowTop != null                     && (! mEdgeGlowTop.isFinished() || !mEdgeGlowBottom.isFinished())) {                 invalidate();             }

相關推薦

[Android例項] Scroll原理-ScrollView原始碼分析

想象一下你拿著放大鏡貼很近的看一副巨大的清明上河圖, 那放大鏡裡可以看到的內容是很有限的, 而隨著放大鏡的上下左右移動,就可以看到不同的內容了 android中手機螢幕就相當於這個放大鏡, 而看到的內容是畫在一個無限大的畫布上~  畫的內容有限, 而手機螢幕

Android中三級快取實現原理及LruCache 原始碼分析

介紹 oom異常:大圖片導致 圖片的三級快取:記憶體、磁碟、網路 下面通過一張圖來了解下三級快取原理: 程式碼: public class Davince { //使用固定執行緒池優化 private static Exec

Android 記憶體快取框架 LruCache 的原始碼分析

LruCache 是 Android 提供的一種基於記憶體的快取框架。LRU 是 Least Recently Used 的縮寫,即最近最少使用。當一塊記憶體最近很少使用的時候就會被從快取中移除。在這篇文章中,我們會先簡單介紹 LruCache 的使用,然後我們會對它的原始碼進行分析。 1、基本的使用示例

Android進階3:Activity原始碼分析(2) —— Activity啟動和銷燬流程(8.0)

上篇文章講述了app從啟動建立Activity呼叫onCreate,onStart, onResume方法,這篇文章講述一下Activity啟動的另一個切入點:startActivity方法,啟動Activity。 通過上一篇文章,我們總結一下: 1:A

Java中HashMap底層實現原理(JDK1.8)原始碼分析

在JDK1.6,JDK1.7中,HashMap採用位桶+連結串列實現,即使用連結串列處理衝突,同一hash值的連結串列都儲存在一個連結串列裡。但是當位於一個桶中的元素較多,即hash值相等的元素較多時,通過key值依次查詢的效率較低。而JDK1.8中,HashMap採用位桶+

(轉載)Java中HashMap底層實現原理(JDK1.8)原始碼分析

近期在看一些java底層的東西,看到一篇分析hashMap不錯的文章,跟大家分享一下。 在JDK1.6,JDK1.7中,HashMap採用位桶+連結串列實現,即使用連結串列處理衝突,同一hash值的連結串列都儲存在一個連結串列裡。但是當位於一個桶中的元素較多,即hash值

Android O: 觸控事件傳遞流程原始碼分析(上)

前面的部落格中,我們通過例子分析了一下Android中事件傳遞的流程, 詳細內容可以參考:Android觸控事件傳遞機制簡要分析 貫穿整個Android的觸控事件分發的流程,基本可以抽象成以下的虛擬碼: public boolean dispatchT

Android LayoutInflater.inflate的使用及原始碼分析

在實際開發中我們常常需要inflate要給佈局然後新增到某個佈局容器裡面去, 要把xml佈局檔案轉成一個View物件 需要使用LayoutInflater.inflate方法. 在開發中常常使用如下幾種方式: inflater.inflate(layout

Spark:BlockManager原理剖析與原始碼分析

BlockManager是Spark的分散式儲存系統,與我們平常說的分散式儲存系統是有區別的,區別就是這個分散式儲存系統只會管理Block塊資料,它執行在所有節點上。BlockManager的結構是Maser-Slave架構,Master就是Driver上的BlockManagerMaste

Spark:Shuffle原理剖析與原始碼分析

spark中的Shuffle是非常重要的,shuffle不管在Hadoop中還是Spark中都是重重之重,特別是在Spark shuffle優化的時間。更是非常的重要。 普通shuffle操作的原理剖析(spark 2.x棄用) 每一個Job提交後都會生成一個ResultStage和

Spark:Task原理剖析與原始碼分析

在Spark中,一個應用程式要想被執行,肯定要經過以下的步驟: 從這個路線得知,最終一個job是依賴於分佈在叢集不同節點中的task,通過並行或者併發的執行來完成真正的工作。由此可見,一個個的分散式的task才是Spark的真正執行者。下面先來張task執行框架整體的對Spark的task

Spark:Executor原理剖析與原始碼分析

Executor原理示意圖 Executor程序的啟動 worker中為application啟動的executor,實際上是啟動的這個CoarseGrainedExecutorBackend程序. 原始碼分析: 第一步:CoarseGrainedExecutor

Spark:TaskScheduler原理剖析與原始碼分析

TaskScheduler是一個介面,DAGScheduler在提交TaskSet給底層排程器的時候是面向介面TaskScheduler。TaskSchduler的核心任務是提交Taskset到叢集運算並彙報結果 原始碼分析 第一步:TaskScheduler 提交tasks的入口 su

Spark:DAGScheduler原理剖析與原始碼分析

Job觸發流程原理與原始碼解析 wordcount案例解析,來分析Spark Job的觸發流程 程式碼:var linesRDD= sc.textFile('hdfs://') SparkContext中textFile方法 /** * hadoopFile方法呼叫會

Spark:Worker原理剖析與原始碼分析

解釋: Master要求Worker啟動Driver和Executor Worker啟動Driver的一個基本的原理,Worker會啟動一個執行緒DriverRunner,然後DriverRunner會去負責啟動Driver程序,然後在之後對Driver程序進行管理

Spark:Master原理剖析與原始碼分析

Master主備切換 Spark原生的standalone模式是支援主備切換的,也就是說master可以配置兩個,當Action Master因故障掛了的時候,系統會自動將Standby Master 切換成 Active Master。 Master的準備切換分為兩種:

Spark:SparkContext原理剖析與原始碼分析

在Spark中,SparkContext是Spark所有功能的一個入口,你無論是用java、scala,甚至是python編寫都必須要有一個SparkContext,它的主要作用,包括初始化Spark應用程式所需的一些核心元件,包括 排程器(DAGSchedule、TaskScheduler

基於邊緣的影象分割——分水嶺演算法(watershed)演算法分析opencv原始碼分析

最近需要做一個影象分割的程式,查了opencv的原始碼,發現opencv裡實現的影象分割一共有兩個方法,watershed和mean-shift演算法。這兩個演算法的具體實現都在segmentation.cpp檔案內。 watershed(分水嶺演算法)方法是一種基於邊界點

Master原理剖析與原始碼分析:資源排程機制原始碼分析(schedule(),兩種資源排程演算法)

1、主備切換機制原理剖析與原始碼分析 2、註冊機制原理剖析與原始碼分析 3、狀態改變處理機制原始碼分析 4、資源排程機制原始碼分析(schedule(),兩種資源排程演算法) * Dri

Android多執行緒(二)AsyncTask原始碼分析

AsyncTask的基本用法這裡就不在贅述了,是個安卓開發者就會。 1.android 3.0以前的 AsyncTask private static final int CORE_POOL_SIZE = 5; private static final int MAX