坐標(biāo)系初探

初探起因

?一開(kāi)始接觸到坐標(biāo)系相關(guān)的東西是使用ScrollView抓狭,因?yàn)镾crollView布局限制高度應(yīng)該為wrap_content,所以項(xiàng)目中實(shí)際展示出來(lái)的布局效果和理想中的布局效果發(fā)了偏差谜洽。如果給布局內(nèi)部的控件設(shè)置固定DP的話,又會(huì)有適配的問(wèn)題的出現(xiàn),所以就在java代碼使用LayoutParams中對(duì)布局高度進(jìn)行動(dòng)態(tài)賦值饥悴,達(dá)到了自己想要的效果故慈。
?后來(lái)看書看到View這部分的時(shí)候,又看到了坐標(biāo)系這一部分的知識(shí)窗慎,加上身邊大佬的Github里也有研究物喷,就打算簡(jiǎn)單先總結(jié)一下,畢竟這個(gè)東西搞懂了遮斥,自定義View的坐標(biāo)相關(guān)以及自己想實(shí)現(xiàn)一些更靈活的效果的話峦失,就更容易了。

流程步驟

?這里大致分為以下幾步:
????????????1. 了解位置參數(shù)
????????????2. 在程序里讓控件可以隨著我們的手指動(dòng)起來(lái)
????????????3. 對(duì)控件的移動(dòng)范圍給予邊界的限制
????????????4.讓控件“聽(tīng)話”

1. 了解布局參數(shù)

?首先我們需要了解一下坐標(biāo)系的概念术吗。這里我們選取了兩個(gè)參照物尉辑,是為了更好的區(qū)別后面的參數(shù)。

參照坐標(biāo).png

?這里有兩個(gè)參照坐標(biāo)系较屿,相對(duì)圖片控件來(lái)說(shuō)隧魄。一個(gè)是根布局,使用黃色標(biāo)注的布局的坐標(biāo)系隘蝎,左上角為坐標(biāo)原點(diǎn)(0,0)购啄,橫向?yàn)閄軸,縱向?yàn)閅軸嘱么。另一個(gè)是父布局狮含,使用了白色標(biāo)注。同上曼振。
?我們?cè)谶@里暫且稱圖片控件為子View几迄,我們子View的位置是由四個(gè)頂點(diǎn)決定,分別對(duì)于四個(gè)屬性:top冰评,left映胁,right,bottom甲雅。其中解孙,top是左上角縱坐標(biāo)坑填,left是左上角橫坐標(biāo),right是右下角橫坐標(biāo)弛姜,bottom是右下角縱坐標(biāo)穷遂。當(dāng)然這些是相對(duì)于我們上面提到的父布局來(lái)說(shuō)的。它是一種相對(duì)坐標(biāo)娱据。
位置參數(shù)1.png

?通俗點(diǎn)來(lái)說(shuō)的話蚪黑,getLeft()是子View的左邊相對(duì)于父布局的左邊的距離,getTop()是子View的上邊相對(duì)于父布局的上邊的距離中剩,getRight()是子View的右邊相對(duì)于父布局的左邊的距離忌穿,getBottom()是子布局的下邊相對(duì)于父布局的上邊的距離。


位置參數(shù)二.png

?上面已經(jīng)說(shuō)到了子View的四個(gè)參數(shù)屬性结啼,因?yàn)槲覀円屪覸iew隨我們的手指移動(dòng)掠剑,所以在手指接觸屏幕后,我們可以通過(guò)MotionEvent得到點(diǎn)擊當(dāng)時(shí)的x和y坐標(biāo)郊愧,系統(tǒng)為我們提供了getX(),getY(),getRawX(),getRawY()這四個(gè)方法朴译,前兩個(gè)方法是觸摸點(diǎn)相對(duì)于子View左上角原點(diǎn)的x和y坐標(biāo),后兩個(gè)方法是相對(duì)于手機(jī)屏幕的x和y坐標(biāo)属铁。

位置參數(shù)三.png

