自定义控件——原创仿地图瓦片动态加载_阶段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,操作更方便哦

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值