最近項目用到地圖繪制效果,可以選中相應的區域進行判讀點位是否在制定區域內,先看效果。
閉合區域效果.png
非閉合區域效果.png
自定義區域效果.png
項目效果圖.png
下面我們先看一下最主要的類,自定義類PathView:
package com.example.lenovo.enclosure;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.RectF;
import android.graphics.Region;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
/**
* 路徑繪制
* Created by kaidaye on 2017/4/19.
*/
public class PathView extends View {
/**
* 線條顏色值
*/
private int paintColor = 0xff4598e5;
/**
* 第二條線顏色值
*/
private int paintTransparentColor = 0x154598e5;
/**
* 線條寬度
*/
private float strokeWidth = 15.0f;
/**
* 第二條線寬度
*/
private float xustrokeWidth = 200.0f;
/**
* 透明度
*/
private int alpha = 80;
/**
* 閉合X坐標距
*/
private int closeX = 300;
/**
* 閉合Y坐標距
*/
private int closeY = 300;
/**
* 是否是閉合只需要閉合狀態
*/
private Boolean isClose = false;
/**
* 當前閉合狀態,true表示閉合,false 表示未閉合
*/
private Boolean isStatus = false;
private Paint mPaint = new Paint();
private Paint transparentPaint = new Paint();
private Path pathCircle = new Path();
private Path mPath = new Path();
private Path transparentPath = new Path();
private Region region = new Region();
private float pathX;
private float pathY;
private float startX;
private float startY;
private Boolean isDrwable = false;
private Boolean isActionUp = false;
private OnFinishListener listener;
public PathView(Context context) {
super(context);
init();
}
public PathView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
//設置畫筆顏色
mPaint.setColor(paintColor);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(strokeWidth);
transparentPaint.setColor(paintTransparentColor);
transparentPaint.setStyle(Paint.Style.STROKE);
transparentPaint.setStrokeCap(Paint.Cap.ROUND);
transparentPaint.setStrokeWidth(xustrokeWidth);
transparentPaint.setAlpha(alpha);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawPath(mPath, mPaint);
if (isDrwable) {
canvas.drawPath(transparentPath, transparentPaint);
}
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (isActionUp) {
isActionUp = false;
mPaint.setStyle(Paint.Style.STROKE);
return true;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
touchDown(event);
break;
case MotionEvent.ACTION_MOVE:
touchMove(event);
break;
case MotionEvent.ACTION_UP:
if (isClose) {//只需要閉合狀態
mPath.close();//路徑閉合方法,如果起點和結束點沒有關閉調用此方法會直接將起點和結束的連線
transparentPath.close();
transparentPaint.setStyle(Paint.Style.FILL);
transparentPaint.setStrokeWidth(strokeWidth);
isStatus = true;
RectF r = new RectF();
mPath.computeBounds(r, true);
//設置區域路徑和剪輯描述的區域
region.setPath(mPath, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
} else {//閉合與非閉合兩種狀態
if (Math.abs(startX - event.getX()) <= closeX && Math.abs(startY - event.getY()) <= closeY) {//距離較近可以閉合
mPath.close();//路徑閉合方法,如果起點和結束點沒有關閉調用此方法會直接將起點和結束的連線
transparentPath.close();
transparentPaint.setStyle(Paint.Style.FILL);
transparentPaint.setStrokeWidth(strokeWidth);
isStatus = true;
RectF r = new RectF();
mPath.computeBounds(r, true);
//設置區域路徑和剪輯描述的區域
region.setPath(mPath, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
} else {
RectF rCircle = new RectF();
pathCircle.computeBounds(rCircle, true);
region.setPath(pathCircle, new Region((int) rCircle.left, (int) rCircle.top, (int) rCircle.right, (int) rCircle.bottom));
isStatus = false;
}
}
isDrwable = true;
if (listener != null)
listener.onFinish(region);
break;
}
postInvalidate();
return true;
}
//手指在屏幕上滑動時調用
private void touchMove(MotionEvent event) {
final float x = event.getX();
final float y = event.getY();
Point point = new Point();
point.x = (int) event.getX();
point.y = (int) event.getY();
pathCircle.addCircle(event.getX(), event.getY(), (xustrokeWidth - strokeWidth) / 2, Path.Direction.CCW);
final float dx = Math.abs(x - pathX);
final float dy = Math.abs(y - pathY);
//兩點之間的距離大于等于3時,生成貝塞爾繪制曲線
if (dx >= 3 || dy >= 3) {
//設置貝塞爾曲線的操作點為起點和終點的一半
float cX = (x + pathX) / 2;
float cY = (y + pathY) / 2;
//二次貝塞爾,實現平滑曲線;previousX, previousY為操作點,cX, cY為終點
mPath.quadTo(pathX, pathY, cX, cY);
transparentPath.quadTo(pathX, pathY, cX, cY);
//第二次執行時,第一次結束調用的坐標值將作為第二次調用的初始坐標值
pathX = x;
pathY = y;
}
}
private void touchDown(MotionEvent event) {
reset();
pathX = event.getX();
pathY = event.getY();
startX = event.getX();
startY = event.getY();
mPath.moveTo(pathX, pathY);
transparentPath.moveTo(pathX, pathY);
isActionUp = true;
}
/**
* 清除所有內容
*/
public void reset() {
mPath.reset();
transparentPath.reset();
pathCircle.reset();
init();
isDrwable = false;
}
public Boolean getClose() {
return isClose;
}
/**
* 是否只是閉合狀態
*
* @param close
*/
public void setClose(Boolean close) {
isClose = close;
}
public int getPaintColor() {
return paintColor;
}
/**
* 設置畫筆顏色
*
* @param paintColor
*/
public void setPaintColor(int paintColor) {
this.paintColor = paintColor;
}
public int getPaintTransparentColor() {
return paintTransparentColor;
}
/**
* 設置粗畫筆顏色
*
* @param paintTransparentColor
*/
public void setPaintTransparentColor(int paintTransparentColor) {
this.paintTransparentColor = paintTransparentColor;
}
public int getCloseX() {
return closeX;
}
/**
* 設置閉合X距離
*
* @param closeX
*/
public void setCloseX(int closeX) {
this.closeX = closeX;
}
public int getCloseY() {
return closeY;
}
/**
* 設置閉合Y距離
*
* @param closeY
*/
public void setCloseY(int closeY) {
this.closeY = closeY;
}
/**
* 當前閉合狀態
*
* @return
*/
public Boolean getStatus() {
return isStatus;
}
public void setStatus(Boolean status) {
isStatus = status;
}
public float getStrokeWidth() {
return strokeWidth;
}
/**
* 設置線條寬度
*
* @param strokeWidth
*/
public void setStrokeWidth(float strokeWidth) {
this.strokeWidth = strokeWidth;
mPaint.setStrokeWidth(strokeWidth);
}
public float getXustrokeWidth() {
return xustrokeWidth;
}
/**
* 設置第二條線寬度
*
* @param xustrokeWidth
*/
public void setXustrokeWidth(float xustrokeWidth) {
this.xustrokeWidth = xustrokeWidth;
transparentPaint.setStrokeWidth(xustrokeWidth);
}
/**
* 設置第二條線透明度
*
* @param alpha
*/
public void setAlpha(int alpha) {
this.alpha = alpha;
transparentPaint.setAlpha(alpha);
}
public void setOnFinishListener(OnFinishListener listener) {
this.listener = listener;
}
public interface OnFinishListener {
public void onFinish(Region p);
}
}
其中最主要的幾個方法,第一:init()方法,初始化畫筆,設置透明度等等。第二:onTouchEvent()方法會獲取用戶的點擊事件,用戶點擊調用touchDown()方法,調用完成之后用戶開始進行“畫”,畫就會調用移動方法touchMove(),當移動完成就抬起手指運行ACTION_UP之后的代碼。
我們直接開始說touchMove()方法:
根據移動的X,Y坐標與上一個點位的X,Y坐標進行相減取絕對值,如果絕對值大于3就進行path路徑的添加,不過這里需要注意的是,添加的路徑是貝塞爾曲線,沒有用lineto,而是用的quadTo,主要是為了解決路徑不平滑問題。
下面就是ACTION_UP,手指抬起運行的代碼:
第一先判斷,當前是否只需要閉合一種狀態,如果只需要這種狀態,進行路徑閉合狀態的關閉
path.close();//路徑閉合方法,如果起點和結束點沒有關閉調用此方法會直接將起點和結束的連線
然后進行路徑的區域獲取
RectF r = new RectF();//添加一個矩形
mPath.computeBounds(r, true);//設置路徑的區域為矩形的區域
最后將路徑的區域和矩形的區域取交集,也就是閉合區域的路徑
region.setPath(mPath, new Region((int) r.left, (int) r.top, (int) r.right, (int) r.bottom));
同理,非閉合區域狀態一樣判斷。
MainActivity代碼:
package com.example.lenovo.enclosure;
import android.graphics.Bitmap;
import android.graphics.Point;
import android.graphics.Region;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import com.baidu.mapapi.SDKInitializer;
import com.baidu.mapapi.map.BitmapDescriptor;
import com.baidu.mapapi.map.BitmapDescriptorFactory;
import com.baidu.mapapi.map.GroundOverlayOptions;
import com.baidu.mapapi.map.MapView;
import com.baidu.mapapi.map.OverlayOptions;
import com.baidu.mapapi.model.LatLng;
import com.baidu.mapapi.model.LatLngBounds;
public class MainActivity extends AppCompatActivity implements PathView.OnFinishListener{
private PathView pview;
private MapView bmapView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
SDKInitializer.initialize(getApplicationContext());
setContentView(R.layout.activity_main);
bmapView = (MapView) findViewById(R.id.bmapView);
pview = (PathView) findViewById(R.id.pview);
pview.setOnFinishListener(this);
}
@Override
public void onFinish(Region p) {
// p.contains(x,y)//閉合區域 判斷用是否在區域里面
pview.setVisibility(View.GONE);
Point point = new Point(0, 0);
Point point1 = new Point(pview.getWidth(), pview.getHeight());
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(getViewBitmap(pview));
LatLng southwest = bmapView.getMap().getProjection().fromScreenLocation(point);
LatLng northeast = bmapView.getMap().getProjection().fromScreenLocation(point1);
LatLngBounds bounds = new LatLngBounds.Builder().include(northeast).include(southwest).build();
OverlayOptions path = new GroundOverlayOptions().positionFromBounds(bounds).image(bitmapDescriptor).zIndex(9);
bmapView.getMap().addOverlay(path);
}
/**
* 將View轉換為圖片
* @param v
* @return
*/
private Bitmap getViewBitmap(View v) {
v.clearFocus();
v.setPressed(false);
boolean willNotCache = v.willNotCacheDrawing();
v.setWillNotCacheDrawing(false);
// Reset the drawing cache background color to fully transparent
// for the duration of this operation
int color = v.getDrawingCacheBackgroundColor();
v.setDrawingCacheBackgroundColor(0);
if (color != 0) {
v.destroyDrawingCache();
}
v.buildDrawingCache();
Bitmap cacheBitmap = v.getDrawingCache();
if (cacheBitmap == null) {
Log.e("Folder", "failed getViewBitmap(" + v + ")", new RuntimeException());
return null;
}
Bitmap bitmap = Bitmap.createBitmap(cacheBitmap);
// Restore the view
v.destroyDrawingCache();
v.setWillNotCacheDrawing(willNotCache);
v.setDrawingCacheBackgroundColor(color);
return bitmap;
}
}
這里需要注意的是onFinish方法,這是當繪制狀態結束之后調用(即手指抬起之后),然后隱藏布局文件的自定義控件,然后再將這個自定義控件繪制完成的view轉換為圖片,并且添加到mapview上面。
布局文件activity_main:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<com.baidu.mapapi.map.MapView
android:id="@+id/bmapView"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<com.example.lenovo.enclosure.PathView
android:id="@+id/pview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
如有紕漏,盡請批正。謝!
如果你喜歡文摘,恰好你也看了我寫的文摘,又恰巧你喜歡專研技術,那么為什么不加入我們那。(QQ群:417487178)歡迎交流。