Android學(xué)習(xí)筆記(三)View基礎(chǔ)

參考書籍:《Android開發(fā)藝術(shù)探索》 任玉剛
如有錯(cuò)漏竿音,請(qǐng)批評(píng)指出!

View基礎(chǔ)知識(shí)

View類
?View類是Android中所有控件的基類寓调,包括ViewGroup(控件組);這也就意味著,View本身可以是單個(gè)控件榆纽,也可以是包含多個(gè)控件的一組控件。

View的位置參數(shù)
?View的位置主要由四個(gè)頂點(diǎn)決定捏肢,分別對(duì)應(yīng)于四個(gè)屬性:top奈籽、left、right鸵赫、bottom衣屏。從Android3.0開始,增加了幾個(gè)額外參數(shù):x辩棒、y狼忱、translationx膨疏、translationY。這些參數(shù)都是相對(duì)于父容器的钻弄。

x:View發(fā)生平移后的左上角橫坐標(biāo)
y:View發(fā)生平移后的左上角縱坐標(biāo)
translationX:View的橫向偏移量(初始為0)
translationY:View的縱向偏移量(初始為0)

MotionEvent
?手指觸摸屏幕后的典型事件類型有:

  • ACTION_DOWN——手指接觸屏幕瞬間成肘;
  • ACTION_MOVE——手指在屏幕上滑動(dòng);
  • ACTION_UP——手指離開屏幕瞬間斧蜕;

通過MotionEvent對(duì)象可以得到點(diǎn)擊事件發(fā)生的x双霍、y坐標(biāo):

  • getX()——獲取觸摸點(diǎn)相對(duì)于當(dāng)前View左上角的橫坐標(biāo);
  • getY()——獲取觸摸點(diǎn)相對(duì)于當(dāng)前View左上角的縱坐標(biāo)批销;
  • getRawX()——獲取觸摸點(diǎn)相對(duì)于手機(jī)屏幕左上角的橫坐標(biāo)洒闸;
  • getRawY()——獲取觸摸點(diǎn)相對(duì)于手機(jī)屏幕左上角的縱坐標(biāo);

TouchSlop
?TouchSlop是系統(tǒng)所能識(shí)別出的被認(rèn)為是滑動(dòng)的最小距離均芽。

  1. 這是一個(gè)常量丘逸,和設(shè)備有關(guān),在不同設(shè)備上這個(gè)值可能不同掀宋;
  2. 當(dāng)手指在屏幕上滑動(dòng)的距離小于這個(gè)值深纲,系統(tǒng)就不會(huì)認(rèn)為這是滑動(dòng)操作;
  3. 獲取方法:
ViewConfiguration.get(getContext()).getScaledTouchSlop();

??這個(gè)常量的意義在于劲妙,當(dāng)我們做滑動(dòng)處理的時(shí)候湃鹊,可以用它來做一些過濾。這樣可以改善用戶的使用體驗(yàn)镣奋。

VelocityTracker
?速度追蹤币呵,用于追蹤手指在滑動(dòng)過程中的速度,包括水平和豎直方向的速度侨颈。下面是使用示例:

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //創(chuàng)建VelocityTracker對(duì)象余赢,并將觸摸界面的滑動(dòng)事件
        //加入到VelocityTracker當(dāng)中。
        VelocityTracker velocityTracker = VelocityTracker.obtain();
        velocityTracker.addMovement(event);
        //指定時(shí)間間隔為1s哈垢,計(jì)算速度
        velocityTracker.computeCurrentVelocity(1000);
        //獲取水平速度和豎直速度(像素/s) 
        int xVelocity = (int)velocityTracker.getXVelocity();
        int yVelocity = (int)velocityTracker.getYVelocity();
        //銷毀對(duì)象妻柒,回收內(nèi)存
        velocityTracker.clear();
        velocityTracker.recycle();
        return super.onTouchEvent(event);
    }

速度 = (終點(diǎn)位置 - 起點(diǎn)位置)/時(shí)間段(可以為負(fù)值)

