參考書籍:《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)的最小距離均芽。
- 這是一個(gè)常量丘逸,和設(shè)備有關(guān),在不同設(shè)備上這個(gè)值可能不同掀宋;
- 當(dāng)手指在屏幕上滑動(dòng)的距離小于這個(gè)值深纲,系統(tǒng)就不會(huì)認(rèn)為這是滑動(dòng)操作;
- 獲取方法:
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 |
?說明:
- 在實(shí)際開發(fā)中暂殖,可以不使用GestureDetector,完全可以自己在View中onTouchEvent方法中實(shí)現(xiàn)所需的監(jiān)聽当纱。
- 建議:如果只是監(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)。
-
使用 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 和 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 是View左邊緣和View內(nèi)容左邊緣在水平方向上的距離
mScrollY 是View上邊緣和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; } }
使用動(dòng)畫實(shí)現(xiàn)
關(guān)于動(dòng)畫的內(nèi)容后面有單獨(dú)的章節(jié)講解,這里先略過寒瓦。-
改變布局參數(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);
-
使用 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ì)比
- ScrollTo / ScrollBy:這兩個(gè)方法是View提供的原生方法,它改變的是View內(nèi)容的位置弓乙,不會(huì)改變View自身的位置參數(shù)末融,因此钧惧,不會(huì)影響View以及其內(nèi)部控件的點(diǎn)擊事件位置暇韧。
- 動(dòng)畫:使用動(dòng)畫可以實(shí)現(xiàn)很多炫酷的動(dòng)畫效果勾习,但是它不適用于有交互的View。
- 改變布局參數(shù):這種方式操作略微復(fù)雜懈玻,比較適合有交互的View巧婶。
- layout:這種方式實(shí)際上通過改變位置參數(shù),重繪View涂乌,會(huì)改變View的位置參數(shù)艺栈。
上一篇:Android學(xué)習(xí)筆記(二) | Activity的啟動(dòng)模式
下一篇:Android學(xué)習(xí)筆記(四)| Android 控件架構(gòu)與自定義控件