Explication détaillée de l'API Canvas (Partie 3)Matrix et drawBitmapMash


Introduction à cette section :

Dans la documentation de l'API Canvas, nous voyons une telle méthode : drawBitmap(Bitmap bitmap, Matrix matrice, Paint paint)

Cette Matrix contient un gros article Nous avons parlé de ColorMatrix dans le ColorFilter de l'API Paint. Matrice de couleur, une matrice 4*5, on peut modifier la teinte, la saturation, etc. en modifiant la valeur de la matrice ! La Matrix dont nous parlons aujourd'hui peut être combinée avec d'autres API pour contrôler la transformation des graphiques et des composants. Par exemple, Canvas fournit ce qui précède Ce drawBitmap est utilisé pour obtenir l'effet de transformation matricielle ! Étudions cela lentement~

Documentation officielle de l'API : Matrix


1. Plusieurs méthodes de transformation couramment utilisées dans Matrix

.
  • setTranslate(float dx, float dy) : Matrice de contrôle à traduire
  • setRotate(float Degrees, float px , float py) : rotation , les paramètres sont : angle de rotation, axe (x, y)
  • setScale(float sx, float sy, float px, float py) : mise à l'échelle, Les paramètres sont : rapport de mise à l'échelle sur l'axe
  • est en fait fondamentalement le même que la méthode de transformation Canvas. Après avoir défini la transformation ci-dessus pour la matrice, appelez le Canvas. La méthode drawBitmap() appelle simplement la matrice~
2. Exemple d'utilisation de la matrice :


Exécution du rendu

 :

Implémentation du code1.gif :

MyView.java:

/**
 * Created by Jay on 2015/11/11 0011.
 */
public class MyView extends View {

    private Bitmap mBitmap;
    private Matrix matrix = new Matrix();
    private float sx = 0.0f;          //设置倾斜度
    private int width,height;         //位图宽高
    private float scale = 1.0f;       //缩放比例
    private int method = 0;

    public MyView(Context context) {
        this(context, null);
    }

    public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public MyView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private void init() {
        mBitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.img_meizi);
        width = mBitmap.getWidth();
        height = mBitmap.getHeight();
    }

    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        switch (method){
            case 0:
                matrix.reset();
                break;
            case 1:
                sx += 0.1;
                matrix.setSkew(sx,0);
                break;
            case 2:
                sx -= 0.1;
                matrix.setSkew(sx,0);
                break;
            case 3:
                if(scale  0.5){
                    scale -= 0.1;
                }
                matrix.setScale(scale,scale);
                break;
        }
        //根据原始位图与Matrix创建新图片
        Bitmap bitmap = Bitmap.createBitmap(mBitmap,0,0,width,height,matrix,true);
        canvas.drawBitmap(bitmap,matrix,null);    //绘制新位图
    }

    public void setMethod(int i){
        method = i;
        postInvalidate();
    }
}

Code de mise en page :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">

        <Button
            android:id="@+id/btn_reset"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="重置" />

        <Button
            android:id="@+id/btn_left"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            android:text="左倾" />

        <Button
            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="放大" />

        <Button
            android:id="@+id/btn_zoomout"
            android:layout_width="0dp"
            android:layout_height="match_parent"
            android:layout_weight="1"
            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" />

MainActivity.java : L'utilisation de

public class MainActivity extends AppCompatActivity implements View.OnClickListener{

    private Button btn_reset;
    private Button btn_left;
    private Button btn_right;
    private Button btn_zoomin;
    private Button btn_zoomout;
    private MyView myView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        bindViews();
    }

    private void bindViews() {
        btn_reset = (Button) findViewById(R.id.btn_reset);
        btn_left = (Button) findViewById(R.id.btn_left);
        btn_right = (Button) findViewById(R.id.btn_right);
        btn_zoomin = (Button) findViewById(R.id.btn_zoomin);
        btn_zoomout = (Button) 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);
                break;
            case R.id.btn_left:
                myView.setMethod(1);
                break;
            case R.id.btn_right:
                myView.setMethod(2);
                break;
            case R.id.btn_zoomin:
                myView.setMethod(3);
                break;
            case R.id.btn_zoomout:
                myView.setMethod(4);
                break;
        }
    }
}

