Android|Android 自定义图片地图坐标功能的实现

一、前言 最近项目要求实现一个在自定义地图图片上添加坐标信息的功能,类似于在图片做标注的功能。如下图所示。坐标的位置是相对于图片宽高的百分比
Android|Android 自定义图片地图坐标功能的实现
文章图片

Android|Android 自定义图片地图坐标功能的实现
文章图片

二、思路 改功能主要分为三个视图,1.继承FrameLayout作为父容器;2.添加一个铺满父布局的ImageView显示地图图片;3.动态添加自定义坐标视图
三、代码实现 1. 自定义坐标视图


class SignView : ConstraintLayout {private val TAG = SignView::class.java.simpleNameprivate var view: Viewprivate var signIv: ImageViewprivate var signNameTv: TextViewprivate var signStateTv: TextViewconstructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : super(context, attrs)constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : super(context,attrs,defStyleAttr)init {view = LayoutInflater.from(context).inflate(R.layout.sign_view, this, true)signIv = view.findViewById(R.id.iv_sign)signNameTv = view.findViewById(R.id.tv_sign_name)signStateTv = view.findViewById(R.id.tv_sign_state)}/*** 设置坐标信息* @param signBean SignBean*/fun setData(signBean: SignBean) {signNameTv.text = signBean.namesignStateTv.text = signBean.state}/*** 计算坐标图标在整个视图的偏移量* @return IntArray*/fun getSignOffset(): IntArray {val w = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)val h = MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED)signIv.measure(w, h)val offset = IntArray(2)val signImageWidth = signIv.measuredWidthval signImageHeight = signIv.measuredHeightoffset[0] = signImageWidth / 2offset[1] = 20 + signImageHeight - offset[0]Log.d(TAG, "getSignOffset: x:${offset[0]}, y:${offset[1]}")return offset}}

自定义的坐标视图是一个组合的控件,主要是要计算出坐标图片在整个控件的偏移量
2. 父容器
class MapView : FrameLayout {private val TAG = MapView::class.java.simpleName//地图图片private var mapImage = ImageView(context)private var mapWidth = 0private var mapHeight = 0private var mapLeft = 0private var mapTop = 0private var signBeanList = listOf()private var signOffsetList = mutableListOf()private var signViewList = mutableListOf()private var capturedViewIndex = 0private val mDragger: ViewDragHelper =ViewDragHelper.create(this, 1.0f, object : ViewDragHelper.Callback() {override fun tryCaptureView(child: View, pointerId: Int): Boolean {return child != mapImage}override fun onViewCaptured(capturedChild: View, activePointerId: Int) {signViewList.forEachIndexed { index, signView ->if (signView == capturedChild) {capturedViewIndex = indexreturn@forEachIndexed}}}override fun onViewPositionChanged(changedView: View,left: Int,top: Int,dx: Int,dy: Int) {signOffsetList[capturedViewIndex][0] += dxsignOffsetList[capturedViewIndex][1] += dy}override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int {val move = if (left <= mapLeft)mapLeftelse if (left >= mapWidth + mapLeft)mapWidth + mapLeftelseleftreturn move}override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int {val move = if (top <= mapTop)mapTopelse if (top >= mapHeight + mapTop)mapHeight + mapLeftelsetopreturn move}})constructor(context: Context) : super(context)constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)constructor(context: Context, attrs: AttributeSet?, @AttrRes defStyleAttr: Int) : this(context,attrs,defStyleAttr,0)constructor(context: Context, attrs: AttributeSet?,@AttrRes defStyleAttr: Int, @StyleRes defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes)/*** 添加地图图片* @param resId Int*/fun setMapImage(@DrawableRes resId: Int) {removeAllViews()mapImage.setImageResource(resId)addView(mapImage)}/*** 设置坐标列表* @param list List*/fun setSignData(list: List) {val mapOffset = getBitmapOffset(mapImage, true)mapLeft = mapOffset[0]mapTop = mapOffset[1]mapWidth = mapImage.width - mapLeft * 2mapHeight = mapImage.height - mapTop * 2var signOffset = IntArray(2)var boolean = trueLog.d(TAG, "mapWidth:$mapWidth, mapHeight:$mapHeight, mapLeft:$mapLeft, mapTop:$mapTop")signBeanList = listremoveViews(1, childCount - 1)signViewList.clear()signOffsetList.clear()list.forEach {val signView = SignView(context).apply {setData(it)}// 只需要计算一次if (boolean) {boolean = falsesignOffset = signView.getSignOffset()}signView.layoutParams = getParams(it, signOffset)addView(signView)signViewList.add(signView)signOffsetList.add(intArrayOf((it.x * mapWidth).toInt(), (it.y * mapHeight).toInt()))}}/*** 获取移动后的坐标信息* @return List*/fun getMoveSignData(): List {val data = https://www.it610.com/article/mutableListOf()signOffsetList.forEachIndexed { index, ints ->val signBean = signBeanList[index]data.add(SignBean(signBean.name,signBean.state,ints[0] / mapWidth.toFloat(),ints[1] / mapHeight.toFloat()))}return data}/*** 计算坐标位置* @param signBean SignBean* @return LayoutParams*/private fun getParams(signBean: SignBean, signOffset: IntArray): LayoutParams {val params = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)params.setMargins((signBean.x * mapWidth + mapLeft - signOffset[0]).toInt(),(signBean.y * mapHeight + mapTop - signOffset[1]).toInt(),0,0)return params}/*** 计算图像在ImageView的位移量* @param img ImageView* @param includeLayout Boolean* @return IntArray?*/private fun getBitmapOffset(img: ImageView, includeLayout: Boolean): IntArray {val offset = IntArray(2)val values = FloatArray(9)val m: Matrix = img.imageMatrixm.getValues(values)offset[0] = values[2].toInt()offset[1] = values[5].toInt()if (includeLayout) {val lp = img.layoutParams as MarginLayoutParamsoffset[0] += img.paddingLeft + lp.leftMarginoffset[1] += img.paddingTop + lp.topMargin}return offset}override fun onInterceptTouchEvent(event: MotionEvent): Boolean {return mDragger.shouldInterceptTouchEvent(event)}override fun onTouchEvent(event: MotionEvent): Boolean {mDragger.processTouchEvent(event)return true}}

父容器中要注意的是由于图片不拉伸,所以会出现图片不会完成铺满ImageView,会有黑边。所以要计算出实际图片显示的大小。
3. Activity

class MainActivity : AppCompatActivity() {private lateinit var binding: ActivityMainBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityMainBinding.inflate(layoutInflater)setContentView(binding.root)binding.map.setMapImage(R.mipmap.map)binding.tvAddSign.setOnClickListener {val list = mutableListOf()list.add(SignBean("美食城", "正常", 0.2f, 0.4f))list.add(SignBean("恐龙危机", "正常", 0.5f, 0.5f))list.add(SignBean("海盗船", "正常", 0.7f, 0.6f))list.add(SignBean("魔法城堡", "正常", 0.4f, 0.8f))binding.map.setSignData(list)}binding.btnGetSign.setOnClickListener {val list = binding.map.getMoveSignData()binding.tvSignList.text = list.toString()}}}

完整代码:https://github.com/MattLjp/FloatParade
【Android|Android 自定义图片地图坐标功能的实现】到此这篇关于Android 自定义图片地图坐标的文章就介绍到这了,更多相关Android 自定义地图内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

    推荐阅读