Androidスムーズ&立体ページめくり効果2

10700 ワード

http://www.ibm.com/developerworks/cn/opensource/os-cn-android-anmt2/
Android launcherのスムーズさと立体ページめくり効果
ここではAndroid launcherプログラムのWorkspaceに関するコードを抽出し,launcherプログラムがマルチページおよび異なるページ間の切り替え効果をどのように実現するかを比較的簡単なコードで示す.このサンプルコードはSDK 2.1で実行され、WVGAの画面サイズが設定されています.
まず、プログラムの実行の効果を見て、感性的な認識を見てみましょう.
図1:スムーズ移動効果Android 平滑和立体翻页效果2_第1张图片図2:立体ページめくり効果Android 平滑和立体翻页效果2_第2张图片
トップに戻る
ウィンドウページのレイアウト
次にプログラムUIのレイアウトを見てみましょう.ActivityのContentViewはlayoutのmainです.xml.その内容は次のとおりです.
リスト1.Android 平滑和立体翻页效果2_第3张图片
FlatWorkspaceのベースクラスはWorkspaceであり、ViewGroupから継承されたコンテナクラスであり、3つのサブViewが含まれ、サブViewはImageViewである.3つのImageViewは3つのページです.この3つのImageViewの作成は、WorkspaceActivityのonCreate関数でWorkspaceを呼び出すinitScreens関数で行われます.コードは次のとおりです.
インベントリ2
		 ViewGroup.LayoutParams p = new iewGroup.LayoutParams(ViewGroup.LayoutParams.FILL_PARENT,ViewGroup.LayoutParams.FILL_PARENT); 	
        	 for (int i = 0; i < 3; i++) { 
			 this.addView(new ImageView(this.getContext()), i, p); 
		 } 
		 ((ImageView)this.getChildAt(0)).setImageResource(R.drawable.image_search); 
		 ((ImageView)this.getChildAt(1)).setImageResource(R.drawable.image_system); 
		 ((ImageView)this.getChildAt(2)).setImageResource(R.drawable.image_top); 

図3:Workspaceとページレイアウト図
3ページを上図のウィンドウレイアウトにするために,WorkspaceのonMeasureとonLayout関数を再ロードし,onLayoutコードに重点を置いた.onLayout関数はlayoutScreens関数を呼び出してレイアウトを完了し、FlatWorkspaceのlayoutScreensは次のように実現されます.
インベントリ3
 protected void layoutScreens() { 
        int childLeft = 0; 
        final int count = getChildCount(); 
        for (int i = 0; i < count; i++) { 
            final View child = getChildAt(i); 
            if (child.getVisibility() != View.GONE) { 
                final int childWidth = child.getMeasuredWidth(); 
                child.layout(childLeft, 0, childLeft + childWidth, child.getMeasuredHeight()); 
                childLeft += childWidth; 
            } 
        } 
 } 


上だLayoutセクションのコードは、X座標系(0,0)-(ScreenWidth,ScreenHeight))と(ScreenWidth,0)-(2*ScreenWidth,ScreenHeight))の3つの矩形領域に3つのページをそれぞれレイアウトします.ここでは矩形領域の左上隅頂点座標と右下隅の頂点座標で行列を表す.
これで、ウィンドウページ全体のレイアウトが完了しました.ウィンドウページのレイアウトサイズは実際の表示画面幅の3倍なので、すべてのページを表示するにはページをスクロールする必要があります.
トップに戻る
ページのスムーズな移動の実装
次に,ユーザtouch moveの場合,プログラムがページをスライドさせ,それらを描画する方法を示す.
ページのスライドは、ViewのscrollByまたはScrollTo関数を呼び出し、WorkspaceのonTouchEvent関数でユーザの指が移動する距離を取得し、次にscrollBy(そのパラメータはX軸とY軸で移動する必要がある距離)を呼び出してWorkspaceというView(ViewGroupでもある)をユーザの指が移動する距離を移動させる.もちろん、Viewが移動する前に、ユーザの指が移動する距離と速度が十分かどうかを判断し、ユーザの誤操作を減らす必要がある.この部分のコードは簡単で深く分析しません.自分でコードを見てください.
WorkspaceというViewがscrollByを呼び出してViewのスクロールを行うと、必然的にこのViewが無効になり、システムによって再描画されるため、dispatchDraw関数はサブView(ImageView)の描画に呼び出され、それ自体に描画するものがないので、WorkspaceのonDraw関数に関心を持つ必要はありません.dispatchDraw関数はdrawScreens(canvas)を呼び出してサブViewを描画します.FlatWorkspaceの実装を見てみましょう.
インベントリ4
 protected void drawScreens(Canvas canvas) { 
        final long drawingTime = getDrawingTime(); 
        final int count = getChildCount(); 
        for (int i = 0; i < count; i++) { 
            drawChild(canvas, getChildAt(i), drawingTime); 
        } 
    } 


