一、前言
最近项目要求实现一个在自定义地图图片上添加坐标信息的功能,类似于在图片做标注的功能。如下图所示。坐标的位置是相对于图片宽高的百分比
二、思路
改功能主要分为三个视图,1.继承FrameLayout作为父容器;2.添加一个铺满父布局的ImageView显示地图图片;3.动态添加自定义坐标视图
三、代码实现
1. 自定义坐标视图
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:layout_width="wrap_content" android:layout_height="wrap_content"> <ImageView android:id="@+id/iv_sign" android:layout_width="20dp" android:layout_height="wrap_content" android:layout_marginTop="20dp" android:src="@mipmap/dot2" app:layout_constraintEnd_toStartOf="@+id/tv_sign_name" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv_sign_name" android:layout_width="80dp" android:layout_height="wrap_content" android:background="@color/white" android:text="美食城" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/tv_sign_state" android:layout_width="80dp" android:layout_height="wrap_content" android:background="@color/teal_200" android:text="正常" android:textColor="@color/white" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="@+id/tv_sign_name" app:layout_constraintTop_toBottomOf="@+id/tv_sign_name" /> </androidx.constraintlayout.widget.ConstraintLayout>
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53class SignView : ConstraintLayout { private val TAG = SignView::class.java.simpleName private var view: View private var signIv: ImageView private var signNameTv: TextView private var signStateTv: TextView constructor(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.name signStateTv.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.measuredWidth val signImageHeight = signIv.measuredHeight offset[0] = signImageWidth / 2 offset[1] = 20 + signImageHeight - offset[0] Log.d(TAG, "getSignOffset: x:${offset[0]}, y:${offset[1]}") return offset } }
自定义的坐标视图是一个组合的控件,主要是要计算出坐标图片在整个控件的偏移量
2. 父容器
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205class MapView : FrameLayout { private val TAG = MapView::class.java.simpleName //地图图片 private var mapImage = ImageView(context) private var mapWidth = 0 private var mapHeight = 0 private var mapLeft = 0 private var mapTop = 0 private var signBeanList = listOf<SignBean>() private var signOffsetList = mutableListOf<IntArray>() private var signViewList = mutableListOf<SignView>() private var capturedViewIndex = 0 private 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 = index return@forEachIndexed } } } override fun onViewPositionChanged( changedView: View, left: Int, top: Int, dx: Int, dy: Int ) { signOffsetList[capturedViewIndex][0] += dx signOffsetList[capturedViewIndex][1] += dy } override fun clampViewPositionHorizontal(child: View, left: Int, dx: Int): Int { val move = if (left <= mapLeft) mapLeft else if (left >= mapWidth + mapLeft) mapWidth + mapLeft else left return move } override fun clampViewPositionVertical(child: View, top: Int, dy: Int): Int { val move = if (top <= mapTop) mapTop else if (top >= mapHeight + mapTop) mapHeight + mapLeft else top return 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<SignBean> */ fun setSignData(list: List<SignBean>) { val mapOffset = getBitmapOffset(mapImage, true) mapLeft = mapOffset[0] mapTop = mapOffset[1] mapWidth = mapImage.width - mapLeft * 2 mapHeight = mapImage.height - mapTop * 2 var signOffset = IntArray(2) var boolean = true Log.d(TAG, "mapWidth:$mapWidth, mapHeight:$mapHeight, mapLeft:$mapLeft, mapTop:$mapTop") signBeanList = list removeViews(1, childCount - 1) signViewList.clear() signOffsetList.clear() list.forEach { val signView = SignView(context).apply { setData(it) } // 只需要计算一次 if (boolean) { boolean = false signOffset = 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<SignBean> */ fun getMoveSignData(): List<SignBean> { val data = mutableListOf<SignBean>() 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.imageMatrix m.getValues(values) offset[0] = values[2].toInt() offset[1] = values[5].toInt() if (includeLayout) { val lp = img.layoutParams as MarginLayoutParams offset[0] += img.paddingLeft + lp.leftMargin offset[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
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51<?xml version="1.0" encoding="utf-8"?> <androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context=".MainActivity"> <com.itc.floatparade.MapView android:id="@+id/map" android:layout_width="0dp" android:layout_height="0dp" android:layout_marginBottom="12dp" android:background="@color/black" app:layout_constraintBottom_toTopOf="@+id/tv_add_sign" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" /> <Button android:id="@+id/tv_add_sign" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginStart="25dp" android:layout_marginBottom="12dp" android:text="添加坐标" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" /> <Button android:id="@+id/btn_get_sign" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginEnd="25dp" android:text="获取坐标" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toBottomOf="@+id/map" /> <TextView android:id="@+id/tv_sign_list" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginEnd="8dp" android:text="" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintEnd_toStartOf="@+id/btn_get_sign" app:layout_constraintStart_toEndOf="@+id/tv_add_sign" app:layout_constraintTop_toBottomOf="@+id/map" /> </androidx.constraintlayout.widget.ConstraintLayout>
复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25class MainActivity : AppCompatActivity() { private lateinit var binding: ActivityMainBinding override 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<SignBean>() 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内容请搜索靠谱客的其他文章。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复