【安卓深度控件開發(1.3)】Creating Custom Views (官方示例文檔漢化版)(3)

<h2>建立視圖交互</h2> <p>圖形用戶界面只是建立自定義視圖的一部分。您還須要使視圖以模仿現實世界行動類似的方式響應用戶輸入。對象始終應像真正對象作的同樣。例如,圖像應不當即彈出並重如今某個地方別的地方,由於在現實世界中的對象不會這樣作。相反,圖像應從一個位置移動到另外一個位置。</p> <p>用戶也感受到細微的行爲或界面上響應最佳模仿現實世界中的細微之處。例如,當用戶甩動一個 UI 對象,他們應該感受動做繼續,摩擦而後在最終中止,最後的位置超出甩動發生時的位置。</p> <p>這節課演示如何使用 Android 框架的功能,將這些真實世界的行爲添加到您的自定義視圖。</p> <h3>處理輸入的手勢</h3> <p>像許多其餘 UI 框架,android 系統支持輸入的事件模型。用戶操做都變成觸發回調的事件,您能夠重寫自定義您的應用程序如何響應用戶的回調。在 Android 系統中最多見的輸入的事件是觸摸,而觸發 <a href="http://developer.android.com/reference/android/view/View.html#onTouchEvent(android.view.MotionEvent)" target="_blank">onTouchEvent(android.view.MotionEvent)</a>。重寫此方法以處理事件:</p> <div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre> @Override <span style="color: #0000ff">public</span> <span style="color: #0000ff">boolean</span> onTouchEvent(MotionEvent event) { <span style="color: #0000ff">return</span> <span style="color: #0000ff">super</span>.onTouchEvent(event); }</pre> </div>html

<br />android

<p>觸控事件自己不是特別有用的。現代觸摸 Ui 定義交互的點擊、 拉、 推、 甩動和放大的手勢。若要將原始觸控事件轉換手勢,Android 提供了 <a href="http://developer.android.com/reference/android/view/GestureDetector.html" target="_blank">GestureDetector</a>。</p>框架

<p>經過傳入一個實現 <a href="http://developer.android.com/reference/android/view/GestureDetector.OnGestureListener.html" target="_blank">GestureDetector.OnGestureListener</a> 類的一個實例構造 <a href="http://developer.android.com/reference/android/view/GestureDetector.html" target="_blank">GestureDetector</a>。若是您只想要處理幾個手勢,您能夠擴展 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html" target="_blank">GestureDetector.SimpleOnGestureListener</a>,而不是實現 <a href="http://developer.android.com/reference/android/view/GestureDetector.OnGestureListener.html" target="_blank">GestureDetector.OnGestureListener</a> 接口。例如,此代碼建立一個類,擴展了 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html" target="_blank">GestureDetector.SimpleOnGestureListener</a> 和重寫 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html#onDown(android.view.MotionEvent)" target="_blank">onDown(MotionEvent)</a>。</p>ide

<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre><span style="color: #0000ff">class</span> mListener <span style="color: #0000ff">extends</span> GestureDetector.SimpleOnGestureListener { @Override <span style="color: #0000ff">public</span> <span style="color: #0000ff">boolean</span> onDown(MotionEvent e) { <span style="color: #0000ff">return</span> <span style="color: #0000ff">true</span>; } } mDetector = <span style="color: #0000ff">new</span> GestureDetector(PieChart.<span style="color: #0000ff">this</span>.getContext(), <span style="color: #0000ff">new</span> mListener());</pre> </div>post

<br />測試

<p>不管是否使用 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html" target="_blank">GestureDetector.SimpleOnGestureListener</a>,您必須實現一個 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html#onDown(android.view.MotionEvent)" target="_blank">onDown()</a> 方法,返回 true。此步驟是必需的由於全部的手勢開始與 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html#onDown(android.view.MotionEvent)" target="_blank">onDown()</a> 消息。若是您從 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html#onDown(android.view.MotionEvent)" target="_blank">onDown()</a> 返回 false,如同 <a href="http://developer.android.com/reference/android/view/GestureDetector.SimpleOnGestureListener.html" target="_blank">GestureDetector.SimpleOnGestureListener</a>,系統將假定您想要忽略其他的姿態,<a href="http://developer.android.com/reference/android/view/GestureDetector.OnGestureListener.html" target="_blank">GestureDetector.OnGestureListener</a> 的其餘方法永遠不會被調用。只有若是您真正想要忽略整個手勢時應該返回 false 。<a href="http://developer.android.com/reference/android/view/GestureDetector.OnGestureListener.html" target="_blank">GestureDetector.OnGestureListener</a> 一旦和建立的 <a href="http://developer.android.com/reference/android/view/GestureDetector.html" target="_blank">GestureDetector</a> 實例,您可使用您的 <a href="http://developer.android.com/reference/android/view/GestureDetector.html" target="_blank">GestureDetector</a> 來解釋您收到在 <a href="http://developer.android.com/reference/android/view/View.html#onTouchEvent(android.view.MotionEvent)" target="_blank">onTouchEvent()</a> 中的觸摸事件。</p>優化

