このセクションの概要:
Canvas の API ドキュメントには、次のようなメソッドがあります。drawBitmap (ビットマップ ビットマップ、 マトリックス マトリックス、ペイント ペイント)
このマトリックスには大きな記事があります。ペイント API の ColorFilter の ColorMatrix カラー マトリックス (4 * 5 マトリックス) について説明しました。マトリックスの値を変更することで、色相、彩度などを変更できます。今日説明したマトリックスは、他の API と組み合わせて、グラフィックスやコンポーネントの変換を制御できます。たとえば、Canvas は行列変換の効果を実現するために上記のdrawBitmap を提供します。ゆっくり勉強していきましょう〜
公式 API ドキュメント:マトリックス
1. Matrix で一般的に使用されるいくつかの変換方法
- setTranslate (float dx, float dy): 翻訳用の制御行列
- setRotate (float 度、float px、float py): 回転、パラメータは次のとおりです: 回転角度、軸 (x、y)
- setScale (float sx、float sy、float px、float py): スケーリング、パラメーターは次のとおりです: X 軸と Y 軸のスケーリング比、スケーリングの軸
- setSkew (float kx, float ky): チルト (歪み)、パラメータは次のとおりです: X、Y 軸上のスケーリング
実は、Canvasの変形方法も基本的に同じで、Matrixに上記の変形を設定した後、CanvasのdrawBitmap()メソッドを呼び出してMatrixを呼び出すだけです~
2. マトリックスの使用例:
ランニング効果図:
コードの実装:
MyView.java:
/** * 2015/11/11 0011 に Jay によって作成されました。 */ public class MyView extends View { private Bitmap mBitmap; プライベート行列行列 = new Matrix(); プライベートフロートsx = 0.0f; //設置傾斜度 private int width,height; //位图宽高 private floatscale = 1.0f; //缩放率 private intメソッド = 0; public MyView(Context context) { this(context, null); public MyView(Context context, AttributeSet attrs) { super(context, attrs); 初期化(); super MyView(Context context, AttributeSet attrs, int defStyleAttr) { (context, attrs, defStyleAttr); プライベート void init() { = BitmapFactory.decodeResource(getResources(), R.mipmap.img_meizi); 幅 = mBitmap.getWidth(); 高さ = mBitmap.getHeight(); @Override protected void onDraw(Canvas Canvas) { super.onDraw ( canvas); スイッチ(メソッド){ ケース0: matrix.reset(); 壊す; ケース 1: sx += 0.1; マトリックス.setSkew(sx,0); 壊す; ケース 2: sx -= 0.1; マトリックス.setSkew(sx,0); 壊す; ケース 3: if(スケール < 2.0){ スケール += 0.1; } マトリックス.setScale(スケール,スケール); ブレーク; ケース 4: if(スケール > 0.5){ スケール -= 0.1; } マトリックス.setScale(スケール,スケール); ブレーク; } //に従って元のビットマップとマトリックスに変換して新しい画像を作成します Bitmap bitmap = Bitmap.createBitmap(mBitmap,0,0,width,height,matrix,true); Canvas.drawBitmap(bitmap,matrix,null); // 新しいビットマップを描画 } public void setMethod(int i){ メソッド = i; postInvalidate(); } }
レイアウトコード: activity_main.xml :
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent"> <LinearLayout android:id="@+id/ly_bar" " android:layout_width="match_parent" android:layout_height="64dp" android:layout_alignParentBottom="true"> <ボタン android:id="@+id/btn_reset" android:layout_width="0dp" android:layout_height="match_parent" アンドロイド:layout_weight="1" アンドロイド:text="重置" /> text="重置" /> <Button android:id="@+id/btn_left" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="左倾" /> <ボタン android:id="@+id/btn_right" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="右倾" /> <Button android:id="@+id/btn_zoomin" android:layout_width="0dp" android:layout_height="match_parent" android:layout_weight="1" android:text="放大" /> android:layout_weight="1" text="ズームアウト" /> <Button android:id="@+id/btn_zoomout" android:layout_width="0dp" android:layout_height="match_parent" android:text="缩小" /> </LinearLayout> <com.jay.canvasdemo3.MyView android:id="@+id/myView" android:layout_width= "match_parent" android:layout_height="match_parent" android:layout_above="@id/ly_bar" /> </RelativeLayout>
MainActivity.java:
public class MainActivity extends AppCompatActivityimplemented View.OnClickListener{ private Button btn_reset; プライベートボタン btn_left; プライベートボタン btn_right; プライベートボタン btn_zoomin; プライベートボタン btn_zoomout; プライベート MyView myView; @Override protected void onCreate(Bundle SavedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); バインドビュー(); private void bindingViews() { btn_reset = (ボタン) findViewById(R.id.btn_reset); btn_left = (ボタン) findViewById(R.id.btn_left); btn_right = (ボタン) findViewById(R.id.btn_right); btn_zoomin = (ボタン) findViewById(R.id.btn_zoomin); btn_zoomout = (ボタン) findViewById(R.id.btn_zoomout); myView = (MyView) findViewById(R.id.myView); btn_reset.setOnClickListener(this); btn_left.setOnClickListener(this); btn_right.setOnClickListener(this); btn_zoomin.setOnClickListener(this); btn_zoomout.setOnClickListener(this); @Override public void onClick(View v) { switch (v.getId()){ case R.id.btn_reset: myView.setMethod ( 0); myView.setMethod(1); 壊す; case R.id.btn_right: myView.setMethod(2); 壊す; ケース R.id.btn_left: ブレーク; case R.id.btn_zoomin: myView.setMethod(3); 壊す; case R.id.btn_zoomout: myView.setMethod(4); 壊す; } } }
使い方はとても簡単なので説明は省きます〜
3.drawBitmapMesh により画像が歪む
API ドキュメントにもそのようなメソッドがあります: drawBitmapMesh (Bitmap bitmap, int MeshWidth, int MeshHeight, float[] verts, int vertOffset, int[] Colors, int colorOffset, Paint ペイント)
パラメータは順番に次のとおりです。
bitmap : 変形する必要がある元のビットマップ
MeshWidth / MeshHeight : 元のビットマップを水平/垂直に分割するグリッドの数
verts : (meshWidth+1)*(meshHeight+2) の長さの配列で、歪んだビットマップの各頂点 (グリッド線の交点) の位置を記録します。これは 1 次元の配列ですが、実際にはデータは (x0,y0)、(x1,y1)..(xN,Yn) の形式のデータであり、これらの配列要素はビットマップの歪み効果を制御します。
vertOffset : どの配列要素からビットマップを変形するかを制御します (verOffset 前のデータの歪み効果を無視します)。
コード例:
ランニング効果図:
コードの実装:
/** * 2015/11/11 0011 に Jay によって作成されました。 */ public class MyView extends View { // 水平方向と垂直方向に 20 個のグリッドに分割 private Final int WIDTH = 20; private Final int HEIGHT = 20 ; private Final int COUNT = (WIDTH + 1) * (HEIGHT + 1); //画像に 21*21 ポイントが含まれていることを記録 private Final float[] verts = new float[COUNT * 2]; //歪み前の 21* 21 ポイントの座標 private Final float[] orig = new float[COUNT * 2]; //歪み後の 21*21 点の座標 private Bitmap mBitmap; private float bH,bW; public MyView(Context context) { this( context, null); } public MyView(Context context, AttributeSet attrs) { super(context, attrs); init(); public float fx = bW * x / WIDTH; orig[インデックス * 2 + 0] = verts[インデックス * 2 + 0] = fx; public MyView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); private void init() { mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_wuliao); bH = mBitmap.getWidth(); bW = mBitmap.getHeight(); int インデックス = 0; for (int y = 0; y <= HEIGHT; y++) { float fy = bH * y / HEIGHT; // 初期化origとverts数組。 for (int x = 0; x <= WIDTH; x++) { orig[index * 2 + 1] = verts[index * 2 + 1] = fy; インデックス += 1; } } //背景色を設定 setBackgroundColor(Color.WHITE); } @Override protected void onDraw(Canvas Canvas) { Canvas .drawBitmapMesh(mBitmap, WIDTH, HEIGHT, verts , 0, null, 0, null); } //タッチ イベントの位置に従って verts 配列内の各要素の値を計算するために使用されるツール メソッド private void warp ( float cx, float cy ) { for (int i = 0; i < COUNT * 2; i += 2) { float dx = cx - orig[i + 0]; float dy = cy - orig[i + 1]; float dd = dx * dx + dy * dy; //各座標点と現在点との距離を計算 (cx, cy) float d = (float)Math.sqrt(dd); //現在点から離れるほど歪みの度合いを計算 (cx, cy) //頂点配列を再割り当てし float pull = 80000 / ((float) (dd * d)); ます (ビットマップ上の 21 * 21 点の歪んだ座標を保存します) if (pull >= 1) { verts[i + 0] = cx; verts[i + 1] = cy; } else { //タッチイベント発生点までの各頂点のオフセットを制御 verts[i + 0] = orig[i + 0] + dx * pull; verts[i + 1] = orig[i + 1] + dy * pull; } } //再描画するように View コンポーネントに通知 validate(); } @Override public boolean onTouchEvent(MotionEvent event) { //warp メソッドを呼び出して、タッチ スクリーン イベントの座標に従って頂点配列を変形します warp(event.getX(),イベント.getY() ); return true; } }
プロセス分析の実現:
まず第一に、この verts 配列に何が格納されているかを把握する必要があります。たとえば、 verts[0] と verts 1、これら 2 つの隣接する要素は、実際には最初の点の x 座標と y 座標を表します。これを理解すると、なぜ 21*21 個の点があるのか、そしてなぜ配列の長さがこの値 *2 に等しいのかがわかります。初期化部分もわかる!
次に、タッチ イベントに従って verts 配列要素の値を計算する実装を見てみましょう。タッチ ポイントの x、y 座標を取得し、この値から対応するポイントの x、y を減算し、計算します。タッチ ポイントと各座標点の間の距離 次に、いわゆる歪みを計算します: 80000 / ((float) (dd * d)); 歪みが >= 1 の場合は、座標点がタッチ ポイントを直接指すようにします。移動して、invalidate() を呼び出して再描画してください~ 以上です~ それでも理解できない場合は、もっと考えてください~ こういうことがあるということだけ知っておいてください!