?要想我們的控件動(dòng)起來(lái)眠寿,我們還需要了解兩個(gè)View的位置參數(shù),他們?cè)赩iew移動(dòng)的時(shí)候會(huì)用到焦蘑。這兩個(gè)方法就是getTranslationX()getTranslationY(),他們是View相對(duì)自身move后的X和Y軸的偏移量盯拱。這兩個(gè)值是相對(duì)來(lái)說(shuō)的,相對(duì)你初始化布局時(shí)候View的位置例嘱,X軸方向向左為負(fù)值狡逢,向右為正值,Y軸方向向上為負(fù)值拼卵,向下為正值奢浑。
?好了,到這里腋腮,我們了解了十個(gè)參數(shù)屬性:
?getLeft()
?getTop()
?getRight()
?getBottom()
?getX()
?getY()
?getRawX()
?getRawY()
?getTranslationX()
?getTranslationY()
?那么接下來(lái)我們就在代碼中使用它們來(lái)讓我們的控件可以隨手指移動(dòng)雀彼。

2. 在程序里讓控件可以隨著我們的手指動(dòng)起來(lái)

?布局很簡(jiǎn)單,上面我有提到為了區(qū)分參數(shù)所以中間使用了一個(gè)父布局嵌套低葫,當(dāng)然大家也可以不用父布局详羡,直接在根布局下寫一個(gè)ImageView控件仍律。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:id="@+id/rl_layout"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary"
    tools:context=".MainActivity">

    <RelativeLayout
        android:id="@+id/rl_parent"
        android:layout_width="300dp"
        android:layout_height="500dp"
        android:layout_centerInParent="true"
        android:background="@color/text_bg">

        <ImageView
            android:id="@+id/iv_test_icon"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:src="@drawable/head_icon" />
    </RelativeLayout>
</RelativeLayout>

?寫完布局后嘿悬,我們?cè)贏S中可以預(yù)覽到是這個(gè)樣子


預(yù)覽布局.png

?然后我們?cè)贛ainActivity中就可以開(kāi)始寫我們想要的效果啦。
?因?yàn)槲覀兿胍丶S我們的手指觸摸移動(dòng)水泉,所以在這里我們用到了setOnTouchListener這個(gè)監(jiān)聽(tīng)善涨。上面我們也提到了getX()getY()這兩個(gè)方法是MotionEvent調(diào)用得到的值窒盐。而MotionEvent這個(gè)對(duì)象我們正好可以在setOnTouchListener下的onTouch方法里拿到這個(gè)對(duì)象。
?說(shuō)到onTouch钢拧,我們還需要知道蟹漓,關(guān)于觸摸,我們會(huì)發(fā)生的狀態(tài)有三種源内,按下葡粒,移動(dòng),抬起膜钓。相應(yīng)的嗽交,在onTouch方法里的MotionEvent對(duì)象我們可以通過(guò)判斷ACTION_DOWNACTION_MOVE颂斜,ACTION_UP來(lái)決定我們的代碼在哪里執(zhí)行夫壁。
?接下來(lái),我們?cè)?code>ACTION_DOWN的時(shí)候去獲取到x和y的值沃疮,然后在ACTION_MOVE的時(shí)候先計(jì)算我們的坐標(biāo)移動(dòng)的x和y的位移距離盒让,然后再通過(guò)setTranslationX()/setTranslationY()去讓控件跟著改變距離就可以讓我們的控件動(dòng)起來(lái)了!

 ivTestIcon.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                final int x = (int) motionEvent.getX();
                final int y = (int) motionEvent.getY();
                switch (motionEvent.getActionMasked()) {
                    case MotionEvent.ACTION_DOWN:
                        mLastX = x;
                        mLastY = y;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int xOffset = x - mLastX;//得到X的位移距離
                        int yOffset = y - mLastY;//得到Y(jié)的位移距離 
                        ivTestIcon.setTranslationX(ivTestIcon.getTranslationX() + xOffset);
                        ivTestIcon.setTranslationY(ivTestIcon.getTranslationY() + yOffset);
                        break;
                    case MotionEvent.ACTION_UP:
                        break;
                }
                return true;
            }
        });

?這里需要注意一下的是onTouch的返回值要改為true司蔬,因?yàn)閒alse的話會(huì)攔截之后的動(dòng)作邑茄,執(zhí)行ACTION_DOWN之后就不會(huì)再執(zhí)行之后的動(dòng)作了。
?然后我們就可以看到下面的效果了