ここでのcanvasの幅の高さは、HVGA画面の320のような画面の可視範囲の大きさである.× 480サイズ)で、3つのサブImageViewのレイアウトは画面の範囲を超え、画面の可視範囲内でない部分は描画されません.この3つのサブImageViewを描く関数は重要で、立方体のページをめくるなどの特効を作る鍵であり、FlatWorkspaceはスムーズなスライド効果を実現するので、3つのサブImageViewを直接描きます.立方体の効果を実現するには、3つのサブImageViewを描画するときに立体感を持たせる必要があります.これはandroidでは、上述したCameraクラスをY軸に沿って一定の角度で回転させることで実現できます.
プログラムはユーザにtouch move操作を行わせる目的は,ユーザに1つのページを選択させることであり,上記の実装によれば,ユーザが最後に指を持ち上げるとページ切替が徹底せず,図1のように2つのページの間にとどまる.したがって,ユーザが指を持ち上げると,プログラムは次の完全なページに移動するのにどれだけの距離があるかを判断し,WorkspaceというViewをこの距離をもう一度移動させて次のページに完全に切り替える必要がある.この移動の過程で,ユーザに滑らかな感覚を与えるためには,この距離をすぐに移動するのではなく,一定の時間間隔を与える必要があり,この時間帯で徐々に移動するので,ここではScrollerクラスの手法を用いて徐々に移動を実現する.具体的には、WorkspaceのonTouchEvent関数でユーザtouch up(指を上げる)を検出したときにどのページに調整すべきかの判断を行い、snapToScreen(targetScreen)を呼び出して必要なページにジャンプし、scrollToScreen(screen)を呼び出してWorkspaceというViewを必要なスクロールさせ、この関数はFlatWorkspaceで次のように実現されます.
インベントリ5
  public void scrollToScreen(int screen) { 
        final int newX = screen * getWidth(); 
        final int deltaX = newX - getScrollX(); 
        Log.e("FlatWorkspace","scrollToScreen call mScroller.startScroll"); 
        mScroller.startScroll(getScrollX(), getScrollY(), deltaX, getScrollY(), Math.abs(deltaX) * 2); 
        invalidate(); 
  } 


ここのポイントはmScrolerです.startScroll部分のコードで、Workspace viewを時間帯にMath.abs(deltaX)*2で次のターゲットページを移動可視化するには移動する距離deltaX(およびターゲットページの座標から現在移動している距離を差し引く)が必要ですが、このdeltaXの計算をよく見てください.ここでは詳しく説明しません.このmScrollerstartScrollはWorkspaceがすぐに移動することはなく、現在のViewが無効になるだけで再描画され、Workspaceが父親のViewによって呼び出されたときにcomputeScroll関数が呼び出されるので、この関数ではWorkspaceにscrollTo関数を呼び出して実際の移動をさせます.コードは次のとおりです.
インベントリ6
 public void computeScroll() { 
		 if (mScroller.computeScrollOffset()) { 	
			 scrollTo(mScroller.getCurrX(), mScroller.getCurrY()); 
			 //postInvalidate(); 
		 } else if (mNextScreen != INVALID_SCREEN) { 
			 mCurrentScreen = mNextScreen; 
			 mNextScreen = INVALID_SCREEN; 

		 } 
	 } 

