View坐標(biāo)分析匯總

一:概述

這里寫圖片描述

下面借用那篇博客的一張圖:
這里寫圖片描述

view提供的方法
getTop:獲取到的呻粹,是view自身的頂邊到其父布局頂邊的距離 getLeft:獲取到的,是view自身的左邊到其父布局左邊的距離 getRight:獲取到的忽肛,是view自身的右邊到其父布局左邊的距離 getBottom:獲取到的,是view自身的底邊到其父布局頂邊的距離

MotionEvent提供的方法
getX():獲取點(diǎn)擊事件相對(duì)控件左邊的x軸坐標(biāo),即點(diǎn)擊事件距離控件左邊的距離 getY():獲取點(diǎn)擊事件相對(duì)控件頂邊的y軸坐標(biāo)诽里,即點(diǎn)擊事件距離控件頂邊的距離 getRawX():獲取點(diǎn)擊事件相對(duì)整個(gè)屏幕左邊的x軸坐標(biāo),即點(diǎn)擊事件距離整個(gè)屏幕左邊的距離 getRawY():獲取點(diǎn)擊事件相對(duì)整個(gè)屏幕頂邊的y軸坐標(biāo)飞蛹,即點(diǎn)擊事件距離整個(gè)屏幕頂邊的距離

下面做個(gè)測(cè)試

這里寫圖片描述

分別點(diǎn)擊A點(diǎn)谤狡,B點(diǎn)后效果
這里寫圖片描述

這里需要注意的是:
點(diǎn)擊B點(diǎn)后(可以看到先是回調(diào)TestTextView中的onTouchEvent方法灸眼,然后才是MainActivity中的onTouchEvent,因?yàn)槲以诙叩膐nTouchEvent方法中都沒有進(jìn)行點(diǎn)擊事件的消費(fèi)處理墓懂,所以會(huì)往上傳遞焰宣,突然扯到了事件分發(fā)機(jī)制,2333~這里就是突然想補(bǔ)充一點(diǎn)捕仔,還是扯回坐標(biāo)吧)
1.TestTextView中g(shù)etY和getRawY取得的值不一樣匕积,這點(diǎn)我們可以理解
2.MainActivity中g(shù)etY和getRawY取得的值一樣!(我們注意到點(diǎn)擊A,B點(diǎn)都是如此)

這里我們得到一條啟發(fā):
getY和getRawY這一系動(dòng)作都是由MotionEvent來定義產(chǎn)生的榜跌。是得看最后MotionEvent是被哪個(gè)View所消耗闪唆。如果MotionEvent沒有被任何View所消耗,最終返回Activity則getY和getRawY則一致钓葫。如果被View所消耗悄蕾,則具體情況具體分析,getY础浮,getRawY可能一致帆调,也可能不一致。

測(cè)試2:類似在ListView這種有滾動(dòng)軸的控件中會(huì)是什么樣的呢霸旗?

這里寫圖片描述

這個(gè)打印我忘記截圖完整了贷帮,這里我說明下打印的log我是在ListView所在的activity中的dispatchTouchEvent,之所以不在activity的onTouchEvent中打印是因?yàn)長(zhǎng)istView中的item消費(fèi)了事件诱告,那么這個(gè)activity的onTouchEvent就不會(huì)打印出Log了撵枢。 貼上代碼

@Override
    public boolean dispatchTouchEvent(MotionEvent event) {
        if (event.getAction() == MotionEvent.ACTION_DOWN) {
            LogUtil.m("SecondActivity  getX " + event.getX() + " getY " + event.getY());
            LogUtil.m("SecondActivity  getrawx " + event.getRawX() + " getrawy " + event.getRawY());
        }
        return super.dispatchTouchEvent(event);
    }
這里寫圖片描述

這次我們點(diǎn)擊的是item7的頂部。 (PS:這時(shí)可能會(huì)有點(diǎn)好奇精居,我們明明點(diǎn)擊的接近是item7的頂部锄禽,為啥得到的Y指卻不是接近0呢,原因后面講)

這里我們得到一條啟發(fā):
對(duì)于這種滑動(dòng)的ViewGroup靴姿,我們?cè)讷@取ViewGroup的坐標(biāo)值時(shí)并不需要考慮它到底滑動(dòng)了多少(實(shí)際滑動(dòng)的我們應(yīng)該看作是ViewGroup中的View在滑動(dòng))

二.獲取

