Android官方开发文档Training系列课程中文版:创建自定义View之View的交互
寫在前面的話:這一章很有價值,想要提升安卓知識的一定要讀一讀。不做安卓的也可以得到其它方面的提升。
原文地址:http://android.xsoftlab.net/training/custom-views/making-interactive.html
UI的繪制只是自定義View的一部分。你還需要使View可以以一種接近真實世界的反饋方式來響應用戶的輸入事件。虛擬世界中的對象應該總是以真實世界中對象的行為方式來行動。比如說,圖像不應該從某處突然出現(xiàn)或消失,因為真實世界中的圖像總是從一個地方移動到另一個地方的。
用戶還應該在UI界面上感知到一些細微的感覺。最好的反饋就是模仿真實世界的微妙行為。舉個栗子,用戶在快速滑動UI對象時,應該在開始時感覺到延遲的摩擦力,在滑出去后還應當繼續(xù)保持慣性滑動。
這節(jié)課將會演示如何使用Android的框架特性為自定義View添加這些真實世界的行為。
處理輸入手勢
與其它UI框架很接近,Android同樣支持輸入事件模型。用戶的行為會被轉(zhuǎn)換為一種會觸發(fā)回調(diào)的事件,你可以通過重寫這些回調(diào)方法來決定如何對這些事件做出響應。Android中最為常見的輸入事件是touch,它會觸發(fā)onTouchEvent(android.view.MotionEvent)。通過重寫這個方法來處理一些事件:
@Overridepublic boolean onTouchEvent(MotionEvent event) {return super.onTouchEvent(event);}觸摸事件本身并不是特別有用處。觸摸UI定義了一些交互手勢,比如雙擊、下拉、上推、快速滑動以及縮放等等。為了將原始觸摸事件轉(zhuǎn)換為手勢,Android提供了GestureDetector。
構(gòu)造GestureDetector需要傳遞一個GestureDetector.OnGestureListener的實現(xiàn)類作為參數(shù)。如果你只是需要處理幾個手勢,你可以繼承GestureDetector.SimpleOnGestureListener。下面的代碼繼承了這個接口,并重寫了它的onDown(MotionEvent)方法。
class mListener extends GestureDetector.SimpleOnGestureListener {@Overridepublic boolean onDown(MotionEvent e) {return true;} } mDetector = new GestureDetector(PieChart.this.getContext(), new mListener());無論你是否使用GestureDetector.SimpleOnGestureListener接口,你都需要實現(xiàn)一個返回true的onDown()方法。這一步是必須的,因為所有的手勢都是從onDown()方法開始的。如果你在onDown()方法中返回了false,那么系統(tǒng)會認為你想忽略這次事件,并且其它的相關方法都不會被調(diào)用。如果你真的想要忽略整個手勢事件,那么在onDown()方法中返回false是唯一的一種方式。一旦實現(xiàn)了GestureDetector.OnGestureListener接口,并創(chuàng)建了GestureDetector的實例,則可以使用GestureDetector對象來與在onTouchEvent()中接收到的觸摸事件進行交互。
@Override public boolean onTouchEvent(MotionEvent event) {boolean result = mDetector.onTouchEvent(event);if (!result) {if (event.getAction() == MotionEvent.ACTION_UP) {stopScrolling();result = true;}}return result; }當傳給onTouchEvent()方法一個不能識別的手勢時,它會返回false,這樣你就可以運行自定義的手勢識別代碼了。
創(chuàng)建物理模擬手勢
手勢是用來控制觸摸屏設備的一種強大方式,除非它們提供了物理模擬效果,否則它們可能是違反直覺的、難以記住的。一個好的示例就是飛速滑動手勢:當用戶快速的在屏幕上滑動手指時,手指突然離開了屏幕,就會觸發(fā)這種手勢。如果UI在同一方向上繼續(xù)滑動然后慢慢的減速,這時就會給用戶造成一種感覺:仿佛在操作一個飛輪一樣。
不管怎樣,模擬飛輪這種感覺并不是沒有價值的。為了正確模擬這種感覺,需要很多的物理及數(shù)學運算。幸運的是,Android為此提供了輔助類。Scroller是一個專門用來處理這種飛輪感覺手勢的輔助類。
為了啟動滑動,需要以一個初始速度值及其它相關速度參數(shù)調(diào)用fling()。有關速度值,你可以通過GestureDetector計算得到。
@Override public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY) {mScroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY);postInvalidate(); }Note: 盡管GestureDetector計算到的這個速度值在物理上很精確,但是很多開發(fā)者感覺使用這個值來啟動滑動還是太快了。通常會在4到8之間取一個系數(shù)來減小x和y。
先調(diào)用fling()為滑動手勢設置物理模型。然后你需要定期調(diào)用Scroller.computeScrollOffset()來更新Scroller。computeScrollOffset()通過讀取當前的時間以及使用物理模型來計算x及y的位置來更新Scroller對象的內(nèi)部狀態(tài)。通過調(diào)用getCurrX()和getCurrY()來接收這些值。
很多View將Scroller對象的x,y的位置值直接傳遞給scrollTo()。餅圖示例在這里有些小小的不同:它使用當前滑動的y的位置來設置餅圖的旋轉(zhuǎn)角度:
if (!mScroller.isFinished()) {mScroller.computeScrollOffset();setPieRotation(mScroller.getCurrY()); }Scroller會為你計算滑動的位置,但是它不會自動的將這些值應用到你的View中。為了使View的滑動效果更佳平滑,獲得并應用這些值是需要你去做的。有兩種方式可以實現(xiàn):
- 在fling()之后調(diào)用postInvalidate(),這樣可以重新繪制界面。這個方法需要每次滑動的偏移量發(fā)生變化之后在onDraw()方法中調(diào)用。
- 為滑動動畫設置ValueAnimator,調(diào)用addUpdateListener()添加監(jiān)聽器,以便處理動畫的更新。
在餅圖示例中使用了第二種方案。這項技術在設置上稍微的有些復雜,但是它的工作過程與動畫系統(tǒng)更為接近,并且不會請求不必要的更新。它的缺點是在API 11之前ValueAnimator并不適用,所以這項技術在Android 3.0之前不可以使用。
Note: ValueAnimator在API 11之前并不可用,但是你仍然還可以在API 11之前使用。你只需要確保在運行時檢查當前的API等級,并且在等級低于11時不調(diào)用View動畫就可以。
mScroller = new Scroller(getContext(), null, true);mScrollAnimator = ValueAnimator.ofFloat(0,1);mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {@Overridepublic void onAnimationUpdate(ValueAnimator valueAnimator) {if (!mScroller.isFinished()) {mScroller.computeScrollOffset();setPieRotation(mScroller.getCurrY());} else {mScrollAnimator.cancel();onScrollFinished();}}});使滑動過程更流暢
用戶不希望狀態(tài)之間的過渡發(fā)生卡頓。所以UI元素的淡入淡出取代了閃現(xiàn)與消失。動作的平滑過渡取代了突然的啟動與停止。Android 3.0中出現(xiàn)的property animation framework使平滑轉(zhuǎn)場更加簡便。
每次屬性的變更都會影響View的外觀,所以不要直接更改它們的屬性。相反,可以使用ValueAnimator來做出變更。在下面的示例中,修改當前所選擇的扇形圖會使整個餅圖發(fā)生旋轉(zhuǎn),所以選擇的這個點在餅圖中看起來是居正中的。ValueAnimator更改旋轉(zhuǎn)用了數(shù)百毫秒的時間。
mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this, "PieRotation", 0); mAutoCenterAnimator.setIntValues(targetAngle); mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); mAutoCenterAnimator.start();如果你想更改View的基礎屬性,那么這項事情就更容易了,因為View有一個內(nèi)置的View屬性動畫框架ViewPropertyAnimator,它專門用來同時作用多個屬性,比如:
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();總結(jié)
以上是生活随笔為你收集整理的Android官方开发文档Training系列课程中文版:创建自定义View之View的交互的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CNN卷积神经网络(吴恩达《卷积神经网络
- 下一篇: 手把手教你-如何查询中文期刊是否属于核心