Android自定義LocationMarker的實現(xiàn)詳解
今天講一個比較簡單的東西自定義繪制Marker 其實就是自定義view, 跟軌跡沒太多關聯(lián),還有軌跡源碼在文末分享出來,對您有幫助的話給個star唄。
如下面的gif中的軌跡中的LocationMarker

自定義View LocationMarker
主要包括繪制水滴狀,繪制圓、繪制文字,繪制底部橢圓陰影,主要是繪制水滴狀,這里用的貝塞爾三階繪制,首先直接看代碼:
public class LocationMarker extends View {
private Path mPath;
private Paint mFillCirclePaint;
private Paint mTextPaint;
private VPoint p2;
private VPoint p4;
private HPoint p1;
private HPoint p3;
private Context mContext;
private float c;
private float blackMagic = 0.551915024494f;
private int wrapperColor;
private int circleColor;
private int radius = DisplayUtil.dip2px(20);
private String mMilePost;
private int textSize = 13;
private boolean drawBottomShader;
public LocationMarker(Context context, int radius, String milePost, int textSize) {
this(context, null);
this.mContext = context;
this.radius = radius;
this.mMilePost = milePost;
this.textSize = textSize;
init();
}
public LocationMarker(Context context, AttributeSet attrs) {
this(context, attrs, 0);
this.mContext = context;
init();
}
public LocationMarker(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
this.mContext = context;
init();
}
/**
* 初始化操作
*/
private void init() {
mFillCirclePaint = new Paint();
mFillCirclePaint.setColor(0xFFFFFFFF);
mFillCirclePaint.setStyle(Paint.Style.FILL);
mFillCirclePaint.setStrokeWidth(1);
mFillCirclePaint.setAntiAlias(true);
mPath = new Path();
p2 = new VPoint();
p4 = new VPoint();
p1 = new HPoint();
p3 = new HPoint();
c = radius * blackMagic;
initTextPain();
wrapperColor = R.color.location_wrapper;
circleColor = R.color.location_inner_circle;
}
public void setColors(int wrapperColorResource, int circleColorResource){
this.wrapperColor = wrapperColorResource;
this.circleColor = circleColorResource;
}
private void initTextPain() {
mTextPaint = new Paint();
mTextPaint.setColor(0xFFFFFFFF);
mTextPaint.setStyle(Paint.Style.FILL);
mTextPaint.setStrokeWidth(1);
mTextPaint.setAntiAlias(true);
mTextPaint.setTypeface(Typeface.DEFAULT_BOLD);
mTextPaint.setTextSize(DisplayUtil.sp2px(mContext, textSize));
}
@Override
protected void onDraw(Canvas canvas) {
mPath.reset();
canvas.translate(getWidth() / 2, getHeight() / 2);
drawWaterDrop(canvas, radius);
}
private void drawWaterDrop(Canvas canvas, int radius) {
canvas.save();
Path path = getPath(radius);
//內(nèi)部圓的path
Path circle = new Path();
circle.addCircle(p3.x, p3.y + radius, radius - radius / 5, Path.Direction.CCW);
//去鋸齒
canvas.setDrawFilter(new PaintFlagsDrawFilter(0, Paint.ANTI_ALIAS_FLAG | Paint.FILTER_BITMAP_FLAG));
drawBottomOval(canvas);
//繪制外部的水滴狀
drawBezierPath(canvas, ColorUtil.getResourcesColor(mContext, wrapperColor), path);
//繪制內(nèi)部的圓
drawBezierPath(canvas, ColorUtil.getResourcesColor(mContext, circleColor), circle);
drawText(canvas, mMilePost);
canvas.restore();
}
//繪制底部的陰影,drawBottomShader控制是否顯示陰影
private void drawBottomOval(Canvas canvas){
if (drawBottomShader){
RectF rectF = new RectF();
float width = DisplayUtil.dip2px(12);
float height = DisplayUtil.dip2px(4);
rectF.set(p1.x - width/2, p1.y - height/2, p1.x + width/2, p1.y + height/2);
int color = mFillCirclePaint.getColor();
mFillCirclePaint.setColor(ColorUtil.getResourcesColor(mContext, R.color.location_bottom_shader));
canvas.drawOval(rectF, mFillCirclePaint);
mFillCirclePaint.setColor(color);
}
}
//繪制Marker中心的文字
private void drawText(Canvas canvas, String mileStr) {
RectF rectF = new RectF();
float width = mTextPaint.measureText(mileStr);
float rectFLeft = p3.x - width / 2 ;
float rectFRight = p3.x + width / 2 ;
float rectHeight = TextUtil.getTxtHeight1(mTextPaint);
float rectTop = p2.y + DisplayUtil.dip2px(2);//調(diào)整位置,看起來居中
float rectBottom = p2.y + rectHeight;
rectF.set(rectFLeft, rectTop, rectFRight, rectBottom);
Paint.FontMetrics fontMetrics = mTextPaint.getFontMetrics();
float top = fontMetrics.top;//為基線到字體上邊框的距離,即上圖中的top
float bottom = fontMetrics.bottom;//為基線到字體下邊框的距離,即上圖中的bottom
int baseLineY = (int) (rectF.centerY() + (top + bottom) / 2);//基線中間點的y軸計算公式
canvas.drawText(mileStr, rectF.left, baseLineY, mTextPaint);
}
/**
* 畫圓
*/
private Path getPath(int radius) {
CircleModel(radius);
Path path = new Path();
p1.setY(p1.y + radius * 0.2f * 1.05f); //設置 p1 底部左右兩個點的y值
p1.y += radius * 0.2f * 1.05f;//設置 p1 自己的y值
path.moveTo(p1.x, p1.y);
path.cubicTo(p1.right.x, p1.right.y, p2.bottom.x, p2.bottom.y, p2.x, p2.y);
path.cubicTo(p2.top.x, p2.top.y, p3.right.x, p3.right.y, p3.x, p3.y);
path.cubicTo(p3.left.x, p3.left.y, p4.top.x, p4.top.y, p4.x, p4.y);
path.cubicTo(p4.bottom.x, p4.bottom.y, p1.left.x, p1.left.y, p1.x, p1.y);
path.close();
return path;
}
private void drawBezierPath(Canvas canvas, int color, Path path) {
int colorOrigin = mFillCirclePaint.getColor();
mFillCirclePaint.setColor(color);
canvas.drawPath(path, mFillCirclePaint);
mFillCirclePaint.setColor(colorOrigin);
}
private void CircleModel(int radius) {
c = radius * blackMagic;
p1.setY(radius);//右邊
p3.setY(-radius);// 左邊
p3.x = p1.x = 0;//圓心
p3.left.x = -c;
p3.right.x = c;
p1.left.x = -c * 0.36f;
p1.right.x = c * 0.36f;
//p1.p3屬于圓的上下兩點
p2.setX(radius); // 下邊
p4.setX(-radius);// 上邊
p2.y = p4.y = 0;// 圓心
p2.top.y = p4.top.y = -c;
p2.bottom.y = p4.bottom.y = c;
}
public void setDrawBottomShader(boolean drawBottomShader) {
this.drawBottomShader = drawBottomShader;
}
}簡單的講一下,主要通過三階貝塞爾曲線來繪制圓,在圓的基礎上,對Bottom方向的P1(包含 p1本身以及l(fā)eft、right兩個control點)拉升,類似行星跟衛(wèi)星的潮汐引力在朝下拉升的方向形成水滴形的尖角。
這里left、top、right、bottom四個方向,每個方向共三個點,共同確定這個圓。
VPoint 代表垂直方向的 left、right,這里是 p2跟p4 分表包含三個點,三點成線跟圓的左邊、右邊相切。
public class VPoint {
public float x;
public float y;
public PointF top = new PointF();
public PointF bottom = new PointF();
public void setX(float x) {
this.x = x;
top.x = x;
bottom.x = x;
}
public void adjustY(float offset) {
top.y -= offset;
bottom.y += offset;
}
public void adjustAllX(float offset) {
this.x += offset;
top.x += offset;
bottom.x += offset;
}
public void adjustAllY(float offset) {
this.y += offset;
top.y += offset;
bottom.y += offset;
}
public void adjustAllXY(float x, float y) {
adjustAllX(x);
adjustAllY(y);
}
}同樣, HPoint 代表水邊方向圓的切線,p1跟p3代表Bottom、Top上的三個點。
public class HPoint {
public float x;
public float y;
public PointF left = new PointF();
public PointF right = new PointF();
public void setY(float y) {
this.y = y;
left.y = y;
right.y = y;
}
public void adjustAllX(float offset) {
this.x += offset;
left.x += offset;
right.x += offset;
}
public void adjustAllY(float offset) {
this.y += offset;
left.y += offset;
right.y += offset;
}
public void adjustAllXY(float x, float y) {
adjustAllX(x);
adjustAllY(y);
}
}其它的見代碼注釋。
應用自定義View到AMapView中
這里就直接參考高德的demo,需要注意一點的是,在加載自定義的LocationMarker時,我在LocationMarker的外層包了兩層父View,試過了一層顯示不出來
private void addMarker(LatLng position, String displayStr,int radius, int textSize, int wrapperColor, int circleColor, boolean showBottomShader){
View view = View.inflate(RecordCorrectShowActivity.this, R.layout.custom_location_view, null);
RelativeLayout locationContainer = view.findViewById(R.id.locationContainer);
LocationMarker locationMarker = new LocationMarker(mMapView.getContext(),
DisplayUtil.dip2px(radius), displayStr, textSize);
locationMarker.setColors(wrapperColor, circleColor);
locationMarker.setDrawBottomShader(showBottomShader);
locationContainer.addView(locationMarker);
BitmapDescriptor markerIcon = BitmapDescriptorFactory.fromView(view);
MarkerOptions optionPosition = new MarkerOptions()
.position(position)
.icon(markerIcon);
Marker marker = mAMap.addMarker(optionPosition);
Animation markerAnimation = new ScaleAnimation(0, 1, 0, 1); //初始化生長效果動畫
markerAnimation.setDuration(1000); //設置動畫時間 單位毫秒
marker.setAnimation(markerAnimation);
marker.startAnimation();
}兩層父view的
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/locationContainer"
android:layout_width="50dp"
android:layout_height="55dp"/>
</LinearLayout>下次講優(yōu)化軌跡路徑,從采集時到最后的優(yōu)化。
代碼請前往 運動軌跡, 代碼包含了很多圖表Chart的代碼沒有分開,有空對這個庫做個系列,因為MAAndroidChart沒法滿足需求做的庫, 代碼中需要自己去高德平臺上注冊Key。
以上就是Android自定義LocationMarker的實現(xiàn)詳解的詳細內(nèi)容,更多關于Android自定義LocationMarker的資料請關注腳本之家其它相關文章!
相關文章
Android 通過onDraw實現(xiàn)在View中繪圖操作的示例
以下是對Android通過onDraw實現(xiàn)在View中繪圖操作的示例代碼進行了詳細的分析介紹,需要的朋友可以過來參考下2013-07-07
Android使用Room數(shù)據(jù)庫解決本地持久化的操作
Room 是一個持久性庫,屬于 Android Jetpack 的一部分,Room 是 SQLite 數(shù)據(jù)庫之上的一個抽象層,Room 并不直接使用 SQLite,而是負責簡化數(shù)據(jù)庫設置和配置以及與數(shù)據(jù)庫交互方面的瑣碎工作,本文介紹了Android使用Room數(shù)據(jù)庫解決本地持久化的操作,需要的朋友可以參考下2024-09-09
Android ListView實現(xiàn)上拉加載更多和下拉刷新功能
這篇文章主要為大家詳細介紹了Android ListView實現(xiàn)上拉加載更多和下拉刷新功能,介紹了ListView刷新原理及實現(xiàn)方法,感興趣的小伙伴們可以參考一下2016-05-05
Android Zipalign工具優(yōu)化Android APK應用
本文主要介紹Android Zipalign工具優(yōu)化Android APK應用,這里整理了相關資料及簡單優(yōu)化實例,有需要的小伙伴可以參考下2016-09-09

