Android自定义LocationMarker的实现详解

目录
  • 自定义View LocationMarker
  • 应用自定义View到AMapView中

今天讲一个比较简单的东西自定义绘制Marker 其实就是自定义view, 跟轨迹没太多关联,还有轨迹源码在文末分享出来,对您有帮助的话给个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);

      //内部圆的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。

以上就是Android自定义LocationMarker的实现详解的详细内容,更多关于Android自定义LocationMarker的资料请关注我们其它相关文章!

(0)

相关推荐

  • android studio 使用Mocklocation虚拟定位

    首先需要在 AndroidManifest.xml 文件中添加「获取模拟定位信息」权限. <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/> <uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="and

  • Android Location服务之LocationManager案例详解

    上次介绍了位置服务中的Geocoder,这次就来介绍一下LocationManager.LocationManager系统服务是位置服务的核心组件,它提供了一系列方法来处理与位置相关的问题,包括查询上一个已知位置.注册和注销来自某个LocationProvider的周期性的位置更新.注册和注销接近某个坐标时对一个已定义的Intent的触发等.今天我们就一起探讨一下LocationManager的简单应用. 在进入正题之前,朋友们需要了解与LocationManager相关的两个知识点: prov

  • 用Android Location获取当前地理位置的方法

    在Android应用中,往往有获取当前地理位置的需求,比如微信获取附近的人需要获取用户当前的位置,不多说,直接上例子. 复制代码 代码如下: public Location getLocation() {// 获取Location通过LocationManger获取!  LocationManager locManger = (LocationManager) getSystemService(Context.LOCATION_SERVICE);  Location loc = locMange

  • Android开发之Location用法实例分析

    本文实例讲述了Android开发中Location用法.分享给大家供大家参考,具体如下: Location 在Android 开发中还是经常用到的,如通过经纬度获取天气,根据Location 获取所在地区详细Address (比如Google Map 开发)等.而在Android 中通过LocationManager来获取Location .通常获取Location 有GPS 获取,WIFI 获取. 这边介绍一个简单的小Demo ,来教大家如何获取Location ,从而获取经纬度. 第一步:创

  • Android原生定位服务LocationManager

    目录 前言 一.LocationManager的使用 二.混合定位 总结 前言 现在的应用,几乎每一个 App 都存在定位的逻辑,方便更好的推荐产品或服务,获取当前设备的经纬度是必备的功能了.有些 App 还是以LBS(基于位置服务)为基础来实现的,比如美团,饿了吗,不获取到位置都无法使用的. 有些同学觉得不就是获取到经纬度么,Android 自带的就有位置服务 LocationManager ,我们无需引入第三方服务,就可以很方便的实现定位逻辑. 确实 LocationManager 的使用很

  • Android自定义LocationMarker的实现详解

    目录 自定义View LocationMarker 应用自定义View到AMapView中 今天讲一个比较简单的东西自定义绘制Marker 其实就是自定义view, 跟轨迹没太多关联,还有轨迹源码在文末分享出来,对您有帮助的话给个star呗. 如下面的gif中的轨迹中的LocationMarker 自定义View LocationMarker 主要包括绘制水滴状,绘制圆.绘制文字,绘制底部椭圆阴影,主要是绘制水滴状,这里用的贝塞尔三阶绘制,首先直接看代码: public class Locati

  • Android 自定义标题栏的实例详解

     Android 自定义标题栏的实例详解 开发 Android APP 经常会用到自定义标题栏,而有多级页面的情况下还需要给自定义标题栏传递数据. 本文要点: 自定义标题填充不完整 自定义标题栏返回按钮的点击事件 一.代码 这里先介绍一下流程: 1. 创建一个标题栏布局文件 mytitlebar.xml 2. 在style.xml中创建 mytitlestyle 主题 3. 创建类 CustomTitleBar 4. 在需要自定义标题栏的Activity的OnCreate方法中实例化 Custo

  • Android自定义单例AlertDialog详解

    当Android开发处理错误信息时,经常会以Dialog的形式显示错误信息,但是每次都new一个Dialog,很麻烦,也增加程序的开销,所以今天就分享一种自定义单例AlertDialog public class AlertDialog { private static AlertDialog alertDialog = null; private Context context; private Dialog dialog; private LinearLayout lLayout_bg; p

  • Android自定义Toolbar使用方法详解

    本篇文章介绍: 如何使用Toolbar; 自定义Toolbar; 先来看一看效果,了解一下toolbar: 布局文件: <android.support.v7.widget.Toolbar android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" android:background="@co

  • Android自定义ViewGroup(侧滑菜单)详解及简单实例

    自定义侧滑菜单的简单实现 不少APP中都有这种侧滑菜单,例如QQ这类的,比较有名开源库如slidingmenu. 有兴趣的可以去研究研究这个开源库. 这里我们将一种自己的实现方法,把学习的 东西做个记录,O(∩_∩)O! 首先看效果图: 这里我们实现的侧滑菜单,是将左侧隐藏的菜单和主面板看作一个整体来实现的,而左侧隐藏的菜单和主面板相当于是这个自定义View的子View. 首先来构造该自定义View的布局: 自定义的SlideMenuView包含两个子view,一个是menuView,另一个是m

  • Android自定义动态壁纸开发详解

    看到有些手机酷炫的动态壁纸,有没有好奇过他们是如何实现的,其实我们自己也可以实现. 一.动态壁纸原理 如果你了解使用过SurfaceView的话,那么开发一款动态壁纸对你来说其实非常简单. 动态壁纸的本质其实就是一个服务在维护一个动态壁纸引擎Engine,所以我们看到的动态效果其实是通过这个引擎画出来的.而维护这个引擎的服务,就是WallpaperService.本篇文章并不讨论内部实现原理,只是让大家知道如何去实现动态壁纸,所以就不详细说了. 二.实现动态壁纸 大体上可分为三个步骤: 创建自定

  • Android自定义View弹性滑动Scroller详解

    本文实例为大家分享了Android弹性滑动类Scroller的具体代码,供大家参考,具体内容如下 Scroller是什么 Scroller就是一个滑动帮助类.它并不可以使View真正的滑动,而是配合scrollTo/ScrollBy让view产生缓慢的滑动,产生动画的效果,其实和属性动画是同一个原理.在我看来,Scroller跟属性动画的平移的效果是一样的. 如何使用 //①实例一个Scroller,它有三个构造方法如下 //public Scroller (Context context) /

  • Android自定义抛出异常的方法详解

    前言 在android开发过程中,我们经常遇到异常的问题,崩溃抛出异常的时候,是非常令人烦闷的.但是异常有一个好处,使得app能在编译的时候给我们提供一些bug信息,有时可能比较模糊,有时可能很精准,甚至提示报错行.基于这一点,今天我们就来讲讲android中的异常吧. 今天要讲的内容: throw 和 throws 异常类型 仿写异常及其好处 一. throw 和 throws 异常通常的处理方式有 throw/throws 以及 try-catch 两种.今天我们主要讲解throw/thro

  • Android Studio 中aidl的自定义类的使用详解

    自己折腾了好久,记录一下. service端: 1:创建类Dog,需要实现Parcelable接口: 2:aidl下创建 Dog.aidl,里面两句话就可以了 (1)package s包名; (2)parcelable Dog; 3:interface.aidl引入Dog类, import s包名.Dog; Client 端: 1:创建类Dog,需要实现Parcelable接口: 2:aidl下创建 Dog.aidl, (1)package c包名; (2)parcelable Dog; 注意:

  • Android开发之自定义加载动画详解

    目录 一.demo简介 二.分析贪吃动画的尺寸比例 三.画圆 四.实现张嘴闭嘴动画 五.小球移动动画 一.demo简介 1.效果展示如下图,我截了三个瞬间,但其实这是一个连续的动画,就是这个大圆不停地吞下小圆. 2.这个动画可以拆分为两部分,首先是大圆张嘴闭嘴的动画,相当于画一个圆弧,规定一下它的角度就好.小圆就是一个从右向左移动的动画.然后不停地刷新界面,让动画的持续时间为永恒,这样就会有一个持续的动态效果. 二.分析贪吃动画的尺寸比例 1.在制作动画之前,我们要先建一个模型,来确定一下大圆和

随机推荐