Android自定義LocationMarker的實(shí)現(xiàn)詳解
今天講一個比較簡單的東西自定義繪制Marker 其實(shí)就是自定義view, 跟軌跡沒太多關(guān)聯(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);//基線中間點(diǎn)的y軸計(jì)算公式 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); //設(shè)置 p1 底部左右兩個點(diǎn)的y值 p1.y += radius * 0.2f * 1.05f;//設(shè)置 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屬于圓的上下兩點(diǎn) 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; } }
簡單的講一下,主要通過三階貝塞爾曲線來繪制圓,在圓的基礎(chǔ)上,對Bottom方向的P1(包含 p1本身以及l(fā)eft、right兩個control點(diǎn))拉升,類似行星跟衛(wèi)星的潮汐引力在朝下拉升的方向形成水滴形的尖角。
這里left、top、right、bottom四個方向,每個方向共三個點(diǎn),共同確定這個圓。
VPoint 代表垂直方向的 left、right,這里是 p2跟p4 分表包含三個點(diǎn),三點(diǎn)成線跟圓的左邊、右邊相切。
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上的三個點(diǎn)。
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); } }
其它的見代碼注釋。
應(yīng)用自定義View到AMapView中
這里就直接參考高德的demo,需要注意一點(diǎn)的是,在加載自定義的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); //設(shè)置動畫時間 單位毫秒 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)化。
代碼請前往 運(yùn)動軌跡, 代碼包含了很多圖表Chart的代碼沒有分開,有空對這個庫做個系列,因?yàn)镸AAndroidChart沒法滿足需求做的庫, 代碼中需要自己去高德平臺上注冊Key。
以上就是Android自定義LocationMarker的實(shí)現(xiàn)詳解的詳細(xì)內(nèi)容,更多關(guān)于Android自定義LocationMarker的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
Android系統(tǒng)中的藍(lán)牙連接程序編寫實(shí)例教程
這篇文章主要介紹了Android系統(tǒng)中的藍(lán)牙連接程序編寫實(shí)例教程,包括藍(lán)牙的設(shè)備查找及自動配對等各種基礎(chǔ)功能的實(shí)現(xiàn),十分給力,需要的朋友可以參考下2016-04-04Android自定義控件實(shí)現(xiàn)底部菜單(上)
這篇文章主要為大家詳細(xì)介紹了Android自定義控件實(shí)現(xiàn)底部菜單的相關(guān)資料,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-01-01Android 通過onDraw實(shí)現(xiàn)在View中繪圖操作的示例
以下是對Android通過onDraw實(shí)現(xiàn)在View中繪圖操作的示例代碼進(jìn)行了詳細(xì)的分析介紹,需要的朋友可以過來參考下2013-07-07Android基礎(chǔ)控件RadioGroup使用方法詳解
這篇文章主要為大家詳細(xì)介紹了Android基礎(chǔ)控件RadioGroup的使用方法,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2019-11-11Android使用Room數(shù)據(jù)庫解決本地持久化的操作
Room 是一個持久性庫,屬于 Android Jetpack 的一部分,Room 是 SQLite 數(shù)據(jù)庫之上的一個抽象層,Room 并不直接使用 SQLite,而是負(fù)責(zé)簡化數(shù)據(jù)庫設(shè)置和配置以及與數(shù)據(jù)庫交互方面的瑣碎工作,本文介紹了Android使用Room數(shù)據(jù)庫解決本地持久化的操作,需要的朋友可以參考下2024-09-09Android ListView實(shí)現(xiàn)上拉加載更多和下拉刷新功能
這篇文章主要為大家詳細(xì)介紹了Android ListView實(shí)現(xiàn)上拉加載更多和下拉刷新功能,介紹了ListView刷新原理及實(shí)現(xiàn)方法,感興趣的小伙伴們可以參考一下2016-05-05Android Zipalign工具優(yōu)化Android APK應(yīng)用
本文主要介紹Android Zipalign工具優(yōu)化Android APK應(yīng)用,這里整理了相關(guān)資料及簡單優(yōu)化實(shí)例,有需要的小伙伴可以參考下2016-09-09