GestureDetector
?手勢(shì)檢測(cè),用于輔助檢測(cè)用戶的單擊耘分、滑動(dòng)举塔、長(zhǎng)按、雙擊等行為陶贼。下面是使用示例:

public class MainActivity extends AppCompatActivity implements
        GestureDetector.OnGestureListener,GestureDetector.OnDoubleTapListener{
    GestureDetector mGestureDetector;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        //首先啤贩,創(chuàng)建GestureDetector對(duì)象并根據(jù)需求實(shí)現(xiàn)OnGestureListener接口
        //和OnDoubleTapListener接口
        mGestureDetector = new GestureDetector(this,this);
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        //將目標(biāo)View中的觸摸事件交給GestureDetector處理(這里是MainActivity中的觸摸事件)
        return mGestureDetector.onTouchEvent(event);
    }
}

?做完上面的事情之后,即可以選擇性的實(shí)現(xiàn)OnGestureListener和OnDoubleTapListener中的方法了拜秧,這兩個(gè)接口中的常用方法如下:

方法名 描述 所屬接口
onDown 手指輕輕觸摸屏幕瞬間,由一個(gè)ACTION_DOWN觸發(fā) OnGestureListener
onSingleTapUp 手指(輕輕觸摸屏幕后)松開章郁,伴隨一個(gè)ACTION_UP而觸發(fā)枉氮,表示單擊行為 OnGestureListener
onScroll 手指按下屏幕并拖動(dòng)志衍,由一個(gè)ACTION_DOWN,多個(gè)ACTION_MOVE觸發(fā)聊替,表示拖動(dòng)行為 OnGestureListener
onFling 用戶按下觸摸屏楼肪,快速滑動(dòng)后松開,由一個(gè)ACTION_DOWN惹悄、多個(gè)ACTION_MOVE和一個(gè)ACTION_UP觸發(fā)春叫,表示快速滑動(dòng)行為 OnGestureListener
onLoongPress 用戶長(zhǎng)久的按住屏幕不放,表示長(zhǎng)按行為 OnGestureListener
onDoubleTap 雙擊泣港,由連續(xù)2次單擊組成 OnDoubleTapListener

?說明:

  1. 在實(shí)際開發(fā)中暂殖,可以不使用GestureDetector,完全可以自己在View中onTouchEvent方法中實(shí)現(xiàn)所需的監(jiān)聽当纱。
  2. 建議:如果只是監(jiān)聽滑動(dòng)相關(guān)呛每,就在onTouchEvent中實(shí)現(xiàn);如果要監(jiān)聽雙擊這種行為坡氯,就使用GestureDetector晨横。

View的滑動(dòng)

