高德地图绘制轨迹和通过经纬度转屏幕坐标绘制轨迹(一)
4 个月前
· 来自专栏
安卓开发笔记
效果图:
1. 高德地图MapView中绘制轨迹
2. 通过经纬度转屏幕坐标自定义MyMapView中绘制轨迹
上代码:
manifest.xml中添加权限
<!-- 6.0以上需要申请动态权限activity中自行申请, 高德地图SDK自行配置此处省略 -->
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
<uses-permission android:name="android.permission.INTERNET" />
build.gradle(app)中添加SDK依赖
// 倒计时
implementation 'in.xiandan:count-down-timer:1.0.3'
// 高德地图相关
implementation 'com.amap.api:3dmap:latest.integration'
implementation 'com.amap.api:location:latest.integration'
注意: 由于国家最新出台用户个人隐私相关整改权限政策, 高德地图使用, 推荐在用户APP界面明确同意(点击事件)使用APP协议后, 添加如下接口:
//设置高德地图权限
MapsInitializer.updatePrivacyShow(this,true,true);
MapsInitializer.updatePrivacyAgree(this,true);
(一)高德地图MapView中绘制轨迹:
需要自己创建数据库存取GPS数据, 此处省略...
1) 实时绘制轨迹并保存GPS数据到本地数据库
package com.yangsion.test.activity;
import android.annotation.SuppressLint;
import android.content.Intent;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.hardware.SensorManager;
import android.os.Bundle;
import android.os.SystemClock;
import android.speech.tts.TextToSpeech;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.Chronometer;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import com.amap.api.location.AMapLocation;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.location.AMapLocationListener;
import com.amap.api.maps.AMap;
import com.amap.api.maps.AMapUtils;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.MapView;
import com.amap.api.maps.model.BitmapDescriptor;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.MyLocationStyle;
import com.amap.api.maps.model.Polyline;
import com.amap.api.maps.model.PolylineOptions;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.yangsion.test.R;
import com.yangsion.test.application.SmuuApplication;
import com.yangsion.test.db.SQLiteUtil;
import com.yangsion.test.en.SportsMoveDataEn;
import com.yangsion.test.en.SportsNumDataEn;
import com.yangsion.test.util.LogUtils;
import com.yangsion.test.util.SimpleDateFormatUtil;
import com.yangsion.test.util.SystemUtil;
import com.yangsion.test.util.UnitConvertUtil;
import com.yangsion.test.view.CircleBarView;
import java.text.DecimalFormat;
import java.text.DecimalFormatSymbols;
import java.util.Calendar;
import java.util.Locale;
import in.xiandan.countdowntimer.CountDownTimerSupport;
import in.xiandan.countdowntimer.OnCountDownTimerListener;
* 运动 高德地图
public class SportsGDActivity extends BaseActivity implements View.OnClickListener, View.OnLongClickListener {
private FloatingActionButton mHistory;
private TextView mTemrTxt;
private LinearLayout mMapLayout;
private LinearLayout mMixInfo;
private LinearLayout mMaxInfo;
private RelativeLayout mLockTrueBtnLayout;
private LinearLayout mAllBtnLayout;
private RelativeLayout mStartLayout;
private RelativeLayout mLockBtnLayout;
private RelativeLayout mStopBtnLayout;
private RelativeLayout mPauseBtnLayout;
private RelativeLayout mStartBtnLayout;
private RelativeLayout mVoiceBtnLayout;
private MapView mGdMap;
private ImageButton mMaxDrawer;
private TextView mGpsDesc;
private TextView mGpsStatusDesc;
private TextView mDistance;
private TextView mDistanceUnit;
private Chronometer mRunTime;
private TextView mPace;
private TextView mStepNum;
private TextView mCalories;
private ImageButton mLockTrue;
private CircleBarView mProgressBarLock;
private CircleBarView mProgressBarStop;
private ImageButton mLock;
private ImageButton mPause;
private ImageButton mStop;
private ImageButton mStart;
private ImageButton mVoice;
private ImageButton mMixDrawer;
private TextView mDistance2;
private TextView mRunNumUnit2;
private Chronometer mRunTime2;
private ImageView mGpsSignal0;
private ImageView mGpsSignal1;
private ImageView mGpsSignal2;
private ImageView mGpsSignal3;
private ImageView[] gpsImgs;
private MyLocationStyle myLocationStyle;
private AMap gd_aMap = null;
//声明AMapLocationClient类对象
public AMapLocationClient mLocationClient = null;
//声明AMapLocationClientOption对象
public AMapLocationClientOption mLocationOption = null;
//绘制实线的内容
public PolylineOptions mPolylineOptions = null;
//绘制虚线的内容
public PolylineOptions mPolylineOptions2 = null;
//绘制marker内容
public MarkerOptions markerOptions = null;
public TextToSpeech mTextToSpeech = null;
private LatLng oldLatLng = null;
private String dviceMac = "";
private String userId = "";
private String weight = "";
private String startTime = "";
private String sportMode = "1";// 运动类型 0-walk,1-run,2-swim,3-riding,4-triathlon(铁人三项)
private String sportsDatas = "";
private CountDownTimerSupport mTimer = new CountDownTimerSupport(3000, 1000);
private long mPauseTime = 0;
private boolean isOpenVoice = false;
private boolean isPause = false;
private float distanceAll = 0;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sports_gd);
initTTS();
initView(savedInstanceState);
initData();
@Override
protected void onResume() {
super.onResume();
mGdMap.onResume();
@Override
protected void onPause() {
super.onPause();
mGdMap.onPause();
//moveTaskToBack(true);//在后台运行
@Override
protected void onDestroy() {
super.onDestroy();
mGdMap.onDestroy();
mLocationClient.onDestroy();//销毁定位客户端,同时销毁本地定位服务。
mTextToSpeech.stop(); // 不管是否正在朗读TTS都被打断
mTextToSpeech.shutdown(); // 关闭,释放资源
@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
super.onSaveInstanceState(outState);
mGdMap.onSaveInstanceState(outState);
private void initView(Bundle savedInstanceState) {
//获取地图控件引用
mGdMap = (MapView) findViewById(R.id.gd_map);
//在activity执行onCreate时执行mMapView.onCreate(savedInstanceState),创建地图
mGdMap.onCreate(savedInstanceState);
mGpsDesc = (TextView) findViewById(R.id.gps_desc);
mGpsDesc.setOnClickListener(this);
mGpsStatusDesc = (TextView) findViewById(R.id.gps_status_desc);
mGpsStatusDesc.setOnClickListener(this);
mHistory = (FloatingActionButton) findViewById(R.id.history);
mHistory.setOnClickListener(this);
mTemrTxt = (TextView) findViewById(R.id.temrTxt);
mMapLayout = (LinearLayout) findViewById(R.id.mapLayout);
mStartLayout = (RelativeLayout) findViewById(R.id.startLayout);
mTimer.start();
mTimer.setOnCountDownTimerListener(mOnCountDownTimerListener);
mMaxDrawer = (ImageButton) findViewById(R.id.maxDrawer);
mMaxDrawer.setOnClickListener(this);
mDistance = (TextView) findViewById(R.id.distance);
mDistance.setOnClickListener(this);
mDistanceUnit = (TextView) findViewById(R.id.distanceUnit);
mDistanceUnit.setOnClickListener(this);
mRunTime = (Chronometer) findViewById(R.id.runTime);
mRunTime.setOnClickListener(this);
mPace = (TextView) findViewById(R.id.pace);
mPace.setOnClickListener(this);
mStepNum = (TextView) findViewById(R.id.step_num);
mStepNum.setOnClickListener(this);
mCalories = (TextView) findViewById(R.id.calories);
mCalories.setOnClickListener(this);
mLockTrue = (ImageButton) findViewById(R.id.lock_true);
mLockTrue.setOnClickListener(this);
mLockTrue.setOnLongClickListener(this);
mLock = (ImageButton) findViewById(R.id.lock);
mLock.setOnClickListener(this);
mProgressBarLock = (CircleBarView) findViewById(R.id.progressBarLock);
mProgressBarLock.setOnClickListener(this);
mPause = (ImageButton) findViewById(R.id.pause);
mPause.setOnClickListener(this);
mProgressBarStop = (CircleBarView) findViewById(R.id.progressBarStop);
mProgressBarStop.setOnClickListener(this);
mStop = (ImageButton) findViewById(R.id.stop);
mStop.setOnClickListener(this);
mStop.setOnLongClickListener(this);
mStart = (ImageButton) findViewById(R.id.start);
mStart.setOnClickListener(this);
mVoice = (ImageButton) findViewById(R.id.voice);
mVoice.setOnClickListener(this);
mMaxInfo = (LinearLayout) findViewById(R.id.maxInfo);
mMaxInfo.setOnClickListener(this);
mMixDrawer = (ImageButton) findViewById(R.id.mixDrawer);
mMixDrawer.setOnClickListener(this);
mDistance2 = (TextView) findViewById(R.id.distance2);
mDistance2.setOnClickListener(this);
mRunNumUnit2 = (TextView) findViewById(R.id.runNumUnit2);
mRunNumUnit2.setOnClickListener(this);
mRunTime2 = (Chronometer) findViewById(R.id.runTime2);
mRunTime2.setOnClickListener(this);
mMixInfo = (LinearLayout) findViewById(R.id.mixInfo);
mMixInfo.setOnClickListener(this);
mGpsSignal0 = (ImageView) findViewById(R.id.gps_signal0);
mGpsSignal1 = (ImageView) findViewById(R.id.gps_signal1);
mGpsSignal2 = (ImageView) findViewById(R.id.gps_signal2);
mGpsSignal3 = (ImageView) findViewById(R.id.gps_signal3);
gpsImgs = new ImageView[]{mGpsSignal0, mGpsSignal1, mGpsSignal2, mGpsSignal3};
mLockTrueBtnLayout = (RelativeLayout) findViewById(R.id.lock_true_btn_layout);
mLockTrueBtnLayout.setOnClickListener(this);
mAllBtnLayout = (LinearLayout) findViewById(R.id.all_btn_layout);
mAllBtnLayout.setOnClickListener(this);
mLockBtnLayout = (RelativeLayout) findViewById(R.id.lock_btn_layout);
mLockBtnLayout.setOnClickListener(this);
mStopBtnLayout = (RelativeLayout) findViewById(R.id.stop_btn_Layout);
mStopBtnLayout.setOnClickListener(this);
mPauseBtnLayout = (RelativeLayout) findViewById(R.id.pause_btn_Layout);
mPauseBtnLayout.setOnClickListener(this);
mStartBtnLayout = (RelativeLayout) findViewById(R.id.start_btn_Layout);
mStartBtnLayout.setOnClickListener(this);
mVoiceBtnLayout = (RelativeLayout) findViewById(R.id.voice_btn_layout);
mVoiceBtnLayout.setOnClickListener(this);
private void initData() {
//异常断点记录不为空, 继续记录数据
sportsDatas = SmuuApplication.getApplication().getSportsDatas();
startTime = SimpleDateFormatUtil.Y_MM_ddHHmmss().format(Calendar.getInstance().getTime());
dviceMac = SmuuApplication.getApplication().getConnectDevice();
userId = SmuuApplication.getApplication().getUserAccount();
weight = SmuuApplication.getApplication().getUserEn().weight;
if (!sportsDatas.equals("")) {
LogUtils.d("gy", "sportsDatas: " + sportsDatas);
String[] split = sportsDatas.split(",");
startTime = split[1];
mDistance.setText(split[3]);
mDistance2.setText(split[3]);
distanceAll = Float.parseFloat(split[3]);
mCalories.setText(split[4]);
mStepNum.setText(split[5]);
mRunTime.setOnChronometerTickListener(new Chronometer.OnChronometerTickListener() {
@Override
public void onChronometerTick(Chronometer chronometer) {
//LogUtils.d("gy", "chronometer: " + chronometer.getBase());
//保存最新记录, 异常情况重新进入, 继续从断点记录
String sportsDatas = sportMode + "," + startTime + "," +
String.valueOf(SystemClock.elapsedRealtime() - mRunTime.getBase()) + "," + split[3] + "," +
split[4] + "," + split[5];
SmuuApplication.getApplication().setSportsDatas(sportsDatas);
initVoiceState();
if (gd_aMap == null) {
gd_aMap = mGdMap.getMap();
myLocationStyle = new MyLocationStyle();//初始化定位蓝点样式类
myLocationStyle.myLocationType(MyLocationStyle.LOCATION_TYPE_LOCATION_ROTATE);//连续定位、且将视角移动到地图中心点,定位点依照设备方向旋转,并且会跟随设备移动。(1秒1次定位)如果不设置myLocationType,默认也会执行此种模式。
myLocationStyle.interval(2000); //设置连续定位模式下的定位间隔,只在连续定位模式下生效,单次定位模式下不会生效。单位为毫秒。
//自定义蓝点icon
myLocationStyle.myLocationIcon(BitmapDescriptorFactory.fromResource(R.drawable.ic_line_head));//注意: 需要png图片
myLocationStyle.strokeColor(Color.parseColor("#00000000"));//设置定位蓝点精度圆圈的边框颜色的方法。
myLocationStyle.strokeWidth(2f);//设置定位蓝点精度圈的边框宽度的方法。
myLocationStyle.radiusFillColor(Color.parseColor("#00000000"));//设置定位蓝点精度圆圈的填充颜色的方法。
myLocationStyle.showMyLocation(true);
//gd_aMap.setLocationSource(this);// 设置定位资源
gd_aMap.setMyLocationStyle(myLocationStyle);//设置定位蓝点的Style
//gd_aMap.getUiSettings().setMyLocationButtonEnabled(true);设置默认定位按钮是否显示,非必需设置。
gd_aMap.setMyLocationEnabled(true);// 设置为true表示启动显示定位蓝点,false表示隐藏定位蓝点并不进行定位,默认是false。
// 缩放级别(zoom):地图缩放级别范围为【4-20级】,值越大地图越详细
gd_aMap.moveCamera(CameraUpdateFactory.zoomTo(18));
gd_aMap.getUiSettings().setZoomControlsEnabled(false);//去掉地图右下角隐藏的缩放按钮
//gd_aMap.setOnMyLocationChangeListener(myLocationChangeListener);//3D
try {
mLocationClient = new AMapLocationClient(getApplicationContext());
} catch (Exception e) {
LogUtils.e("gye", "AMapLocationClient Exception: " + e.getMessage());
e.printStackTrace();
// //设置定位回调监听
// mLocationClient.setLocationListener(mLocationListener);
//初始化AMapLocationClientOption对象
mLocationOption = new AMapLocationClientOption();
//设置定位模式为AMapLocationMode.Hight_Accuracy,高精度模式。
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
// //设置定位模式为AMapLocationMode.Battery_Saving,低功耗模式。
// mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Battery_Saving);
// //设置定位模式为AMapLocationMode.Device_Sensors,仅设备模式。
// mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors);
//设置定位场景,目前支持三种场景(签到、出行、运动,默认无场景)
mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport);
if (null != mLocationOption) {
mLocationClient.setLocationOption(mLocationOption);
//设置场景模式后最好调用一次stop,再调用start以保证场景模式生效
mLocationClient.stopLocation();
mLocationClient.startLocation();
//设置绘制线的宽度和颜色
mPolylineOptions = new PolylineOptions().width(10).color(Color.GREEN);
mPolylineOptions2 = new PolylineOptions().setDottedLine(true).width(10).color(Color.GRAY);
markerOptions = new MarkerOptions();
markerOptions.title("start location");
markerOptions.visible(true);
BitmapDescriptor bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(BitmapFactory.decodeResource(getResources(), R.drawable.ic_starting_point));
markerOptions.icon(bitmapDescriptor);
private void initVoiceState() {
isOpenVoice = SmuuApplication.getApplication().isOpenVoice();
if (isOpenVoice) {
mVoice.setBackgroundResource(R.drawable.ic_voice);
} else {
mVoice.setBackgroundResource(R.drawable.ic_not_voice);
@Override
public void onClick(View v) {
if (v == mHistory) {
startActivity(this, SportsRecordActivity.class, Intent.FLAG_ACTIVITY_SINGLE_TOP, false);
} else if (v == mMaxDrawer) {//向下隐藏
mMaxInfo.setVisibility(View.GONE);
mMixInfo.setVisibility(View.VISIBLE);
} else if (v == mMixDrawer) {//向上展示
mMixInfo.setVisibility(View.GONE);
mMaxInfo.setVisibility(View.VISIBLE);
} else if (v == mLockTrue) {
showToast(R.string.activity_sports_longtouch_unlock);
} else if (v == mLock) {
mLockTrueBtnLayout.setVisibility(View.VISIBLE);
mAllBtnLayout.setVisibility(View.GONE);
} else if (v == mStop) {
showToast(R.string.activity_sports_longtouch_over);
} else if (v == mPause) {
isPause = true;
mStopBtnLayout.setVisibility(View.VISIBLE);
mStartBtnLayout.setVisibility(View.VISIBLE);
mPauseBtnLayout.setVisibility(View.GONE);
mRunTime.stop();
mRunTime2.stop();
mRunTime2.setText(mRunTime.getText());
mPauseTime = SystemClock.elapsedRealtime() - mRunTime.getBase();
//LogUtils.i("gy", "mPauseTime: " + mPauseTime);
//LogUtils.i("gy", "sportsTime: " + (SystemClock.elapsedRealtime() - mPauseTime));
} else if (v == mStart) {
isPause = false;
mStopBtnLayout.setVisibility(View.GONE);
mStartBtnLayout.setVisibility(View.GONE);
mPauseBtnLayout.setVisibility(View.VISIBLE);
mRunTime.setBase(SystemClock.elapsedRealtime() - mPauseTime);
mRunTime2.setBase(mRunTime.getBase());
mRunTime.start();
mRunTime2.start();
} else if (v == mVoice) {
if (isOpenVoice) {
isOpenVoice = false;
SmuuApplication.getApplication().setOpenVoice(isOpenVoice);
mVoice.setBackgroundResource(R.drawable.ic_not_voice);
} else {
isOpenVoice = true;
SmuuApplication.getApplication().setOpenVoice(isOpenVoice);
mVoice.setBackgroundResource(R.drawable.ic_voice);
public long startTimeSS = 0;
@SuppressLint("ClickableViewAccessibility")
@Override
public boolean onLongClick(View v) {
startTimeSS = System.currentTimeMillis();
if (v == mLockTrue) {// 解锁
mProgressBarLock.setVisibility(View.VISIBLE);
mProgressBarLock.setMaxNum(360f);
mProgressBarLock.setProgressNum(360f, 1500);
mProgressBarLock.setOnAnimationEndListener(new CircleBarView.OnAnimationEndListener() {
@Override
public void onAnimationEnd() {
if (mProgressBarLock.getProgressNum() == 360f) {
mAllBtnLayout.setVisibility(View.VISIBLE);
mLockTrueBtnLayout.setVisibility(View.GONE);
mProgressBarLock.setVisibility(View.INVISIBLE);
mLockTrue.setOnTouchListener((v1, event) -> {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
int end = (int) ((System.currentTimeMillis() - startTimeSS) / 1000);
//LogUtils.e("gy", "time SS: " + end);
if (end == 3) {
mProgressBarLock.setVisibility(View.INVISIBLE);
mAllBtnLayout.setVisibility(View.VISIBLE);
mLockTrueBtnLayout.setVisibility(View.GONE);
} else {
mProgressBarLock.setProgressNum(0f, 600);
break;
return false;
return true;
} else if (v == mStop) {// 停止运动
mProgressBarStop.setVisibility(View.VISIBLE);
mProgressBarStop.setMaxNum(360f);
mProgressBarStop.setProgressNum(360f, 1500);
mProgressBarStop.setOnAnimationEndListener(new CircleBarView.OnAnimationEndListener() {
@Override
public void onAnimationEnd() {
if (mProgressBarStop.getProgressNum() == 360f) {
saveSportsData();
mProgressBarStop.setVisibility(View.INVISIBLE);
mStop.setOnTouchListener((v1, event) -> {
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
int end = (int) ((System.currentTimeMillis() - startTimeSS) / 1000);
if (end == 3) {
mProgressBarStop.setVisibility(View.INVISIBLE);
} else {
mProgressBarStop.setProgressNum(0f, 600);
break;
return false;
return true;
return false;
* 保存运动数据
public void saveSportsData() {
SQLiteUtil sqLiteUtil = new SQLiteUtil(getApplicationContext());
if (distanceAll <= 0) {//距离为0 删除这条运动数据
LogUtils.e("gy", "distanceAll == 0");
sqLiteUtil.SqlDelete("sportsdata", "date", new String[]{startTime});
} else {
// List<SportsMoveDataEn> allSportsData = sqLiteUtil.getAllSportsData();
// for (SportsMoveDataEn en : allSportsData) {
// LogUtils.d("gy", "startTime :" + en.startTime);
// LogUtils.d("gy", "sportsTime :" + en.sportsTime);
// LogUtils.d("gy", "distance :" + en.distance);
// LogUtils.d("gy", "calories :" + en.calories);
// LogUtils.d("gy", "lat :" + en.lat);
// LogUtils.d("gy", "lng :" + en.lng);
// }
//运动停止后, 记录这次运动数据, 如果逻辑没有走到这里, 则为异常情况,
//当重新打开此界面或进入APP进行判断, 在运动时被中断, 继续进入界面进行运动计算.
String sportsDatas = SmuuApplication.getApplication().getSportsDatas();
if (!sportsDatas.equals("")) {
//String 组拼顺序
//sportsDatas = "sportMode,startTime,sportsTime,distanceStr,aloriesStr,stepNum";
String[] split = sportsDatas.split(",");
SportsNumDataEn dataEn = new SportsNumDataEn();
dataEn.user = userId;
dataEn.deviceId = dviceMac;
dataEn.deviceType = "0";//0手机,1手表 判断数据是否从手表来的
dataEn.index = "-1";
dataEn.sportMode = split[0];
dataEn.startTime = split[1];
dataEn.sportsTime = split[2];
dataEn.pace = "0";//每条记录的平均值
dataEn.stepFreq = "0";//等于总步数 / 总用时
dataEn.distance = split[3];
dataEn.calories = split[4];
dataEn.stepNum = split[5];
dataEn.speed = "0";//每小时速度 = 总距离 / 总用时(分钟) * 60
sqLiteUtil.saveSportsNumData(dataEn);
SmuuApplication.getApplication().setSportsDatas("");//正常完成运动数据记录,重置为空
finish();
private int index = 0;//监听回调次数
private float oldRunTime = 0;//第一次定位的时间
private float allStep = 0;//累计步数
//声明定位回调监听器
public AMapLocationListener mLocationListener = new AMapLocationListener() {
@Override
public void onLocationChanged(AMapLocation aMapLocation) {
if (aMapLocation != null) {
// 定位精度
float accuracy = aMapLocation.getAccuracy();
// 海拔
double altitude = aMapLocation.getAltitude();
// GPS信号状态: 强,弱,未知
int gpsStatus = aMapLocation.getGpsAccuracyStatus();
// LogUtils.i("gy", "accuracy: " + accuracy);
// LogUtils.i("gy", "altitude: " + altitude);
// LogUtils.i("gy", "gpsStatus: " + gpsStatus);
LatLng newLatLng = new LatLng(aMapLocation.getLatitude(), aMapLocation.getLongitude());
if (oldLatLng == null) {
LogUtils.i("gy", "oldLatLng is null");
if (newLatLng.latitude != 0 && newLatLng.longitude != 0) {//经纬度不为0, 才记录
markerOptions.position(newLatLng);
gd_aMap.addMarker(markerOptions);
//第一次定位位置
oldLatLng = newLatLng;
//保存到数据库
SQLiteUtil sqLiteUtil = new SQLiteUtil(getApplicationContext());
SportsMoveDataEn smdEn = new SportsMoveDataEn();
smdEn.user = userId.equals("") ? "000000" : userId;
smdEn.deviceId = dviceMac.equals("1") ? "000000" : dviceMac;
smdEn.sportMode = sportMode;
smdEn.startTime = startTime;
smdEn.sportsTime = "0";
smdEn.distance = "0";
smdEn.calories = "0";
smdEn.stepNum = "0";
smdEn.stepFreq = "0";
smdEn.heartRate = "0";
smdEn.speed = "0";
smdEn.pace = "0";
smdEn.pa = "0";
smdEn.altitude = "0";
smdEn.lat = String.valueOf(newLatLng.latitude);
smdEn.lng = String.valueOf(newLatLng.longitude);
sqLiteUtil.saveSportsData(smdEn);
if (gpsStatus == AMapLocation.GPS_ACCURACY_UNKNOWN) {
mGpsStatusDesc.setVisibility(View.VISIBLE);
mGpsStatusDesc.setText(R.string.activity_sport_gps_status_no);
for (int i = 0; i < gpsImgs.length; i++) {
gpsImgs[i].setBackgroundColor(getColor(R.color.gray));
// 没有GPS信号, 使用传感器
//设置定位模式为AMapLocationMode.Device_Sensors,仅设备模式。
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Device_Sensors);
//设置运动场景
mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport);
//给定位客户端对象设置定位参数
mLocationClient.setLocationOption(mLocationOption);
mLocationClient.stopLocation();
mLocationClient.startLocation();
mPace.setText("- -");
} else {// 有GPS信号
//设置定位模式为AMapLocationMode.Device_Sensors,高精度模式。
mLocationOption.setLocationMode(AMapLocationClientOption.AMapLocationMode.Hight_Accuracy);
//设置运动场景
mLocationOption.setLocationPurpose(AMapLocationClientOption.AMapLocationPurpose.Sport);
//给定位客户端对象设置定位参数
mLocationClient.setLocationOption(mLocationOption);
mLocationClient.stopLocation();
mLocationClient.startLocation();
// 有信号情况
if (gpsStatus == AMapLocation.GPS_ACCURACY_BAD) {// GPS信号弱
mGpsStatusDesc.setVisibility(View.VISIBLE);
mGpsStatusDesc.setText(R.string.activity_sport_gps_status_0);
for (int i = 0; i < gpsImgs.length; i++) {
if (i < 1) {
gpsImgs[i].setBackgroundColor(getColor(R.color.orangered));
} else {
gpsImgs[i].setBackgroundColor(getColor(R.color.gray));
} else if (gpsStatus == AMapLocation.GPS_ACCURACY_GOOD) {// GPS信号强
mGpsStatusDesc.setVisibility(View.GONE);
for (int i = 0; i < gpsImgs.length; i++) {
if (accuracy <= 10) {// 精度10米
gpsImgs[i].setBackgroundColor(getColor(R.color.orangered));
} else if (accuracy > 10 && accuracy <= 30) {
if (i < 2) {
gpsImgs[i].setBackgroundColor(getColor(R.color.orangered));
} else {
gpsImgs[i].setBackgroundColor(getColor(R.color.gray));
} else if (accuracy > 30) {
mGpsStatusDesc.setVisibility(View.VISIBLE);
mGpsStatusDesc.setText(R.string.activity_sport_gps_status_1_low);
if (i < 1) {
gpsImgs[i].setBackgroundColor(getColor(R.color.orangered));
} else {
gpsImgs[i].setBackgroundColor(getColor(R.color.gray));
if (!isPause) {//未点击暂停, 正常记录与绘制
//绘制路线
if (index % 2 == 0) {//监听每回调2次, 进入判断绘制一次
if (aMapLocation.getLatitude() > 0 && aMapLocation.getLongitude() > 0) {
if (oldLatLng.latitude != newLatLng.latitude || oldLatLng.longitude != newLatLng.longitude) {
// 精度20米内或者无GPS信号仅设备传感器模式, 绘制并保存数据
if (accuracy <= 20 || mLocationOption.getLocationMode().equals(AMapLocationClientOption.AMapLocationMode.Device_Sensors)) {
float lineDistance = AMapUtils.calculateLineDistance(oldLatLng, newLatLng);//米
//LogUtils.e("gy", "lineDistance: " + lineDistance);
// 跑步移动的速度每4秒不超过40米, 超过不记录
if (lineDistance > 0 && lineDistance < 40) {
mPolylineOptions.color(Color.GREEN);
mPolylineOptions.setDottedLine(false);// 不为虚线,实线
mPolylineOptions.add(oldLatLng, newLatLng);
Polyline polyline = gd_aMap.addPolyline(mPolylineOptions);
//计算距离
float old_distance = lineDistance / 1000;//公里
distanceAll += old_distance;
String distanceStr = String.format(Locale.ENGLISH, "%.2f", distanceAll);
DecimalFormat df = new DecimalFormat("0.00", new DecimalFormatSymbols(Locale.ENGLISH));
int drinkType = SmuuApplication.getApplication().getDrinkType(SmuuApplication.getApplication().UNIT);
String distance = df.format(Double.parseDouble(distanceStr));
if (drinkType == 0) {// 公制
mDistanceUnit.setText(R.string.activity_target_run_km);
} else {// 英制
mDistanceUnit.setText(R.string.activity_target_run_mile);
distance = df.format(Double.parseDouble(distance) * 0.62);
mDistance.setText(distance);
mDistance2.setText(distance);
//计算卡路里
float weightF = Float.valueOf(weight);
double alories = weightF * distanceAll * 1.036;
String aloriesStr = String.format(Locale.ENGLISH, "%.2f", alories);
mCalories.setText(aloriesStr);
//通过气压计算海拔
float p0 = SensorManager.PRESSURE_STANDARD_ATMOSPHERE;//海平面标准气压
float p = 1014.22f;//假设当前实时气压
float coef = 1.0f / 5.255f;//系数
float h = 44330.0f * (1.0f - (float) Math.pow(p / p0, coef));//海拔
//通过气压sensor API 调用(需要设备有气压传感器)
//float altitude = mSensorManager.getAltitude(SensorManager.PRESSURE_STANDARD_ATMOSPHERE, pa);//pa=实时气压
//通过海拔计算气压
float pa = (float) (p0 * (Math.pow(1.0f - 70.0f / 44330.0f, 5.255f)));
//计算速度, 配速
float newRunTime = UnitConvertUtil.runTime(mRunTime.getText().toString());
float duration = newRunTime - oldRunTime;
//计算速度
float speed = 0;
if (duration > 0) {
speed = lineDistance / duration * 60 * 60 / 1000;
//由于GPS数据误差导致短时间内计算出的速度不合常理, 加判断
if (speed >= 20) {// 速度20公里/小时以上为异常数据置0 (人类极限大约为36公里/小时)
speed = 0;
String speedStr = String.format(Locale.ENGLISH, "%.2f", speed);
//计算配速
float pace = 0;
if (speed > 0) {
float sm = speed * 1000 / 60 / 60;//每秒多少米
pace = 1000f / sm;//每公里多少秒
float m = pace / 60;
float s = pace % 60;
if (m >= 0 && s >= 0) {
mPace.setText((int) m + "'" + (int) s + "\"");
String paceStr = String.format(Locale.ENGLISH, "%.2f", pace);
//计算步数
float stride = UnitConvertUtil.stride();//步幅
float step = lineDistance / stride;
String stepStr = String.format(Locale.ENGLISH, "%.0f", step);
allStep += step;
String allStepStr = String.format(Locale.ENGLISH, "%.0f", allStep);
mStepNum.setText(allStepStr);
//计算步频
float stepFreq = 0f;
if (step > 0 && duration > 0) {
stepFreq = step / duration * 60;
if (stepFreq <= 0) {
stepFreq = 0;
String stepFreqStr = String.format(Locale.ENGLISH, "%.0f", stepFreq);
////////////////计算end///////////////
oldRunTime = newRunTime;
oldLatLng = newLatLng;
SQLiteUtil sqLiteUtil = new SQLiteUtil(getApplicationContext());
SportsMoveDataEn smdEn = new SportsMoveDataEn();
smdEn.user = userId.equals("") ? "000000" : userId;
smdEn.deviceId = dviceMac.equals("1") ? "000000" : dviceMac;
smdEn.sportMode = sportMode;
smdEn.startTime = startTime;
smdEn.sportsTime = String.valueOf(SystemClock.elapsedRealtime() - mRunTime.getBase());
smdEn.distance = distanceStr;
smdEn.calories = aloriesStr;
smdEn.stepNum = stepStr;
smdEn.stepFreq = stepFreqStr;
smdEn.heartRate = "0";//暂无
smdEn.speed = speedStr;
smdEn.pace = paceStr;
smdEn.pa = String.valueOf(pa);
smdEn.altitude = String.valueOf((int)(altitude));
smdEn.lat = String.valueOf(newLatLng.latitude);
smdEn.lng = String.valueOf(newLatLng.longitude);
//保存最新记录, 异常情况重新进入, 继续从断点记录
String sportsDatas = sportMode + "," + startTime + "," +
smdEn.sportsTime + "," + distanceStr + "," +
aloriesStr + "," + smdEn.stepNum;
SmuuApplication.getApplication().setSportsDatas(sportsDatas);
sqLiteUtil.saveSportsData(smdEn);
} else {
//偏移超过40米不记录这条GPS数据
mPolylineOptions.color(Color.GRAY);
mPolylineOptions.setDottedLine(true);// 为虚线
mPolylineOptions.add(oldLatLng, newLatLng);
Polyline polyline = gd_aMap.addPolyline(mPolylineOptions);
oldLatLng = newLatLng;
} else {
//mPace.setText("- -");
index++;
* 倒计时3秒
private int millIndex = 3;
private Animation animation;
public OnCountDownTimerListener mOnCountDownTimerListener = new OnCountDownTimerListener() {
@Override
public void onTick(long millisUntilFinished) {
long mill = millisUntilFinished / 1000;
if (animation != null) {
animation.reset();
if (millIndex > 0) {
mTemrTxt.setText(String.valueOf(mill));
animation = AnimationUtils.loadAnimation(SportsGDActivity.this, R.anim.sets);
mTemrTxt.startAnimation(animation);
startVoice(String.valueOf(mill));
millIndex--;
@Override
public void onFinish() {
WindowManager.LayoutParams attrs = getWindow().getAttributes();
attrs.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setAttributes(attrs);
getWindow().clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
mTemrTxt.clearAnimation();
mStartLayout.setVisibility(View.GONE);
mMapLayout.setVisibility(View.VISIBLE);
startVoice(getString(R.string.activity_sports_sports_start));// 运动开始 go
if (sportsDatas.equals("")) {
// 开始计时
mRunTime.setBase(SystemClock.elapsedRealtime());
mRunTime2.setBase(mRunTime.getBase());
int hour = (int) ((SystemClock.elapsedRealtime() - mRunTime.getBase()) / 1000 / 60 / 60);
mRunTime.setFormat("0" + String.valueOf(hour) + ":%s");
mRunTime2.setFormat("0" + String.valueOf(hour) + ":%s");
mRunTime.start();
mRunTime2.start();
} else {
String[] split = sportsDatas.split(",");
LogUtils.e("gy", "time: " + split[2]);
mRunTime.setBase(SystemClock.elapsedRealtime() - Long.parseLong(split[2]));
mRunTime2.setBase(mRunTime.getBase());
int hour = (int) ((SystemClock.elapsedRealtime() - mRunTime.getBase()) / 1000 / 60 / 60);
mRunTime.setFormat("0" + String.valueOf(hour) + ":%s");
mRunTime2.setFormat("0" + String.valueOf(hour) + ":%s");
mRunTime.start();
mRunTime2.start();
// if (SmuuApplication.getApplication().getCurrConnectState()) {// 连接了手表
// // 发送 开始GPS运动指令给手表, 经纬度使用APP的, 其他数据使用手表的(扩展预留项)
// } else {
// //设置定位回调监听
// mLocationClient.setLocationListener(mLocationListener);
// }
//设置定位回调监听
mLocationClient.setLocationListener(mLocationListener);
@Override
public void onCancel() {
* 实例化自带语音对象
private void initTTS() {
mTextToSpeech = new TextToSpeech(this, new TextToSpeech.OnInitListener() {
@Override
public void onInit(int status) {
if (status == mTextToSpeech.SUCCESS) {
mTextToSpeech.setPitch(1.2f);//设置音调,值越大声音越尖(女生),值越小则变成男声,1.0是常规
mTextToSpeech.setSpeechRate(1.5f);//用来控制语速
//支持下面两种语言
if (!SystemUtil.getSystemLanguage().contains("zh")) {
mTextToSpeech.setLanguage(Locale.ENGLISH);
} else {
mTextToSpeech.setLanguage(Locale.CHINESE);
startVoice("3");
} else {
LogUtils.e("gye", "Device does not support or data is lost 设备不支持或数据丢失");
//showToast("数据丢失或不支持");
* @param voice
private void startVoice(String voice) {
if (isOpenVoice) {
// 输入文字,若不支持的设备则不会读出来
mTextToSpeech.speak(voice, TextToSpeech.QUEUE_FLUSH, null, null);
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
showToast(R.string.activity_sports_click_puase_a_sports_over);
return true;
return super.onKeyDown(keyCode, event);
}
activity_sports_gd.xml
<?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=".activity.SportsGDActivity">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:id="@+id/mapLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:visibility="visible">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!--<com.amap.api.maps2d.MapView-->
<com.amap.api.maps.MapView
android:id="@+id/gd_map"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1" />
<LinearLayout
android:id="@+id/maxInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="40dp"
android:background="@color/white"
android:orientation="vertical">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/maxDrawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/ic_max_drawer" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp">
<TextView
android:id="@+id/distance"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="0.00"
android:textColor="@color/black"
android:textSize="36sp" />
<TextView
android:id="@+id/distanceUnit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="5dp"
android:layout_toEndOf="@+id/distance"
android:gravity="center"
android:text="@string/activity_target_run_km"
android:textColor="@color/black"
android:textSize="16sp" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/activity_sports_sports_time"
android:textColor="@color/gray" />
<Chronometer
android:id="@+id/runTime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format="00:00:00"
android:text="00:00:00"
android:textColor="@color/black"
android:textSize="20dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/activity_target_run_kcal"
android:textColor="@color/gray" />
<TextView
android:id="@+id/calories"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="0"
android:textColor="@color/black"
android:textSize="20dp" />
</LinearLayout>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="5dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/activity_sports_pace"
android:textColor="@color/gray" />
<TextView
android:id="@+id/pace"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="- -"
android:textColor="@color/black"
android:textSize="20dp" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/fragment_steps_num"
android:textColor="@color/gray" />
<TextView
android:id="@+id/step_num"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="- -"
android:textColor="@color/black"
android:textSize="20dp" />
</LinearLayout>
</LinearLayout>
<!--<View
android:layout_width="match_parent"
android:layout_height="2dp"
android:layout_margin="10dp"
android:background="?android:attr/listDivider" />-->
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_marginTop="20dp">
<RelativeLayout
android:id="@+id/lock_true_btn_layout"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:visibility="gone">
<com.yangsion.test.view.CircleBarView
android:id="@+id/progressBarLock"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerInParent="true"
android:visibility="invisible"
app:bar_width="8dp"
app:bg_color="@color/white"
app:progress_color="@color/gray"
app:start_angle="-100"
app:sweep_angle="360" />
<ImageButton
android:id="@+id/lock_true"
android:layout_width="55dp"
android:layout_height="55dp"
android:layout_centerInParent="true"
android:background="@drawable/bg_health_item_main_indicator_view2"
android:src="@drawable/ic_lock" />
</RelativeLayout>
<LinearLayout
android:id="@+id/all_btn_layout"
android:layout_width="match_parent"
android:layout_height="90dp"
android:layout_centerInParent="true"
android:layout_weight="1"
android:gravity="center_vertical"
android:orientation="horizontal"
android:paddingStart="20dp"
android:paddingEnd="20dp"
android:visibility="visible">
<RelativeLayout
android:id="@+id/lock_btn_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageButton
android:id="@+id/lock"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/ic_lock" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/stop_btn_Layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="gone">
<com.yangsion.test.view.CircleBarView
android:id="@+id/progressBarStop"
android:layout_width="70dp"
android:layout_height="70dp"
android:layout_centerInParent="true"
android:visibility="invisible"
app:bar_width="8dp"
app:bg_color="@color/white"
app:progress_color="@color/gray"
app:start_angle="-100"
app:sweep_angle="360" />
<ImageButton
android:id="@+id/stop"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
android:background="@drawable/ic_stop" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/pause_btn_Layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="visible">
<ImageButton
android:id="@+id/pause"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
android:background="@drawable/ic_pause" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/start_btn_Layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1"
android:visibility="gone">
<ImageButton
android:id="@+id/start"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_centerInParent="true"
android:background="@drawable/ic_start2" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/voice_btn_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<ImageButton
android:id="@+id/voice"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/ic_voice" />
</RelativeLayout>
</LinearLayout>
</RelativeLayout>
</LinearLayout>
<LinearLayout
android:id="@+id/mixInfo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="40dp"
android:background="@color/white"
android:orientation="vertical"
android:visibility="gone">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageButton
android:id="@+id/mixDrawer"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="@drawable/ic_mix_drawer" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="horizontal">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
<TextView
android:id="@+id/distance2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text="0.00"
android:textColor="@color/black"
android:textSize="36sp" />
<TextView
android:id="@+id/runNumUnit2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginStart="5dp"
android:layout_toEndOf="@+id/distance2"
android:gravity="center"
android:text="@string/activity_target_run_km"
android:textColor="@color/black"
android:textSize="16sp" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:orientation="vertical">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/activity_sports_sports_time"
android:textColor="@color/gray" />
<Chronometer
android:id="@+id/runTime2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:format="00:00:00"
android:text="00:00:00"
android:textColor="@color/black"
android:textSize="20dp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="20dp"
android:layout_marginTop="40dp">
<androidx.cardview.widget.CardView
android:id="@+id/card_gps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:cardBackgroundColor="@color/white"
app:cardCornerRadius="6dp"
android:layout_centerVertical="true"
app:cardElevation="2dp"
app:contentPadding="3dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<TextView
android:id="@+id/gps_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:text="GPS"
android:textColor="@color/black"
android:textSize="10sp" />
<RelativeLayout
android:layout_width="wrap_content"
android:layout_height="10dp"
android:layout_gravity="center_vertical"
android:layout_marginStart="5dp">
<ImageView
android:id="@+id/gps_signal0"
android:layout_width="2dp"
android:layout_height="4dp"
android:layout_alignParentBottom="true"
android:background="@color/gray" />
<ImageView
android:id="@+id/gps_signal1"
android:layout_width="2dp"
android:layout_height="6dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="2dp"
android:layout_toEndOf="@+id/gps_signal0"
android:background="@color/gray" />
<ImageView
android:id="@+id/gps_signal2"
android:layout_width="2dp"
android:layout_height="8dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="2dp"
android:layout_toEndOf="@+id/gps_signal1"
android:background="@color/gray" />
<ImageView
android:id="@+id/gps_signal3"
android:layout_width="2dp"
android:layout_height="10dp"
android:layout_alignParentBottom="true"
android:layout_marginStart="2dp"
android:layout_toEndOf="@+id/gps_signal2"
android:background="@color/gray" />
</RelativeLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
<TextView
android:id="@+id/gps_status_desc"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toEndOf="@+id/card_gps"
android:padding="5dp"
android:layout_centerVertical="true"
android:layout_marginStart="20dp"
android:textColor="@color/white"
android:visibility="gone"
android:textSize="10sp"
android:background="@drawable/bg_gps_status_desc"
android:text=""/>
</RelativeLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="@+id/history"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_marginTop="30dp"
android:layout_marginEnd="20dp"
android:clickable="true"
android:padding="5dp"
android:scaleType="center"
android:visibility="gone"
app:backgroundTint="@color/white"
app:elevation="6dp"
app:fabCustomSize="36dp"
app:rippleColor="@color/gray"
app:srcCompat="@drawable/ic_history" />
</RelativeLayout>
</LinearLayout>
<RelativeLayout
android:id="@+id/startLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/orangereds"
android:visibility="visible">
<TextView
android:id="@+id/temrTxt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:gravity="center"
android:text=""
android:textColor="@color/white"
android:textSize="100sp" />
</RelativeLayout>
</RelativeLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
CircleBarView.java
package com.yangsion.test.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.view.View;
import android.view.animation.Animation;
import android.view.animation.Transformation;
import android.widget.TextView;
import android.widget.Toast;
import com.yangsion.test.R;
public class CircleBarView extends View {
private Paint bgPaint;//绘制背景圆弧的画笔
private Paint progressPaint;//绘制圆弧的画笔
private RectF mRectF;//绘制圆弧的矩形区域
private CircleBarAnim anim;
private float progressNum;//可以更新的进度条数值
private float maxNum;//进度条最大值
private int progressColor;//进度条圆弧颜色
private int bgColor;//背景圆弧颜色
private float startAngle;//背景圆弧的起始角度
private float sweepAngle;//背景圆弧扫过的角度
private float barWidth;//圆弧进度条宽度
private int defaultSize;//自定义View默认的宽高
private float progressSweepAngle;//进度条圆弧扫过的角度
private TextView textView;
private OnAnimationListener onAnimationListener;
private Context context;
public CircleBarView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
init(context,attrs);
private void init(Context context,AttributeSet attrs){
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.CircleBarView);
progressColor = typedArray.getColor(R.styleable.CircleBarView_progress_color, getResources().getColor(R.color.orangereds,null));
bgColor = typedArray.getColor(R.styleable.CircleBarView_bg_color,getResources().getColor(R.color.white,null));
startAngle = typedArray.getFloat(R.styleable.CircleBarView_start_angle,0);
sweepAngle = typedArray.getFloat(R.styleable.CircleBarView_sweep_angle,360);
barWidth = typedArray.getDimension(R.styleable.CircleBarView_bar_width,UIUtils.dp2px(context,10));
typedArray.recycle();
progressNum = 0;
maxNum = 100;
defaultSize = UIUtils.dp2px(context,150);
mRectF = new RectF();
progressPaint = new Paint();
progressPaint.setStyle(Paint.Style.STROKE);//只描边,不填充
progressPaint.setColor(progressColor);
progressPaint.setAntiAlias(true);//设置抗锯齿
progressPaint.setStrokeWidth(barWidth);
progressPaint.setStrokeCap(Paint.Cap.ROUND);//设置画笔为圆角
bgPaint = new Paint();
bgPaint.setStyle(Paint.Style.STROKE);//只描边,不填充
bgPaint.setColor(bgColor);
bgPaint.setAntiAlias(true);//设置抗锯齿
bgPaint.setStrokeWidth(barWidth);
bgPaint.setStrokeCap(Paint.Cap.ROUND);
anim = new CircleBarAnim();
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
int height = measureSize(defaultSize, heightMeasureSpec);
int width = measureSize(defaultSize, widthMeasureSpec);
int min = Math.min(width, height);// 获取View最短边的长度
setMeasuredDimension(min, min);// 强制改View为以最短边为长度的正方形
if(min >= barWidth*2){
mRectF.set(barWidth/2,barWidth/2,min-barWidth/2,min-barWidth/2);
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
canvas.drawArc(mRectF,startAngle,sweepAngle,false,bgPaint);
canvas.drawArc(mRectF,startAngle,progressSweepAngle,false, progressPaint);
public class CircleBarAnim extends Animation {
public CircleBarAnim(){
@Override
protected void applyTransformation(float interpolatedTime, Transformation t) {//interpolatedTime从0渐变成1,到1时结束动画,持续时间由setDuration(time)方法设置
super.applyTransformation(interpolatedTime, t);
progressSweepAngle = interpolatedTime * sweepAngle * progressNum / maxNum;
if(onAnimationListener!=null){
if(textView !=null){
textView.setText(onAnimationListener.howToChangeText(interpolatedTime, progressNum,maxNum));
onAnimationListener.howTiChangeProgressColor(progressPaint,interpolatedTime, progressNum,maxNum);
postInvalidate();
public interface OnAnimationEndListener {
void onAnimationEnd();
public OnAnimationEndListener onAnimationEndListener;
public void setOnAnimationEndListener(OnAnimationEndListener onAnimationEndListener){
this.onAnimationEndListener = onAnimationEndListener;
private int measureSize(int defaultSize,int measureSpec) {
int result = defaultSize;
int specMode = View.MeasureSpec.getMode(measureSpec);
int specSize = View.MeasureSpec.getSize(measureSpec);
if (specMode == View.MeasureSpec.EXACTLY) {
result = specSize;
} else if (specMode == View.MeasureSpec.AT_MOST) {
result = Math.min(result, specSize);
return result;
* 设置进度条最大值
* @param maxNum
public void setMaxNum(float maxNum) {
this.maxNum = maxNum;
* 设置进度条数值
* @param progressNum 进度条数值
* @param time 动画持续时间
public void setProgressNum(float progressNum, int time) {
this.progressNum = progressNum;
if (time > 0) {
anim.setDuration(time);
this.startAnimation(anim);
* 获取进度条数值
public float getProgressNum() {
return this.progressNum;
@Override
protected void onAnimationEnd() {
super.onAnimationEnd();
onAnimationEndListener.onAnimationEnd();
* 设置显示文字的TextView
* @param textView
public void setTextView(TextView textView) {
this.textView = textView;
public interface OnAnimationListener {
* 如何处理要显示的文字内容
* @param interpolatedTime 从0渐变成1,到1时结束动画
* @param updateNum 进度条数值
* @param maxNum 进度条最大值
* @return
String howToChangeText(float interpolatedTime, float updateNum, float maxNum);
* 如何处理进度条的颜色
* @param paint 进度条画笔
* @param interpolatedTime 从0渐变成1,到1时结束动画
* @param updateNum 进度条数值
* @param maxNum 进度条最大值
void howTiChangeProgressColor(Paint paint, float interpolatedTime, float updateNum, float maxNum);
public void setOnAnimationListener(OnAnimationListener onAnimationListener) {
this.onAnimationListener = onAnimationListener;
attrs.xml
<!--注意这里的name要和自定义View的名称一致,不然在xml布局中无法引用-->
<declare-styleable name="CircleBarView">
<attr name="progress_color" format="color"></attr>
<attr name="bg_color" format="color"></attr>
<attr name="bar_width" format="dimension"></attr>
<attr name="start_angle" format="float"></attr>
<attr name="sweep_angle" format="float"></attr>
</declare-styleable>
2)绘制数据库获取的GPS数据
为下图代码
package com.yangsion.test.fragment.sports.record;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.RelativeLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import com.amap.api.location.AMapLocationClient;
import com.amap.api.location.AMapLocationClientOption;
import com.amap.api.maps.AMap;
import com.amap.api.maps.CameraUpdate;
import com.amap.api.maps.CameraUpdateFactory;
import com.amap.api.maps.CoordinateConverter;
import com.amap.api.maps.MapView;
import com.amap.api.maps.model.BitmapDescriptorFactory;
import com.amap.api.maps.model.CameraPosition;
import com.amap.api.maps.model.LatLng;
import com.amap.api.maps.model.LatLngBounds;
import com.amap.api.maps.model.Marker;
import com.amap.api.maps.model.MarkerOptions;
import com.amap.api.maps.model.MyLocationStyle;
import com.amap.api.maps.model.PolylineOptions;
import com.amap.api.maps.utils.SpatialRelationUtil;
import com.amap.api.maps.utils.overlay.MovingPointOverlay;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.yangsion.test.R;
import com.yangsion.test.activity.BaseActivity;
import com.yangsion.test.activity.SharePreviewActivity;
import com.yangsion.test.application.SmuuApplication;
import com.yangsion.test.db.SQLiteUtil;
import com.yangsion.test.en.SportsMoveDataEn;
import com.yangsion.test.en.SportsNumDataEn;
import com.yangsion.test.util.UnitConvertUtil;
import java.io.File;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
public class TrajectoryFragment extends Fragment implements View.OnClickListener {
private MapView mGdMap;
private RelativeLayout mDataView;
private TextView mDistance;
private TextView mDistanceUnit;
private TextView mStartTime;
private TextView mSportsTime;
private TextView mAveragePace;
private TextView mCalories;
private FloatingActionButton mHiedView;
private FloatingActionButton mSendView;
private MyLocationStyle myLocationStyle;
private AMap gd_aMap = null;
//声明AMapLocationClient类对象
public AMapLocationClient mLocationClient = null;
//声明AMapLocationClientOption对象
public AMapLocationClientOption mLocationOption = null;
public CameraUpdate cameraUpdate = null;
private SportsNumDataEn sportsNumDataEn = null;
private List<LatLng> latlngList = new ArrayList<>();
//是否显示地图内容
private boolean isHiedView = false;
public static TrajectoryFragment newInstance(Bundle sndEn_bundle) {
TrajectoryFragment fragment = new TrajectoryFragment();
fragment.setArguments(sndEn_bundle);
return fragment;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
sportsNumDataEn = (SportsNumDataEn) getArguments().getSerializable("sndEn");
@Override
public View onCreateView(
@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_trajectory, container, false);
initView(root, savedInstanceState);
initData();
return root;
@Override
public void onResume() {
super.onResume();
mGdMap.onResume();
@Override
public void onPause() {
super.onPause();
mGdMap.onPause();
@Override
public void onDestroy() {
super.onDestroy();
mGdMap.onDestroy();
private void initView(View root, Bundle savedInstanceState) {
mGdMap = (MapView) root.findViewById(R.id.gd_map);
mDataView = (RelativeLayout) root.findViewById(R.id.dataView);
mGdMap.onCreate(savedInstanceState);
mDistance = (TextView) root.findViewById(R.id.distance);
mDistanceUnit = (TextView) root.findViewById(R.id.distanceUnit);
mStartTime = (TextView) root.findViewById(R.id.startTime);
mSportsTime = (TextView) root.findViewById(R.id.sportsTime);
mAveragePace = (TextView) root.findViewById(R.id.averagePace);
mCalories = (TextView) root.findViewById(R.id.calories);
mHiedView = (FloatingActionButton) root.findViewById(R.id.hiedView);
mHiedView.setOnClickListener(this::onClick);
mSendView = (FloatingActionButton) root.findViewById(R.id.sendView);
mSendView.setOnClickListener(this::onClick);
private void initData() {
UnitConvertUtil.drinkType(sportsNumDataEn.distance, mDistance, mDistanceUnit);
mStartTime.setText(sportsNumDataEn.startTime);
if (sportsNumDataEn.deviceType.equals("0")) {//0手机,1手表
mSportsTime.setText(UnitConvertUtil.getGapTime(Long.parseLong(sportsNumDataEn.sportsTime)));
} else {
mSportsTime.setText(UnitConvertUtil.getGapTime2(Long.parseLong(sportsNumDataEn.sportsTime)));
mCalories.setText(sportsNumDataEn.calories);
if (gd_aMap == null) {
gd_aMap = mGdMap.getMap();
SQLiteUtil sqLiteUtil = new SQLiteUtil(getActivity().getApplicationContext());
List<SportsMoveDataEn> allSportsData = sqLiteUtil.getAllSportsData();
// LogUtils.d("gy", "allSportsData.size :" + allSportsData.size());
if (allSportsData.size() > 0) {
float allPace = 0;
for (SportsMoveDataEn en : allSportsData) {
// LogUtils.e("gy", "sportsNumDataEn.startTime :" + sportsNumDataEn.startTime);
// LogUtils.e("gy", "en.startTime :" + en.startTime);
if (sportsNumDataEn.startTime.equals(en.startTime)) {
// LogUtils.d("gy", "startTime :" + en.startTime);
// LogUtils.d("gy", "distance :" + en.distance);
// LogUtils.d("gy", "calories :" + en.calories);
// LogUtils.d("gy", "pace :" + en.pace);
// LogUtils.d("gy", "lat :" + en.lat);
// LogUtils.d("gy", "lng :" + en.lng);
allPace += Float.parseFloat(en.pace);
LatLng latLng = new LatLng(Double.parseDouble(en.lat), Double.parseDouble(en.lng));
if (sportsNumDataEn.deviceType.equals("1")) {
CoordinateConverter converter = new CoordinateConverter(getActivity());
// CoordType.GPS 待转换坐标类型
converter.from(CoordinateConverter.CoordType.GPS);
// sourceLatLng待转换坐标点 LatLng类型
converter.coord(latLng);
// 执行转换操作
latLng = converter.convert();
// LogUtils.d("gy", "latitude:" + latLng.latitude);
// LogUtils.d("gy", "longitude:" + latLng.longitude);
latlngList.add(latLng);
if (sportsNumDataEn.deviceType.equals("0")) {
UnitConvertUtil.averagePace(allPace / allSportsData.size(), mAveragePace, null);
} else {
mAveragePace.setText(sportsNumDataEn.pace);
gd_aMap.clear();
gd_aMap.setMapType(AMap.MAP_TYPE_NORMAL);
gd_aMap.getUiSettings().setZoomControlsEnabled(false);//去掉地图右下角隐藏的缩放按钮
if (latlngList.size() > 0) {
LatLng replayGeoPoint = latlngList.get(0);
cameraUpdate = CameraUpdateFactory.newCameraPosition(new CameraPosition(replayGeoPoint, 15, 10, 10));
gd_aMap.moveCamera(cameraUpdate);
mHandler.sendEmptyMessageDelayed(200, 1000);
@Override
public void onClick(View v) {
if (v == mHiedView) {
if (isHiedView) {
//gd_aMap.setTrafficEnabled(true);
gd_aMap.showBuildings(true);
gd_aMap.showMapText(true);
//gd_aMap.setConstructingRoadEnable(true);
gd_aMap.setMapType(AMap.MAP_TYPE_NORMAL);
isHiedView = false;
} else {
hiedView();
isHiedView = true;
} else if (v == mSendView) {// 分享预览
int measuredWidth = mDataView.getMeasuredWidth();
int measuredHeight = mDataView.getMeasuredHeight();
Bitmap bitmap = Bitmap.createBitmap(measuredWidth, measuredHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
mDataView.draw(canvas);
SmuuApplication.getApplication().bitmaps[1] = bitmap;
// 获取高德地图Bitmap
gd_aMap.getMapScreenShot(new AMap.OnMapScreenShotListener() {
@Override
public void onMapScreenShot(Bitmap bitmap) {
@Override
public void onMapScreenShot(Bitmap bitmap, int status) {
if (null != bitmap) {
SmuuApplication.getApplication().bitmaps[0] = bitmap;
BaseActivity.startActivity(getActivity(), SharePreviewActivity.class, Intent.FLAG_ACTIVITY_SINGLE_TOP, false);
* 不显示地图内容
private void hiedView() {
gd_aMap.setTrafficEnabled(false);
gd_aMap.showBuildings(false);
gd_aMap.showMapText(false);
gd_aMap.setConstructingRoadEnable(false);
String style = copyFile("gd_map_style/style.data", "/GD_MAP_STYLE", "style.data");
String style_extra = copyFile("gd_map_style/style_extra.data", "/GD_MAP_STYLE", "style_extra.data");
gd_aMap.setCustomMapStyle(
new com.amap.api.maps.model.CustomMapStyleOptions()
.setEnable(true)
.setStyleDataPath(style)
.setStyleExtraPath(style_extra)
// .setStyleTexturePath("/mnt/sdcard/amap/textures.zip")
.setStyleTexturePath(null)
private String copyFile(String path, String parent, String name) {
// "GD_MAP_STYLE" "GOOGLE_MAP_STYLE"
String path_sd = getActivity().getExternalFilesDir(null).getPath() + parent;
try {
File file_folder = new File(path_sd);
if (!file_folder.exists()) {
file_folder.mkdirs();
File file = new File(path_sd, name);
InputStream inputStream = getActivity().getAssets().open(path);
int index;
byte[] bytes = new byte[1024];
FileOutputStream loadFile = new FileOutputStream(file);
while ((index = inputStream.read(bytes)) != -1) {
loadFile.write(bytes, 0, index);
loadFile.flush();
inputStream.close();
loadFile.close();
return file.getAbsolutePath();
} catch (Exception exception) {
exception.printStackTrace();
return null;
private void drawLine() {
LatLng replayGeoPoint = latlngList.get(0);
LatLng replayEndPoint = latlngList.get(latlngList.size() - 1);
LatLngBounds bounds = new LatLngBounds(latlngList.get(0), latlngList.get(latlngList.size() - 2));
gd_aMap.animateCamera(CameraUpdateFactory.newLatLngBounds(bounds, 50));
/*已过时
//SmoothMoveMarker smoothMarker = new SmoothMoveMarker(gd_aMap);
//// 设置滑动的图标
//smoothMarker.setDescriptor(BitmapDescriptorFactory.fromResource(R.drawable.danwiqiehuan));
// 添加位置
MarkerOptions markerOptions = new MarkerOptions();
markerOptions
//.position(replayGeoPoint)
.title("移动icon")
.snippet("")
.icon(BitmapDescriptorFactory
.fromBitmap(BitmapFactory.decodeResource(
getResources(), R.drawable.ic_line_head)))
.anchor(0.5f, 0.5f);
Marker marker = gd_aMap.addMarker(markerOptions);
MovingPointOverlay smoothMarker = new MovingPointOverlay(gd_aMap, marker);
LatLng drivePoint = latlngList.get(0);
Pair<Integer, LatLng> pair = SpatialRelationUtil.calShortestDistancePoint(latlngList, drivePoint);
latlngList.set(pair.first, drivePoint);
List<LatLng> subList = latlngList.subList(pair.first, latlngList.size());
// 设置滑动的轨迹左边点
smoothMarker.setPoints(subList);
// 设置滑动的总时间
if (latlngList.size() < 20) {
smoothMarker.setTotalDuration(1);
} else if (latlngList.size() < 50) {
smoothMarker.setTotalDuration(2);
} else {
smoothMarker.setTotalDuration(5);
// 开始滑动
smoothMarker.startSmoothMove();
// 移动监听
LatLng[] oldLatlng = {latlngList.get(0)};
smoothMarker.setMoveListener(new MovingPointOverlay.MoveListener() {
@Override
public void move(double v) {
// LogUtils.i("gy", "move: " + v);
if (v == 0) {
//hiedView();
marker.remove();
gd_aMap.addMarker(new MarkerOptions()
.position(replayEndPoint)
.title("终点")
.icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory
.decodeResource(
getResources(),
R.drawable.ic_stop_point))));
cameraUpdate = CameraUpdateFactory.newCameraPosition(new CameraPosition(smoothMarker.getPosition(), 18, 10, 10));
gd_aMap.setMapType(AMap.MAP_TYPE_NORMAL);
gd_aMap.animateCamera(cameraUpdate);
// 绘制移动线
if (oldLatlng[0].latitude != smoothMarker.getPosition().latitude &&
oldLatlng[0].longitude != smoothMarker.getPosition().longitude) {
PolylineOptions polylineOptions = new PolylineOptions()
.add(oldLatlng[0], smoothMarker.getPosition())
.color(Color.GREEN).width(10f);
gd_aMap.addPolyline(polylineOptions);
oldLatlng[0] = smoothMarker.getPosition();
// 经纬度转屏幕像素
// Point point = gd_aMap.getProjection().toScreenLocation(smoothMarker.getPosition());
// LogUtils.d("gy_view", "point.x: " + point.x);
// LogUtils.d("gy_view", "point.y: " + point.y);
gd_aMap.addMarker(new MarkerOptions()
.position(replayGeoPoint)
.title("起点")
.icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory
.decodeResource(
getResources(),
R.drawable.ic_starting_point))));
// gd_aMap.addMarker(new MarkerOptions()
// .position(replayEndPoint)
// .title("终点")
// .icon(BitmapDescriptorFactory.fromBitmap(BitmapFactory
// .decodeResource(
// getResources(),
// R.drawable.ic_stop_point))));
public Handler mHandler = new Handler(new WeakReference<Handler.Callback>(new Handler.Callback() {
@Override
public boolean handleMessage(@NonNull Message msg) {
if (msg.what == 200) {
if (isAdded()) {
drawLine();
return true;