1. 程式人生 > >Android 滑動scrollBy()和scrollTo()兩個方法的簡單認識

Android 滑動scrollBy()和scrollTo()兩個方法的簡單認識

涉及到滑動,就涉及到VIEW,大家都知道,Android的UI介面都是由一個一個的View以及View的派生類組成,View作為基類,而常用的佈局裡面的各種佈局就是它派生出來的ViewGroup的子類,ViewGroup作為各個元件的容器搭建了整體的UI。以下是android UI的結構示示意圖:


檢視原始碼

  1. /** 
  2.  * Implement this to do your drawing. 
  3.  * 
  4.  * @param canvas the canvas on which the background will be drawn 
  5.  */
  6. protectedvoid onDraw(Canvas canvas) {  
  7. }  
可以發現,View的實現一般是通過繪製onDraw方法進行,如果你要改變它的介面可以重寫onDraw,達到你的效果,在原始碼中,你找不到    
  1. publicvoid addView(View child) {  
  2.         addView(child, -1);  
  3.     }  
這個方法,因為它不知道,只有在它的派生類例如ViewGroup中會有這個方式去新增子佈局。

而ViewGroup作為一個元件容器,它可以包含任何元件,可是你必須重寫他的onLayout() 方法和 onMeasure()來設定容器佈局的位置和繪製它的大小才能正常顯示。

首先 ,我們必須明白在Android View檢視是沒有邊界的,Canvas是沒有邊界的

,只不過我們通過繪製特定的View時對 Canvas物件進行了一定的操作,例如 : translate(平移)、clipRect(剪下)等,以便達到我們的對該Canvas物件繪製的要求 ,我們可以將這種無邊界的檢視稱為“檢視座標”-----它不受物理螢幕限制。通常我們所理解的一個Layout佈局檔案只是該檢視的顯示區域,超過了這個顯示區域將不能顯示到父檢視的區域中 ,對應的,我們可以將這種有邊界的檢視稱為“佈局座標”------ 父檢視給子檢視分配的佈局(layout)大小。而且, 一個檢視的在螢幕的起始座標位於檢視座標起始處,如下圖所示。


其實是相對於父類檢視的左上角座標為原點(0,0),而不是整體ViewGroup的左上角為原點。

由於佈局座標只能顯示特定的一塊內容,所以我們只有移動佈局座標的座標原點就可以將檢視座標的任何位置顯示出來。

(注:例如cocos2D的佈局就和android的佈局座標原點座標不一樣,是左下角會原點,所以會有所差異。)

這裡就大致提下View和ViewGroup,(網上很多大神都對這塊進行了分析,這裡只是做了少量摘抄記錄)目的是為了引出今天的主角scrollTo 和 scrollBy。

檢視下原始碼可以發現:
  1. <span style="font-family:SimSun;font-size:14px;">  /** 
  2.      * The offset, in pixels, by which the content of this view is scrolled 
  3.      * horizontally. 
  4.      * {@hide} 
  5.      */
  6.     @ViewDebug.ExportedProperty(category = "scrolling")  
  7.     protectedint mScrollX;  
  8.     /** 
  9.      * The offset, in pixels, by which the content of this view is scrolled 
  10.      * vertically. 
  11.      * {@hide} 
  12.      */
  13.     @ViewDebug.ExportedProperty(category = "scrolling")  
  14.     protectedint mScrollY;  
  15.     /** 
  16.      * Return the scrolled left position of this view. This is the left edge of 
  17.      * the displayed part of your view. You do not need to draw any pixels 
  18.      * farther left, since those are outside of the frame of your view on 
  19.      * screen. 
  20.      * 
  21.      * @return The left edge of the displayed part of your view, in pixels. 
  22.      */
  23.     publicfinalint getScrollX() {  
  24.         return mScrollX;  
  25.     }  
  26.     /** 
  27.      * Return the scrolled top position of this view. This is the top edge of 
  28.      * the displayed part of your view. You do not need to draw any pixels above 
  29.      * it, since those are outside of the frame of your view on screen. 
  30.      * 
  31.      * @return The top edge of the displayed part of your view, in pixels. 
  32.      */
  33.     publicfinalint getScrollY() {  
  34.         return mScrollY;  
  35.     }</span>  

mScrollX:表示離檢視起始位置的x水平方向的偏移量

mScrollY:表示離檢視起始位置的y垂直方向的偏移量

分別通過getScrollX() 和getScrollY()方法獲得。

