小Demo大知識-通過控制Button移動(dòng)來學(xué)習(xí)Android坐標(biāo)
今天分享一個(gè)簡單的Demo。Demo實(shí)現(xiàn)的功能就是,用鼠標(biāo)點(diǎn)中button的時(shí)候,然后拖動(dòng)Button。這時(shí)候Button會根據(jù)你鼠標(biāo)的移動(dòng)而移動(dòng),同時(shí),你鼠標(biāo)點(diǎn)中的Button的位置也不會改變。比如你點(diǎn)在Button的左上角,那移動(dòng)的時(shí)候。鼠標(biāo)還是在Button的左上角
一言不合上效果圖
大家不要介意上面那么模糊的gif圖,畢竟我是用手機(jī)拍的。(介意你又能拿我怎么辦。哈哈)
我們先來打個(gè)預(yù)防針,先學(xué)習(xí)基本的知識點(diǎn):
涉及到的方法一共有下面幾個(gè):
- view獲取自身坐標(biāo):getLeft(),getTop(),getRight(),getBottom()
- view獲取自身寬高:getHeight(),getWidth()
- motionEvent獲取坐標(biāo):getX(),getY(),getRawX(),getRawY()
1.view獲取自身坐標(biāo)
如上圖所以,綠色區(qū)域的父視圖是黃色區(qū)域,所以left是55,top是55。
黃色區(qū)域的父視圖是藍(lán)色區(qū)域,所以left是60,top是115。
2.view獲取自身寬高
沒錯(cuò)。從字面意思看就能理解,就是獲取View的寬高。
這里提到一個(gè)以前遇到的一個(gè)問題,就是在Activity中有時(shí)候獲取某個(gè)View的width和height會為0。
3.motionEvent獲取坐標(biāo)
- getX():獲取點(diǎn)擊事件相對控件左邊的x軸坐標(biāo),即點(diǎn)擊事件距離控件左邊的距離
- getY():獲取點(diǎn)擊事件相對控件頂邊的y軸坐標(biāo),即點(diǎn)擊事件距離控件頂邊的距離
- getRawX():獲取點(diǎn)擊事件相對整個(gè)屏幕左邊的x軸坐標(biāo),即點(diǎn)擊事件距離整個(gè)屏幕左邊的距離
- getRawY():獲取點(diǎn)擊事件相對整個(gè)屏幕頂邊的y軸坐標(biāo),即點(diǎn)擊事件距離整個(gè)屏幕頂邊的距離
所以當(dāng)我們用鼠標(biāo)點(diǎn)擊Button中間時(shí)候,那這時(shí)候getX()就是我們鼠標(biāo)點(diǎn)擊的位置與Button左邊邊界的距離。getY()就是點(diǎn)擊的位置與Button頂邊邊界的距離。
其實(shí)設(shè)置Button跟著鼠標(biāo)滑動(dòng),很簡單,就是在鼠標(biāo)滑動(dòng)的時(shí)候,重新設(shè)置Button的x和y坐標(biāo)。即使用setX()和setY()。這時(shí)候就有問題了。那二個(gè)方法中該填入的值是多少呢。讓我們畫個(gè)圖來看下就知道了。
首先我們比如對一個(gè)Button設(shè)置setX(200),setY(200),這時(shí)候是如下圖所示這樣:
所以實(shí)際上對一個(gè)Button設(shè)置setX(m),setY(n),實(shí)際上是這個(gè)Button的左上角的坐標(biāo)為(m,n)。所以我們在拖動(dòng)的時(shí)候不能簡單的把我們點(diǎn)擊的X和Y坐標(biāo)傳過去。
如上圖所示,假如我們點(diǎn)中紅色的區(qū)域,來準(zhǔn)備移動(dòng)這個(gè)Button,并且鼠標(biāo)移動(dòng)了綠色區(qū)域那個(gè)地方,那么這個(gè)Button也會移到圖上所示那樣。這樣才是我們所期望的樣子。
但是如果單純把綠色區(qū)域的X和Y坐標(biāo)傳過去,讓Button來進(jìn)行setX和setY 。則會出現(xiàn)如下那個(gè)Button所示位置。所以發(fā)現(xiàn)比我們期望的位置更靠右邊及下邊了。
這時(shí)候我們發(fā)現(xiàn)多的位置正好是綠色區(qū)域在這個(gè)Button內(nèi)部中相對位置的X和Y坐標(biāo)。
這下我們是不是就想到,對Button設(shè)置setX(getRawX()-getX())和setY(getRawY()- getY()),如果這時(shí)候你已經(jīng)這么想到了。恭喜你,你已經(jīng)距離最后的成功差一小步了。當(dāng)你高興的這么寫后,你會發(fā)現(xiàn)你移動(dòng)后的Button總是在鼠標(biāo)點(diǎn)擊的下方。你會發(fā)現(xiàn)。X軸的的確已經(jīng)正確了。但是Y軸還是錯(cuò)誤。如下圖所示:
這時(shí)候你一定會問,WHY???
原來這么分析是沒問題的。But這個(gè)我們前面的假設(shè)都是在這個(gè)坐標(biāo)系中,但是這個(gè)坐標(biāo)系的位置在哪里???
錯(cuò)誤原因:
因?yàn)槲覀冋{(diào)用的getRawY()方法獲取到的是屏幕左上角到我們點(diǎn)的區(qū)域的Y軸的距離,也就是以藍(lán)色坐標(biāo)系來做參考。而我們對Button設(shè)置setY()方法的時(shí)候是綠色區(qū)域的左上角到我們點(diǎn)的區(qū)域的Y軸距離,也就是以紅色坐標(biāo)系來做參考。所以我們知道了。我們在Y軸上還要減去狀態(tài)欄的高度及應(yīng)用標(biāo)題欄的高度才可以。
那么又有新的問題了。如何獲取狀態(tài)欄的高度,和應(yīng)用標(biāo)題欄的高度:
獲取狀態(tài)欄高度
- int statusBarHeight = -1;
- //獲取status_bar_height資源的ID
- int resourceId = getResources().getIdentifier("status_bar_height", "dimen", "android");
- if (resourceId > 0) {
- //根據(jù)資源ID獲取響應(yīng)的尺寸值
- statusBarHeight = getResources().getDimensionPixelSize(resourceId);
- }
獲取標(biāo)題欄高度
- // 獲取標(biāo)題欄高度
- Window window = getWindow();
- int contentViewTop = getWindow()
- .findViewById(Window.ID_ANDROID_CONTENT).getTop();
- // statusBarHeight是上面所求的狀態(tài)欄的高度
- titleBarHeight = contentViewTop - statusBarHeight;
結(jié)論:
所以最后我們再拖動(dòng)Button的時(shí)候,會對它setX(getRawX()-getX())及setY(getRawY()-getY()-狀態(tài)欄高度-標(biāo)題欄高度)。其中g(shù)etX()和getY()是在你點(diǎn)擊下去的時(shí)候就獲取的。也就是在motionEvent.getAction() == MotionEvent.ACTION_DOWN的時(shí)候去獲取這二個(gè)值即可。因?yàn)樵趍otionEvent.getAction() == MotionEvent.ACTION_MOVE的時(shí)候去獲取getX()和getY()可能因?yàn)槟阃蟿?dòng)的速度原因造成值不同,比如你拖動(dòng)很快,鼠標(biāo)先過去了。Button后面才跟隨著過來。這時(shí)候的getX()及getY()都不同。
既然點(diǎn)擊按鈕后可以拖動(dòng)Button,那肯定對Button設(shè)置了OnTouch監(jiān)聽。直接上關(guān)鍵代碼:
- 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是上面所求的狀態(tài)欄的高度
- int titleBarHeight = contentViewTop - getStatusBarHeight();
- return titleBarHeight;
- }
- }