1 / 52

Chapter 12

Chapter 12. Android 動畫程式設計. 前言. Android 在動畫程式設計上有非常多成熟的工具可以使用 。 例如說: Canvas 畫布、 ViewFlipper 動畫等,另外也可以使用 OpenGL 去設計 2D/3D 圖形或動畫,使手機多媒體或是操作介面有更多爆炸性的發展。. 畫布 / 畫筆. Canvas.

Lucy
Download Presentation

Chapter 12

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


  1. Chapter 12 Android動畫程式設計

  2. 前言 Android在動畫程式設計上有非常多成熟的工具可以使用。 例如說:Canvas畫布、ViewFlipper動畫等,另外也可以使用OpenGL去設計2D/3D圖形或動畫,使手機多媒體或是操作介面有更多爆炸性的發展。

  3. 畫布/畫筆

  4. Canvas • Canvas物件在Java應用程式上已經非常成熟,用於Android上也是開發動畫或UI的一個好工具。Canvas就像手機中的畫布,可以在Canvas上繪製圖形或者圖片,一般我們可以使用以下四個組成部分,在Android上繪製圖形: • 點陣圖(包含像素) • Canvas畫布(包含繪畫內容,寫入點陣圖 ) • 初始圖形(例如Rect、Bitmap、text…等等 ) • Paint(用來描述上面初始圖形的顏色和類型…等等)

  5. Canvas • View類別的onDraw方法會傳入一個Canvas物件,用來繪製元件界面的畫布。在實作onDraw方法時,經常會看到呼叫到save和restore方法,下面就解釋這兩個方法的作用: • save:保存Canvas的狀態。save之後,可以呼叫Canvas中的位移、縮放、旋轉、裁切…等等操作。 • restore:回復Canvas之前保存的狀態。防止save後對Canvas執行的操作對後續繪製有所影響。

  6. Canvas save和restore要同時使用,如果restore呼叫次數比save多,會引發Error。 下面將有一簡易範例解說Canvas中較重要的部分,如圖所示。

  7. Canvas 程式碼(CanvasEX.java): package ncu.bnlab.CanvasExample; import android.app.Activity; import android.os.Bundle; public class CanvasEX extends Activity { DrawAction drawAction; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); drawAction = new DrawAction(this); setContentView(drawAction); /* requestFocus方法用於設置新焦點 */ drawAction.requestFocus(); } }

  8. Canvas 程式碼(DrawAction.java): 由於程式碼過多,完整程式碼請參考光碟中CanvasEX.java、DrawAction.java public void onDraw(Canvas canvas) { /* 取得寬度 */ int px = getMeasuredWidth(); int py = getMeasuredWidth(); /* 設定線畫筆顏色 */ linePaint.setColor(Color.WHITE); /* 設置為true可消除邊緣效果 */ linePaint.setAntiAlias(true); /* 設定背景畫筆顏色 */ backgroundPaint.setColor(Color.BLUE); backgroundPaint.setAntiAlias(true); /* 設定方塊畫筆顏色 */ rectPaint.setColor(Color.RED); rectPaint.setAntiAlias(true); /* 於畫布繪製圖形 */ canvas.drawRect(0, 0, px, py, backgroundPaint); /* 呼叫save方法儲存目前狀態 */ canvas.save(); ........ /* 呼叫restore方法 */ canvas.restore(); /* 於畫布繪製方塊 */ canvas.drawRect(px, py, 300, 300, rectPaint);

  9. Canvas 從範例可看出,其中紅色的方塊位置有明顯差異。 造成這樣的原因是因為將Canvas中save與restore方法註解,所有的圖都是在旋轉90°後的畫布上繪製。當執行完onDraw方法,系統自動將畫布恢復回來。

  10. Paint Paint類別擁有樣式與顏色資訊,主要是有關於如何繪製幾何圖形、文字及點陣圖的方法,範例如圖所示。

  11. Paint 布局文件(res/layout/main.xml): <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" > <Button android:id="@+id/btnPrevious" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Previous" /> <Button android:id="@+id/btnNext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Next" /> </LinearLayout> <ViewFlipper android:id="@+id/viewFlipper" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" /> </LinearLayout>

  12. Paint 程式碼(PaintEX.java): package ncu.bnlab.PaintExample; import android.app.Activity; import android.os.Bundle; import android.view.Window; import android.view.WindowManager; @SuppressWarnings("unused") public class PaintEX extends Activity { DrawAction drawAction; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); /* getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); */ /* requestWindowFeature(Window.FEATURE_NO_TITLE); */ drawAction = new DrawAction(this); setContentView(drawAction); /* requestFocus方法用於設置新焦點 */ drawAction.requestFocus(); } }

  13. Paint 程式碼-1(DrawAction.java): 程式碼-2(DrawAction.java): @Override public void onDraw(Canvas canvas) { for (Point point : points) { /*於畫布上繪製圖形*/ canvas.drawCircle(point.x, point.y, 5, paint); } } public boolean onTouch(View view, MotionEvent event) { Point point = new Point(); /* 取得目前觸碰螢幕之x,y值 */ point.x = event.getX(); point.y = event.getY(); /* 新增點至Point物件 */ points.add(point); /* 用於更新View */ invalidate(); return true; } List<Point> points = new ArrayList<Point>(); Paint paint = new Paint(); public DrawAction(Context context) { super(context); /* 設定可取得焦點 */ setFocusable(true); /* 設定在觸控模式可取得焦點 */ setFocusableInTouchMode(true); /* 設定監聽器 */ this.setOnTouchListener(this); /* 設定畫筆顏色 */ paint.setColor(Color.WHITE); paint.setAntiAlias(true); }

  14. Paint 了解上述Canvas與Paint結合的範例後,接著解說關於在Android中如何顯示字體,因為字體在所有應用中是最常被使用到的。字體一般擁有的屬性有大小、顏色、對齊方式、粗體、斜體、下劃線等。

  15. Paint 在Android中使用Typeface類別來定義字體。 Typeface可以指定字體和字體風格,並可用Paint繪製字型,Typeface就類似Paint中的其它屬性textSize,textSkewX,textScaleX...等等。下表為字體常數的定義:

  16. Paint 這些字體常數,在應用程式中是可以直接使用的,例如說:Typeface. SERIF。 另外Typeface也包含了一些用來處理字體的方法,例如:建立字體Create(),取得字體屬性getStyle()、isBold()、isItalic()…等等。 Typeface類別不僅定義了字體,還包括粗體(Bold)、斜體(Italic)。

  17. Paint 其它對顯示String有影響的因素,都可以在Paint類別中找到它們的方法,如下頁表所示:

  18. Paint 至此讀者應對Paint有基本的瞭解,實際上在Paint中還有其他一些功能,例如用Alpha來處理透明度、用Dither來處理混色...等等,詳細內容可參考Android SDK。

  19. Paint 接著就以一個簡單的範例來演練Typeface與Paint結合的做法,程式如圖所示。

  20. Paint 首先需將ttf字型檔加入專案中的fonts資料夾(於asset下新建),如圖所示。

  21. Paint 程式碼(TypefaceEX.java): private static class FontView extends View { /* Paint.ANTI_ALIAS_FLAG為消除鋸齒 */ private Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); private Typeface typeFace; public FontView(Context context) { super(context); /* 自定義字體 */ typeFace = Typeface.createFromAsset(getContext().getAssets(), "fonts/verdanaz.ttf"); // typeFace = Typeface.createFromFile("/sdcard/verdanaz.ttf"); /* 設定字體大小 */ paint.setTextSize(32); } @Override protected void onDraw(Canvas canvas) { canvas.drawColor(Color.WHITE); /* 繪製預設字體 */ paint.setTypeface(null); canvas.drawText("Default Font", 10, 100, paint); /* 繪製自定義字體 */ paint.setTypeface(typeFace); canvas.drawText("Custom Font", 10, 200, paint); } }

  22. 動畫效果

  23. ViewFlipper ViewFlipper可以包含多個View) 且View之間的切換有動畫效果,例如說漸變效果。它也可以根據時間週期切換顯示項目,像是一個幻燈片播放的效果,範例如圖所示。

  24. ViewFlipper • ViewAnimator的作用是為FrameLayout裡面的View切換提供動畫效果。 • ViewAnimator類別有幾個和動畫相關的方法: • setInAnimation:設定View進入螢幕時候使用的動畫。 • setOutAnimation: 設定View退出螢幕時候使用的動畫。 • showNext:呼叫此方法顯示Layout裡面的下一個View。 • showPrevious:呼叫此方法顯示Layout裡面的上一個View。

  25. ViewFlipper • 一般不直接使用ViewAnimator而是使用它的兩個子類別ViewFlipper和ViewSwitcher。 • ViewFlipper可以用來指定Layout內多個View之間的切換效果,可以一次指定也可以每次切換的時候都指定單獨的效果。此類別額外提供了如下幾個方法: • isFlipping:用來判斷View切換是否正在進行。 • setFilpInterval:設定View之間切換的時間間隔。 • startFlipping:使用上面設定的時間間隔來切換所有View,會以幻燈片方式進行。 • stopFlipping: 停止View切換。

  26. ViewFlipper 布局文件(res/layout/main.xml): <?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="fill_parent" android:layout_height="fill_parent" > <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="horizontal" android:layout_width="wrap_content" android:layout_height="wrap_content" > <Button android:id="@+id/btnPrevious" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Previous" /> <Button android:id="@+id/btnNext" android:layout_width="wrap_content" android:layout_height="wrap_content" android:text="Next" /> </LinearLayout> <ViewFlipper android:id="@+id/viewFlipper" android:layout_width="fill_parent" android:layout_height="fill_parent" android:gravity="center" /> </LinearLayout>

  27. ViewFlipper 程式碼-1(ViewFlipperEX.java): public class ViewFlipperEX extends Activity { public final static int VIEW_TEXT = 0; public final static int VIEW_IMAGE = 1; private Button btnPrevious; private Button btnNext; private ViewFlipper viewFlipper; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); initView(); /* 新增圖檔及文字於viewFlipper此View中 */ viewFlipper.addView( addTextByText("Android 1.6") ); viewFlipper.addView( addImageById(R.drawable.play) ); viewFlipper.addView( addTextByText("Android 2.0") ); viewFlipper.addView( addImageById(R.drawable.normal) ); viewFlipper.addView( addTextByText("HTC Hero") ); }

  28. ViewFlipper 程式碼-2(ViewFlipperEX.java): 程式碼-3(ViewFlipperEX.java): public View addTextByText(String text) { /* 設定TextView屬性並回傳 */ TextView textView = new TextView(this); textView.setText(text); textView.setGravity(1); return textView; } public View addImageById(int id) { /* 設定ImageView屬性並回傳 */ ImageView imageView = new ImageView(this); imageView.setImageResource(id); return imageView; } 完整程式碼請參考光碟中ViewFlipperEX.java private OnClickListener listener = new OnClickListener() { public void onClick(View v) { /* 判斷點擊到哪個按鈕 */ switch(v.getId()) { /* 點擊到上一張按鈕就呼叫showPrevious方法 */ case R.id.btnPrevious: viewFlipper.showPrevious(); break; /* 點擊到下一張按鈕就呼叫showNext方法 */ case R.id.btnNext: viewFlipper.showNext(); break; } } };

  29. ViewSwitcher ViewSwitcher簡單來說就是Switcher在兩個View之間切換,可以透過ViewSwitcher指定一個ViewSwitcher.ViewFactory 來建立兩個View。 此類別具有兩個子類別ImageSwitcher、TextSwitcher分別用於圖片和文字切換,範例如下頁圖所示。

  30. ViewSwitcher 程式範例圖:

  31. ViewSwitcher 一開始要在res資料夾底下新增兩個XML檔案,接著將一開始新增的兩個XML檔,作為兩個View,並使用ViewSwitcher的方法去做兩個View之間的切換。 當按下讀取更多的按鈕時,ViewSwitcher就會切換到另外一個View,當背景作業處理完成後,才會切換回原本的View。

  32. ViewSwitcher 布局文件(res/layout/button.xml): <?xml version="1.0" encoding="utf-8"?> <!--使用 ViewSwitcher 切換的第一個View--> <Button xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/btn_loadmorecontacts" android:text="Load More Items" android:layout_width="fill_parent" android:layout_height="wrap_content" android:textAppearance="?android:attr/textAppearanceLarge" android:minHeight="?android:attr/listPreferredItemHeight" android:textColor="#FFFFFF" android:background="@android:drawable/list_selector_background" android:clickable="true" android:onClick="onClick" />

  33. ViewSwitcher 布局文件(res/layout/progress.xml): <?xml version="1.0" encoding="utf-8"?> <!--使用 ViewSwitcher 切換的第二個View--> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="wrap_content" android:layout_height="wrap_content" android:gravity="center_horizontal" android:minHeight="?android:attr/listPreferredItemHeight"> <ProgressBar android:id="@+id/progressBar" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerVertical="true" /> <TextView android:text="Loading…" android:textAppearance="?android:attr/textAppearanceLarge" android:layout_height="wrap_content" android:layout_width="wrap_content" android:layout_toRightOf="@+id/progressBar" android:layout_centerVertical="true" android:gravity="center" android:padding="10dip" android:textColor="#FFFFFF" /> </RelativeLayout>

  34. ViewSwitcher 程式碼-1(ViewSwitcherEX.java): public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); switcher = new ViewSwitcher(this); button = (Button)View.inflate(this, R.layout.button, null); progress = View.inflate(this, R.layout.progress, null); /* 將button與progressbar加入switcher中 */ switcher.addView(button); switcher.addView(progress); /* 取得ListView並將switcher加入 */ getListView().addFooterView(switcher); /* 設定ListAdapter,其中第二個參數可選擇樣式 */ setListAdapter(new ArrayAdapter(this, android.R.layout.simple_list_item_1, ITEMS)); }

  35. ViewSwitcher 程式碼-2(ViewSwitcherEX.java): 完整程式碼請參考光碟中ViewSwitcherEX.java private class getMoreItems extends AsyncTask { @Override protected Object doInBackground(Object... params) { /* 此處可撰寫新增項目之程式碼 */ try { /* 執行緒會呼叫sleep方法 */ Thread.sleep(3000); } catch (InterruptedException e) { e.printStackTrace(); } return null; } @Override protected void onPostExecute(Object result) { switcher.showPrevious(); } }

  36. OpenGL

  37. 前言 在Android中支援高效能的3D圖形,主要是透過OpenGL/ES API。 所謂的ES API就是OpenGL所規範給嵌入式設備使用的。

  38. GLSurfaceView GLSurfaceView是一個新API,開始使用是從Android1.5版。 GLSurfaceView可以使得創造一個OpenGL ES應用程式更為容易,通常用於遊戲或快速更新畫面的應用程式上。

  39. GLSurfaceView • GLSurfaceView具有下列優點: • 提供了glue code來連接OpenGL ES的視圖系統。 • 提供了glue code使得OpenGL ES與活動生命週期一起工作。 • 可以更簡單的選擇適合的Frame buffer像素格式。 • 建立與管理一個獨立的繪圖執行緒,使得動畫效果更為流暢。 • 提供簡易的除錯工具。

  40. GLSurfaceView 下面將以一個簡易的範例使讀者更了解GLSurfaceView的使用方式,範例執行結果如圖所示。

  41. GLSurfaceView 程式碼-1(GLSurfaceViewEX.java): 程式碼-2(GLSurfaceViewEX.java): class ClearRenderer implements GLSurfaceView.Renderer { private float mRed; private float mGreen; private float mBlue; public void onSurfaceCreated(GL10 gl, EGLConfig config) { } public void onSurfaceChanged(GL10 gl, int w, int h) { /* 決定視窗上的繪圖區域 */ gl.glViewport(0, 0, w, h); } public void onDrawFrame(GL10 gl) { /* 清除顏色buffer */ gl.glClearColor(mRed, mGreen, mBlue, 1.0f); gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); } public void setColor(float r, float g, float b) { mRed = r; mGreen = g; mBlue = b; } } class ClearGLSurfaceView extends GLSurfaceView { private ClearRenderer mRenderer; public ClearGLSurfaceView(Context context) { super(context); mRenderer = new ClearRenderer(); setRenderer(mRenderer); } public boolean onTouchEvent(final MotionEvent event) { /* 啟動執行緒,並傳入座標設定顏色 */ queueEvent(new Runnable() { public void run() { mRenderer.setColor(event.getX() / getWidth(), event.getY() / getHeight(), 1.0f); }}); return true; } } }

  42. SurfaceView 依照傳統的作法,要快速更新畫面或做遊戲之類的應用,通常會新增執行緒處理工作後,使用handler傳送訊息給View顯示畫面或是在非使用者執行緒呼叫View。

  43. SurfaceView SurfaceView通常用於遊戲中或是需快速更新畫面之應用程式,藉此來加快程式畫面運行速度。SurfaceView也是View的一種,但有獨立buffer,稱為surface。 由於此buffer不需透過 Android framework做畫面更新,可直接對應到畫面上的區塊,提供更好的效能。

  44. SurfaceView 但實際上還是間接更新到畫面上,但少了 Android framework 這一層,而且可透過硬體加速(加速的功能視平台而定),範例程式如圖所示。

  45. SurfaceView 程式碼-1(SurfaceViewEX.java): 程式碼-2(SurfaceViewEX.java): SurfaceHolder holder; public AnimView(Context context) { super(context); /* 取得Holder */ holder = this.getHolder(); //holder /* 新增Callback */ holder.addCallback(this); } @Override public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) { /* 啟動執行緒繪圖 */ new Thread(new AnimThread(holder)).start(); } 完整程式碼請參考光碟中SurfaceViewEX.java public void run() { while( running ) { try { /* 鎖定畫布 */ canvas = holder.lockCanvas(null); /* 移動畫布 */ canvas.translate(dx, dy); paint = new Paint(); paint.setColor(Color.RED); /* 繪製畫布 */ canvas.drawColor(Color.BLACK); /* 於畫布繪製圖形 */ canvas.drawRect(new RectF( pLeft,pTop,pRight,pBottom ), paint); dx++; } catch(Exception ex) {} finally { /* 解鎖畫布 */ holder.unlockCanvasAndPost(canvas); } /* 移動邊界限制 */ if(width - 100 <= dx) running = false; } }

  46. SurfaceView 在這個範例當中,需覆寫surfaceChanged方法,在此方法中啟動執行緒,使用translate方法,並設定條件,達成方塊移動的動畫效果。 藉此範例可以了解到有關SurfaceView中的標準用法,流程將於後面敘述說明。

  47. SurfaceView流程 當程式需要更好的效能時,繼承SurfaceView也是一種方式。透過 SurfaceView,canvas不需透過onDraw()取得。 可以透過呼叫SurfaceView.getHolder()取得surface holder,而SurfaceHolder.lockCanvas()會傳回SkCanvas。 Native code 可以在這個 SkCanvas上作畫,然後呼叫 SurfaceHolder.unlockCanvasAndPost(),將內容更新到畫面上。

  48. SurfaceView注意事項 每次更新畫面前,都要透過 SurfaceHolder.lockCanvas() 取得一個新的SkCanvas,在這個canvas上繪圖。 繪圖完成後都需呼叫SurfaceHolder.unlockCanvasAndPost() 進行更新,才能正確的更新畫面。

  49. Matrix Android的2D繪圖功能其實是非常強大的,尤其是matrix。透過matrix,可以非常容易的控制Android繪圖座標的位移、旋轉、縮放…等等功能。範例程式執行結果如下頁圖所示。

  50. Matrix 點選縮小按鈕可將旁邊圖示縮小;選放大按鈕則可將圖示放大,其它還有像setRotate、setSinCos、setScale、setTranslate…等等方法可以應用。

More Related