成果1.gif

?那么現(xiàn)在手指觸摸控件是可以開(kāi)始移動(dòng)了俊啼,但是我們會(huì)發(fā)現(xiàn)一個(gè)問(wèn)題撩扒,當(dāng)我們的圖片超出父布局的范圍的部分就消失了,這樣的體驗(yàn)很不好吨些,所以搓谆,接下來(lái)我們給我們圖片的規(guī)定一個(gè)移動(dòng)范圍,讓它只能在我們的父布局內(nèi)部移動(dòng)豪墅。

3. 對(duì)控件的移動(dòng)范圍給予邊界的限制

?首先我們得獲取我們子View的寬高和父布局的寬高(這里以我的布局為例子)泉手,目的是為了計(jì)算我們子View的活動(dòng)范圍來(lái)判斷我們的邊界限制條件。然后偶器,我們用父布局的寬度減去子View的寬度就得到了它可以移動(dòng)的X軸的范圍斩萌,同理,我們得到它可以移動(dòng)的Y軸的范圍屏轰。然后因?yàn)槲覀兿鄬?duì)父布局為坐標(biāo)范圍颊郎,所以我們?cè)O(shè)定最小范圍就是0。

ivTestIcon.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent motionEvent) {
                final int x = (int) motionEvent.getX();
                final int y = (int) motionEvent.getY();
                switch (motionEvent.getActionMasked()) {
                    case MotionEvent.ACTION_DOWN:
                        mLastX = x;
                        mLastY = y;
                        break;
                    case MotionEvent.ACTION_MOVE:
                        int xOffset = x - mLastX;//得到X的位移距離
                        int yOffset = y - mLastY;//得到Y(jié)的位移距離
                        final int oldTransX = (int) ivTestIcon.getTranslationX();
                        final int newTransX = oldTransX + xOffset;
                        final int transXLowerLimit = 0;
                        if (newTransX < transXLowerLimit) {
                            xOffset = transXLowerLimit - oldTransX;//X霎苗,left姆吭,邊界判斷
                        }
                        final int transXUpperLimit = parentW - childW;
                        if (newTransX > transXUpperLimit) {
                            xOffset = transXUpperLimit - oldTransX;//X,right,邊界判斷
                        }

                        final int oldTransY = (int) ivTestIcon.getTranslationY();
                        final int newTransY = oldTransY + yOffset;
                        final int transYLowerLimit = 0;
                        if (newTransY < transYLowerLimit) {
                            yOffset = transYLowerLimit - oldTransY;//Y唁盏,top,邊界判斷
                        }
                        final int transYUpperLimit = parentH - childH;
                        if (newTransY > transYUpperLimit) {
                            yOffset = transYUpperLimit - oldTransY;//Y内狸,bottom检眯,邊界判斷
                        }
                        ivTestIcon.setTranslationX(ivTestIcon.getTranslationX() + xOffset);
                        ivTestIcon.setTranslationY(ivTestIcon.getTranslationY() + yOffset);
                        break;
                    case MotionEvent.ACTION_UP:
                        break;
                }
                return true;
            }
        });

?然后我們就可以看到我們?nèi)ove我們的子View的時(shí)候,就不會(huì)有超出父布局的部分了昆淡。


成果2.gif
4.讓控件“聽(tīng)話”

?這里實(shí)現(xiàn)的效果是锰瘸,我們給我們的子View去再次設(shè)定更小的范圍,讓子View在我們ACTION_UP的時(shí)候昂灵,自己置頂或者到底部避凝。這里的子View自己移動(dòng)的部分是使用了屬性動(dòng)畫ObjectAnimator,以后會(huì)寫一個(gè)動(dòng)畫的總結(jié)博客來(lái)總結(jié)自己動(dòng)畫的使用眨补。

        final int transYLimit = parentH - childH;
        final int transYHalfLimit = transYLimit / 2;
        final float translationY = ivTestIcon.getTranslationY();
        if (translationY == 0 || translationY == transYLimit) {
            return;
        }
        if (translationY < transYHalfLimit) {
            mTransAnimator.setDuration((long) (1 * translationY));
            mTransAnimator.setFloatValues(translationY, 0);
        } else {
            mTransAnimator.setDuration((long) (1 * (transYLimit - translationY)));
            mTransAnimator.setFloatValues(translationY, transYLimit);
        }
        mTransAnimator.start();

