今天是一個簡單的Demo。Demo實現的功能就是,用鼠標點中button的時候,然后拖動Button。這時候Button會根據你鼠標的移動而移動,同時,你鼠標點中的Button的位置也不會改變。比如你點在Button的左上角,那移動的時候。鼠標還是在Button的左上角
--------------------------第三套廣播體操:時代在召喚。現在開始-----------------------
一言不合上效果圖。
大家不要介意上面那么模糊的gif圖,畢竟我是用手機拍的。(介意你又能拿我怎么辦。哈哈)
我們先來打個預防針,先學習基本的知識點:
涉及到的方法一共有下面幾個:
- view獲取自身坐標:getLeft(),getTop(),getRight(),getBottom()
- view獲取自身寬高:getHeight(),getWidth()
- motionEvent獲取坐標:getX(),getY(),getRawX(),getRawY()
1.View獲取自身坐標
其中getLeft(),getTop(),getRight(),getBottom()是View 根據父視圖相應的left,top,right,buttom的位置。
如上圖所以,綠色區域的父視圖是黃色區域,所以left是55,top是55。
黃色區域的父視圖是藍色區域,所以left是60,top是115。
2.view獲取自身寬高
沒錯。從字面意思看就能理解,就是獲取View的寬高。
這里提到一個以前遇到的一個問題,就是在Activity中有時候獲取某個View的width和height會為0。
沒錯,下面的鏈接你就可以搞定這個問題。哈哈。點起來。
Activity中獲取view的高度和寬度為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軸上還要減去狀態欄的高度及應用標題欄的高度才可以。
那么又有新的問題了。如何獲取狀態欄的高度,和應用標題欄的高度:
獲取狀態欄高度
int statusBarHeight = -1;
//獲取status_bar_height資源的ID
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
//根據資源ID獲取響應的尺寸值
statusBarHeight = getResources().getDimensionPixelSize(resourceId);
}
獲取標題欄高度
// 獲取標題欄高度
Window window = getWindow();
int contentViewTop = getWindow()
.findViewById(Window.ID_ANDROID_CONTENT).getTop();
// statusBarHeight是上面所求的狀態欄的高度
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監聽。直接上關鍵代碼:
package yunyuan.androiddemo.coordinatelayout;
import android.app.Activity;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import butterknife.BindView;
import butterknife.ButterKnife;
import yunyuan.androiddemo.R;
/**
* Created by willy on 16/12/19.
*/
public class Act_CoordinateLayout extends Activity{
@BindView(R.id.btn)
Button btn;
float dx,dy;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.act_coordinatelayout);
ButterKnife.bind(this);
btn.setOnTouchListener(new Button.OnTouchListener() {
@Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction() == MotionEvent.ACTION_DOWN){
dx = motionEvent.getX();
dy = motionEvent.getY();
} else if(motionEvent.getAction() == MotionEvent.ACTION_MOVE){
view.setX(motionEvent.getRawX() - dx);
view.setY(motionEvent.getRawY()- dy - getStatusBarHeight() - getTitleBarHeight());
}
return true;
}
});
}
public int getStatusBarHeight(){
int result = 0;
int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
if (resourceId > 0) {
result = getResources().getDimensionPixelSize(resourceId);
}
return result;
}
public int getTitleBarHeight(){
Window window = getWindow();
int contentViewTop = getWindow()
.findViewById(Window.ID_ANDROID_CONTENT).getTop();
// statusBarHeight是上面所求的狀態欄的高度
int titleBarHeight = contentViewTop - getStatusBarHeight();
return titleBarHeight;
}
}