在上面我們留下了一個(gè)疑問:我們明明點(diǎn)擊的接近是item7的頂部沃但,得到的Y指卻不是接近0。 原因在于getRawY返回的是點(diǎn)擊事件距離整個(gè)屏幕頂邊的距離佛吓,所以點(diǎn)擊item7的頂部宵晚,得到的Y值其實(shí)就是狀態(tài)欄的值。
當(dāng)然我們有時(shí)候碰到的不僅就只有狀態(tài)欄维雇,有時(shí)候是狀態(tài)欄與標(biāo)題欄并存的淤刃。所以我們?cè)讷@取ViewGroup的Y值是一定要注意是否需要減去狀態(tài)欄,標(biāo)題欄(如果有)的高度吱型,否則計(jì)算得到的Y值并不是正確的逸贾。

這里為了讓大家更清晰的了解,大家可以看看這篇文章http://bbs.51cto.com/thread-1072344-1.html(Android4.0窗口機(jī)制和創(chuàng)建過程分析 )
下面我們用圖來初略說明(這是我的理解,有誤歡迎指正)

這里寫圖片描述

現(xiàn)在我們?cè)賮碚f說怎么取得坐標(biāo)值的時(shí)機(jī)
因?yàn)镸otionEvent提供的獲取坐標(biāo)的方法是在頁(yè)面完完全全顯示在用戶眼前且用戶點(diǎn)擊后才會(huì)使用到的方法铝侵,所以并不存在獲取不到的問題灼伤,下面就論述下 view提供的獲取坐標(biāo)方法

看到這,你可能會(huì)說咪鲜,那還不簡(jiǎn)單當(dāng)布局被加載出來的時(shí)候狐赡,我們?nèi)カ@取不就OK了嗎? 于是我們就會(huì)看到這樣的錯(cuò)誤:在一個(gè)Activity的onCreate方法中嗜诀,設(shè)置完setContentView后猾警,就開始View的getLeft,getTop等方法隆敢,結(jié)果發(fā)現(xiàn)為0发皿,為啥?
原因就是:
對(duì)于View拂蝎,ViewGroup來說穴墅,width、height温自、top玄货、left等屬性值是在Measure與Layout過程完成之后才開始正確賦值的,而Measure與Layout卻都晚于onCreate方法執(zhí)行悼泌,所以onCreate中g(shù)etLeft根本就取不到值松捉!