?然后運(yùn)行恕曲,我們的控件就可以聽(tīng)代碼的話,自動(dòng)置頂或者到底部了


成果3.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末渤涌,一起剝皮案震驚了整個(gè)濱河市佩谣,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌实蓬,老刑警劉巖茸俭,帶你破解...
    沈念sama閱讀 206,311評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異安皱,居然都是意外死亡调鬓,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,339評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門酌伊,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)腾窝,“玉大人,你說(shuō)我怎么就攤上這事居砖『绺” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 152,671評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵奏候,是天一觀的道長(zhǎng)循集。 經(jīng)常有香客問(wèn)我,道長(zhǎng)蔗草,這世上最難降的妖魔是什么咒彤? 我笑而不...
    開(kāi)封第一講書人閱讀 55,252評(píng)論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮咒精,結(jié)果婚禮上镶柱,老公的妹妹穿的比我還像新娘。我一直安慰自己模叙,他們只是感情好歇拆,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,253評(píng)論 5 371
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著,像睡著了一般查吊。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上湖蜕,一...
    開(kāi)封第一講書人閱讀 49,031評(píng)論 1 285
  • 那天逻卖,我揣著相機(jī)與錄音,去河邊找鬼昭抒。 笑死评也,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的灭返。 我是一名探鬼主播盗迟,決...
    沈念sama閱讀 38,340評(píng)論 3 399
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼熙含!你這毒婦竟也來(lái)了罚缕?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 36,973評(píng)論 0 259
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤怎静,失蹤者是張志新(化名)和其女友劉穎邮弹,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體蚓聘,經(jīng)...
    沈念sama閱讀 43,466評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡腌乡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,937評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了夜牡。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片与纽。...
    茶點(diǎn)故事閱讀 38,039評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖塘装,靈堂內(nèi)的尸體忽然破棺而出急迂,到底是詐尸還是另有隱情,我是刑警寧澤蹦肴,帶...
    沈念sama閱讀 33,701評(píng)論 4 323
  • 正文 年R本政府宣布袋毙,位于F島的核電站,受9級(jí)特大地震影響冗尤,放射性物質(zhì)發(fā)生泄漏听盖。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,254評(píng)論 3 307
  • 文/蒙蒙 一裂七、第九天 我趴在偏房一處隱蔽的房頂上張望皆看。 院中可真熱鬧,春花似錦背零、人聲如沸腰吟。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 30,259評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)毛雇。三九已至嫉称,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間灵疮,已是汗流浹背织阅。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留震捣,地道東北人荔棉。 一個(gè)月前我還...
    沈念sama閱讀 45,497評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像蒿赢,于是被迫代替她去往敵國(guó)和親润樱。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,786評(píng)論 2 345

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

  • Android 自定義View的各種姿勢(shì)1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,506評(píng)論 25 707
  • 一羡棵、 Android分發(fā)機(jī)制概述: Android如此受歡迎壹若,就在于其優(yōu)秀的交互性,這其中皂冰,Android優(yōu)秀...
    IT楓閱讀 2,428評(píng)論 2 9
  • ¥開(kāi)啟¥ 【iAPP實(shí)現(xiàn)進(jìn)入界面執(zhí)行逐一顯】 〖2017-08-25 15:22:14〗 《//首先開(kāi)一個(gè)線程舌稀,因...
    小菜c閱讀 6,358評(píng)論 0 17
  • “自古逢秋悲寂寥,我言秋日勝春朝灼擂,晴空一鶴排云上壁查,便引詩(shī)情到碧霄√抻Γ” 曾經(jīng)很喜歡劉禹錫的這首詩(shī)睡腿,其中的每一字一句都...
    Aylin楚吟閱讀 662評(píng)論 8 22
  • 2018.3.28 星期三 珠海晴 月底了,一季度的業(yè)務(wù)沖刺進(jìn)行得如火如荼的峻贮,自己又在這個(gè)時(shí)候掉鏈子...
    wu溧蕙閱讀 50評(píng)論 0 0