滑動(dòng)在Android開發(fā)中具有很重要的作用,很多絢麗的滑動(dòng)效果其實(shí)都是由基本的滑動(dòng)操作和特效組成箫柳。書中講了三種實(shí)現(xiàn)View滑動(dòng)的方式:第一種是使用scrollTo / scrollBy 方法手形;第二種是通過動(dòng)畫給View施加平移效果來實(shí)現(xiàn)滑動(dòng);第三種是通過改變View的LayoutParams使得View重新布局從而實(shí)現(xiàn)滑動(dòng)悯恍。這里我還要補(bǔ)充一種叁幢,使用 layout 方法改變View的位置參數(shù)從而實(shí)現(xiàn)滑動(dòng)。

  1. 使用 scrollto / scrollBy 方法
    先來看看這兩個(gè)方法的源碼:

    /**
     * Set the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the x position to scroll to
     * @param y the y position to scroll to
     */
    public void scrollTo(int x, int y) {
        if (mScrollX != x || mScrollY != y) {
            int oldX = mScrollX;
            int oldY = mScrollY;
            mScrollX = x;
            mScrollY = y;
            invalidateParentCaches();
            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
            if (!awakenScrollBars()) {
                postInvalidateOnAnimation();
            }
        }
    }
    
    /**
     * Move the scrolled position of your view. This will cause a call to
     * {@link #onScrollChanged(int, int, int, int)} and the view will be
     * invalidated.
     * @param x the amount of pixels to scroll by horizontally
     * @param y the amount of pixels to scroll by vertically
     */
    public void scrollBy(int x, int y) {
        scrollTo(mScrollX + x, mScrollY + y);
    }
    

    首先我們先了解源碼中的 mScrollX 和 mScrollY 這兩個(gè)值是什么:
    mScrollX 是View左邊緣和View內(nèi)容左邊緣在水平方向上的距離
    mScrollY 是View上邊緣和View內(nèi)容上邊緣在豎直方向上的距離

    從mScrollX 和 mScrollY的描述來看坪稽,這兩個(gè)方法只能改變View內(nèi)容的位置曼玩,并不能改變View的位置,也就是說窒百,調(diào)用View的 scrollTo()和scroollBy() 方法不會(huì)改變View的位置參數(shù)(left黍判、top、right篙梢、buttom顷帖、x、y渤滞、translationX贬墩、translationY),如果當(dāng)前View還有子View時(shí)妄呕,子View也僅僅是內(nèi)容發(fā)生平移陶舞,自身的位置參數(shù)不變。關(guān)于什么是View以及它的內(nèi)容可以看下圖:

    說明:mScrollX 和 mScrollY的單位為像素绪励,當(dāng)View左邊緣在View內(nèi)容右邊時(shí)肿孵,mScrollX 為正值唠粥,反之為負(fù)值;當(dāng)View上邊緣在View內(nèi)容下面時(shí)停做,mScrollY為正值晤愧,反之為負(fù)值。具體來說蛉腌,當(dāng)我們想要讓View內(nèi)容右移或下移時(shí)官份,我們傳入scrollBy()或scrollTo()方法的參數(shù)應(yīng)該為負(fù)值。

    從源碼來看烙丛,scrollBy() 方法實(shí)際上也是調(diào)用了scrollTo() 方法舅巷,因此,這兩個(gè)方法都是改變 mScrollX 和 mScrollY 的值蜀变,不過scrollTo(int x, int y)方法是直接將傳入的參數(shù)賦值給mScrollX 和 mScrollY悄谐,即將View的內(nèi)容移動(dòng)到指定的坐標(biāo)位置;而scrollBy(int x, int y)方法則是將View的內(nèi)容從當(dāng)前位置向上或下平移x個(gè)像素库北,向左或右平移y個(gè)像素爬舰。下面看看實(shí)現(xiàn)過程和效果:

    <LinearLayout
        android:id="@+id/ll_left"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    
        <View
            android:id="@+id/view_left"
            android:layout_width="100dp"
            android:layout_height="100dp"
            android:background="@color/colorRed"/>
    
    </LinearLayout>
    
    public void move(View v) {
        switch (v.getId()){
            case R.id.but_show:
                ll_left.scrollBy(-100,-100);
                break;
            case R.id.but_hide:
                ll_left.scrollBy(100,100);
                break;
            default:
                break;
        }
    }
    

    效果圖如下:
  2. 使用動(dòng)畫實(shí)現(xiàn)
    關(guān)于動(dòng)畫的內(nèi)容后面有單獨(dú)的章節(jié)講解,這里先略過寒瓦。

  3. 改變布局參數(shù)
    改變布局參數(shù)情屹,也就是改變LayoutParams。比如我們想把一個(gè)Buttom向右平移100px杂腰,我們只需要將這個(gè)Buttom的Layoutparams里的marginLeft參數(shù)的值增加100px即可垃你。當(dāng)然,這只是為了達(dá)到讓View平移目的的一種實(shí)現(xiàn)方式喂很,類似的還可以在這個(gè)Buttom左邊放一個(gè)width為0的空View惜颇,當(dāng)我們需要平移時(shí),就改變空View的width少辣,從而將Button擠到右邊凌摄。具體如何實(shí)現(xiàn)呢?

    MarginLayoutParams params = (MarginLayoutParams)view_left.getLayoutParams();
    //改變View的width
    params.width += 100;
    //改變View的 MarginLeft
    params.leftMargin += 100;
    view_left.setLayoutParams(params);
    

    看看效果:
  4. 使用 layout(int l, int t, int r, int b) 方法實(shí)現(xiàn)
    這種方法和第一種方式相比漓帅,它是實(shí)實(shí)在在改變了View的left锨亏、top、right忙干、buttom這幾個(gè)位置參數(shù)的器予。我們直接來看代碼:

    @OnClick({R.id.but_show, R.id.but_hide})
    public void move(View v) {
        int mLeft, mRight, mTop, mButtom;
        switch (v.getId()){
            case R.id.but_show:
                mLeft = view_left.getLeft() + 100;
                mTop = view_left.getTop() + 100;
                mRight = view_left.getRight() + 100;
                mButtom = view_left.getBottom() + 100;
                view_left.layout(mLeft, mTop, mRight, mButtom);
                break;
            case R.id.but_hide:
                mLeft = view_left.getLeft() - 100;
                mTop = view_left.getTop() - 100;
                mRight = view_left.getRight() - 100;
                mButtom = view_left.getBottom() - 100;
                view_left.layout(mLeft, mTop, mRight, mButtom);
                break;
            default:
                break;
        }
    }
    

    效果和第一種看起來相同,只不過第一種是View的內(nèi)容發(fā)生平移捐迫,這里是View自身發(fā)生平移乾翔。

