編輯:關於Android編程
寫在前面的話:這一章很有價值,想要提升安卓知識的一定要讀一讀。不做安卓的也可以得到其它方面的提升。
原文地址:http://android.xsoftlab.net/training/custom-views/making-interactive.html
UI的繪制只是自定義View的一部分。你還需要使View可以以一種接近真實世界的反饋方式來響應用戶的輸入事件。虛擬世界中的對象應該總是以真實世界中對象的行為方式來行動。比如說,圖像不應該從某處突然出現或消失,因為真實世界中的圖像總是從一個地方移動到另一個地方的。
用戶還應該在UI界面上感知到一些細微的感覺。最好的反饋就是模仿真實世界的微妙行為。舉個栗子,用戶在快速滑動UI對象時,應該在開始時感覺到延遲的摩擦力,在滑出去後還應當繼續保持慣性滑動。
這節課將會演示如何使用Android的框架特性為自定義View添加這些真實世界的行為。
與其它UI框架很接近,Android同樣支持輸入事件模型。用戶的行為會被轉換為一種會觸發回調的事件,你可以通過重寫這些回調方法來決定如何對這些事件做出響應。Android中最為常見的輸入事件是touch,它會觸發onTouchEvent(android.view.MotionEvent)。通過重寫這個方法來處理一些事件:
@Override public boolean onTouchEvent(MotionEvent event) { return super.onTouchEvent(event); }
觸摸事件本身並不是特別有用處。觸摸UI定義了一些交互手勢,比如雙擊、下拉、上推、快速滑動以及縮放等等。為了將原始觸摸事件轉換為手勢,Android提供了GestureDetector。
構造GestureDetector需要傳遞一個GestureDetector.OnGestureListener的實現類作為參數。如果你只是需要處理幾個手勢,你可以繼承GestureDetector.SimpleOnGestureListener。下面的代碼繼承了這個接口,並重寫了它的onDown(MotionEvent)方法。
class mListener extends GestureDetector.SimpleOnGestureListener { @Override public boolean onDown(MotionEvent e) { return true; } } mDetector = new GestureDetector(PieChart.this.getContext(), new mListener());
無論你是否使用GestureDetector.SimpleOnGestureListener接口,你都需要實現一個返回true的onDown()方法。這一步是必須的,因為所有的手勢都是從onDown()方法開始的。如果你在onDown()方法中返回了false,那麼系統會認為你想忽略這次事件,並且其它的相關方法都不會被調用。如果你真的想要忽略整個手勢事件,那麼在onDown()方法中返回false是唯一的一種方式。一旦實現了GestureDetector.OnGestureListener接口,並創建了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,這樣你就可以運行自定義的手勢識別代碼了。
手勢是用來控制觸摸屏設備的一種強大方式,除非它們提供了物理模擬效果,否則它們可能是違反直覺的、難以記住的。一個好的示例就是飛速滑動手勢:當用戶快速的在屏幕上滑動手指時,手指突然離開了屏幕,就會觸發這種手勢。如果UI在同一方向上繼續滑動然後慢慢的減速,這時就會給用戶造成一種感覺:仿佛在操作一個飛輪一樣。
不管怎樣,模擬飛輪這種感覺並不是沒有價值的。為了正確模擬這種感覺,需要很多的物理及數學運算。幸運的是,Android為此提供了輔助類。Scroller是一個專門用來處理這種飛輪感覺手勢的輔助類。
為了啟動滑動,需要以一個初始速度值及其它相關速度參數調用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計算到的這個速度值在物理上很精確,但是很多開發者感覺使用這個值來啟動滑動還是太快了。通常會在4到8之間取一個系數來減小x和y。
先調用fling()為滑動手勢設置物理模型。然後你需要定期調用Scroller.computeScrollOffset()來更新Scroller。computeScrollOffset()通過讀取當前的時間以及使用物理模型來計算x及y的位置來更新Scroller對象的內部狀態。通過調用getCurrX()和getCurrY()來接收這些值。
很多View將Scroller對象的x,y的位置值直接傳遞給scrollTo()。餅圖示例在這裡有些小小的不同:它使用當前滑動的y的位置來設置餅圖的旋轉角度:
if (!mScroller.isFinished()) { mScroller.computeScrollOffset(); setPieRotation(mScroller.getCurrY()); }
Scroller會為你計算滑動的位置,但是它不會自動的將這些值應用到你的View中。為了使View的滑動效果更佳平滑,獲得並應用這些值是需要你去做的。有兩種方式可以實現:
在fling()之後調用postInvalidate(),這樣可以重新繪制界面。這個方法需要每次滑動的偏移量發生變化之後在onDraw()方法中調用。 為滑動動畫設置ValueAnimator,調用addUpdateListener()添加監聽器,以便處理動畫的更新。在餅圖示例中使用了第二種方案。這項技術在設置上稍微的有些復雜,但是它的工作過程與動畫系統更為接近,並且不會請求不必要的更新。它的缺點是在API 11之前ValueAnimator並不適用,所以這項技術在Android 3.0之前不可以使用。
Note: ValueAnimator在API 11之前並不可用,但是你仍然還可以在API 11之前使用。你只需要確保在運行時檢查當前的API等級,並且在等級低於11時不調用View動畫就可以。
mScroller = new Scroller(getContext(), null, true); mScrollAnimator = ValueAnimator.ofFloat(0,1); mScrollAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { if (!mScroller.isFinished()) { mScroller.computeScrollOffset(); setPieRotation(mScroller.getCurrY()); } else { mScrollAnimator.cancel(); onScrollFinished(); } } });
用戶不希望狀態之間的過渡發生卡頓。所以UI元素的淡入淡出取代了閃現與消失。動作的平滑過渡取代了突然的啟動與停止。Android 3.0中出現的property animation framework使平滑轉場更加簡便。
每次屬性的變更都會影響View的外觀,所以不要直接更改它們的屬性。相反,可以使用ValueAnimator來做出變更。在下面的示例中,修改當前所選擇的扇形圖會使整個餅圖發生旋轉,所以選擇的這個點在餅圖中看起來是居正中的。ValueAnimator更改旋轉用了數百毫秒的時間。
mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.this, "PieRotation", 0); mAutoCenterAnimator.setIntValues(targetAngle); mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); mAutoCenterAnimator.start();
如果你想更改View的基礎屬性,那麼這項事情就更容易了,因為View有一個內置的View屬性動畫框架ViewPropertyAnimator,它專門用來同時作用多個屬性,比如:
animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();
這只是初步的實現,並沒有加入自動編譯等功能。需要手動更改更新的xml文件和最新的apk。 共涉及到四個文件!一、客戶端AndroidU
概述 本文以學習、研究和分享為主,歡迎轉載,但必須在文章頁面明顯位置給出原文連接。願與志同道合的朋友一起成長在上一個博文 Anroid沉浸式狀態欄中提到了,畫了一個圖,這
深入理解Activity啟動流程(一)–Activity啟動相關類的類圖Activity啟動時的概要交互流程用戶從Launcher程序點擊應用圖標
RecyclerView 水平多行排列添加頭和尾import android.content.Context;import android.support.v7.widg