MapView源代码

时间:2019-08-18
本文章向大家介绍MapView源代码,主要包括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;
}

}

原文地址:https://www.cnblogs.com/hyhy904/p/11373123.html