滑動(dòng)方式對(duì)比

  1. ScrollTo / ScrollBy:這兩個(gè)方法是View提供的原生方法,它改變的是View內(nèi)容的位置弓乙,不會(huì)改變View自身的位置參數(shù)末融,因此钧惧,不會(huì)影響View以及其內(nèi)部控件的點(diǎn)擊事件位置暇韧。
  2. 動(dòng)畫:使用動(dòng)畫可以實(shí)現(xiàn)很多炫酷的動(dòng)畫效果勾习,但是它不適用于有交互的View。
  3. 改變布局參數(shù):這種方式操作略微復(fù)雜懈玻,比較適合有交互的View巧婶。
  4. layout:這種方式實(shí)際上通過改變位置參數(shù),重繪View涂乌,會(huì)改變View的位置參數(shù)艺栈。

上一篇:Android學(xué)習(xí)筆記(二) | Activity的啟動(dòng)模式
下一篇:Android學(xué)習(xí)筆記(四)| Android 控件架構(gòu)與自定義控件

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市湾盒,隨后出現(xiàn)的幾起案子湿右,更是在濱河造成了極大的恐慌,老刑警劉巖罚勾,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件毅人,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡尖殃,警方通過查閱死者的電腦和手機(jī)丈莺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來送丰,“玉大人缔俄,你說我怎么就攤上這事∑黪铮” “怎么了俐载?”我有些...
    開封第一講書人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)登失。 經(jīng)常有香客問我遏佣,道長(zhǎng),這世上最難降的妖魔是什么壁畸? 我笑而不...
    開封第一講書人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任贼急,我火速辦了婚禮,結(jié)果婚禮上捏萍,老公的妹妹穿的比我還像新娘太抓。我一直安慰自己,他們只是感情好令杈,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開白布走敌。 她就那樣靜靜地躺著,像睡著了一般逗噩。 火紅的嫁衣襯著肌膚如雪掉丽。 梳的紋絲不亂的頭發(fā)上跌榔,一...
    開封第一講書人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音捶障,去河邊找鬼僧须。 笑死,一個(gè)胖子當(dāng)著我的面吹牛项炼,可吹牛的內(nèi)容都是我干的担平。 我是一名探鬼主播,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼锭部,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼暂论!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起拌禾,我...
    開封第一講書人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤取胎,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后湃窍,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體闻蛀,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年坝咐,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了循榆。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡墨坚,死狀恐怖秧饮,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情泽篮,我是刑警寧澤盗尸,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站帽撑,受9級(jí)特大地震影響泼各,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜亏拉,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一扣蜻、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧及塘,春花似錦莽使、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至,卻和暖如春亿笤,著一層夾襖步出監(jiān)牢的瞬間翎迁,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來泰國(guó)打工净薛, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留汪榔,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓罕拂,卻偏偏與公主長(zhǎng)得像揍异,于是被迫代替她去往敵國(guó)和親全陨。 傳聞我的和親對(duì)象是個(gè)殘疾皇子爆班,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

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