<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre>@Override <span style="color: #0000ff">public</span> <span style="color: #0000ff">boolean</span> onTouchEvent(MotionEvent event) { <span style="color: #0000ff">boolean</span> result = mDetector.onTouchEvent(event); <span style="color: #0000ff">if</span> (!result) { <span style="color: #0000ff">if</span> (event.getAction() == MotionEvent.ACTION_UP) { stopScrolling(); result = <span style="color: #0000ff">true</span>; } } <span style="color: #0000ff">return</span> result; }</pre> </div>動畫

<br />ui

<p>當您傳遞 <a href="http://developer.android.com/reference/android/view/View.html#onTouchEvent(android.view.MotionEvent)" target="_blank">onTouchEvent()</a> 一個觸摸事件,它不能識別做爲一種姿態的一部分時,它將返回 false。而後,您能夠運行您本身的自定義手勢檢測代碼。</p>this

<p>&#160;</p>

<h3>建立物理運動</h3>

<p>手勢是控制觸摸屏設備,功能強大的方法,但他們能夠是違反直覺的很難記住除非他們產生物理可信的結果。一個很好的例子是甩動姿式,用戶快速地在屏幕上移動手指,而後舉起它。這種姿態是若是用戶界面響應快速移動的方向的甩動,而後放慢,猶如該用戶已推上飛輪和設置它旋轉的道理的。</p>

<p>可是,模擬飛輪的感受並很簡單。物理和數學的不少都須要獲得正常的飛輪模型。幸運的是,Android 提供了幫助器類來模擬這和其餘的行爲。<a href="http://developer.android.com/reference/android/widget/Scroller.html" target="_blank">Scroller</a> 類是用於處理飛輪樣式甩動手勢的基礎。</p>

<p>若要啓動甩動,調用 <a href="http://developer.android.com/reference/android/widget/Scroller.html#fling(int, int, int, int, int, int, int, int)" target="_blank">fling()</a> 與甩動起始的速度和最小和最大 x 和 y 值。對於速度值,您可使用由 <a href="http://developer.android.com/reference/android/view/GestureDetector.html" target="_blank">GestureDetector</a> 爲您計算的值。</p>

<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre>@Override <span style="color: #0000ff">public</span> <span style="color: #0000ff">boolean</span> onFling(MotionEvent e1, MotionEvent e2, <span style="color: #0000ff">float</span> velocityX, <span style="color: #0000ff">float</span> velocityY) { mScroller.fling(currentX, currentY, velocityX / SCALE, velocityY / SCALE, minX, minY, maxX, maxY); postInvalidate(); }</pre> </div>

<div style="border-left: #258aaf 4px solid; padding-bottom: 0.5em; padding-left: 10px; padding-right: 0px; padding-top: 0px"><strong>注:</strong> 雖然按 GestureDetector 計算的速度是物理上準確的,但許多開發人員以爲甩動動畫使用此值太快。它是一般使用 x 和 y 除 4 至 8 倍的速度。 </div>

<p>調用 <a href="http://developer.android.com/reference/android/widget/Scroller.html#fling(int, int, int, int, int, int, int, int)" target="_blank">fling()</a> 設置一個甩動手勢。以後,您須要經過定時調用 <a href="http://developer.android.com/reference/android/widget/Scroller.html#computeScrollOffset()" target="_blank">Scroller.computeScrollOffset()</a> 來更新 <a href="http://developer.android.com/reference/android/widget/Scroller.html" target="_blank">Scroller</a>,<a href="http://developer.android.com/reference/android/widget/Scroller.html#computeScrollOffset()" target="_blank">computeScrollOffset()</a> 經過甩動設置的初始屬性與當前時間,計算當時的 x 與 y 座標。調用 <a href="http://developer.android.com/reference/android/widget/Scroller.html#getCurrX()" target="_blank">getCurrX()</a> 與 <a href="http://developer.android.com/reference/android/widget/Scroller.html#getCurrY()" target="_blank">getCurY()</a> 獲取該值。</p>

<p>多數視圖經過調用 <a href="http://developer.android.com/reference/android/view/View.html#scrollTo(int, int)" target="_blank">scrollTo()</a> 直接設置 <a href="http://developer.android.com/reference/android/widget/Scroller.html" target="_blank">Scroller</a> 的 x,y。PieChart 示例中稍有不一樣:它使用當前滾動 y 座標設置餅的旋轉角度。</p>

<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre><span style="color: #0000ff">if</span> (!mScroller.isFinished()) { mScroller.computeScrollOffset(); setPieRotation(mScroller.getCurrY()); }</pre> </div>

<p><a href="http://developer.android.com/reference/android/widget/Scroller.html" target="_blank">Scroller</a> 類能夠爲您計算滾動座標,但它不會自動應用到您的視圖。您須要獲取和應用平滑的滾動動畫。有兩種方式實現:</p>

