自定義控件——原創仿地圖瓦片動態加載_階段5_一種Excel類App的可能Demo

使用這種本控件,控件內部可以根據你的需要生成不同大小的Unit矩陣,並賦予每個矩陣單元一個tag,在移動到邊界時將會回收再利用,tag也會根據移動時的情況更改值,你可以在tag被更新時加載內存或者外存的數據,刷新到Unit中,形成地圖加載器或者Excel類軟件,而且不會像之前用ImageView組成的矩陣那樣,內部分辨率被固定住了。

 

可以看效果,視頻中格子只有20*20個,但是可以通過Unit到邊界之後回收複用的辦法,形成圖像上的無限眼延伸效果,非常適合加載地圖類數據、用來做白板類APP的畫布或者做Excel類應用:

控件代碼:

package com.testcanvaszoom3;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.PointF;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;

import java.util.Queue;
import java.util.concurrent.LinkedBlockingQueue;

import cjz.project.whiteboardlab.R;

/**
 * Created by cjz on 2019/10/28.
 */

public class DrawView extends View{

    private Bitmap testBitmap = null;
    private final int MATRIX_LEN = 20;
    private Unit unitMatrix[][] = new Unit[MATRIX_LEN][MATRIX_LEN];
    private PointF currentCenter = new PointF();
    private PointF prevCurrentCenter = null;
    private float prevDistance = Float.MIN_VALUE;
    /**將觸摸點的座標平均化**/
    private float avergeX = 0, avergeY = 0;
    private int prevPointCount = 0;

    /*** 觸摸點點距隊列**/
    private Queue<Float> touchDistanceQueue = new LinkedBlockingQueue<>();

    private float totalScale = 1f;
    private int mWidth, mHeight;

    public DrawView(Context context) {
        super(context);
        init();
    }

    public DrawView(Context context, @Nullable AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    public DrawView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init();
    }