注意:mScrollX和mScrollY指的並不是座標,而是偏移量。

  1. <span style="font-family:SimSun;font-size:14px;"/** 
  2.      * Set the scrolled position of your view. This will cause a call to 
  3.      * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  4.      * invalidated. 
  5.      * @param x the x position to scroll to 
  6.      * @param y the y position to scroll to 
  7.      */
  8.     publicvoid scrollTo(int x, int y) {  
  9.         if (mScrollX != x || mScrollY != y) {  
  10.             int oldX = mScrollX;  
  11.             int oldY = mScrollY;  
  12.             mScrollX = x;  
  13.             mScrollY = y;  
  14.             invalidateParentCaches();  
  15.             onScrollChanged(mScrollX, mScrollY, oldX, oldY);  
  16.             if (!awakenScrollBars()) {  
  17.                 postInvalidateOnAnimation();  
  18.             }  
  19.         }  
  20.     }  
  21.     /** 
  22.      * Move the scrolled position of your view. This will cause a call to 
  23.      * {@link #onScrollChanged(int, int, int, int)} and the view will be 
  24.      * invalidated. 
  25.      * @param x the amount of pixels to scroll by horizontally 
  26.      * @param y the amount of pixels to scroll by vertically 
  27.      */
  28.     publicvoid scrollBy(int x, int y) {  
  29.         scrollTo(mScrollX + x, mScrollY + y);  
  30.     }</span>  

從以上的程式碼可以看出,scrollTo 和 scrollBy區別,其實2者的效果是一樣的。

scrollTo(int x,int y):

如果偏移位置發生了改變,就會給mScrollX和mScrollY賦新值,改變當前位置。

注意:x,y代表的不是座標點,而是偏移量。

例如:

我要移動view到座標點(100,100),那麼我的偏移量就是(0,,0)  - (100,100) = (-100 ,-100)  ,我就要執行view.scrollTo(-100,-100),達到這個效果。

scrollBy(int x,int y):

從原始碼中看出,它實際上是呼叫了scrollTo(mScrollX + x, mScrollY + y);

mScrollX + x和mScrollY + y,即表示在原先偏移的基礎上在發生偏移,通俗的說就是相對我們當前位置偏移。

根據父類VIEW裡面移動,如果移動到了超出的地方,就不會顯示。

檢視上文中的示意圖你就會知道大概。

下面通過一個小例子瞭解下這2個方法之間的的使用,

效果圖如下:


核心程式碼就是本文講的2個方法。

scrollBy(int dx, int dy)主要用於滑屏操作,第一個引數dx代表滑屏後與滑屏前的x座標之差,第二個引數dy同理。那下面我們來試試吧。

自定義View,使用scrollBy

首先我們新建了類DragView繼承自Button

public class DragView extends Button
{
    private int mDownX;
    private int mDownY;

    public DragView(Context context)
    {
        this(context, null);
    }

    public DragView(Context context, AttributeSet attrs)
    {
        this(context, attrs, 0);
    }

