内容简介:今天讲一个比较简单的东西自定义绘制Marker 其实就是自定义view, 跟轨迹没太多关联,还有轨迹源码在文末分享出来,对您有帮助的话给个star呗。如下面的视频中的轨迹中的LocationMarker主要包括绘制水滴状,绘制圆、绘制文字,绘制底部椭圆阴影,主要是绘制水滴状,这里用的贝塞尔三阶绘制,首先直接看代码:
今天讲一个比较简单的东西自定义绘制Marker 其实就是自定义view, 跟轨迹没太多关联,还有轨迹源码在文末分享出来,对您有帮助的话给个star呗。
如下面的视频中的轨迹中的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);
//内部圆的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);
//绘制内部的圆
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);//调整位置,看起来居中
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本身以及left、right两个control点)拉升,类似行星跟卫星的潮汐引力在朝下拉升的方向形成水滴形的尖角。
这里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>
复制代码
下次讲优化轨迹路径,从采集时到最后的优化。
代码请前往 运动轨迹 , 代码包含了很多图表Chart的代码没有分开,有空对这个库做个系列,因为MAAndroidChart没法满足需求做的库。 代码中需要自己去高德平台上注册Key,可以参考高德的文档,也可以去参考Amap地图轨迹
以上所述就是小编给大家介绍的《运动App 自定义LocationMarker》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- CVPR 2019 | 条件运动传播:从运动中学习物体性质
- 敏捷运动发起人马丁·福勒认为当前敏捷运动是一场悲剧
- 运动模糊滤镜
- 原生JavaScript之完美运动框架
- RMS 称开源运动是非道德的
- RMS 称开源运动是非道德的
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
The Nature of Code
Daniel Shiffman / The Nature of Code / 2012-12-13 / GBP 19.95
How can we capture the unpredictable evolutionary and emergent properties of nature in software? How can understanding the mathematical principles behind our physical world help us to create digital w......一起来看看 《The Nature of Code》 这本书的介绍吧!