    private void init(){
        if(testBitmap == null){
            testBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.timg);
//            testBitmap = BitmapFactory.decodeResource(getResources(), R.drawable.shape_recongize_circle);
            for(int i = 0; i < MATRIX_LEN; i++){
                unitMatrix[i] = new Unit[MATRIX_LEN];
                for(int j = 0; j < MATRIX_LEN; j++){
                    unitMatrix[i][j] = new Unit(160, 60, "");
                    unitMatrix[i][j].setTag(new int[]{i, j});
                    unitMatrix[i][j].translate(i * 160, j * 60);
                }
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int width = MeasureSpec.getSize(widthMeasureSpec);
        int height = MeasureSpec.getSize(heightMeasureSpec);
        mWidth = width;
        mHeight = height;
    }


    /**matrix:
     * [MSCALE_X, MSKEW_X, MTRANS_X,
     *  MSKEW_Y, MSCALE_Y, MTRANS_Y,
     *  MPRESP_0, MPRESP_1, MPRESP_2]**/
    private void translate(float dx, float dy){
        for(int i = 0; i < MATRIX_LEN; i++){
            for(int j = 0; j < MATRIX_LEN; j++){
                unitMatrix[i][j].translate(dx, dy);
            }
        }
        float values[] = new float[9];
        float valuesCompare[] = new float[9];
        //x軸,y軸要分開兩個循環處理,否則會引發混亂,而且以左上角爲縮放中心縮放的,不像View是中心爲縮放中心
        for (int yPos = 0; yPos < MATRIX_LEN; yPos++) {
            for (int xPos = 0; xPos < MATRIX_LEN; xPos++) { //單元格溢出到了屏幕左邊,移動到當前對應行最右邊
                Unit unit = unitMatrix[xPos][yPos];
                unit.getMatrix().getValues(values);
                //移除去的部分添加到未顯示的部分的末尾,
                if(values[2] + unit.getWidth() * values[0] < 0 && unit.getWidth() > 0){
                    if(xPos == 0){
                        //
                        unitMatrix[MATRIX_LEN - 1][yPos].getMatrix().getValues(valuesCompare);
                        values[2] = valuesCompare[2] + unitMatrix[MATRIX_LEN - 1][yPos].getWidth() * valuesCompare[0];
                        unit.getMatrix().setValues(values);
                        int targetPos[] = unitMatrix[MATRIX_LEN - 1][yPos].getTag();
                        unit.setTag(new int[]{targetPos[0] + 1, targetPos[1]}); //重設單元格標記
                        for (int i = xPos; i < MATRIX_LEN - 1; i++) {
                            unitMatrix[i][yPos] = unitMatrix[i + 1][yPos];
                        }
                        unitMatrix[MATRIX_LEN - 1][yPos] =  unit;
                    }
                } else if(values[2] > mWidth) {
                    if (xPos == MATRIX_LEN - 1) { //因爲初始化時顯示的Unit是最左上角的Unit,有可能導致非最後一列的內容被平移,這違反自動補充的邏輯,會出bug,所以要加判斷
                        //重設位置(設置和最後一個的左上角座標直接重合(setx用於設定左上角座標),再減去控件寬度*縮放量使得目標控件右上角和最後一個控件左上角對齊)
                        unitMatrix[0][yPos].getMatrix().getValues(valuesCompare);
                        values[2] = valuesCompare[2] - unitMatrix[0][yPos].getWidth() * valuesCompare[0];
                        unit.getMatrix().setValues(values);
                        int targetPos[] =  unitMatrix[0][yPos].getTag();
                        unit.setTag(new int[]{targetPos[0] - 1, targetPos[1]}); //重設單元格標記
                        Unit temp = unitMatrix[MATRIX_LEN - 1][yPos];
                        for (int i = MATRIX_LEN - 1; i > 0; i--) {
                            unitMatrix[i][yPos] = unitMatrix[i - 1][yPos];
                        }
                        unitMatrix[0][yPos] = temp;
                    }
                }
            }
        }

        for (int yPos = 0; yPos < MATRIX_LEN; yPos++) {
            for (int xPos = 0; xPos < MATRIX_LEN; xPos++) {
                Unit unit = unitMatrix[xPos][yPos];
                unit.getMatrix().getValues(values);
                if(values[5] + unit.getHeight() * values[4] < 0 && unit.getHeight() > 0){
                    if (yPos == 0) {
                        //重設位置
                        unitMatrix[xPos][MATRIX_LEN - 1].getMatrix().getValues(valuesCompare);
                        values[5] = valuesCompare[5] + unitMatrix[xPos][MATRIX_LEN - 1].getHeight() * valuesCompare[4];
                        unit.getMatrix().setValues(values);
                        int targetPos[] = unitMatrix[xPos][MATRIX_LEN - 1].getTag();
                        unit.setTag(new int[]{targetPos[0], targetPos[1] + 1}); //重設單元格標記
                        for (int i = yPos; i < MATRIX_LEN - 1; i++) {
                            unitMatrix[xPos][i] = unitMatrix[xPos][i + 1];
                        }
                        unitMatrix[xPos][MATRIX_LEN - 1] = unit;
                    }
                } else if(values[5] > mHeight){
                    if (yPos == MATRIX_LEN - 1) {
                        //重設位置(設置和最後一個的左上角座標直接重合(setx用於設定左上角座標),再減去控件寬度*縮放量使得目標控件右上角和最後一個控件左上角對齊)
                        unitMatrix[xPos][0].getMatrix().getValues(valuesCompare);
                        values[5] = valuesCompare[5] - unitMatrix[xPos][0].getHeight() * valuesCompare[4];
                        unit.getMatrix().setValues(values);
                        int targetPos[] =  unitMatrix[xPos][0].getTag();
                        unit.setTag(new int[]{targetPos[0], targetPos[1] - 1}); //重設單元格標記
                        Unit temp = unitMatrix[xPos][MATRIX_LEN - 1];
                        for (int i = MATRIX_LEN - 1; i > 0; i--) {
                            unitMatrix[xPos][i] = unitMatrix[xPos][i - 1];
                        }
                        unitMatrix[xPos][0] = temp;
                    }
                }
            }
        }

    }

    private void scale(float scale, float sx, float sy){
        for(int i = 0; i < MATRIX_LEN; i++){
            for(int j = 0; j < MATRIX_LEN; j++){
                unitMatrix[i][j].scale(scale, sx, sy);
            }
        }
    }

    /**移動縮放,觸發第一次移動縮放時,MapView會把表面圖層切圖並放入各Unit中**/
    private void transAndScale(MotionEvent event){
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                prevDistance = 0;
                prevPointCount = event.getPointerCount();
                //算出移動中心座標、點間距離
                for(int i = 0; i < event.getPointerCount(); i++){
                    avergeX += event.getX(i);
                    avergeY += event.getY(i);
                    if(i + 1 < event.getPointerCount()){
                        prevDistance += Math.sqrt(Math.pow(event.getX(i + 1) - event.getX(i), 2) + Math.pow(event.getY(i + 1) - event.getY(i), 2));
                    }
                }
                avergeX /= event.getPointerCount();
                avergeY /= event.getPointerCount();
                if(prevCurrentCenter == null){
                    prevCurrentCenter = new PointF(avergeX, avergeY);
                } else {
                    prevCurrentCenter.set(avergeX, avergeY);
                }
                break;
            case MotionEvent.ACTION_MOVE:
                avergeX = 0;
                avergeY = 0;
                float nowDistance = 0;
                //算出移動中心座標、點間距離
                for(int i = 0; i < event.getPointerCount(); i++){
                    avergeX += event.getX(i);
                    avergeY += event.getY(i);
                    if(i + 1 < event.getPointerCount()){
                        nowDistance += Math.sqrt(Math.pow(event.getX(i + 1) - event.getX(i), 2) + Math.pow(event.getY(i + 1) - event.getY(i), 2));
                    }
                }
                //現在的點間距離 除以 上次點間距離 這次得到縮放比例
                avergeX /= event.getPointerCount();
                avergeY /= event.getPointerCount();
                if((prevPointCount != event.getPointerCount()) || event.getPointerCount() <= 1 || prevPointCount <= 1){ //觸摸點數突然改變 或者 觸摸點不超過2,不允許縮放
                    prevDistance = nowDistance = 0;
                }
                //如果縮放數據有效,則進行平均平滑化並且進行縮放
                if(prevDistance > 0 && nowDistance > 0){
                    touchDistanceQueue.add(nowDistance / prevDistance);
                    if(touchDistanceQueue.size() >= 6) {
                        Float point[] = new Float[touchDistanceQueue.size()];
                        touchDistanceQueue.toArray(point);
                        float avergDistance = 0;
                        for(int i = 0; i < point.length; i++){
                            avergDistance += point[i];
                        }
                        avergDistance /= point.length;
                        double scale = Math.sqrt(avergDistance);
                        scale((float) scale, avergeX, avergeY);

                        totalScale *= scale;
                        ToastUtil.showToast(String.format("縮放量:%.2f", totalScale * 100));
                        while(touchDistanceQueue.size() > 6){
                            touchDistanceQueue.poll();
                        }
                    }
                }
                prevPointCount = event.getPointerCount();
                prevDistance = nowDistance;
                //當前座標 - 上次座標 = 偏移值,然後進行位置偏移
                if(prevCurrentCenter == null) {
                    prevCurrentCenter = new PointF(avergeX, avergeY);
                } else {
                    translate(avergeX - prevCurrentCenter.x, avergeY - prevCurrentCenter.y);
                    prevCurrentCenter.set(avergeX, avergeY);
                }
                break;
            case MotionEvent.ACTION_UP:
                //擡起,清理乾淨數據
                avergeX = 0;
                avergeY = 0;
                touchDistanceQueue.clear();
                break;
        }
    }