    public DragView(Context context, AttributeSet attrs, int defStyleAttr)
    {
        super(context, attrs, defStyleAttr);
        setBackgroundColor(0x88FF0000);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        switch (event.getAction())
        {
            case MotionEvent.ACTION_DOWN:
                mDownX = (int) event.getX();
                mDownY = (int) event.getY();
                break;

            case MotionEvent.ACTION_MOVE:
                int mX = (int) event.getX();
                int mY = (int) event.getY();

                int dX = mX - mDownX;
                int dY = mY - mDownY;

                scrollBy(dX, dY);
                break;
        }
        return true;
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

可以看到,我們設定了淺紅色的背景,聲明瞭兩個全域性變數,並重寫了onTouchEvent方法,裡面判斷了單擊和滑動事件,單擊時記錄x和y的座標,賦值給mDownX和mDownY,滑動的時候也獲取x和y的座標,和單擊時的座標相減取得偏移量,呼叫scrollBy方法。

然後,我們在佈局中使用:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <com.sun.androidqyz.DragView
        android:layout_width="100dp"
        android:layout_height="100dp"/>

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

很簡單吧,效果圖如下:

這裡寫圖片描述

咦,咋滑不動呢?(我是真的滑了,不是在滑動滑鼠)先不管為啥拖不動。我們在佈局中新增幾個屬性:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
              android:orientation="vertical"
              android:layout_width="match_parent"
              android:layout_height="match_parent">

    <com.sun.androidqyz.DragView
        android:layout_width="100dp"
        android:layout_height="100dp"
        android:gravity="center"
        android:text="@string/app_name"/>

</LinearLayout>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

可以看到,為自定義的View增加了Android:text屬性,重新執行程式,看看效果如何:

這裡寫圖片描述

哈,現在是不是可以看到滑動的效果了呢?不對啊,我們明明自定義的View,現在為啥拖動的不是View而是View裡面的字呢,啥子情況哦?

其實呢,在自定義View中直接呼叫scrollBy滑動的是View的Content內容,對於Button,它的Content就是文字,ImageView就是drawable了。

還有一個問題,不知你們發現沒有?開始滑動時,可以看到滑鼠是向上滑動的,按照人們的正常思維,那“AndroidQYZ”這幾個字母也應該向上滑動才對,而現在是向下滑動。

這個就不太好理解了。做iOS開發的同學肯定用過UIScrolView,沒錯,看到這我才明白androidios這麼相似。其實是這樣的:首先我們就要知道佈局是沒有邊界的,就像很大一塊畫布,而手機螢幕就像是一個放大鏡,放大了畫布上的一小部分內容,當我們滑動螢幕時,畫布是沒有滑動的,滑動的是放大鏡,就是我們的螢幕,可以這也說,當放大鏡向上滑動時,我們就可以看到畫布在向下滑動,這就是為啥滑鼠明明向上滑動,而“AndroidQYZ”這幾個字母卻向下滑動的原因了,既然我們知道了為啥,那怎麼修改呢?

我們先修改第二個問題,程式碼如下:

scrollBy(-dX, -dY);
  • 1
  • 1

so easy,沒錯,我們取了負值就可以了,不信?我們可以看看效果:

這裡寫圖片描述

可以看到,“AndroidQYZ”這幾個字母已經跟隨滑鼠的移動而移動了,可還是沒有讓自定義View滑動呀,你這不是騙人嘛?不著急,之前說直接呼叫scrollBy滑動的是Content,這就簡單了,我們直接呼叫自定義View的父View的scrollBy不就好了嘛,看下面:

((View) getParent()).scrollBy(-dX, -dY);
  • 1
  • 1

getParent()方法獲取此View的ViewParent並強轉為View,再呼叫scrollBy方法,要不要看看效果呢?就怕你們不信。

這裡寫圖片描述

哈哈,怎麼樣,可以滑動了吧。

相關推薦

Android 滑動scrollBy()scrollTo()方法簡單認識

涉及到滑動,就涉及到VIEW,大家都知道,Android的UI介面都是由一個一個的View以及View的派生類組成,View作為基類,而常用的佈局裡面的各種佈局就是它派生出來的ViewGroup的子類,ViewGroup作為各個元件的容器搭建了整體的UI。以下是andro

學習筆記-JAVA-考點10-什麼情況下需要重寫equalshashcode()方法

一、什麼情況下需要重寫equals和hashcode()兩個方法? 1、加入到hashset中的自定義類的物件,為確保他們不重複,需要對他們的類重寫equals()和hashcode()的方法。 如果不重寫equals,相同內容不同引用的物件會被當做不同的物件被加入到has

android螢幕休眠喚醒方法(newWakeLock)

1.WakeLock主要程式碼如下: PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE); wakeLock = pm.newWakeLock(PowerManager.PARTIA

Java集合框架上機練習題:編寫一個Book類,該類至少有nameprice屬性。該類要實現Comparable接口,在接口的compareTo()方法.....

ext .cn 數據庫 識別 方法 屬性 set package compareto 編寫一個Book類,該類至少有name和price兩個屬性。該類要實現Comparable接口,在接口的compareTo()方法中規定兩個Book類實例的大小關系為二者的price屬性的

JavaScript中setTimeout()setInterval()定時器的區別使用方法

兩者最直接的區別是: setTimeout()在載入後延遲指定時間去執行一次表示式,只是一次。 setInterval()它從載入後,每隔指定的時間就執行一次表示式 所以要讓一段程式碼, 某個函式以固定頻率重複執行應該使用setInterval()函式 如果讓一段程式碼, 某個函式在

Android Scroller、scrollByscrollTo

先說座標系 檢視座標 Android view的檢視是沒有邊界的,也就是說canvas是沒有邊界的,可以無限繪製, 那麼這種無邊界的座標被成為檢視座標。(絕對座標,座標原點為螢幕左上角) 佈局座標 也交相對座標,在View Layout時以當前view的左上角

在vue中,methodscomputed這方法的區別。

        我們可以使用methods來代替computed,實際上效果是一樣,其中methods【有括號()】,computed不帶括號。        computed是基於它的依賴快取,只有相關依賴發生改變時才會重新取值。        methods在重新渲染的時

Java ArrayList 不為人知的陷阱,及add(int index, E element)set(int index, E element)方法的說明

一般使用List集合,估計都是使用這個ArrayList,一般呢也就是簡單遍歷資料和儲存資料。 很少使用到add(int index, E element)和set(int index, E eleme

button的方法 setImage setBackgroundImage區別

setImage:[UIImageimageNamed:@"enjoy.png"]forState:UIControlStateNormal]; setBackgroundImage:[UIImage imageNamed:@"enjoy.png"] forState:

解決多執行緒安全問題-無非方法synchronizedlock

部落格引用處(以下內容在原有部落格基礎上進行補充或更改,謝謝這些大牛的部落格指導): 解決多執行緒安全問題-無非兩個方法synchronized和lock 具體原理(百度) 還有其他的鎖,如果想要了解,參考:JAVA鎖機制-可重入鎖,可中斷鎖,公平鎖,讀寫鎖,自旋鎖, 解決多執行緒的

webview的方法:setWebChromeClientsetWebClient

android.webkit庫聚合了webkit核心的瀏覽器功能,webview就是她的一個控制元件,可以使得網頁輕鬆的內嵌到app裡。 並且比較強大的是,還可以直接跟js相互呼叫。 webview有兩個方法:setWebChromeClient和setWebClient

Executor.submit()Executor.execute()這方法有什麼區別?

此問題來自另外一篇文章,《15個最流行的java多執行緒面試問題》,現在對熟練掌握併發技能的開發者的需求越來越大,因此這個問題也越來越引起大家的重視。答案是:前者返回一個Future物件,可以通過這個物件來獲得工作執行緒執行的結果。 當我們考察異常處理的時候,又會發現另

Java Executor.submit()Executor.execute()這方法有什麼區別?

此問題來自另外一篇文章,《15個最流行的java多執行緒面試問題》,現在對熟練掌握併發技能的開發者的需求越來越大,因此這個問題也越來越引起大家的重視。答案是:前者返回一個Future物件,可以通過這個物件來獲得工作執行緒執行的結果。 當我們考察異常處理的時候,又會發現另

一個類有方法,其中一個是同步的,另一個是非同步的; 現在又執行緒AB,請問:當執行緒A訪問此類的同步方法時,執行緒B是否能訪問此類的非同步方法

一個類有兩個方法,其中一個是同步的,另一個是非同步的;現在又兩個執行緒A和B,請問:當執行緒A訪問此類的同步方法時,執行緒B是否能訪問此類的非同步方法? 答案:可以 驗證 package com.my.test2; public class ClassA { public syn

1.建立一個Rectangle類,新增widthheight成員變數。 2.在Rectangle中新增方法分別計算矩形的周長和麵積 3.程式設計利用Rectangle輸出一個矩形的周

/* * 1.建立一個Rectangle類,新增width和height兩個成員變數。 * 2.在Rectangle中新增兩種方法分別計算矩形的周長和麵積 * 3.程式設計利用Rectangle輸出一個矩形的周長和麵積 */ public class ke1 {//

Hibernate中為什麼要重寫equals()hashCode()這方法

學到Hibernate的多對一對映,書上列舉的例子是多個訂單(Order)對應一個客戶(Customer)對應,同時提到在Hibernate中通過比較兩個持久化物件的識別符號屬性值(ID)來

三種不同方法將陣列分成奇數偶數陣列

//C方式編寫的函式,使用指標 #include <iostream> using namespace std; void printIntArr(const int arr[], int size) { for (int i = 0; i < si

自動化測試(python))——解決appium每次測試指令碼都要安裝unlocksettingsapk問題的方法

用自己的手機每次測試指令碼總是會提示安裝appium settings 和 unlock兩個apk檔案,可以說是非常麻煩了,這裡找到一個解決辦法: 找到**appium安裝目錄,在 appiu

二十、python 字串去重,主要依賴maketranstranslate這方法

最近遇到一個問題,怎麼來對一個字串去重? 我的思路是1.先分割字串;2.然後使用set去重;3.用join連線起來。 那就引入了另一個問題:怎麼分割字串來保證結果中不包含空格?當然有很笨的方法,本篇主要談論有沒有一種更簡單的方法? 首先,先來看一下需要分

關於Android中根據ID名動態獲取資源的方法

在開發中, 我們習慣了類似下面這種方式去實現引用資源: context.getResources().getDrawable(R.drawable.flower);但是,當我們提前知道這個資源的id,想動態去引用,而不是在id裡面固化應該怎麼辦呢? 比如某個圖片資源的id是