zoukankan      html  css  js  c++  java
  • MapView源代码

    package cjz.project.maptry4;

    import android.annotation.NonNull;
    import android.content.Context;
    import android.graphics.Bitmap;
    import android.graphics.Canvas;
    import android.graphics.Color;
    import android.graphics.Paint;
    import android.graphics.Path;
    import android.graphics.PointF;
    import android.graphics.PorterDuff;
    import android.graphics.Rect;
    import android.os.Handler;
    import android.util.AttributeSet;
    import android.util.Log;
    import android.view.MotionEvent;
    import android.view.View;
    import android.view.ViewGroup;
    import android.widget.FrameLayout;
    import android.widget.LinearLayout;

    import java.util.LinkedList;
    import java.util.List;
    import java.util.Map;
    import java.util.Queue;
    import java.util.concurrent.LinkedBlockingQueue;

    import cjz.project.maptry.R;

    /**
    * 以地图的方式取代SurfaceView,避免写进去的Path之类的东西老是要做偏移之类的麻烦操作
    * Created by cjz on 2019/4/30.
    */

    public class MapView extends FrameLayout {


    /**缩放比例下限**/
    private final float MIN_SCALE = 0.8f;
    /**缩放比例上限**/
    private final float MAX_SCALE = 1.2f;
    /**缩放比例**/
    private float totalScale = 1;
    /**
    * 单元格矩阵长宽均有多少个单元
    **/
    private final int MATRIX_LENGTH = 12;
    /**单元格表**/
    private MapUnit mapUnitMatrix[][] = new MapUnit[MATRIX_LENGTH][MATRIX_LENGTH];
    private boolean initFinished = false;
    /**画布位图**/
    private Bitmap canvasBitmap = null;
    /**画布**/
    private Canvas canvas = null;
    /**是否需要把刚刚写到画布上的画面刷新到啊单元格上,在漫游或者缩放时需要这么做**/
    private boolean isNeedRefresh = false;
    /**是否已经刷新了前景画布**/
    private boolean frontCanvasAlreadyClear = true;
    /**表面图层**/
    private Surface surface;

    /**
    * 表面图层,用于在保存之前显示每一步的修改
    * Created by cjz on 2019/7/15.
    */
    private class Surface extends View{

    private Bitmap bitmap;

    public Surface(Context context) {
    super(context);
    }

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

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

    protected void drawBitmap(Bitmap bitmap){
    this.bitmap = bitmap;
    invalidate();
    // setBackgroundColor(Color.RED);
    }


    @Override
    protected void onDraw(Canvas canvas) {
    if(bitmap != null){
    canvas.drawBitmap(bitmap, 0, 0, null);
    }
    Log.i("表面绘制", "");
    super.onDraw(canvas);

    }
    }

    public MapView(Context context) {
    super(context);
    }

    public MapView(Context context, AttributeSet attrs) {
    super(context, attrs);

    }

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

    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if(!initFinished){
    int width = (int)(MeasureSpec.getSize(widthMeasureSpec) * 1.25f);
    int height = (int)(MeasureSpec.getSize(heightMeasureSpec) * 1.25f);
    this.setLayoutParams(new LayoutParams(width, height));
    createView(width, height);
    initFinished = true;
    }
    }

    private void createView(int width, int height) {
    final int widthSplit = MATRIX_LENGTH - 3;
    //创建
    for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    MapUnit mapUnit = new MapUnit(getContext());
    mapUnit.setLayoutParams(new ViewGroup.LayoutParams(width / widthSplit, height / widthSplit));
    addView(mapUnit);
    mapUnitMatrix[xPos][yPos] = mapUnit;
    mapUnit.setTag(new int[]{xPos, yPos});
    }
    }
    //排列
    for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    mapUnitMatrix[xPos][yPos].setX(xPos * width / widthSplit);
    mapUnitMatrix[xPos][yPos].setY(yPos * height / widthSplit);
    }
    }
    //缩放
    // for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    // for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    // View view = mapUnitMatrix[xPos][yPos];
    // //以本View中心点为缩放中心缩放
    // view.setScaleX(view.getScaleX() * 0.5f);
    // view.setScaleY(view.getScaleY() * 0.5f);
    // //求本view中心点在屏幕中的坐标
    // float centerX = view.getX() + view.getWidth() / 2;
    // float centerY = view.getY() + view.getHeight() / 2;
    // /**向缩放中心靠拢,例如缩放为原来的80%,那么缩放中心x到view中心x的距离则为0.8*(缩放中心x - view中心x),
    // * 那么view的x距离屏幕左边框的距离则 为 view中心x + (1 - 0.8) * (缩放x - view中心x) ****/
    // float centerXAfterScale = centerX + (0 - centerX) * (1 - 0.5f); //view中心向缩放中心聚拢或扩散,例如scale为0.8,那么收缩0.2,现在的宽度就是之前宽度的0.8了,得到收缩目的
    // float centerYAfterScale = centerY + (0 - centerY) * (1 - 0.5f);
    // view.setX(centerXAfterScale - view.getWidth() / 2); //setXY是set左上角的x,y,所以view中心点要减去宽度/高度的一般来重新得到应该去的左上角坐标
    // view.setY(centerYAfterScale - view.getHeight() / 2);
    //// viewFind(view, this.scale);
    // Log.i("View" + view.hashCode() + "的信息", String.format("长度:%d, 宽度:%d, 坐标x:%f, 坐标y:%f", view.getWidth(), view.getHeight(), view.getX(), view.getY()));
    // }
    // }
    if(canvasBitmap != null && !canvasBitmap.isRecycled()){
    canvasBitmap.recycle();
    canvasBitmap = null;
    }
    canvasBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
    canvas = new Canvas(canvasBitmap);
    //创建表面图层
    surface = new Surface(getContext());
    addView(surface);
    }

    /**
    * 缩放函数
    **/
    public void scale(float scale, float px, float py) {
    refreshUnitImage();
    if(totalScale * scale < MIN_SCALE || totalScale * scale > MAX_SCALE){
    return;
    }
    Log.i("缩放", "缩放成功");
    totalScale *= scale;
    // for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    // for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    // View view = mapUnitMatrix[xPos][yPos];
    // //以本View中心点为缩放中心缩放
    // view.setScaleX(view.getScaleX() * scale);
    // view.setScaleY(view.getScaleY() * scale);
    // //求本view中心点在屏幕中的坐标
    // float centerX = view.getX() + view.getWidth() / 2;
    // float centerY = view.getY() + view.getHeight() / 2;
    // /**向缩放中心靠拢,例如缩放为原来的80%,那么缩放中心x到view中心x的距离则为0.8*(缩放中心x - view中心x),
    // * 那么view的x距离屏幕左边框的距离则 为 view中心x + (1 - 0.8) * (缩放x - view中心x) ****/
    // float centerXAfterScale = centerX + (px - centerX) * (1 - scale); //view中心向缩放中心聚拢或扩散,例如scale为0.8,那么收缩0.2,现在的宽度就是之前宽度的0.8了,得到收缩目的
    // float centerYAfterScale = centerY + (py - centerY) * (1 - scale);
    // view.setX(centerXAfterScale - view.getWidth() / 2); //setXY是set左上角的x,y,所以view中心点要减去宽度/高度的一般来重新得到应该去的左上角坐标
    // view.setY(centerYAfterScale - view.getHeight() / 2);
    //// viewFind(view, this.scale);
    // Log.i("View" + view.hashCode() + "的信息", String.format("长度:%d, 宽度:%d, 坐标x:%f, 坐标y:%f", view.getWidth(), view.getHeight(), view.getX(), view.getY()));
    // }
    // }
    setScaleX(getScaleX() * scale);
    setScaleY(getScaleY() * scale);
    //求本view中心点在屏幕中的坐标
    float centerX = getX() + getWidth() / 2;
    float centerY = getY() + getHeight() / 2;
    /**向缩放中心靠拢,例如缩放为原来的80%,那么缩放中心x到view中心x的距离则为0.8*(缩放中心x - view中心x),
    * 那么view的x距离屏幕左边框的距离则 为 view中心x + (1 - 0.8) * (缩放x - view中心x) ****/
    float centerXAfterScale = centerX + (0 - centerX) * (1 - scale); //view中心向缩放中心聚拢或扩散,例如scale为0.8,那么收缩0.2,现在的宽度就是之前宽度的0.8了,得到收缩目的
    float centerYAfterScale = centerY + (0 - centerY) * (1 - scale);
    setX(centerXAfterScale - getWidth() / 2); //setXY是set左上角的x,y,所以view中心点要减去宽度/高度的一般来重新得到应该去的左上角坐标
    setY(centerYAfterScale - getHeight() / 2);
    Log.i("缩放", String.format("百分比:%f", totalScale));
    }

    /**
    * 移动函数 (效率有点问题,但暂时不管,反正以后要用OpenGL重写的,自定义View的显示效率不是最终追求的最优选择)
    **/
    public void translate(float distanceX, float distanceY) {
    // setScaleX(getScaleX() * 1.01f);
    // setScaleY(getScaleY() * 1.01f);
    refreshUnitImage();
    for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    View view = mapUnitMatrix[xPos][yPos];
    view.setX(view.getX() + (distanceX));
    view.setY(view.getY() + (distanceY));
    }
    }
    //x轴,y轴要分开两个循环处理,否则会引发混乱
    for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    View view = mapUnitMatrix[xPos][yPos];
    //移除去的部分添加到未显示的部分的末尾
    if(view.getX() + (1 - view.getScaleX()) / 2 * view.getWidth() + view.getWidth() * view.getScaleX() < 0 && getWidth() > 0) { //单元格溢出到了屏幕左边,移动到当前对应行最右边
    if(xPos == 0) {
    //重设位置
    view.setX(mapUnitMatrix[MATRIX_LENGTH - 1][yPos].getX() + mapUnitMatrix[MATRIX_LENGTH - 1][yPos].getWidth() * mapUnitMatrix[MATRIX_LENGTH - 1][yPos].getScaleX());
    int targetPos[] = (int[])mapUnitMatrix[MATRIX_LENGTH - 1][yPos].getTag();
    view.setTag(new int[]{targetPos[0] + 1, targetPos[1]}); //重设单元格标记
    for (int i = xPos; i < MATRIX_LENGTH - 1; i++) {
    mapUnitMatrix[i][yPos] = mapUnitMatrix[i + 1][yPos];
    }
    mapUnitMatrix[MATRIX_LENGTH - 1][yPos] = (MapUnit) view;
    // view.invalidate(); //申请重绘
    }
    }
    else if (view.getX() + (1 - view.getScaleX()) / 2 * view.getWidth() > getWidth() && getWidth() > 0) {
    if(xPos == MATRIX_LENGTH - 1){ //因为初始化时显示的Unit是最左上角的Unit,有可能导致非最后一列的内容被平移,这违反自动补充的逻辑,会出bug,所以要加判断
    //重设位置(设置和最后一个的左上角坐标直接重合(setx用于设定左上角坐标),再减去控件宽度*缩放量使得目标控件右上角和最后一个控件左上角对齐)
    view.setX(mapUnitMatrix[0][yPos].getX() - mapUnitMatrix[0][yPos].getWidth() * mapUnitMatrix[0][yPos].getScaleX());
    int targetPos[] = (int[])mapUnitMatrix[0][yPos].getTag();
    view.setTag(new int[]{targetPos[0] - 1, targetPos[1]}); //重设单元格标记
    MapUnit temp = mapUnitMatrix[MATRIX_LENGTH - 1][yPos];
    for(int i = MATRIX_LENGTH - 1; i > 0 ; i--){
    mapUnitMatrix[i][yPos] = mapUnitMatrix[i - 1][yPos];
    }
    mapUnitMatrix[0][yPos] = temp;
    // view.invalidate(); //申请重绘
    }
    }
    }
    }
    for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    View view = mapUnitMatrix[xPos][yPos];
    if (view.getY() + (1 - view.getScaleY()) / 2 * view.getHeight() + view.getHeight() * view.getScaleY() < 0 && getHeight() > 0) {
    if (yPos == 0) {
    //重设位置
    view.setY(mapUnitMatrix[xPos][MATRIX_LENGTH - 1].getY() + mapUnitMatrix[xPos][MATRIX_LENGTH - 1].getHeight() * mapUnitMatrix[xPos][MATRIX_LENGTH - 1].getScaleY());
    int targetPos[] = (int[])mapUnitMatrix[xPos][MATRIX_LENGTH - 1].getTag();
    view.setTag(new int[]{targetPos[0], targetPos[1] + 1}); //重设单元格标记
    for (int i = yPos; i < MATRIX_LENGTH - 1; i++) {
    mapUnitMatrix[xPos][i] = mapUnitMatrix[xPos][i + 1];
    }
    mapUnitMatrix[xPos][MATRIX_LENGTH - 1] = (MapUnit) view;
    // view.invalidate(); //申请重绘
    }
    }
    else if (view.getY() + (1 - view.getScaleY()) / 2 * view.getHeight() > getHeight() && getHeight() > 0) {
    if (yPos == MATRIX_LENGTH - 1) {
    //Log.i("越位", "到了屏幕下边界");
    //重设位置(设置和最后一个的左上角坐标直接重合(setx用于设定左上角坐标),再减去控件宽度*缩放量使得目标控件右上角和最后一个控件左上角对齐)
    view.setY(mapUnitMatrix[xPos][0].getY() - view.getHeight() * view.getScaleY());
    int targetPos[] = (int[])mapUnitMatrix[xPos][0].getTag();
    view.setTag(new int[]{targetPos[0], targetPos[1] - 1}); //重设单元格标记
    MapUnit temp = mapUnitMatrix[xPos][MATRIX_LENGTH - 1];
    for (int i = MATRIX_LENGTH - 1; i > 0; i--) {
    mapUnitMatrix[xPos][i] = mapUnitMatrix[xPos][i - 1];
    }
    mapUnitMatrix[xPos][0] = temp;
    // view.invalidate(); //申请重绘
    }
    }
    }
    }
    // Log.i("移动", String.format("x位移:%f, y位移:%f", distanceX, distanceY));
    // invalidate();
    }

    /**绘制Path**/
    public void drawPath(@NonNull Path path, @NonNull Paint paint){
    canvas.drawPath(path, paint);
    surface.drawBitmap(canvasBitmap);
    isNeedRefresh = true;
    }


    /****/




    /**刷新单元图案**/
    private void refreshUnitImage(){
    if(isNeedRefresh){
    for(int yPos = 0; yPos < MATRIX_LENGTH; yPos++) {
    for (int xPos = 0; xPos < MATRIX_LENGTH; xPos++) {
    MapUnit mapUnit = mapUnitMatrix[xPos][yPos];
    mapUnit.drawBitmap(canvasBitmap); //分配给各单元进行切图操作
    }
    }
    //清空主画布
    canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
    }
    isNeedRefresh = false;
    }

    @Override
    protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    //贴前景画布
    // canvas.drawBitmap(canvasBitmap, new Rect(0, 0, canvas.getWidth(), canvas.getHeight()), new Rect(0, 0, canvas.getWidth(), canvas.getHeight()), null);

    }

    @Override
    protected boolean drawChild(Canvas canvas, View child, long drawingTime ) {
    boolean result = super.drawChild(canvas, child, drawingTime);
    return result;
    }

    }

  • 相关阅读:
    什么是云安全
    VMWare vForum 2013看点
    循环和数据的操作命令
    程序交互
    数据类型
    基础变量
    模块和包
    ['hello', 'sb']正则表达式
    os模块
    内置函数
  • 原文地址:https://www.cnblogs.com/hyhy904/p/11373123.html
Copyright © 2011-2022 走看看