Android实现绘制LocationMarkerView图的示例代码

 更新时间:2023年02月10日 14:07:29   作者:cxy107750  
LocationMarker是运动轨迹上Start、End, 以及整公里点上笔者自定义绘制的一个MarkerView。这篇文章主要介绍了Android实现绘制LocationMarkerView图的示例代码,希望对大家有所帮助

LocationMarker是运动轨迹上Start、End, 以及整公里点上笔者自定义绘制的一个MarkerView, 当时之所以没有用设计给的icon是这个MarkerView里需要填充动态的数字,自定义的话自主性比较大些也方面做动画,之前的Android 传统自定义View的实现可以看这篇文章介绍 运动App自定义LocationMarker

这里先看下gif动图:

LocationMarkerView图的绘制

绘制方面基本没有太多的逻辑,通过Compose的自定义绘制Canvas() 绘制 一个构建的Path,生成View的Path其实是主要的实现过程。

Canvas(modifier = Modifier.size(0.dp)){
  drawPath(AndroidPath(markerViewPath), color = color)
  drawPath(AndroidPath(bottomOval), color = colorOval)
}

这里Compose的path,还有好些接口对不上以及缺少API,所以通过AndroidPath(nativepath)接口进行转化进行绘制,bottomOval是 Start、End点底部阴影的Path。生成markerViewPath以及bottomOval的逻辑都在LocationMarker类中,LocationMarker主要包含了上下两套点 p1、p3(HPoint), 左右两套点p2、p4(VPoint), 以及绘制View的参数属性集合类MarkerParams.

获取markerViewPath, 首先给p1、p3(HPoint),p2、p4(VPoint)中8个点设置Value值,circleModel(radius),然后从底部p1底部点逆时针转圈依次调用三阶贝塞尔函数接口,最后close实现水滴倒置状态的Path,见实现:

fun getPath(radius: Float): Path{
  circleModel(radius)
  val path = Path()
  p1.setYValue(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()
  val circle = Path()
  circle.addCircle(p3.x, p3.y + radius, markerParams.circleRadius.value, Path.Direction.CCW)
  path.op(circle, Path.Op.DIFFERENCE)
  return path
}

拿到相应的Path后,在Composeable函数里进行如上所述的绘制Path即可:

val locationMarker = LocationMarker(markerParams)
val markerViewPath = locationMarker.getPath(markerParams.radius.value)
val bottomOval = locationMarker.getBottomOval()
val color = colorResource(id = markerParams.wrapperColor)
val colorOval = colorResource(R.color.location_bottom_shader)
​
Canvas(modifier = Modifier.size(0.dp)){
  drawPath(AndroidPath(markerViewPath), color = color)
  drawPath(AndroidPath(bottomOval), color = colorOval)
}

绘制整公里的文字

Compose的Canvas 里目前的Version并不支持drawText的绘制,不过开放了一个调用原始drawText的转换API, 原始的drawText 是需要Paint参数的, 同时依赖Paint来计算Text 对应RectF的Height值,这里Paint()是Compose的一个Paint,需要调用asFrameworkPaint() 进行转化

val paint = Paint().asFrameworkPaint().apply {
  setColor(-0x1)
  style = android.graphics.Paint.Style.FILL
  strokeWidth = 1f
  isAntiAlias = true
  typeface = Typeface.DEFAULT_BOLD
  textSize = markerParams.txtSize.toFloat()
}

计算Text 绘制依赖的RectF,并将rectF.left作为drawText的X值,同时计算drawText的基线 baseLineY,最后传入nativeCanvas.drawText() 接口进行绘制。

val rectF = createTextRectF(locationMarker, paint, markerParams)
val baseLineY = getTextBaseY(rectF, paint)
Canvas(modifier = Modifier.size(0.dp)){
  drawIntoCanvas {
    it.nativeCanvas.drawText(markerParams.markerStr,  rectF.left, baseLineY, paint)
  }
}

drawText获取绘制基线 baseLineY的工具类方法:

fun getTextBaseY(rectF: RectF, paint: Paint): Float {
    val fontMetrics = paint.fontMetrics
    return rectF.centerY() - fontMetrics.top / 2 - fontMetrics.bottom / 2
}

添加动画

这里简单的用一个放大的动画实现,跟原始的高德地图、Mapbox地图的一个growth过程的一个动画有些差距的,暂且先这样实现吧。首先是定义两个radius相关的State对象,具体来说是Proxy, 以及一个动画生长的大小控制的Float的变量Fraction,再通过自定义animateDpAsState作为 animation值的对象,最终给到MarkParams作为参数,animation值的变化,会导致MarkParams的变化,最后导致Recompose,形成动画。

  val circleRadius by rememberSaveable{ mutableStateOf(25) }
  val radius by rememberSaveable{ mutableStateOf(60) }
  var animatedFloatFraction by remember { mutableStateOf(0f) }
  val radiusDp by animateDpAsState(
    targetValue = (radius * animatedFloatFraction).dp,
    animationSpec = tween(
      durationMillis = 1000,
      delayMillis = 500,
      easing = LinearOutSlowInEasing
    )
  )
​
  val circleRadiusDp by animateDpAsState(
    targetValue = (circleRadius * animatedFloatFraction).dp,
    animationSpec = tween(
      durationMillis = 1000,
      delayMillis = 500,
      easing = LinearOutSlowInEasing
    )
  )
​
  val markerParams by remember {
    derivedStateOf { MarkerParams(radiusDp, circleRadiusDp, wrapperColor = wrapperColor) }
  }

Compose 自定义View LocationMarkerView 主要通过drawPath,以及调用原生的drawText, 最后添加了一个scale类似的动画实现,最终实现运动轨迹里的一个小小的View的实现。

代码见:https://github.com/yinxiucheng/compose-codelabs/ 下的CustomerComposeView

到此这篇关于Android实现绘制LocationMarkerView图的示例代码的文章就介绍到这了,更多相关Android绘制LocationMarkerView图内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Android实现秒表功能

    Android实现秒表功能

    这篇文章主要为大家详细介绍了Android实现简易秒表功能,具备启停功能,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-09-09
  • Android实现蒙板效果

    Android实现蒙板效果

    这篇文章主要为大家详细介绍了Android实现蒙板效果的关键性代码,感兴趣的小伙伴们可以参考一下
    2016-04-04
  • Android拍照或从图库选择图片并裁剪

    Android拍照或从图库选择图片并裁剪

    这篇文章主要介绍了Android拍照或从图库选择图片并裁剪的相关资料,具有一定的参考价值,需要的朋友可以参考下
    2016-08-08
  • Android 编译出错版本匹配问题解决办法

    Android 编译出错版本匹配问题解决办法

    这篇文章主要介绍了Android 编译出错 app\build\intermediates\res\merged\debug\values-v23\values-v23.xml 的问题解决办法,需要的朋友可以参考下
    2017-07-07
  • android中SharedPreferences实现存储用户名功能

    android中SharedPreferences实现存储用户名功能

    本篇文章主要介绍了android中SharedPreferences实现保存用户名功能,详细的介绍了SharedPreferences的功能,需要的朋友可以参考下
    2017-04-04
  • Android内存溢出及内存泄漏原因进解析

    Android内存溢出及内存泄漏原因进解析

    这篇文章主要介绍了Android内存溢出及内存泄漏原因解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-08-08
  • Android沉浸式状态栏设计的实例代码

    Android沉浸式状态栏设计的实例代码

    本篇文章主要介绍了Android沉浸式状态栏设计的实例代码,整理了详细的代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-07-07
  • Android开发实现自定义水平滚动的容器示例

    Android开发实现自定义水平滚动的容器示例

    这篇文章主要介绍了Android开发实现自定义水平滚动的容器,涉及Android滚动容器的事件响应、属性运算与修改相关操作技巧,需要的朋友可以参考下
    2017-10-10
  • Android Studio打包H5网址页面,封装成APK

    Android Studio打包H5网址页面,封装成APK

    大家好,本篇文章主要讲的是Android Studio打包H5网址页面,封装成APK,感兴趣的同学赶快来看一看吧,对你有帮助的话记得收藏一下,方便下次浏览
    2021-12-12
  • 如何将Android Studio卸载干净

    如何将Android Studio卸载干净

    这篇文章主要介绍了如何将Android Studio卸载干净,文章围绕主题展开详细的内容介绍,具有一定的参考价值,需要的小伙伴可以参考一下
    2022-09-09

最新评论