これで、Workspaceの全体的な動作メカニズムとスムーズな移動の効果をどのように実現するかについて説明しました.立体ページをめくる効果がどのように実現されているかを具体的に説明します.
トップに戻る
立体ページ効果の実現
前述の解析から,立体ページ効果はスムーズページ効果に基づいて3つのサブImageViewの描画を書き換えることで達成できることが分かった.同時に,ページをめくる際のユーザ操作過程は,指を置いて画面に触れ,指を動かし,指を持ち上げる3つのステップに分けられることが分かった.指が画面に触れると、ページ間のスライドが始まります.指を動かす時、ページはユーザーの指の移動距離に従って対応する距離の移動を行うべきで、同時にシステムはページの移動位置によってWorkspaceの中の3つのサブView(つまりページ)を描きます;指を上げたときにどのページに移動すべきか、どのくらいの距離を移動すべきかを判断し、必要な距離をスムーズに移動して目的のページにジャンプします.
立体効果を表示するためには、各サブImageViewの描画時にY軸に沿って一定の角度を回転させる方法を考えなければなりません.前にandroidはCameraというクラスを通じてこの機能を提供しています.opengl ESを使う必要はありません.もちろん、より良い3 D効果を作るにはopengl ESの強力な機能が必要です.一定の角度を回転させる以上、この角度はどのように計算しますか?この角度をユーザの指の移動距離に関連付けた.この立方体はY軸に沿ってしか回転しないので、この3つの面の立方体の頂部だけを見ると十分です.その頂部はY軸の矢印の方向に沿って等辺三角形で、携帯電話の画面に対するY軸に沿って回転する角度の計算方法は下図のようになっています.
図4:初期画面位置模式図
下図は、スクリーン1がY軸に沿って45回転した後、他の2つのスクリーンがY軸に沿って回転する角度である.
図5:45度回転後の画面位置模式図Android 平滑和立体翻页效果2_第4张图片
この変換の部分はコードCubeWorkspaceの関数drawScreenのコードを見てください.以下のようにします.
インベントリ7
 protected void drawScreen(Canvas canvas, int screen, long drawingTime) { 
        final int width = getWidth(); 
        final int scrollWidth = screen * width; 
        final int scrollX = this.getScrollX();  
        if(scrollWidth > scrollX + width || scrollWidth + width < scrollX) { 
            return; 
        } 
        final View child = getChildAt(screen); 
        final int faceIndex = screen; 
        final float faceDegree = currentDegree - faceIndex * preFaceDegree; 
        if(faceDegree > 90  faceDegree < -90) { 
            return; 
        } 
        final float centerX = (scrollWidth < scrollX)?scrollWidth + width:scrollWidth; 
        final float centerY = getHeight()/2; 
        final Camera camera = mCamera; 
        final Matrix matrix = mMatrix; 
        canvas.save(); 
        camera.save(); 
        camera.rotateY(-faceDegree); 
        camera.getMatrix(matrix); 
        camera.restore(); 
        matrix.preTranslate(-centerX, -centerY); 
        matrix.postTranslate(centerX, centerY); 
        canvas.concat(matrix); 
        drawChild(canvas, child, drawingTime); 
        child.setBackgroundColor(Color.TRANSPARENT); 
        canvas.restore(); 
    } 

上の関数のcurrentDegree変数は固定値ではなく、この変数値を変更する方法は比較的隠蔽されており、AngelBaseWorkspaceのscrollTo関数にあります.AngelBaseWorkspaceのscrollTo関数はViewクラスの関数をリロードします.この関数はViewのscrollBy関数によって呼び出されるので、touch画面とmoveのたびにAngelBaseWorkspaceのscrollTo関数が呼び出されます(onTouchEventはscrollBy、scrollByはscrollToを呼び出します).ユーザーtouch moveが移動する距離に応じて、現在のページの角度、すなわち変数currentDegreeの値が変更されます.具体的には、次のコードを参照してください.
インベントリ8
 public void scrollTo(int x, int y) { 
        if (getScrollX() != x || getScrollY() != y) { 
            int oldX = getScrollX(); 
            int oldY = getScrollY(); 

            super.scrollTo(x, y); 
            //x is the touch action X direction move distance 
            currentDegree = x * degreeOffset;            
            onScrollChanged(x, y, oldX, oldY); 
            invalidate(); 
        } 
    } 


この立方体の特効部分のコードはここに紹介されています.
トップに戻る
終わりの言葉
Android launcherのスムーズ化と立体ページめくり効果の実現を紹介し、開発者がAndroidのアニメーションフレームワークの原理を深く理解し、androidの既存フレームワークを十分に利用してまぶしく、クールなアニメーション効果を得ることができるようにした.