est très simple et je ne l'expliquerai pas~

3.drawBitmapMash image déformée


Il existe également une telle méthode dans la documentation de l'API :

drawBitmapMesh

(Bitmap bitmap, int meshWidth, int meshHeight, float[] verts, int vertOffset, int[] colours, int colorOffset, Paint paint)

Les paramètres sont :

bitmap

 : le bitmap d'origine qui doit être déformé

meshWidth/

meshHeight

 : En combien de grilles l'image originale sera-t-elle divisée horizontalement/verticalement verts : La longueur est ( meshWidth+1) *(meshHeight+2) tableau, qui enregistre chaque sommet du bitmap déformé (intersection de la ligne de grille) Position, bien qu'il s'agisse d'un tableau unidimensionnel, les données qu'il enregistre réellement sont au format (x0, y0), (x1, y1).. (xN, Yn), Ces éléments du tableau contrôlent l'effet de distorsion sur le bitmap bitmap

vertOffset : Contrôle le tableau des verts à partir de quel élément du tableau déformer le bitmap (en ignorant les données avant verOffset effet de distorsion)

Exemple de code :

Exécution du rendu :

3.gif

Implémentation du code :

/**
 * Created by Jay on 2015/11/11 0011.
 */
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 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 index = 0;
        //初始化orig和verts数组。
        for (int y = 0; y <= HEIGHT; y++)
        {
            float fy = bH * y / HEIGHT;
            for (int x = 0; x <= WIDTH; x++)
            {
                float fx = bW * x / WIDTH;
                orig[index * 2 + 0] = verts[index * 2 + 0] = fx;
                orig[index * 2 + 1] = verts[index * 2 + 1] = fy;
                index += 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 = 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组件重绘
        invalidate();
    }

    @Override
    public boolean onTouchEvent(MotionEvent event)
    {
        //调用warp方法根据触摸屏事件的座标点来扭曲verts数组
        warp(event.getX(), event.getY());
        return true;
    }

}

Mettre en œuvre une analyse des processus:

Tout d'abord, vous devez comprendre ce que stocke ce tableau de verts ? Par exemple verts[0] et verts1, ces deux éléments adjacents représentent en fait la coordonnée x et la coordonnée y de notre premier point ! Sachant cela, vous savez pourquoi il y a 21 * 21 points, et pourquoi la longueur du tableau est égale à cette valeur * 2 ! Vous comprendrez la partie initialisation !

Jetons ensuite un coup d'œil à l'implémentation du calcul de la valeur de l'élément du tableau verts en fonction des événements tactiles : Obtenez les coordonnées x, y du point de contact, soustrayez cette valeur des coordonnées x, y du point correspondant et calculez la distance entre le point de contact et chaque point de coordonnées. Calculez ensuite le soi-disant degré de distorsion : 80000 / ((float) (dd * d)) ; si le degré de distorsion >= 1, laissez directement les coordonnées Si le point pointe vers ce point de contact, < 1, chaque sommet sera décalé par rapport au point de contact, puis invalidate() sera appelé pour redessiner~ C'est tout~ Réfléchissez davantage et réfléchissez-y davantage, si vous ne comprenez toujours pas, oubliez ça~ C'est bon de savoir que cette chose existe !


4. Téléchargez l'exemple de cette section :

CanvasDemo3.zip

CanvasDemo4.zip


Résumé de cette section :

La plupart du contenu de cette section est extrait du Crazy Handout "Android" de Li Gang, qui peut être un peu plus facile à comprendre ~ Matrix devrait être compréhensible par la plupart des enfants, mais drawBitmapMash peut prendre un certain temps pour déformer l'image. Il est temps de digérer et de digérer, ce n'est pas grave si tu ne comprends pas ~ Bon, c'est tout pour cette section, merci 4.gif