一、引言
我的上一篇学习日志《安卓应用开发学习:通过腾讯地图SDK实现定位功能》记录了利用腾讯地图SDK实现手机定位功能,并能获取地图中心点的经纬度信息。这之后的几天里,我对《Android App 开发进阶与项目实战》一书第九章的内容深入解读,看明白了其中关于地点搜索和路线规划功能。原书中的这些功能都是分别做成不同的Activity,我则通过自己的努力尝试,将这些功能都集成到一个Activity中(见下图),在一些具体细节上,我花了一些心思,也收获了不少开发经验。
(路线规划功能) (搜索功能)
(之前就实现的定位功能)
二、新增的功能
1.界面调整
其实我很想照着专业的地图软件设计界面,奈何能力有限,只能保持之前的风格了,但由于增加了新的功能,如果把这些组件都显示在页面中,就没法看了。因此,我在界面上做一些调整,首先是将界面顶部放置定位功能组件的区域改成了放置“出行”、“搜索”、“图层”三个按钮。分别对应路线规划、搜索和定位三个功能模块。这三个功能模块则放置在了一个可隐藏的LinearLayout布局中,通过点击不同的按钮, 显示不同的组件。
1.1 xml文件代码
<!-- ll_pop布局中包含的是出行、搜索、图层按钮对应的功能 -->
<!-- 出行、搜索、图层三个组件的显示互斥 -->
<LinearLayout
android:id="@+id/ll_pop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="vertical"
android:background="@drawable/radius_border_15"
app:layout_constraintEnd_toEndOf="@id/mapView"
app:layout_constraintStart_toStartOf="@id/mapView"
app:layout_constraintTop_toTopOf="@id/mapView">
<!-- 此RadioGroup组件对应出行按钮 -->
<RadioGroup
android:id="@+id/rg_travel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb_walk"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="步行"
android:textColor="@color/black"
android:textSize="17sp" />
<RadioButton
android:id="@+id/rb_drive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="false"
android:text="驾车"
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_start"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:text="出发"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/btn_redo"
android:layout_width="80dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_height="wrap_content"
android:text="重来"
tools:ignore="ButtonStyle" />
</RadioGroup>
<!-- 以下的三个LinearLayout布局ll_search1、ll_search2、ll_search3对应搜索按钮 -->
<LinearLayout
android:id="@+id/ll_search1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_searchType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:textColor="@color/black"
android:textSize="16sp"
android:text="搜索方式:" />
<Spinner
android:id="@+id/sp_searchType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/searchType" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="在"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_scope"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:background="@drawable/editext_selector"
android:textColor="@color/black"
android:textSize="17sp"
android:hint=" "
android:autofillHints="范围"
android:inputType="none"/>
<TextView
android:id="@+id/tv_scope_desc"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_marginEnd="5dp"
android:text="市内找"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_search2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:orientation="horizontal">
<SearchView
android:id="@+id/sv_searchPoi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/gray_245" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_search3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<Button
android:id="@+id/btn_next_data"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="下一组"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/btn_clearMarked"
android:layout_width="100dp"
android:layout_marginStart="5dp"
android:layout_height="wrap_content"
android:text="清除标记"
tools:ignore="ButtonStyle" />
</LinearLayout>
<!-- 以下的ll_layer对应图层按钮 -->
<LinearLayout
android:id="@+id/ll_layer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioGroup
android:id="@+id/rg_layer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb_common"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="普通"
android:textColor="@color/black"
android:textSize="17sp" />
<RadioButton
android:id="@+id/rb_satellite"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="false"
android:text="卫星"
android:textColor="@color/black"
android:textSize="17sp" />
<CheckBox
android:id="@+id/ck_traffic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="交通情况"
android:textColor="@color/black"
android:textSize="17sp" />
<CheckBox
android:id="@+id/ck_centerPoint"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:checked="false"
android:text=""
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_getCenter"
android:layout_width="78dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="中心点"/>
</RadioGroup>
</LinearLayout>
1.2 java文件中的逻辑代码
点击其中一个按钮后,其对应的功能组件设置为显示状态,其它功能组件设置为隐藏状态。
@Override
public void onClick(View v) {
ll_pop.setVisibility(View.INVISIBLE);
if (v.getId() == R.id.btn_travel) { // 出行按钮
if (isTravelPop) {
ll_pop.setVisibility(View.INVISIBLE);
} else {
// 对出行、搜索、图层功能的相关组件的显示、隐藏进行设置
ll_pop.setVisibility(View.VISIBLE);
rg_travel.setVisibility(View.VISIBLE);
ll_search1.setVisibility(View.GONE);
ll_search2.setVisibility(View.GONE);
ll_search3.setVisibility(View.GONE);
ll_layer.setVisibility(View.GONE);
travelModeOn = true; // 出行模式开
}
isTravelPop = !isTravelPop;
isSearchPop = false;
isLayerPop = false;
} else if (v.getId() == R.id.btn_search) { // 搜索按钮
if (isSearchPop) {
ll_pop.setVisibility(View.INVISIBLE);
} else {
// 对出行、搜索、图层功能的相关组件的显示、隐藏进行设置
ll_pop.setVisibility(View.VISIBLE);
rg_travel.setVisibility(View.GONE);
ll_search1.setVisibility(View.VISIBLE);
ll_search2.setVisibility(View.VISIBLE);
ll_search3.setVisibility(View.VISIBLE);
ll_layer.setVisibility(View.GONE);
travelModeOn = false; // 出行模式关,转为搜索模式
}
isSearchPop = !isSearchPop;
isTravelPop = false;
isLayerPop = false;
} else if (v.getId() == R.id.btn_layer) { // 图层按钮
if (isLayerPop) {
ll_pop.setVisibility(View.INVISIBLE);
} else {
// 对出行、搜索、图层功能的相关组件的显示、隐藏进行设置
ll_pop.setVisibility(View.VISIBLE);
rg_travel.setVisibility(View.GONE);
ll_search1.setVisibility(View.GONE);
ll_search2.setVisibility(View.GONE);
ll_search3.setVisibility(View.GONE);
ll_layer.setVisibility(View.VISIBLE);
}
isLayerPop = !isLayerPop;
isTravelPop = false;
isSearchPop = false;
}
2.增加的搜索功能
这个功能模块是参照的书上第9章 9.3.3 小节开发的。主要功能有:
2.1 分两种方式(搜城市、搜周边)进行搜索。
(搜城市) (搜周边)
搜城市,可以任意指定一座城市,输入关键字进行搜索;搜周边着在手机定位附近进行搜索。
2.2选点、测距、生成闭合多边形
在搜索模式下,点击地图上的非POI,会绘制一个红色的点,再次点击另外一处会生成第二个点,两点间有连线,连线旁显示两点距离。
多次点击,可以形成闭合多边形。
2.3点击POI(兴趣点)显示标识
这一功能是根据腾讯位置服务官网上的资料,由我自己添加的。点击地图上有名称的地点,就会显示一个标识。
2.4搜索功能有配额限制
这个模块刚设计完成后进行搜索地点测试时,出现了“此Key每日调用量已打上限”的提示,导致搜索功能不能正常使用。
我在腾讯位置服务官网的“Android地图SDK / 开发指南 / 检索功能概述”页面找到了答案。原来,Android地图SDK提供的检索能力依托于腾讯地图开放平台提供的 WebService API,所有的检索接口都有配额限制。而我在添加Key的时候并没有勾选WebService API。需要对key进行编辑,并分配额度。
(登录自己的用户进入我的应用,点key对应的编辑按钮)
(勾选WebService API,默认选中域的白名单不用修改,点保存)
(进入账户额度页面,给应用分配额度,将所有的项目都进行分配)
(分配了额度立即生效,再次运行搜索,能正常使用,且可以在调用统计中查看额度使用情况)
3.增加的出行功能
这个功能模块是参照书上第9章 9.3.4 小节开发的。出行功能具有步行和驾车两种模式。选好模式后,在地图上选两个点,会在两点间显示连线。点击“出发”按钮,软件就会对出行轨迹进行动画演示。
(步行模式) (驾车模式)
三、效果展示
演示动画
腾讯地图SDK应用展示
四、关键代码
最后把部分代码贴出来。
1.activity文件
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import android.text.TextUtils;
import android.util.Log;
import android.view.View;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.RadioGroup;
import android.widget.SearchView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.bahamutjapp.util.MapTencentUtil;
import com.tencent.lbssearch.TencentSearch;
import com.tencent.lbssearch.httpresponse.BaseObject;
import com.tencent.lbssearch.httpresponse.HttpResponseListener;
import com.tencent.lbssearch.object.param.DrivingParam;
import com.tencent.lbssearch.object.param.SearchParam;
import com.tencent.lbssearch.object.param.WalkingParam;
import com.tencent.lbssearch.object.result.DrivingResultObject;
import com.tencent.lbssearch.object.result.SearchResultObject;
import com.tencent.lbssearch.object.result.WalkingResultObject;
import com.tencent.map.geolocation.TencentLocation;
import com.tencent.map.geolocation.TencentLocationListener;
import com.tencent.map.geolocation.TencentLocationManager;
import com.tencent.map.geolocation.TencentLocationRequest;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdate;
import com.tencent.tencentmap.mapsdk.maps.CameraUpdateFactory;
import com.tencent.tencentmap.mapsdk.maps.MapView;
import com.tencent.tencentmap.mapsdk.maps.TencentMap;
import com.tencent.tencentmap.mapsdk.maps.UiSettings;
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptor;
import com.tencent.tencentmap.mapsdk.maps.model.BitmapDescriptorFactory;
import com.tencent.tencentmap.mapsdk.maps.model.CameraPosition;
import com.tencent.tencentmap.mapsdk.maps.model.LatLng;
import com.tencent.tencentmap.mapsdk.maps.model.LatLngBounds;
import com.tencent.tencentmap.mapsdk.maps.model.MapPoi;
import com.tencent.tencentmap.mapsdk.maps.model.Marker;
import com.tencent.tencentmap.mapsdk.maps.model.MarkerOptions;
import com.tencent.tencentmap.mapsdk.maps.model.PolygonOptions;
import com.tencent.tencentmap.mapsdk.maps.model.PolylineOptions;
import com.tencent.tencentmap.mapsdk.vector.utils.animation.MarkerTranslateAnimator;
import java.util.ArrayList;
import java.util.List;
public class MapNavigationActivity extends AppCompatActivity implements TencentLocationListener,
TencentMap.OnMapClickListener, TencentMap.OnMapPoiClickListener, View.OnClickListener {
private final static String TAG = "MapNavigationActivity";
private TencentLocationManager mLocationManager; // 声明一个腾讯定位管理器对象
private MapView mMapView; // 声明一个地图视图对象
private TencentMap mTencentMap; // 声明一个腾讯地图对象
private boolean isFirstLoc = true; // 是否首次定位
private LatLng mMyLatLng; // 我的位置
private MarkerOptions mooMarker; // 手机定位处的标记
private MapPoi mSelectMapPoi; // 选中的Poi
// 出行功能
private Boolean isTravelPop = false;
private Boolean travelModeOn = false; // 出行和搜索模式切换开关
private RadioGroup rg_travel; // 出行方式单选按钮组(同时也作为布局器)
private final List<LatLng> mSaEPosList = new ArrayList<>(); // 起点和终点
private final List<LatLng> mRouteList = new ArrayList<>(); // 导航路线列表
// 搜索功能
private Boolean isSearchPop = false;
private TencentSearch mTencentSearch; // 搜索-声明一个腾讯搜索对象
private int mLoadIndex = 1; // 搜索-搜索结果的第几页
private EditText et_scope; // 搜索-声明一个编辑框对象
private TextView tv_scope_desc; // 搜索-声明一个文本视图对象
private SearchView sv_searchPoi; // 搜索-搜索组件
private int mSearchType; // 下列选框选中项索引号
private String mKeyWord = ""; // 获取搜索框中的内容
private final int SEARCH_CITY = 0; // 搜城市
private final int SEARCH_NEARBY = 1; // 搜周边
// 图层功能
private Boolean isLayerPop = false;
private Button btn_getCenter; // 图层-获取中心点按钮
private TextView tv_centerPoint; // 图层-中心点标记
private LatLng mCenterLatLng; // 地图中心位置
// 布局
private LinearLayout ll_pop, ll_search1, ll_search2, ll_search3, ll_layer; // 面板布局,搜索、图层
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_map_navigation);
initLocation(); // 初始化定位服务
initView(); // 初始化视图
}
// 初始化视图
private void initView() {
// 出行、搜索、图层菜单对应面板初始化
ll_pop = findViewById(R.id.ll_pop); // 包含出行、搜索、图层功能的布局
rg_travel = findViewById(R.id.rg_travel); // 出行方式单选按钮组(附带布局功能)
ll_search1 = findViewById(R.id.ll_search1); // 搜索功能布局1
ll_search2 = findViewById(R.id.ll_search2); // 搜索功能布局2
ll_search3 = findViewById(R.id.ll_search3); // 搜索功能布局3
ll_layer = findViewById(R.id.ll_layer); // 图层功能布局
ll_pop.setVisibility(View.INVISIBLE); // 将此布局设置为不可见
// 中心点初始化
tv_centerPoint = findViewById(R.id.tv_centerPoint); // 中心点图标
tv_centerPoint.setVisibility(View.INVISIBLE); // 中心点图标不可见
btn_getCenter = findViewById(R.id.btn_getCenter); // 中心点按钮
btn_getCenter.setEnabled(false); // 中心点按钮不可用
// 出行功能初始化
rg_travel.setOnCheckedChangeListener((group, checkedId) -> showRoute()); // 出行方式监听
findViewById(R.id.btn_start).setOnClickListener(v -> { // 出发按钮
if (mSaEPosList.size() < 2) {
Toast.makeText(this, "请选中起点和终点后再出发", Toast.LENGTH_SHORT).show();
} else {
playDriveAnim(); // 播放行驶过程动画
}
});
findViewById(R.id.btn_redo).setOnClickListener(v -> { // 重来按钮
mTencentMap.clearAllOverlays(); // 清除所有覆盖物
mSaEPosList.clear(); // 清除起点和终点位置
mRouteList.clear(); // 清除路线
showMyMarker(); // 显示我的位置标记
});
// 搜索功能初始化
mTencentSearch = new TencentSearch(this); // 腾讯地图搜索
// 搜索-下拉列表框
Spinner sp_searchType = findViewById(R.id.sp_searchType); // 搜索方式下列选框
sp_searchType.setSelection(0); // 设置默认选择项
sp_searchType.setOnItemSelectedListener(new MethodSelectedListener());
et_scope = findViewById(R.id.et_scope); // 搜索范围输入框
tv_scope_desc = findViewById(R.id.tv_scope_desc); // 搜索范围文本描述
sv_searchPoi = findViewById(R.id.sv_searchPoi); // 搜索框
sv_searchPoi.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
// 当点击搜索按钮时触发该方法
@Override
public boolean onQueryTextSubmit(String s) {
//Toast.makeText(this, "您选择的是:" + s, Toast.LENGTH_SHORT).show();
sv_searchPoi.clearFocus(); // 移除焦点
mKeyWord = s;
searchPoi(); // 搜索兴趣点
return false;
}
// 当搜索内容改变时触发该方法
@Override
public boolean onQueryTextChange(String s) {
return false;
}
});
findViewById(R.id.btn_next_data).setOnClickListener(v -> { // 下一组按钮
mLoadIndex++;
mTencentMap.clearAllOverlays(); // 清除所有覆盖物
mTencentMap.addMarker(mooMarker); // 往地图添加手机定位标记
searchPoi(); // 搜索指定的地点列表
});
findViewById(R.id.btn_clearMarked).setOnClickListener(v -> { // 清除标记按钮
et_scope.setText(""); // 清除范围文本输入框内容
sv_searchPoi.setQuery("", false); // 清除搜索框内容
mTencentMap.clearAllOverlays(); // 清除所有覆盖物
mPosList.clear(); // 清除标记点列表
mSaEPosList.clear(); // 清除起点和终点列表
mRouteList.clear(); // 清除路线列表
isPolygon = false;
mTencentMap.addMarker(mooMarker); // 往地图添加手机定位标记
});
// 图层功能初始化
RadioGroup rg_layer = findViewById(R.id.rg_layer); // 地图类型单选按钮组
rg_layer.setOnCheckedChangeListener((group, checkedId) -> {
if (checkedId == R.id.rb_common) {
mTencentMap.setMapType(TencentMap.MAP_TYPE_NORMAL); // 设置普通地图
} else if (checkedId == R.id.rb_satellite) {
mTencentMap.setMapType(TencentMap.MAP_TYPE_SATELLITE); // 设置卫星地图
}
});
CheckBox ck_traffic = findViewById(R.id.ck_traffic); // 交通情况复选框
ck_traffic.setOnCheckedChangeListener((buttonView, isChecked) -> {
mTencentMap.setTrafficEnabled(isChecked); // 是否显示交通拥堵状况
});
CheckBox ck_centerPoint = findViewById(R.id.ck_centerPoint); // 中心点复选框
ck_centerPoint.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (isChecked) {
tv_centerPoint.setVisibility(View.VISIBLE); // 显示中心点图标
btn_getCenter.setEnabled(true); // 中心点按钮可用
} else {
tv_centerPoint.setVisibility(View.INVISIBLE);
btn_getCenter.setEnabled(false);
}
});
// 设置点击监听器
findViewById(R.id.btn_travel).setOnClickListener(this); // 出行按钮
findViewById(R.id.btn_search).setOnClickListener(this); // 搜索按钮
findViewById(R.id.btn_layer).setOnClickListener(this); // 图层按钮
findViewById(R.id.tv_enlarge).setOnClickListener(this); // 放大地图
findViewById(R.id.tv_narrow).setOnClickListener(this); // 缩小地图
findViewById(R.id.img_btn_goMyPlace).setOnClickListener(this); // 回到我的位置
btn_getCenter.setOnClickListener(this); // 获取中心点
} // initView-end
// 初始化定位服务
private void initLocation() {
mMapView = findViewById(R.id.mapView);
mTencentMap = mMapView.getMap(); // 获取腾讯地图对象
UiSettings mysetting = mTencentMap.getUiSettings();
mysetting.setCompassEnabled(true); // 开启指南针
mTencentMap.setOnMapClickListener(this); // 设置地图的点击监听器
mTencentMap.setOnMapPoiClickListener(this); // 设置地图POI点击监听器
mLocationManager = TencentLocationManager.getInstance(this);
// 创建腾讯定位请求对象
TencentLocationRequest request = TencentLocationRequest.create();
request.setInterval(30000).setAllowGPS(true);
request.setRequestLevel(TencentLocationRequest.REQUEST_LEVEL_ADMIN_AREA);
mLocationManager.requestLocationUpdates(request, this); // 开始定位监听
}
// 定位变更监听
@Override
public void onLocationChanged(TencentLocation location, int resultCode, String resultDesc) {
if (resultCode == TencentLocation.ERROR_OK) { // 定位成功
if (location != null && isFirstLoc) { // 首次定位
isFirstLoc = false;
// 创建一个经纬度对象
mMyLatLng = new LatLng(location.getLatitude(), location.getLongitude());
showMyMarker(); // 显示我的位置标记
}
} else { // 定位失败
Log.d(TAG, "定位失败,错误代码为"+resultCode+",错误描述为"+resultDesc);
}
}
@Override
public void onStatusUpdate(String s, int i, String s1) { }
// 腾讯地图生命周期方法
@Override
protected void onStart() {
super.onStart();
mMapView.onStart();
}
@Override
protected void onStop() {
super.onStop();
mMapView.onStop();
}
@Override
public void onPause() {
super.onPause();
mMapView.onPause();
}
@Override
public void onResume() {
super.onResume();
mMapView.onResume();
}
@Override
protected void onDestroy() {
super.onDestroy();
mLocationManager.removeUpdates(this); // 移除定位监听
mMapView.onDestroy();
}
// 显示我的位置标记
private void showMyMarker() {
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(mMyLatLng, 12);
mTencentMap.moveCamera(update); // 把相机视角移动到指定地点
showPosMarker(mMyLatLng, R.drawable.ic_my_location_32, "这是您的当前位置"); // 显示位置标记
}
// 显示位置标记
private void showPosMarker(LatLng latLng, int imageId, String desc) {
// 从指定图片中获取位图描述
BitmapDescriptor bitmapDesc = BitmapDescriptorFactory.fromResource(imageId);
mooMarker = new MarkerOptions(latLng).draggable(false) // 不可拖动
.visible(true).icon(bitmapDesc).title("手机定位").snippet(desc);
mTencentMap.addMarker(mooMarker); // 往地图添加手机定位标记
}
// 出行功能
private Marker mCarMarker; // 声明一个小车标记
// 播放行驶过程动画
private void playDriveAnim() {
if (mSaEPosList.size() < 2) {
return;
}
if (mCarMarker != null) {
mCarMarker.remove(); // 移除小车标记
}
int imageId;
if (rg_travel.getCheckedRadioButtonId() == R.id.rb_walk) {
imageId = R.drawable.icon_locate; // 步行
} else {
imageId = R.drawable.car; // 驾车
}
// 从指定图片中获取位图描述
BitmapDescriptor bitmapDesc = BitmapDescriptorFactory.fromResource(imageId);
MarkerOptions ooMarker = new MarkerOptions(mRouteList.get(0))
.anchor(0.5f, 0.5f).icon(bitmapDesc).flat(true).clockwise(false);
mCarMarker = mTencentMap.addMarker(ooMarker); // 往地图添加标记
LatLng[] routeArray = mRouteList.toArray(new LatLng[0]);
// 创建平移动画
MarkerTranslateAnimator anim = new MarkerTranslateAnimator(mCarMarker, 50 * 1000, routeArray, true);
// 动态调整相机视角
mTencentMap.animateCamera(CameraUpdateFactory.newLatLngBounds(
LatLngBounds.builder().include(mRouteList).build(), 50));
anim.startAnimation(); // 开始播放动画
}
// 出行功能-展示导航路线
private void showRoute() {
if (mSaEPosList.size() >= 2) {
mRouteList.clear();
LatLng beginPos = mSaEPosList.get(0); // 获取起点
LatLng endPos = mSaEPosList.get(mSaEPosList.size()-1); // 获取终点
mTencentMap.clearAllOverlays(); // 清除所有覆盖物
mTencentMap.addMarker(mooMarker); // 往地图添加手机定位标记
showPosMarker(beginPos, R.drawable.icon_geo, "起点"); // 显示位置标记
showPosMarker(endPos, R.drawable.icon_geo, "终点"); // 显示位置标记
if (rg_travel.getCheckedRadioButtonId() == R.id.rb_walk) {
getWalkingRoute(beginPos, endPos); // 规划步行导航
} else {
getDrivingRoute(beginPos, endPos); // 规划行车导航
}
}
}
// 出行功能-规划步行导航
private void getWalkingRoute(LatLng beginPos, LatLng endPos) {
WalkingParam walkingParam = new WalkingParam();
walkingParam.from(beginPos); // 指定步行的起点
walkingParam.to(endPos); // 指定步行的终点
// 创建一个腾讯搜索对象
TencentSearch tencentSearch = new TencentSearch(getApplicationContext());
Log.d(TAG, "checkParams:" + walkingParam.checkParams());
// 根据步行参数规划导航路线
tencentSearch.getRoutePlan(walkingParam, new HttpResponseListener<WalkingResultObject>() {
@Override
public void onSuccess(int statusCode, WalkingResultObject object) {
if (object==null || object.result==null || object.result.routes==null) {
Log.d(TAG, "导航路线为空");
return;
}
Log.d(TAG, "message:" + object.message);
for (WalkingResultObject.Route result : object.result.routes) {
mRouteList.addAll(result.polyline);
// 往地图上添加一组连线
mTencentMap.addPolyline(new PolylineOptions().addAll(mRouteList)
.color(0x880000ff).width(20));
}
}
@Override
public void onFailure(int statusCode, String responseString, Throwable throwable) {
Log.d(TAG, statusCode + " " + responseString);
}
});
}
// 出行功能-规划行车导航
private void getDrivingRoute(LatLng beginPos, LatLng endPos) {
// 创建导航参数
DrivingParam drivingParam = new DrivingParam(beginPos, endPos);
// 指定道路类型为主路
drivingParam.roadType(DrivingParam.RoadType.ON_MAIN_ROAD); // 道路类型-主路
drivingParam.heading(90); // 起点位置的车头方向
drivingParam.accuracy(5); // 行车导航的精度,单位米
// 创建一个腾讯搜索对象
TencentSearch tencentSearch = new TencentSearch(this);
// 根据行车参数规划导航路线
tencentSearch.getRoutePlan(drivingParam, new HttpResponseListener<DrivingResultObject>() {
@Override
public void onSuccess(int statusCode, DrivingResultObject object) {
if (object==null || object.result==null || object.result.routes==null) {
Log.d(TAG, "导航路线为空");
return;
}
Log.d(TAG, "message:" + object.message);
for (DrivingResultObject.Route route : object.result.routes){
mRouteList.addAll(route.polyline);
// 往地图上添加一组连线
mTencentMap.addPolyline(new PolylineOptions().addAll(mRouteList)
.color(0x880000ff).width(20));
}
}
@Override
public void onFailure(int statusCode, String responseString, Throwable throwable) {
Log.d(TAG, statusCode + " " + responseString);
}
});
}
// 搜索功能-搜索方式下列选框选择监听器
class MethodSelectedListener implements AdapterView.OnItemSelectedListener {
public void onItemSelected(AdapterView<?> arg0, View arg1, int arg2, long arg3) {
mSearchType = arg2;
if (mSearchType == SEARCH_CITY) {
tv_scope_desc.setText("市内找");
} else if (mSearchType == SEARCH_NEARBY) {
tv_scope_desc.setText("米内找");
}
et_scope.setText("");
sv_searchPoi.setQuery("", false); // 清除搜索框内容
}
public void onNothingSelected(AdapterView<?> arg0) {}
}
// 搜索功能-搜索指定的地点列表
public void searchPoi() {
Log.d(TAG, "et_scope=" + et_scope.getText().toString()
+ ", SearchKey=" + mKeyWord
+ ", mLoadIndex=" + mLoadIndex);
String scope = et_scope.getText().toString(); // 获取输入的范围
// 测试时发现scope为空时附近查询会闪退,需要对scope进行发现bug,如果,就会导致程序闪退,需要添加判断语句
if (TextUtils.isEmpty(scope)) {
Toast.makeText(this, "搜索的范围为空", Toast.LENGTH_SHORT).show();
return;
}
SearchParam searchParam = new SearchParam();
if (mSearchType == SEARCH_CITY) { // 城市搜索
SearchParam.Region region = new SearchParam
.Region(scope) // 设置搜索城市
.autoExtend(false); // 设置搜索范围不扩大
searchParam = new SearchParam(mKeyWord, region); // 构建地点检索
} else if (mSearchType == SEARCH_NEARBY) { // 周边搜索
int radius;
try {
radius = Integer.parseInt(scope); // scope必须是有效值,否则不使用try会闪退
} catch (Exception e){
e.printStackTrace();
Toast.makeText(this, "搜索范围不是有效数字", Toast.LENGTH_SHORT).show();
return;
}
SearchParam.Nearby nearby = new SearchParam
.Nearby(mMyLatLng, radius).autoExtend(false); // 不扩大搜索范围
searchParam = new SearchParam(mKeyWord, nearby); // 构建地点检索
}
searchParam.pageSize(10); // 每页大小
searchParam.pageIndex(mLoadIndex); // 第几页
// 根据搜索参数查找符合条件的地点列表
mTencentSearch.search(searchParam, new HttpResponseListener<BaseObject>() {
@Override
public void onFailure(int arg0, String arg2, Throwable arg3) {
Toast.makeText(getApplicationContext(), arg2, Toast.LENGTH_LONG).show();
}
@Override
public void onSuccess(int arg0, BaseObject arg1) { // 搜索成功
if (arg1 == null) {
return;
}
SearchResultObject obj = (SearchResultObject) arg1;
if(obj.data==null || obj.data.size()==0){
return;
}
// 将地图中心坐标移动到检索到的第一个地点
CameraUpdate update = CameraUpdateFactory.newLatLngZoom(obj.data.get(0).latLng, 12);
mTencentMap.moveCamera(update); // 把相机视角移动到指定地点
// 将其他检索到的地点在地图上用 marker 标出来
for (SearchResultObject.SearchResultData data : obj.data){
Log.d(TAG,"title:"+data.title + ";" + data.address);
// 往地图添加标记
mTencentMap.addMarker(new MarkerOptions(data.latLng)
.title(data.title).snippet(data.address));
}
}
});
}
// 下面是绘图代码
// 功能:在地图上点击后,会添加一个点,再次点击,添加第二个点,在两点间添加连线,并显示两点距离,以此类推
// 通过“清除标记”按钮,可将添加的点和连线清除
private final int lineColor = 0x55FF0000; // 红色-线段颜色
private final int textColor = 0x990000FF; // 蓝色-字体颜色
private final int polygonColor = 0x77FFFF00; // 黄色-多边形底色
private final int radiusLimit = 100; // 当前点与第一个点的距离限定
private final List<LatLng> mPosList = new ArrayList<>(); // 创建列表,保存用户在地图上点击的位置
private boolean isPolygon = false;
// 往地图上添加一个点
private void addDot(LatLng pos) {
if (isPolygon) {
mPosList.clear();
isPolygon = false;
}
boolean isFirst = false;
LatLng thisPos = pos;
// 将当前点与第一个点和前一个点进行比较
if (mPosList.size() > 0) {
LatLng firstPos = mPosList.get(0);
int distance = (int) Math.round(MapTencentUtil.getShortDistance(
thisPos.longitude, thisPos.latitude,
firstPos.longitude, firstPos.latitude)); // 当前点与第一个点的距离
if (mPosList.size() == 1 && distance <= 0) { // 多次点击起点,要忽略之
return;
} else if (mPosList.size() > 1) {
LatLng lastPos = mPosList.get(mPosList.size() - 1);
int lastDistance = (int) Math.round(MapTencentUtil.getShortDistance(
thisPos.longitude, thisPos.latitude,
lastPos.longitude, lastPos.latitude)); // 当前点与前一个点的距离
if (lastDistance <= 0) { // 重复响应当前位置的点击,要忽略之
return;
}
}
if (distance < radiusLimit * 2) { // 当前点与第一个点的距离小于限定值,形成闭环
thisPos = firstPos;
isFirst = true;
}
Log.d(TAG, "distance=" + distance + ", radiusLimit=" + radiusLimit + ", isFirst=" + isFirst);
// 在当前点与前一个点间画直线
LatLng lastPos = mPosList.get(mPosList.size() - 1);
List<LatLng> pointList = new ArrayList<>();
pointList.add(lastPos);
pointList.add(thisPos);
PolylineOptions ooPolyline = new PolylineOptions().width(2)
.color(lineColor).addAll(pointList);
// 计算两点之间距离
distance = (int) Math.round(MapTencentUtil.getShortDistance(
thisPos.longitude, thisPos.latitude,
lastPos.longitude, lastPos.latitude));
String disText;
if (distance > 1000) {
disText = Math.round(distance * 10.0f / 1000) / 10d + "公里";
} else {
disText = distance + "米";
}
PolylineOptions.SegmentText segment = new PolylineOptions.SegmentText(0, 1, disText);
PolylineOptions.Text text = new PolylineOptions.Text.Builder(segment)
.color(textColor).size(15).build();
ooPolyline.text(text);
mTencentMap.addPolyline(ooPolyline); // 往地图上添加一组连线
}
if (!isFirst) {
// 从指定图片中获取位图描述
BitmapDescriptor bitmapDesc = BitmapDescriptorFactory.fromResource(R.drawable.icon_geo);
MarkerOptions ooMarker = new MarkerOptions(thisPos).draggable(false) // 不可拖动
.visible(true).icon(bitmapDesc);
mTencentMap.addMarker(ooMarker); // 往地图添加标记
// 设置地图标记的点击监听器
mTencentMap.setOnMarkerClickListener(marker -> {
LatLng markPos = marker.getPosition();
addDot(markPos); // 往地图上添加一个点
marker.showInfoWindow(); // 显示标记的信息窗口
return true;
});
} else { // isFirst为真,mPosList中的点已形成闭环
if (mPosList.size() < 3) { // 可能存在地图与标记同时响应点击事件的情况
mPosList.clear();
isPolygon = false;
return;
}
// 画多边形
PolygonOptions ooPolygon = new PolygonOptions().addAll(mPosList)
.strokeColor(0xFF00FF00).strokeWidth(3)
.fillColor(polygonColor); // 多边形为绿边
mTencentMap.addPolygon(ooPolygon); // 往地图上添加多边形
isPolygon = true;
}
mPosList.add(thisPos);
}
// 地图点击响应
@Override
public void onMapClick(LatLng latLng) {
if (travelModeOn) { // 出行模式
if (mSaEPosList.size() < 2) {
mSaEPosList.add(latLng);
} else {
mSaEPosList.set(mSaEPosList.size()-1, latLng);
}
if (mSaEPosList.size() == 1) {
showPosMarker(latLng, R.drawable.icon_geo, "起点"); // 显示位置标记
}
showRoute(); // 展示导航路线
} else { // 非出行模式(搜索模式)执行添加点操作
addDot(latLng); // 往地图上添加一个点
Log.d(TAG, "当前点击位置的经纬度:" + latLng.longitude + " ;" + latLng.altitude);
}
}
// 点击地图上的POI响应
@Override
public void onClicked(MapPoi mapPoi) {
if (!travelModeOn) {
Log.d(TAG, "选中的Poi为:" + mapPoi.name);
Toast.makeText(this, mapPoi.name + "\n" + mapPoi.getLongitude() + ", "
+ mapPoi.getLatitude(), Toast.LENGTH_SHORT).show();
// 清除之前的标记
mTencentMap.clearAllOverlays();
mTencentMap.addMarker(mooMarker); // 往地图添加手机定位标记
mSelectMapPoi = mapPoi;
// 从指定图片中获取位图描述
BitmapDescriptor bitmapDesc = BitmapDescriptorFactory.fromResource(R.drawable.icon_current);
MarkerOptions ooMarker = new MarkerOptions(mSelectMapPoi.position).draggable(false) // 不可拖动
.visible(true).icon(bitmapDesc);
mTencentMap.addMarker(ooMarker); // 往地图添加标记
} else {
addDot(mapPoi.position); // 往地图上添加一个点
}
}
@Override
public void onClick(View v) {
ll_pop.setVisibility(View.INVISIBLE);
if (v.getId() == R.id.btn_travel) { // 出行按钮
if (isTravelPop) {
ll_pop.setVisibility(View.INVISIBLE);
} else {
// 对出行、搜索、图层功能的相关组件的显示、隐藏进行设置
ll_pop.setVisibility(View.VISIBLE);
rg_travel.setVisibility(View.VISIBLE);
ll_search1.setVisibility(View.GONE);
ll_search2.setVisibility(View.GONE);
ll_search3.setVisibility(View.GONE);
ll_layer.setVisibility(View.GONE);
travelModeOn = true; // // 出行模式开
}
isTravelPop = !isTravelPop;
isSearchPop = false;
isLayerPop = false;
} else if (v.getId() == R.id.btn_search) { // 搜索按钮
if (isSearchPop) {
ll_pop.setVisibility(View.INVISIBLE);
} else {
// 对出行、搜索、图层功能的相关组件的显示、隐藏进行设置
ll_pop.setVisibility(View.VISIBLE);
rg_travel.setVisibility(View.GONE);
ll_search1.setVisibility(View.VISIBLE);
ll_search2.setVisibility(View.VISIBLE);
ll_search3.setVisibility(View.VISIBLE);
ll_layer.setVisibility(View.GONE);
travelModeOn = false; // 出行模式关,转为搜索模式
}
isSearchPop = !isSearchPop;
isTravelPop = false;
isLayerPop = false;
} else if (v.getId() == R.id.btn_layer) { // 图层按钮
if (isLayerPop) {
ll_pop.setVisibility(View.INVISIBLE);
} else {
// 对出行、搜索、图层功能的相关组件的显示、隐藏进行设置
ll_pop.setVisibility(View.VISIBLE);
rg_travel.setVisibility(View.GONE);
ll_search1.setVisibility(View.GONE);
ll_search2.setVisibility(View.GONE);
ll_search3.setVisibility(View.GONE);
ll_layer.setVisibility(View.VISIBLE);
}
isLayerPop = !isLayerPop;
isTravelPop = false;
isSearchPop = false;
} else if (v.getId() == R.id.tv_enlarge) { // 放大地图
mTencentMap.moveCamera(CameraUpdateFactory.zoomIn()); // 放大一级
// Toast.makeText(this, "放大地图", Toast.LENGTH_SHORT).show();
} else if (v.getId() == R.id.tv_narrow) { // 缩小地图
mTencentMap.moveCamera(CameraUpdateFactory.zoomOut()); // 缩小一级
// Toast.makeText(this, "缩小地图", Toast.LENGTH_SHORT).show();
} else if (v.getId() == R.id.img_btn_goMyPlace) { // 回到我的位置
CameraUpdate update = CameraUpdateFactory.newLatLng(mMyLatLng);
mTencentMap.moveCamera(update); // 把相机视角移动到指定地点
// Toast.makeText(this, "回到我的位置", Toast.LENGTH_SHORT).show();
} else if (v.getId() == R.id.btn_getCenter) { // 获取中心点坐标
CameraPosition cameraPosition = mTencentMap.getCameraPosition();
mCenterLatLng = cameraPosition.target;
Toast.makeText(this, "中心点坐标:" + mCenterLatLng.latitude + ";" + mCenterLatLng.longitude, Toast.LENGTH_LONG).show();
}
}
}
2. mxl文件
<?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=".MapNavigationActivity">
<LinearLayout
android:id="@+id/ll_toolBar"
android:layout_width="match_parent"
android:layout_height="45dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:gravity="center_horizontal"
android:orientation="horizontal"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<Button
android:id="@+id/btn_travel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="出行"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/btn_search"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="15dp"
android:layout_marginEnd="15dp"
android:text="搜索"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/btn_layer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="图层"
tools:ignore="ButtonStyle" />
</LinearLayout>
<com.tencent.tencentmap.mapsdk.maps.MapView
android:id="@+id/mapView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/ll_toolBar">
<TextView
android:id="@+id/tv_centerPoint"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:gravity="center"
android:text="+"
android:textColor="@color/red"
android:textSize="48sp" />
</com.tencent.tencentmap.mapsdk.maps.MapView>
<LinearLayout
android:id="@+id/ll_zoom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="20dp"
android:layout_marginBottom="120dp"
android:orientation="vertical"
android:background="@drawable/radius_border_15"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent">
<TextView
android:id="@+id/tv_enlarge"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="10dp"
android:layout_marginBottom="5dp"
android:gravity="center"
android:text="+"
android:textColor="@color/black"
android:textSize="32sp"
android:textStyle="bold" />
<TextView
android:id="@+id/tv_narrow"
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_gravity="center_horizontal"
android:layout_marginTop="5dp"
android:layout_marginBottom="10dp"
android:gravity="center"
android:text="-"
android:textColor="@color/black"
android:textSize="32sp"
android:textStyle="bold" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_goMyPlace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginBottom="55dp"
android:orientation="horizontal"
android:background="@drawable/radius_border_15"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent">
<ImageButton
android:id="@+id/img_btn_goMyPlace"
android:layout_width="54dp"
android:layout_height="54dp"
android:backgroundTint="@color/white"
android:src="@drawable/ic_go_my_place"
tools:ignore="ContentDescription" />
</LinearLayout>
<!-- ll_pop布局中包含的是出现、搜索、图层按钮对应的功能 -->
<!-- 显示互斥布局rg_travel;ll_search1、sv_searchPoi、ll_search2;ll_layer -->
<LinearLayout
android:id="@+id/ll_pop"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="15dp"
android:orientation="vertical"
android:background="@drawable/radius_border_15"
app:layout_constraintEnd_toEndOf="@id/mapView"
app:layout_constraintStart_toStartOf="@id/mapView"
app:layout_constraintTop_toTopOf="@id/mapView">
<RadioGroup
android:id="@+id/rg_travel"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb_walk"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="步行"
android:textColor="@color/black"
android:textSize="17sp" />
<RadioButton
android:id="@+id/rb_drive"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="false"
android:text="驾车"
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_start"
android:layout_width="80dp"
android:layout_height="wrap_content"
android:text="出发"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/btn_redo"
android:layout_width="80dp"
android:layout_marginStart="5dp"
android:layout_marginEnd="5dp"
android:layout_height="wrap_content"
android:text="重来"
tools:ignore="ButtonStyle" />
</RadioGroup>
<LinearLayout
android:id="@+id/ll_search1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="5dp"
android:orientation="horizontal">
<TextView
android:id="@+id/tv_searchType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginStart="10dp"
android:textColor="@color/black"
android:textSize="16sp"
android:text="搜索方式:" />
<Spinner
android:id="@+id/sp_searchType"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:entries="@array/searchType" />
<TextView
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:text="在"
android:textColor="@color/black"
android:textSize="17sp" />
<EditText
android:id="@+id/et_scope"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:background="@drawable/editext_selector"
android:textColor="@color/black"
android:textSize="17sp"
android:hint=" "
android:autofillHints="范围"
android:inputType="none"/>
<TextView
android:id="@+id/tv_scope_desc"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:layout_marginEnd="5dp"
android:text="市内找"
android:textColor="@color/black"
android:textSize="17sp" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_search2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:layout_marginEnd="10dp"
android:orientation="horizontal">
<SearchView
android:id="@+id/sv_searchPoi"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/gray_245" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_search3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:layout_marginBottom="5dp"
android:orientation="horizontal">
<Button
android:id="@+id/btn_next_data"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="下一组"
tools:ignore="ButtonStyle" />
<Button
android:id="@+id/btn_clearMarked"
android:layout_width="100dp"
android:layout_marginStart="5dp"
android:layout_height="wrap_content"
android:text="清除标记"
tools:ignore="ButtonStyle" />
</LinearLayout>
<LinearLayout
android:id="@+id/ll_layer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<RadioGroup
android:id="@+id/rg_layer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/rb_common"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="普通"
android:textColor="@color/black"
android:textSize="17sp" />
<RadioButton
android:id="@+id/rb_satellite"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="false"
android:text="卫星"
android:textColor="@color/black"
android:textSize="17sp" />
<CheckBox
android:id="@+id/ck_traffic"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:checked="false"
android:text="交通情况"
android:textColor="@color/black"
android:textSize="17sp" />
<CheckBox
android:id="@+id/ck_centerPoint"
android:layout_width="30dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:checked="false"
android:text=""
android:textColor="@color/black"
android:textSize="17sp" />
<Button
android:id="@+id/btn_getCenter"
android:layout_width="78dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:text="中心点"/>
</RadioGroup>
</LinearLayout>
</LinearLayout>
</androidx.constraintlayout.