    @Override
    public boolean onTouchEvent(MotionEvent event) {
        Log.i(getClass().getName(), event.toString());
        transAndScale(event);
        invalidate();
        return true;
    }

    @Override
    protected void onDraw(Canvas canvas) {
        for(int i = 0; i < MATRIX_LEN; i++){
            for(int j = 0; j < MATRIX_LEN; j++){
                unitMatrix[i][j].draw(canvas);
            }
        }
    }
}

Unit代碼:

package com.testcanvaszoom3;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;

/**
 * Created by cjz on 2019/10/28.
 */
/**不夠內存的時候要釋放這些Unit裏面的位圖,要用的時候再從外面讀寫**/

public class Unit {

    /**圖塊根目錄**/
    private String rootPath;

    /**本Unit圖塊目錄**/
    private String unitDataPath;

    /**Debug時顯示tag座標**/
    private Paint paintPen;

    /**是否要顯示tag座標**/
    private boolean isDebug = true;

    /**matrix:
     * [MSCALE_X, MSKEW_X, MTRANS_X,
     *  MSKEW_Y, MSCALE_Y, MTRANS_Y,
     *  MPRESP_0, MPRESP_1, MPRESP_2]**/

    /**單元位置和縮放大小的控制矩陣**/
    private Matrix matrix = new Matrix();