那要是我們想要在onCreate中取到我們想要的值,我們應(yīng)該怎么做呢馆里? 大家可以參考這兩篇博文 (http://www.cnblogs.com/kissazi2/p/4133927.html) (http://blog.csdn.net/codezjx/article/details/45341309
我覺得寫得很好了
歸納如下:
監(jiān)聽Draw/Layout事件:ViewTreeObserver
將一個(gè)runnable添加到Layout隊(duì)列中:View.post()
重寫View的onLayout方法
重寫Activity的onWindowFocusChanged方法,在該方法中獲取

這里我推薦2,4這兩種隘世,即

view.post(new Runnable() {  
    @Override  
    public void run() {  
        view.getHeight();  
    }  
});

@Override  
public void onWindowFocusChanged(boolean hasFocus) {  
    super.onWindowFocusChanged(hasFocus);  
    //此處可以正常獲取width、height等  
} 

三.計(jì)算

現(xiàn)在對(duì)坐標(biāo)系是咋樣的鸠踪,我們已經(jīng)了解了丙者。
對(duì)啥時(shí)候獲取,以及獲取后是否需要糾正得到正確的Y营密,我們也已經(jīng)分析了械媒。
下面就來說說本文的重頭戲 ——— 坐標(biāo)的計(jì)算。
(之所以說是重頭戲评汰,是因?yàn)槲覀冎暗玫降亩际且粋€(gè)點(diǎn)的正確坐標(biāo)纷捞,現(xiàn)在我們需要做的是在得到多個(gè)正確坐標(biāo)后,進(jìn)行正確的計(jì)算被去,這樣我們才能實(shí)現(xiàn)滑動(dòng)這樣炫酷的效果`(∩_∩)′)

首先我們需要建立一個(gè)概念
在Android的坐標(biāo)系中兰绣,原點(diǎn)在屏幕左上角,向右x為正编振,向下y為正。

這里寫圖片描述
(為了好計(jì)算,圖片中的坐標(biāo)單位是px)
(下面就以這個(gè)為例踪央,我們要將View從原點(diǎn)移動(dòng)到(200,400)的位置臀玄,即B點(diǎn)與C點(diǎn)重合)
想實(shí)現(xiàn)View移動(dòng)大致有這幾種方式(代碼見下面)
這里寫圖片描述

XML文件

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">
    <LinearLayout
        android:id="@+id/ly"
        android:background="#EFAA88"
        android:layout_centerInParent="true"
        android:layout_width="300px"
        android:layout_height="500px">
        <mr_immortalz.com.testlocation.TestTextView
            android:background="#aabbcc"
            android:id="@+id/tv"
            android:text="你好"
            android:layout_width="100px"
            android:layout_height="100px" />
    </LinearLayout>
</LinearLayout>

TestTextView.java

/**
 * Created by Mr_immortalZ on 2016/4/16.
 * email : mr_immortalz@qq.com
 */
public class TestTextView extends TextView {
    public TestTextView(Context context) {
        super(context);
    }

    public TestTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public TestTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                /*LogUtil.m("TestTextView  getX "+event.getX()+" getY "+event.getY());
                LogUtil.m("TestTextView  getrawx "+event.getRawX()+" getrawy "+event.getRawY());*/
                //layout(getLeft() + 200, getTop() + 400, getRight() + 200, getBottom() + 400);

                /*offsetLeftAndRight(200);
                offsetTopAndBottom(400);*/
               /* ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams();
                lp.leftMargin = getLeft() + 200;
                lp.topMargin = getTop() + 400;
                setLayoutParams(lp);*/

                //((View)getParent()).scrollTo(-200,-400);

                //scrollTo(-50,-10);

                //scrollTo(300, 500);

                //((View)getParent()).scrollBy(-200,-400);
                /*AnimatorSet set = new AnimatorSet();
                set.playTogether(
                        ObjectAnimator.ofFloat(this, "translationX", 200),
                        ObjectAnimator.ofFloat(this,"translationY", 400)

                );
                set.start();*/
                TranslateAnimation anim = new TranslateAnimation(0, 200, 0, 400);
                anim.setFillAfter(true);
                startAnimation(anim);

                LogUtil.m("移動(dòng)后 getX " + getX() + "  getY " + getY());
                LogUtil.m("移動(dòng)后 getLeft " + getLeft() + "tv getTop " + getTop()
                        + " tv getRight " + getRight() + " tv getBottom " + getBottom());
                break;
        }
        return true;
    }
}

我們移動(dòng)到指定位置的有7種方式
1.layout
layout(getLeft() + 200, getTop() + 400, getRight() + 200, getBottom() + 400);

移動(dòng)后getLeft等值改變

這里寫圖片描述

2.offsetLeftAndRight、offsetTopAndBottom
offsetLeftAndRight(200);offsetTopAndBottom(400);

移動(dòng)后getLeft等值改變

這里寫圖片描述

3.修改LayoutParams
ViewGroup.MarginLayoutParams lp = (ViewGroup.MarginLayoutParams) getLayoutParams(); lp.leftMargin = getLeft() + 200; lp.topMargin = getTop() + 400; setLayoutParams(lp);

移動(dòng)后getLeft等值不改變

這里寫圖片描述

4.scrollTo
((View)getParent()).scrollTo(-200,-400);

移動(dòng)后getLeft等值不改變

這里寫圖片描述

5.scrollBy
((View)getParent()).scrollBy(-200,-400);

移動(dòng)后getLeft等值不改變

這里寫圖片描述

對(duì)于scrollTo畅蹂、scrollBy需要注意的有兩個(gè)問題 問題1:
移動(dòng)計(jì)算值 = 最開始點(diǎn)坐標(biāo) - 最后移動(dòng)到的坐標(biāo) 原因是因?yàn)樽罱K會(huì)調(diào)用這個(gè)方法 —— invalidateInternal(l - scrollX, t - scrollY, r - scrollX, b - scrollY, true, false); 其中l(wèi),t,r,b為原來坐標(biāo)點(diǎn)健无,scrollX,scrollY為目標(biāo)坐標(biāo)點(diǎn),只有當(dāng)目標(biāo)坐標(biāo)點(diǎn)值是負(fù)數(shù)時(shí)液斜,移動(dòng)到的位置才為正數(shù)累贤! 例如scrollTo ,我們要從(0,0)移動(dòng)到(200,400)這個(gè)點(diǎn)少漆,根據(jù)上面的公式可知為負(fù)值

問題2
為什么需要加上 ((View)getParent())
TestTextView本身是View臼膏,scrollTo、scrollBy移動(dòng)的都是View的Content示损,如果不加的話渗磅,使用的效果則是TestTextView的文字位置變化,而TestTextView本身不會(huì)變化检访。 如果在ViewGroup中使用scrollTo始鱼、scrollBy,則移動(dòng)的是ViewGroup中的View.我們這里需要讓TestTextView移動(dòng)脆贵,則需要先 ((View)getParent())医清,然后再((View)getParent()).scrollTo…

6.屬性動(dòng)畫
我就以O(shè)bjectAnimator為例子
AnimatorSet set = new AnimatorSet(); set.playTogether( ObjectAnimator.ofFloat(this, "translationX", 200), ObjectAnimator.ofFloat(this,"translationY", 400) ); set.start();

移動(dòng)后getLeft等值不改變


這里寫圖片描述

7.位移動(dòng)畫
TranslateAnimation anim = new TranslateAnimation(0,200,0,400); anim.setFillAfter(true); startAnimation(anim);

移動(dòng)后getLeft等值不改變
[圖片上傳中。卖氨。会烙。(7)]
關(guān)于位移動(dòng)畫的補(bǔ)充點(diǎn):
我們經(jīng)常用這樣的需求,要求一個(gè)popupwindow從屏幕底部彈出或者從屏幕頂部彈出双泪。 這里的位移設(shè)置同樣還是如此(原點(diǎn)在屏幕左上角持搜,向右x為正,向下y為正)
這篇博文可以參考學(xué)習(xí)下焙矛,下面這張神圖也是來自這篇博文(Android動(dòng)畫之translate(位移動(dòng)畫)) http://www.cnblogs.com/bavariama/archive/2013/01/29/2881225.html

這里寫圖片描述

注意點(diǎn)
屬性動(dòng)畫是真實(shí)改變View的位置的葫盼,雖然屬性動(dòng)畫、位移動(dòng)畫的getLeft等沒有改變村斟,但是屬性動(dòng)畫的getX贫导、getY是改變了的,位移動(dòng)畫的getX蟆盹、getY仍未改變孩灯!

最后再來回顧下這張圖

這里寫圖片描述

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市逾滥,隨后出現(xiàn)的幾起案子峰档,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 212,294評(píng)論 6 493
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件讥巡,死亡現(xiàn)場(chǎng)離奇詭異掀亩,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)欢顷,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,493評(píng)論 3 385
  • 文/潘曉璐 我一進(jìn)店門槽棍,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人抬驴,你說我怎么就攤上這事炼七。” “怎么了布持?”我有些...
    開封第一講書人閱讀 157,790評(píng)論 0 348
  • 文/不壞的土叔 我叫張陵豌拙,是天一觀的道長(zhǎng)。 經(jīng)常有香客問我鳖链,道長(zhǎng)姆蘸,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 56,595評(píng)論 1 284
  • 正文 為了忘掉前任芙委,我火速辦了婚禮逞敷,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘灌侣。我一直安慰自己推捐,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,718評(píng)論 6 386
  • 文/花漫 我一把揭開白布侧啼。 她就那樣靜靜地躺著牛柒,像睡著了一般。 火紅的嫁衣襯著肌膚如雪痊乾。 梳的紋絲不亂的頭發(fā)上皮壁,一...
    開封第一講書人閱讀 49,906評(píng)論 1 290
  • 那天,我揣著相機(jī)與錄音哪审,去河邊找鬼蛾魄。 笑死,一個(gè)胖子當(dāng)著我的面吹牛湿滓,可吹牛的內(nèi)容都是我干的滴须。 我是一名探鬼主播,決...
    沈念sama閱讀 39,053評(píng)論 3 410
  • 文/蒼蘭香墨 我猛地睜開眼叽奥,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼扔水!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起朝氓,我...
    開封第一講書人閱讀 37,797評(píng)論 0 268
  • 序言:老撾萬榮一對(duì)情侶失蹤魔市,失蹤者是張志新(化名)和其女友劉穎主届,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體待德,經(jīng)...
    沈念sama閱讀 44,250評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡岂膳,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,570評(píng)論 2 327
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了磅网。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,711評(píng)論 1 341
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡筷屡,死狀恐怖涧偷,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情毙死,我是刑警寧澤燎潮,帶...
    沈念sama閱讀 34,388評(píng)論 4 332
  • 正文 年R本政府宣布,位于F島的核電站扼倘,受9級(jí)特大地震影響确封,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜再菊,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 40,018評(píng)論 3 316
  • 文/蒙蒙 一爪喘、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧纠拔,春花似錦秉剑、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,796評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至臀叙,卻和暖如春略水,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背劝萤。 一陣腳步聲響...
    開封第一講書人閱讀 32,023評(píng)論 1 266
  • 我被黑心中介騙來泰國(guó)打工渊涝, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人稳其。 一個(gè)月前我還...
    沈念sama閱讀 46,461評(píng)論 2 360
  • 正文 我出身青樓驶赏,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親既鞠。 傳聞我的和親對(duì)象是個(gè)殘疾皇子煤傍,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,595評(píng)論 2 350

推薦閱讀更多精彩內(nèi)容