小Demo大知識-通過控制Button移動來學習Android座標

今天分享一個簡單的Demo。Demo實現的功能就是,用鼠標點中button的時候,然後拖動Button。這時候Button會根據你鼠標的移動而移動,同時,你鼠標點中的Button的位置也不會改變。比如你點在Button的左上角,那移動的時候。鼠標還是在Button的左上角

一言不合上效果圖

大家不要介意上面那麼模糊的gif圖,畢竟我是用手機拍的。(介意你又能拿我怎麼辦。哈哈)

我們先來打個預防針,先學習基本的知識點:

涉及到的方法一共有下面幾個:

  • view獲取自身座標:getLeft(),getTop(),getRight(),getBottom()
  • view獲取自身寬高:getHeight(),getWidth()
  • motionEvent獲取座標:getX(),getY(),getRawX(),getRawY()

1.view獲取自身座標

如上圖所以,綠色區域的父視圖是黃色區域,所以left是55,top是55。

黃色區域的父視圖是藍色區域,所以left是60,top是115。

2.view獲取自身寬高

沒錯。從字面意思看就能理解,就是獲取View的寬高。

這裏提到一個以前遇到的一個問題,就是在Activity中有時候獲取某個View的width和height會爲0。

3.motionEvent獲取座標

  • getX():獲取點擊事件相對控件左邊的x軸座標,即點擊事件距離控件左邊的距離
  • getY():獲取點擊事件相對控件頂邊的y軸座標,即點擊事件距離控件頂邊的距離
  • getRawX():獲取點擊事件相對整個屏幕左邊的x軸座標,即點擊事件距離整個屏幕左邊的距離
  • getRawY():獲取點擊事件相對整個屏幕頂邊的y軸座標,即點擊事件距離整個屏幕頂邊的距離

所以當我們用鼠標點擊Button中間時候,那這時候getX()就是我們鼠標點擊的位置與Button左邊邊界的距離。getY()就是點擊的位置與Button頂邊邊界的距離。

其實設置Button跟着鼠標滑動,很簡單,就是在鼠標滑動的時候,重新設置Button的x和y座標。即使用setX()和setY()。這時候就有問題了。那二個方法中該填入的值是多少呢。讓我們畫個圖來看下就知道了。

首先我們比如對一個Button設置setX(200),setY(200),這時候是如下圖所示這樣:

所以實際上對一個Button設置setX(m),setY(n),實際上是這個Button的左上角的座標爲(m,n)。所以我們在拖動的時候不能簡單的把我們點擊的X和Y座標傳過去。 

如上圖所示,假如我們點中紅色的區域,來準備移動這個Button,並且鼠標移動了綠色區域那個地方,那麼這個Button也會移到圖上所示那樣。這樣纔是我們所期望的樣子。

但是如果單純把綠色區域的X和Y座標傳過去,讓Button來進行setX和setY 。則會出現如下那個Button所示位置。所以發現比我們期望的位置更靠右邊及下邊了。  

這時候我們發現多的位置正好是綠色區域在這個Button內部中相對位置的X和Y座標。

這下我們是不是就想到,對Button設置setX(getRawX()-getX())和setY(getRawY()- getY()),如果這時候你已經這麼想到了。恭喜你,你已經距離最後的成功差一小步了。當你高興的這麼寫後,你會發現你移動後的Button總是在鼠標點擊的下方。你會發現。X軸的的確已經正確了。但是Y軸還是錯誤。如下圖所示: 

這時候你一定會問,WHY???

原來這麼分析是沒問題的。But這個我們前面的假設都是在這個座標系中,但是這個座標系的位置在哪裏??? 

錯誤原因:

因爲我們調用的getRawY()方法獲取到的是屏幕左上角到我們點的區域的Y軸的距離,也就是以藍色座標系來做參考。而我們對Button設置setY()方法的時候是綠色區域的左上角到我們點的區域的Y軸距離,也就是以紅色座標系來做參考。所以我們知道了。我們在Y軸上還要減去狀態欄的高度及應用標題欄的高度纔可以。

那麼又有新的問題了。如何獲取狀態欄的高度,和應用標題欄的高度:

獲取狀態欄高度

 
  
  1. int statusBarHeight = -1;   
  2. //獲取status_bar_height資源的ID   
  3. int resourceId = getResources().getIdentifier("status_bar_height""dimen""android");   
  4. if (resourceId > 0) {   
  5.     //根據資源ID獲取響應的尺寸值   
  6.     statusBarHeight = getResources().getDimensionPixelSize(resourceId);   
  7.  

獲取標題欄高度

 
  
  1. // 獲取標題欄高度   
  2. Window window = getWindow();   
  3. int contentViewTop = getWindow()   
  4.         .findViewById(Window.ID_ANDROID_CONTENT).getTop();   
  5. // statusBarHeight是上面所求的狀態欄的高度   
  6. titleBarHeight = contentViewTop - statusBarHeight;  

結論:

所以最後我們再拖動Button的時候,會對它setX(getRawX()-getX())及setY(getRawY()-getY()-狀態欄高度-標題欄高度)。其中getX()和getY()是在你點擊下去的時候就獲取的。也就是在motionEvent.getAction() == MotionEvent.ACTION_DOWN的時候去獲取這二個值即可。因爲在motionEvent.getAction() == MotionEvent.ACTION_MOVE的時候去獲取getX()和getY()可能因爲你拖動的速度原因造成值不同,比如你拖動很快,鼠標先過去了。Button後面纔跟隨着過來。這時候的getX()及getY()都不同。

既然點擊按鈕後可以拖動Button,那肯定對Button設置了OnTouch監聽。直接上關鍵代碼: 

 
  
  1. package yunyuan.androiddemo.coordinatelayout;
  2. import android.app.Activity; 
  3. import android.os.Bundle; 
  4. import android.view.MotionEvent; 
  5. import android.view.View
  6. import android.view.Window; 
  7. import android.widget.Button; 
  8.  
  9. import butterknife.BindView; 
  10. import butterknife.ButterKnife; 
  11. import yunyuan.androiddemo.R; 
  12.  
  13. /** 
  14.  * Created by willy on 16/12/19. 
  15.  */ 
  16.  
  17. public class Act_CoordinateLayout extends Activity{ 
  18.  
  19.     @BindView(R.id.btn) 
  20.     Button btn; 
  21.  
  22.     float dx,dy; 
  23.  
  24.  
  25.     @Override 
  26.     protected void onCreate(Bundle savedInstanceState) { 
  27.         super.onCreate(savedInstanceState); 
  28.         setContentView(R.layout.act_coordinatelayout); 
  29.         ButterKnife.bind(this); 
  30.  
  31.         btn.setOnTouchListener(new Button.OnTouchListener() { 
  32.             @Override 
  33.             public boolean onTouch(View view, MotionEvent motionEvent) { 
  34.                 if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){ 
  35.                     dx = motionEvent.getX(); 
  36.                     dy = motionEvent.getY(); 
  37.  
  38.                 } else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){ 
  39.  
  40.                     view.setX(motionEvent.getRawX() - dx); 
  41.                     view.setY(motionEvent.getRawY()- dy - getStatusBarHeight() - getTitleBarHeight()); 
  42.                 } 
  43.                 return true
  44.             } 
  45.         }); 
  46.     } 
  47.  
  48.  
  49.     public int getStatusBarHeight(){ 
  50.         int result = 0; 
  51.         int resourceId = getResources().getIdentifier("status_bar_height""dimen""android"); 
  52.         if (resourceId > 0) { 
  53.             result = getResources().getDimensionPixelSize(resourceId); 
  54.         } 
  55.         return result; 
  56.     } 
  57.  
  58.  
  59.     public int getTitleBarHeight(){ 
  60.         Window window = getWindow(); 
  61.         int contentViewTop = getWindow() 
  62.                 .findViewById(Window.ID_ANDROID_CONTENT).getTop(); 
  63.         // statusBarHeight是上面所求的狀態欄的高度 
  64.         int titleBarHeight = contentViewTop - getStatusBarHeight(); 
  65.         return titleBarHeight; 
  66.     } 
  67.  



作者:青蛙要fly
來源:51CTO