<ul> <li>在調用 <a href="http://developer.android.com/reference/android/widget/Scroller.html#fling(int, int, int, int, int, int, int, int)" target="_blank">fling()</a> 以後調用 <a href="http://developer.android.com/reference/android/view/View.html#postInvalidate()" target="_blank">postInvalidate()</a> 強制重繪。這種技術須要您在 <a href="http://developer.android.com/reference/android/view/View.html#onDraw(android.graphics.Canvas)" target="_blank">onDraw()</a> 中計算滾動偏移並在每次滾動偏移改變時使用 <a href="http://developer.android.com/reference/android/view/View.html#postInvalidate()" target="_blank">postInvalidate()</a>。 </li>

<li>設置一個 <a href="http://developer.android.com/reference/android/animation/ValueAnimator.html" target="_blank">ValueAnimator</a> 處理甩動動畫並使用 <a href="http://developer.android.com/reference/android/animation/ValueAnimator.html#addUpdateListener(android.animation.ValueAnimator.AnimatorUpdateListener)" target="_blank">addUpdateListener()</a> 持續處理動畫更新。 </li> </ul>

<p>PieChart 示例使用了第二種方法。這種技術稍複雜,但它與動畫系統密切工做,可以減小沒必要要的重繪請求。缺點是 ViewAnimator 不在 API 11 版本以前提供,因此不能在 Android 版本低於 3.0 的設備上使用。</p>

<div style="border-left: #258aaf 4px solid; padding-bottom: 0.5em; padding-left: 10px; padding-right: 0px; padding-top: 0px"><strong>注:</strong> 雖然 ValueAnimator 不能在 API 11 以前使用,但你仍能夠在低於這個 API 級別上運行。您只須要在運行時進行版本測試,若是低於 11 就活力視圖動畫請求。</div>

<p>&#160;</p>

<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre> mScroller = <span style="color: #0000ff">new</span> Scroller(getContext(), <span style="color: #0000ff">null</span>, <span style="color: #0000ff">true</span>); mScrollAnimator = ValueAnimator.ofFloat(0,1); mScrollAnimator.addUpdateListener(<span style="color: #0000ff">new</span> ValueAnimator.AnimatorUpdateListener() { @Override <span style="color: #0000ff">public</span> <span style="color: #0000ff">void</span> onAnimationUpdate(ValueAnimator valueAnimator) { <span style="color: #0000ff">if</span> (!mScroller.isFinished()) { mScroller.computeScrollOffset(); setPieRotation(mScroller.getCurrY()); } <span style="color: #0000ff">else</span> { mScrollAnimator.cancel(); onScrollFinished(); } } });</pre> </div>

<h3></h3>

<br />

<h3>平滑過渡</h3>

<p>用戶所期待的現代UI在各狀態間平滑過渡。UI 元素淡入淡出而不是直接顯示與消失。動做平滑的開始和結束而非忽然開始和中止。Android 3.0 提供了<a href="http://developer.android.com/guide/topics/graphics/prop-animation.html" target="_blank">屬性動畫框架</a>讓平滑過渡更簡單。</p>

<p>若要使用動畫系統,將會影響視圖外觀的屬性更改時,不要直接更改該屬性。相反,使用 <a href="http://developer.android.com/reference/android/animation/ValueAnimator.html" target="_blank">ValueAnimator</a> 來進行更改。在如下示例中,修改當前所選的餅圖扇區在餅圖中使整個圖表旋轉,使選擇指針位於所選切片的中心。<a href="http://developer.android.com/reference/android/animation/ValueAnimator.html" target="_blank">ValueAnimator</a> 在一段幾百毫秒時間內更改旋轉,而不是當即設置新的旋轉值。</p>

<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre>mAutoCenterAnimator = ObjectAnimator.ofInt(PieChart.<span style="color: #0000ff">this</span>, &quot;<span style="color: #8b0000">PieRotation</span>&quot;, 0); mAutoCenterAnimator.setIntValues(targetAngle); mAutoCenterAnimator.setDuration(AUTOCENTER_ANIM_DURATION); mAutoCenterAnimator.start();</pre> </div>

<br />

<p>若是您要設置的值是基本 View 屬性,作動畫就更簡單了,由於視圖內建了 ViewPropertyAnimator 更優化並能同時支持多個屬性值的動畫。例如:</p>

<div style="border-bottom: #ddd 1px solid; border-left: #ddd 1px solid; padding-bottom: 1em; margin: 0px 0px 1em; padding-left: 1em; padding-right: 1em; background: #f7f7f7; overflow: auto; border-top: #ddd 1px solid; border-right: #ddd 1px solid; padding-top: 1em"> <pre>animate().rotation(targetAngle).setDuration(ANIM_DURATION).start();</pre> </div>

相關文章
相關標籤/搜索