    /**matrix映射**/
    private float[] matrixVal = new float[9];

    /**這次的試驗品可以傳入位圖進行繪製,而且是共享一張位圖,這樣平鋪完之後非常節約內存**/
    private Bitmap bitmap = null;

    private int width, height;


    private int[] tag = new int[2];

    public Unit(int width, int height, String rootPath) {
        this.width = width;
        this.height = height;
        this.rootPath = rootPath;
        if (isDebug) {
            paintPen = new Paint();
            paintPen.setStrokeWidth(2f);
            paintPen.setStyle(Paint.Style.STROKE);
            paintPen.setColor(Color.RED);
            paintPen.setTextSize(16f);
            paintPen.setAntiAlias(true);
        }

    }

    /**繪製**/
    public void draw(Canvas canvas){
        if(bitmap != null){
            matrix.getValues(matrixVal);
            if(matrixVal[2] + width * matrixVal[0] > 0 && matrixVal[2] < canvas.getWidth() && matrixVal[5] + height * matrixVal[4] > 0 && matrixVal[5] < canvas.getHeight()){ //可見區域之外不用渲染
                canvas.drawBitmap(bitmap, matrix, null);
            }
        }
        if (isDebug) {
            float[] matrixVal = new float[9];
            matrix.getValues(matrixVal);
            canvas.drawText(String.format("%d, %d", tag[0], tag[1]), matrixVal[2] + 50 * matrixVal[0], matrixVal[5] + 40 * matrixVal[4], paintPen);
            paintPen.setStrokeWidth(2 * matrixVal[0]);
            paintPen.setTextSize(16 * matrixVal[0]);
            canvas.drawRect(matrixVal[2], matrixVal[5], matrixVal[2] + width * matrixVal[0], matrixVal[5] + height * matrixVal[4], paintPen);

        }
    }

    public void translate(float dx, float dy){
        matrix.postTranslate(dx, dy);
    }

    public void scale(float scale, float px, float py){
        matrix.postScale(scale, scale, px, py);
    }

    public Matrix getMatrix() {
        return matrix;
    }

    public Bitmap getBitmap() {
        return bitmap;
    }

    public int[] getTag() {
        return tag;
    }

    public void setTag(int[] tag) {
        this.tag = tag;
    }

    public int getWidth() {
        return width;
    }

    public int getHeight() {
        return height;
    }
}

效果視頻,我沒有限制縮放程度,所以縮小到一定程度之後再拖動,可以直觀地感受到Unit在複用:

鏈接: https://pan.baidu.com/s/14oa83n3ErRzkR8Au3-q8kw 提取碼: 26wh 複製這段內容後打開百度網盤手機App,操作更方便哦

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章