1.route页ui功能改版

This commit is contained in:
yezhiqiu
2026-04-14 17:10:34 +08:00
parent 20ff76b933
commit 69aa917897
50 changed files with 1584 additions and 67 deletions

View File

@@ -28,9 +28,9 @@ android {
applicationId "com.abbidot.tracker"
minSdkVersion 23
targetSdkVersion 35
versionCode 2113
// versionName "2.1.13"
versionName "2.1.13-Beta3"
versionCode 2202
// versionName "2.2.2"
versionName "2.2.2-Beta1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -55,7 +55,6 @@
android:protectionLevel="signature" /> <!-- Optional. Required for location feature -->
<uses-permission android:name="${applicationId}.permission.JPUSH_MESSAGE" /> <!-- 为了提高sdk识别唯一用户的能力保证消息推送的精准送达建议集成以下权限可选 -->
<!-- <uses-permission -->
<!-- android:name="android.permission.QUERY_ALL_PACKAGES" -->
<!-- tools:ignore="QueryAllPackagesPermission" /> -->
<!-- 如您需要接入地理围栏业务,建议集成以下权限(可选) -->
<uses-permission android:name="android.permission.GET_TASKS" /> <!-- 如您需要对应设备通知相关的能力,建议集成以下权限(可选) -->

View File

@@ -0,0 +1,67 @@
package com.abbidot.tracker.adapter
import android.content.Context
import android.text.format.DateUtils
import android.view.View
import com.abbidot.baselibrary.list.BaseRecyclerAdapter
import com.abbidot.baselibrary.list.RecyclerViewHolder
import com.abbidot.baselibrary.util.Utils
import com.abbidot.tracker.R
import com.abbidot.tracker.bean.MessageBean
import com.abbidot.tracker.constant.ConstantInt
/**
*Created by .yzq on 2026/4/10/周五.
* @link
* @description:
*/
class HistoryFenceAdapter(
ctx: Context, list: MutableList<MessageBean>?
) : BaseRecyclerAdapter<MessageBean>(ctx, list) {
override fun getEmptyLayoutId(viewType: Int) = 0
override fun getItemLayoutId(viewType: Int) = R.layout.item_history_fence_layout
override fun bindData(holder: RecyclerViewHolder?, position: Int, item: MessageBean) {
holder?.apply {
getImageView(R.id.iv_history_fence_type_image).let {
when (item.deviceMessageType) {
ConstantInt.Type1, ConstantInt.Type3 -> it.setImageResource(R.drawable.icon_history_danger_fence_image)
ConstantInt.Type2 -> it.setImageResource(R.drawable.icon_history_save_fence_image)
ConstantInt.Type4 -> it.setImageResource(R.drawable.icon_history_no_save_fence_image)
}
}
getTextView(R.id.tv_history_fence_type_time).text =
getRelativeTimeString(item.timeStamp)
getTextView(R.id.tv_history_fence_type_content).text = item.message
getImageView(R.id.iv_history_fence_type_line).let {
it.visibility = if (position == getData().size - 1) View.GONE
else View.VISIBLE
}
}
}
private fun getRelativeTimeString(timeStamp: Long): String {
val formatDateStr: String
val cTimeStamp = Utils.stringToTimestamp(
Utils.formatTime(
timeStamp, Utils.DATE_FORMAT_PATTERN_CN2
)
)
val now = System.currentTimeMillis()
formatDateStr = if (DateUtils.isToday(cTimeStamp)) {
mContext.getString(R.string.txt_today) + " " + Utils.formatTime(
timeStamp, Utils.DATE_FORMAT_PATTERN_EN14
)
} else if (now - cTimeStamp < (48 * 60 * 60 * 1000)) {
mContext.getString(R.string.txt_yesterday) + " " + Utils.formatTime(
timeStamp, Utils.DATE_FORMAT_PATTERN_EN14
)
} else {
Utils.formatTime(
timeStamp, Utils.DATE_FORMAT_PATTERN_EN15
)
}
return formatDateStr
}
}

View File

@@ -58,7 +58,7 @@ class NotificationV2Adapter(
}
private fun getRelativeTimeString(timeStamp: Long): String {
var formatDateStr = ""
val formatDateStr: String
val cTimeStamp = Utils.stringToTimestamp(
Utils.formatTime(
timeStamp, Utils.DATE_FORMAT_PATTERN_CN2

View File

@@ -0,0 +1,40 @@
package com.abbidot.tracker.adapter;
import android.content.Context;
import android.view.View;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by .yzq on 2026/4/13/周一.
*
* @link
* @description:
*/
public class TopSlideLayoutManager extends LinearLayoutManager {
private int mTopOffset;
public TopSlideLayoutManager(Context context, int orientation, boolean reverseLayout) {
super(context, orientation, reverseLayout);
}
public void setTopOffset(int topOffset) {
mTopOffset = topOffset;
requestLayout();
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
// 可以在这里调整子项的位置,例如将顶部几个项上移
if (getChildCount() > 0) {
View firstChild = getChildAt(0);
if (firstChild != null) {
int top = getDecoratedTop(firstChild) - mTopOffset;
layoutDecoratedWithMargins(firstChild, firstChild.getLeft(), top, firstChild.getRight(), top + firstChild.getHeight());
}
}
}
}

View File

@@ -25,7 +25,7 @@ import java.util.Calendar
class ShowCalenderAndTimeDialog(
context: Context,
showCalenderTextView: TextView,
okListener: OnDialogOkListener? = null,
okListener: OnDialogSelectListener? = null,
format: String = Utils.DATE_FORMAT_PATTERN_EN1,
canSelectFutureDate: Boolean = false,
calenderShowTimestamp: Long = 0,
@@ -86,7 +86,7 @@ class ShowCalenderAndTimeDialog(
0,
23,
5,
24,
16,
NumberPickerValueFill0Format(),
this@ShowCalenderAndTimeDialog
)
@@ -98,7 +98,7 @@ class ShowCalenderAndTimeDialog(
0,
59,
5,
24,
16,
NumberPickerValueFill0Format(),
this@ShowCalenderAndTimeDialog
)
@@ -172,16 +172,18 @@ class ShowCalenderAndTimeDialog(
}:${Utils.fill2Digits(npDialogCalenderMin.value)}:00"
val timesTamp = Utils.stringToTimestamp(selectMonthYear)
val nowTimestamp = System.currentTimeMillis()
if (timesTamp > nowTimestamp) {
//时间戳还原
setSelectDate(nowTimestamp)
mShowCalenderTextView.text = Utils.formatTime(nowTimestamp, mDateFormat)
mOkListener?.onSelectClick(this@ShowCalenderAndTimeDialog, nowTimestamp)
} else {
mShowCalenderTextView.text = Utils.stringToDate(
selectMonthYear, Utils.DATE_FORMAT_PATTERN_CN2, mDateFormat
)
mOkListener?.onSelectClick(this@ShowCalenderAndTimeDialog, timesTamp)
}
mOkListener?.onOkClick(this@ShowCalenderAndTimeDialog)
dismiss()
}
}

View File

@@ -837,6 +837,16 @@ interface INetworkService {
@Query("toTime") toTime: Long
): BaseResponse<MutableList<HistoryDataBean>>
/**
* 获取设备进出围栏历史数据
*/
@GET("data/getHistoryInOutFenceList")
suspend fun getHistoryFenceList(
@Query("deviceId") deviceId: String,
@Query("beginTimeStamp") fromTime: Long,
@Query("endTimeStamp") toTime: Long
): BaseResponse<MutableList<MessageBean>>
/**
* 设置运动目标
*/

View File

@@ -753,6 +753,13 @@ object NetworkApi : BaseNetworkApi<INetworkService>(INetworkService.BASE_URL) {
service.getHistoryByDay(deviceId, fromTime, toTime)
}
/**
* 获取设备进出围栏历史数据
*/
suspend fun getHistoryFenceList(deviceId: String, fromTime: Long, toTime: Long) = getResult {
service.getHistoryFenceList(deviceId, fromTime, toTime)
}
/**
* 设置运动目标
*/

View File

@@ -46,7 +46,7 @@ import com.abbidot.tracker.ui.activity.device.AddNewTracker1Activity
import com.abbidot.tracker.ui.activity.device.MyTrackerV2Activity
import com.abbidot.tracker.ui.fragment.account.AccountV2Fragment
import com.abbidot.tracker.ui.fragment.data.ActivityV2Fragment
import com.abbidot.tracker.ui.fragment.data.RouteV2Fragment
import com.abbidot.tracker.ui.fragment.data.RouteV3Fragment
import com.abbidot.tracker.ui.fragment.map.MapV3Fragment
import com.abbidot.tracker.ui.fragment.pet.PetV2Fragment
import com.abbidot.tracker.util.SocketUtilManage
@@ -111,7 +111,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
private val mFragments = mutableListOf<Fragment>(
ActivityV2Fragment.newInstance(this),
RouteV2Fragment.newInstance(this),
RouteV3Fragment.newInstance(this),
MapV3Fragment.newInstance(this),
PetV2Fragment.newInstance(this),
AccountV2Fragment.newInstance(this)
@@ -460,7 +460,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
onChangeClick(mSelectPetPosition)
when (mViewBinding.homeV2ViewPager2.currentItem) {
0 -> {
(mFragments[1] as RouteV2Fragment).setNeedUpdateTag()
(mFragments[1] as RouteV3Fragment).setNeedUpdateTag()
(mFragments[3] as PetV2Fragment).setNeedUpdateTag()
}
@@ -471,13 +471,13 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
2, 4 -> {
(mFragments[0] as ActivityV2Fragment).setNeedUpdateTag()
(mFragments[1] as RouteV2Fragment).setNeedUpdateTag()
(mFragments[1] as RouteV3Fragment).setNeedUpdateTag()
(mFragments[3] as PetV2Fragment).setNeedUpdateTag()
}
3 -> {
(mFragments[0] as ActivityV2Fragment).setNeedUpdateTag()
(mFragments[1] as RouteV2Fragment).setNeedUpdateTag()
(mFragments[1] as RouteV3Fragment).setNeedUpdateTag()
}
}
}
@@ -638,7 +638,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
fun refreshLocationBtnState() {
when (mViewBinding.homeV2ViewPager2.currentItem) {
0 -> (mFragments[0] as ActivityV2Fragment).checkNotifyLocationState()
1 -> (mFragments[1] as RouteV2Fragment).checkNotifyLocationState()
1 -> (mFragments[1] as RouteV3Fragment).checkNotifyLocationState()
3 -> (mFragments[3] as PetV2Fragment).checkNotifyLocationState()
}
}
@@ -654,7 +654,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
mChangePetListDialogAdapter.setSelectPetPos(mSelectPetPosition)
when (mViewBinding.homeV2ViewPager2.currentItem) {
0 -> (mFragments[0] as ActivityV2Fragment).showPetNameAndHead(position)
1 -> (mFragments[1] as RouteV2Fragment).showPetNameAndHead(position)
1 -> (mFragments[1] as RouteV3Fragment).showPetNameAndHead(position)
2 -> (mFragments[2] as MapV3Fragment).showPetNameAndHead(position)
3 -> (mFragments[3] as PetV2Fragment).showPetNameAndHead(position)
}

View File

@@ -1,8 +1,10 @@
package com.abbidot.tracker.ui.common.map
import android.content.Context
import androidx.appcompat.widget.AppCompatSeekBar
import androidx.fragment.app.Fragment
import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.tracker.base.BaseMapCommon
import com.abbidot.tracker.bean.HistoryDataBean
import com.abbidot.tracker.bean.PetBean
@@ -10,7 +12,6 @@ import com.abbidot.tracker.ui.fragment.map.baidumap.HistoryDataBaiduMapFragment
import com.abbidot.tracker.ui.fragment.map.googlemap.HistoryDataGoogleMapFragment
import com.abbidot.tracker.vm.GeoCoderViewModel
import com.abbidot.tracker.widget.MapMarkerInfoView
import com.abbidot.tracker.widget.VerticalTopToBottomSeekBar
/**
*Created by .yzq on 2022/7/5/005.
@@ -26,7 +27,7 @@ class HistoryDataMapCommon : BaseMapCommon() {
context: Context,
markerInfoView: MapMarkerInfoView,
geoCoderViewModel: GeoCoderViewModel,
verticalTopToBottomSeekBar: VerticalTopToBottomSeekBar,
verticalTopToBottomSeekBar: AppCompatSeekBar,
mapLoadOk: () -> Unit
): Fragment {
return if (AppUtils.isChina(AppUtils.SWITCH_MAP_TYPE)) {
@@ -102,10 +103,15 @@ class HistoryDataMapCommon : BaseMapCommon() {
}
fun setLatLngData(data: MutableList<HistoryDataBean>) {
if (null != mHistoryDataBaiduMapFragment) {
mHistoryDataBaiduMapFragment!!.setLatLngData(data)
} else if (null != mHistoryDataGoogleMapFragment) {
mHistoryDataGoogleMapFragment!!.setLatLngData(data)
if (data.size == 0) return
try {
if (null != mHistoryDataBaiduMapFragment) {
mHistoryDataBaiduMapFragment!!.setLatLngData(data)
} else if (null != mHistoryDataGoogleMapFragment) {
mHistoryDataGoogleMapFragment!!.setLatLngData(data)
}
} catch (e: Exception) {
LogUtil.e("setLatLngData异常:${e.message}")
}
}
@@ -117,6 +123,10 @@ class HistoryDataMapCommon : BaseMapCommon() {
}
}
fun resetMarkerInfoViewOffset() {
mHistoryDataGoogleMapFragment?.resetMarkerInfoViewOffset()
}
fun setPetUserLatLng() {
if (null != mHistoryDataBaiduMapFragment) {
} else if (null != mHistoryDataGoogleMapFragment) {

View File

@@ -449,8 +449,8 @@ class RouteV2Fragment : BaseFragment<FragmentRouteV2Binding>(FragmentRouteV2Bind
mFromCalenderDialog = ShowCalenderAndTimeDialog(
mContext!!,
tvHomeRouteCalendarFrom,
object : BaseDialog.OnDialogOkListener {
override fun onOkClick(dialog: BaseDialog<*>) {
object : BaseDialog.OnDialogSelectListener {
override fun onSelectClick(dialog: BaseDialog<*>, any: Any) {
mCurrentTimestamp = System.currentTimeMillis()
isSelectCustomDate = true
getHistoryDay(tvHomeRouteCalendarFrom)
@@ -477,8 +477,8 @@ class RouteV2Fragment : BaseFragment<FragmentRouteV2Binding>(FragmentRouteV2Bind
mToCalenderDialog = ShowCalenderAndTimeDialog(
mContext!!,
tvHomeRouteCalendarTo,
object : BaseDialog.OnDialogOkListener {
override fun onOkClick(dialog: BaseDialog<*>) {
object : BaseDialog.OnDialogSelectListener {
override fun onSelectClick(dialog: BaseDialog<*>, any: Any) {
isSelectCustomDate = true
getHistoryDay(tvHomeRouteCalendarTo)
}

View File

@@ -0,0 +1,648 @@
package com.abbidot.tracker.ui.fragment.data
import android.annotation.SuppressLint
import android.content.Context
import android.view.MotionEvent
import android.view.View
import android.widget.SeekBar
import androidx.core.view.WindowInsetsCompat
import androidx.core.view.isVisible
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.abbidot.baselibrary.constant.EventName
import com.abbidot.baselibrary.eventbus.XEventBus
import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.baselibrary.util.Utils
import com.abbidot.tracker.R
import com.abbidot.tracker.adapter.HistoryFenceAdapter
import com.abbidot.tracker.base.BaseDialog
import com.abbidot.tracker.base.BaseFragment
import com.abbidot.tracker.bean.HistoryDataBean
import com.abbidot.tracker.bean.MessageBean
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.constant.GetResultCallback
import com.abbidot.tracker.databinding.FragmentRouteV3Binding
import com.abbidot.tracker.dialog.ShowCalenderAndTimeDialog
import com.abbidot.tracker.ui.activity.HomeV2Activity
import com.abbidot.tracker.ui.common.map.HistoryDataMapCommon
import com.abbidot.tracker.util.Util
import com.abbidot.tracker.util.ViewUtil
import com.abbidot.tracker.vm.DataDetailViewModel
import com.abbidot.tracker.vm.GeoCoderViewModel
import com.abbidot.tracker.widget.NoClickSlideSeekBar
import com.abbidot.tracker.widget.TypefaceTextView
import kotlinx.coroutines.launch
/**
* 首页轨迹
* create an instance of this fragment.
*/
class RouteV3Fragment : BaseFragment<FragmentRouteV3Binding>(FragmentRouteV3Binding::inflate) {
private val mDataDetailViewModel: DataDetailViewModel by viewModels()
private val mGeoCoderViewModel: GeoCoderViewModel by viewModels()
private lateinit var mFragment: Fragment
private var mFromCalenderDialog: ShowCalenderAndTimeDialog? = null
private var mToCalenderDialog: ShowCalenderAndTimeDialog? = null
//当前这个页面显示的宠物下标
private var mCurrentShowPetPos = -1
//最多只能选择多少天的历史记录
private val mOnlySelectDay = 7L
//当前的时间戳
private var mCurrentTimestamp = 0L
private var mFromTimestamp = 0L
private var mToTimestamp = 0L
//方便保存地理反编译信息
private var mHistoryDataList = mutableListOf<HistoryDataBean>()
private val mHistoryDataMapCommon = HistoryDataMapCommon()
//是否选过自定义日期没有则再次回到这个页面刷新取24小时的记录
private var isSelectCustomDate = false
// private var isFirst = true
private var mProgress = 0
//原来有列表一个元素的高度
private var mLastScrollViewHeight = 0
private var mTotalHeight = 0
private lateinit var mHistoryFenceAdapter: HistoryFenceAdapter
private val mAllHistoryFenceList = mutableListOf<MessageBean>()
//充值续费或升级返回类型
private var mRechargeBackType = ConstantInt.SpecialType
//列表是否展开
private var isListExpand = false
//判断是否滑过
private var isHaveSlide = false
private var mLastY = 0f
private var mDiffHeight = 0
//是否上滑
private var isSlideUp = false
// private lateinit var mTopSlideLayoutManager: TopSlideLayoutManager
companion object {
@JvmStatic
fun newInstance(context: Context) = RouteV3Fragment().apply {
mContext = context
}
}
@SuppressLint("ClickableViewAccessibility")
override fun initData() {
mViewBinding.apply {
getHomeV2Activity()?.edgeToEdgeAdapterBars(
root, WindowInsetsCompat.Type.statusBars()
)
mFragment = mHistoryDataMapCommon.getMapFragment(
mContext!!, miHomeRouteAddressViewV3, mGeoCoderViewModel, vsbMapRouteLineV3
) {
mapLoadOk()
}
mHistoryFenceAdapter = HistoryFenceAdapter(mContext!!, null)
ViewUtil.instance.setRecyclerViewVerticalLinearLayout(
mContext!!, rvMapRouteLineV3FencesList, mHistoryFenceAdapter
)
// rvMapRouteLineV3FencesList.let {
// mTopSlideLayoutManager = TopSlideLayoutManager(
// mContext!!, LinearLayoutManager.VERTICAL, false
// )
// it.layoutManager = mTopSlideLayoutManager
// it.adapter = mHistoryFenceAdapter
// }
ilHomeRoutePetHeadV3.ivTopPetBtnSmall.visibility = View.GONE
//设置不可点击状态
vsbMapRouteLineV3.setStatus(NoClickSlideSeekBar.STATUS.STATUS_SLIDE)
setOnClickListenerViews(
llHomeRouteCalendarFromV3,
llHomeRouteCalendarToV3,
ilHomeRoutePetHeadV3.homeDataPetHeadSmall.root,
ilHomeRoutePetHeadV3.homeDataPetNameSmall,
ivHomeRouteMapTypeBtnV3,
ivMapRouteLineV3LastBtn,
ivMapRouteLineV3NextBtn
)
llMapRouteLineV3ZoomingLayout.setOnTouchListener { _, event ->
if (rlMapRouteLineV3TimeLayout.isVisible) {
if (mLastScrollViewHeight == 0) {
mTotalHeight = cvHomeRouteV3Card.height + svHomeRouteMapScroll.height
mLastScrollViewHeight = svHomeRouteMapScroll.height
}
when (event.action) {
MotionEvent.ACTION_DOWN -> {
LogUtil.e("ACTION_DOWN")
mLastY = event.rawY
}
MotionEvent.ACTION_MOVE -> {
val dy = mLastY - event.rawY
val layoutParams = svHomeRouteMapScroll.layoutParams
val diff = (svHomeRouteMapScroll.height + dy).toInt()
isSlideUp = dy >= 0.0f
if (diff > mLastScrollViewHeight) {
isHaveSlide = true
if (mHistoryFenceAdapter.getData().size != mAllHistoryFenceList.size) {
mHistoryFenceAdapter.setData(mAllHistoryFenceList, true)
}
mDiffHeight = diff
layoutParams.height = diff
svHomeRouteMapScroll.layoutParams = layoutParams
mLastY = event.rawY
}
}
MotionEvent.ACTION_UP -> {
LogUtil.e("ACTION_UP")
val layoutParams = svHomeRouteMapScroll.layoutParams
if (mDiffHeight > mTotalHeight / 2) {
setSeekBarShowHide(false)
layoutParams.height = mTotalHeight
svHomeRouteMapScroll.layoutParams = layoutParams
isListExpand = true
} else {
svHomeRouteMapScroll.postDelayed(
{ mHistoryDataMapCommon.setLatLngData(mHistoryDataList) }, 200
)
if (mDiffHeight > mLastScrollViewHeight) {
layoutParams.height = if (isSlideUp) {
isListExpand = true
setSeekBarShowHide(false)
mTotalHeight / 2
} else {
isListExpand = false
setSeekBarShowHide(true)
if (mHistoryFenceAdapter.getData().size > 1) {
mHistoryFenceAdapter.setData(
mAllHistoryFenceList.subList(
0, 1
), true
)
}
mLastScrollViewHeight
}
}
svHomeRouteMapScroll.layoutParams = layoutParams
}
}
}
}
false
}
if (AppUtils.isDebug()) {
ilHomeRoutePetHeadV3.homeDataPetHeadSmall.root.setOnLongClickListener {
getHomeV2Activity()?.goToDebugActivity()
true
}
}
}
//解决报错java.lang.IllegalArgumentException
//No view found for id 0x7f090254 (com.abbidot.tracker:id/fc_home_route_fragment) for fragment HomeMapGoogleMapFragment
//https://www.jianshu.com/p/9235092f407a
childFragmentManager.commit {
add(R.id.fc_home_route_fragment_v3, mFragment)
}
initEven()
initState()
}
private fun getHomeV2Activity(): HomeV2Activity? {
return if (null == activity) {
LogUtil.e("RouteV2Fragment,getHomeV2Activity,null == activity")
null
} else {
activity as HomeV2Activity
}
}
/**
* 一开始获取24小时内的数据
*/
private fun get24HourTime() {
mCurrentTimestamp = System.currentTimeMillis()
mViewBinding.apply {
mFromTimestamp = Utils.getBeforeHowTimestamp(mCurrentTimestamp, 1)
mToTimestamp = mCurrentTimestamp
tvHomeRouteCalendarToV3.text =
Utils.formatTime(mToTimestamp, Utils.DATE_FORMAT_PATTERN_EN13)
tvHomeRouteCalendarFromV3.text =
Utils.formatTime(mFromTimestamp, Utils.DATE_FORMAT_PATTERN_EN13)
}
}
private fun initState() {
mViewBinding.miHomeRouteAddressViewV3.visibility = View.GONE
mHistoryDataMapCommon.clearAllMarker()
setSeekBarMax(0)
}
private fun initEven() {
mViewBinding.vsbMapRouteLineV3.setOnSeekBarChangeListener(object :
SeekBar.OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
if (mProgress == progress) {
return
}
mProgress = progress
mViewBinding.miHomeRouteAddressViewV3.visibility = View.GONE
mHistoryDataMapCommon.slideChanged(progress)
}
override fun onStartTrackingTouch(seekBar: SeekBar) {
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
mHistoryDataMapCommon.slideStopChanged(seekBar.progress)
}
})
}
override fun onResume() {
super.onResume()
//续费成功后,不执行
if (mRechargeBackType == ConstantInt.Type1) {
return
}
getHomeV2Activity()?.apply {
if (mCurrentShowPetPos == mSelectPetPosition) {
if (!isSelectCustomDate) {
if (mHistoryDataMapCommon.isMapLoadOk()) {
get24HourTime()
getHistoryDay(mViewBinding.tvHomeRouteCalendarFromV3)
}
}
} else {
//其他页面是否选择了宠物
if (!isSelectCustomDate) get24HourTime()
showPetNameAndHead(mSelectPetPosition)
}
checkNotifyLocationState()
}
}
fun checkNotifyLocationState() {
getHomeV2Activity()?.apply {
checkNotifyRefreshLocation(mViewBinding.ilHomeRoutePetHeadV3.homeDataPetNameSmall)
}
}
override fun liveDataObserve() {
//更新套餐数据
XEventBus.observe(this, EventName.RefreshPackage) {
//监听续费成功
if (mRechargeBackType == ConstantInt.Type0) {
mRechargeBackType = ConstantInt.Type1
}
}
mDataDetailViewModel.apply {
mHistoryDetailLive.observe(this@RouteV3Fragment) {
dealRequestResult(it, object : GetResultCallback {
override fun onResult(any: Any) {
val data = it.getOrNull()!!
setLatLngData(data)
getHomeV2Activity()?.let { a ->
a.getPet(false)?.let { p ->
mAllHistoryFenceList.clear()
getHistoryFenceList(
a, p.deviceId, mFromTimestamp / 1000, mToTimestamp / 1000
)
}
}
}
override fun onRequestError(exceptionCode: String?) {
setSeekBarShowHide(false)
}
}, isShowNoDataTip = false)
}
mHistoryFenceEvents.observe(this@RouteV3Fragment) {
dealRequestResult(it, object : GetResultCallback {
override fun onResult(any: Any) {
it.getOrNull()?.let { d ->
if (d.size > 0) {
mViewBinding.rlMapRouteLineV3TimeLayout.visibility = View.VISIBLE
mAllHistoryFenceList.addAll(d)
if (isListExpand) {
mHistoryFenceAdapter.setData(
mAllHistoryFenceList, true
)
} else {
mHistoryFenceAdapter.setData(
mAllHistoryFenceList.subList(0, 1), true
)
}
if (isHaveSlide) {
//判断滑过,从无数据到有数据布局错乱
val layoutParams =
mViewBinding.svHomeRouteMapScroll.layoutParams
if (layoutParams.height < mLastScrollViewHeight) {
layoutParams.height = mLastScrollViewHeight
mViewBinding.svHomeRouteMapScroll.layoutParams =
layoutParams
}
}
} else {
mViewBinding.rlMapRouteLineV3TimeLayout.visibility = View.GONE
val lastScrollViewNoListHeight =
mViewBinding.rlHomeRouteCalendarV3Layout.height
if (isHaveSlide) {
val layoutParams =
mViewBinding.svHomeRouteMapScroll.layoutParams
layoutParams.height = lastScrollViewNoListHeight
mViewBinding.svHomeRouteMapScroll.layoutParams = layoutParams
}
isListExpand = false
}
if (mViewBinding.cvHomeRouteV3Card.height > 0) mViewBinding.svHomeRouteMapScroll.postDelayed(
{ mHistoryDataMapCommon.setLatLngData(mHistoryDataList) }, 200
)
}
}
}, isShowNoDataTip = false)
}
}
//地理反编译成功返回
mGeoCoderViewModel.mLatLonAddressLiveData.observe(this) {
val position = mViewBinding.vsbMapRouteLineV3.progress
if (mHistoryDataList.size <= position) return@observe
mHistoryDataList[position].apply {
//详细位置
address = it
val timeString = Utils.formatTime(timeStamp * 1000, Utils.DATE_FORMAT_PATTERN_EN10)
//更新位置时间
dayTime = timeString
mViewBinding.root.postDelayed(
{
mViewBinding.miHomeRouteAddressViewV3.let { mi ->
mi.visibility = View.VISIBLE
mi.setShowText(timeString, it)
}
}, 100
)
}
}
}
/**
* 地图加载好了
*/
private fun mapLoadOk() {
if (mCurrentShowPetPos == -1) return
getHomeV2Activity()?.apply {
showPetNameAndHead(mSelectPetPosition)
}
}
/**
* 设置需要更新的标识
*/
fun setNeedUpdateTag() {
mCurrentShowPetPos = -1
}
/**
* 显示选择的宠物
*/
fun showPetNameAndHead(position: Int) {
getHomeV2Activity()?.apply {
if (mPetList.size == 0) {
return
}
mCurrentShowPetPos = position
ViewUtil.instance.selectPetDialogShow(
mContext,
mPetList,
position,
mViewBinding.ilHomeRoutePetHeadV3.homeDataPetNameSmall,
mViewBinding.ilHomeRoutePetHeadV3.homeDataPetHeadSmall.appHeadImage
)
if (mHistoryDataMapCommon.isMapLoadOk()) {
getHistoryDay(mViewBinding.tvHomeRouteCalendarFromV3)
}
}
}
private fun setLatLngData(data: MutableList<HistoryDataBean>) {
viewLifecycleOwner.lifecycleScope.launch {
mHistoryDataList = data
getHomeV2Activity()?.getPet(false)?.let {
mHistoryDataMapCommon.setPetBean(it)
}
if (isResumed) {
getHomeV2Activity()?.getPet()?.let { pet ->
if (Util.checkPackageLimit(getHomeV2Activity()!!, pet.deviceId)) {
mRechargeBackType = pet.availableOrder
mHistoryDataMapCommon.setPetUserLatLng()
return@launch
}
}
}
if (data.size == 0) {
setSeekBarShowHide(false)
mHistoryDataMapCommon.setPetUserLatLng()
return@launch
}
setSeekBarShowHide(true)
val max = data.size - 1
setSeekBarMax(max)
mHistoryDataMapCommon.setLatLngData(data)
}
}
/**
* 设置SeekBar相关布局隐藏显示
*/
private fun setSeekBarShowHide(show: Boolean) {
mViewBinding.apply {
if (show && !isListExpand) {
vsbMapRouteLineV3.visibility = View.VISIBLE
ivMapRouteLineV3LastBtn.visibility = View.VISIBLE
ivMapRouteLineV3NextBtn.visibility = View.VISIBLE
} else {
vsbMapRouteLineV3.visibility = View.GONE
ivMapRouteLineV3LastBtn.visibility = View.GONE
ivMapRouteLineV3NextBtn.visibility = View.GONE
}
}
}
private fun setSeekBarMax(max: Int) {
mViewBinding.vsbMapRouteLineV3.let {
it.max = max
//定位中心显示最后一个经纬度
it.progress = max
}
}
/**
* cSelectView 当前设置日期的view
*/
private fun getHistoryDay(cSelectView: TypefaceTextView) {
mViewBinding.apply {
// mFromTimestamp = Utils.stringToTimestamp(
// tvHomeRouteCalendarFromV3.text.toString(), Utils.DATE_FORMAT_PATTERN_EN13
// )
// mToTimestamp = Utils.stringToTimestamp(
// tvHomeRouteCalendarToV3.text.toString(), Utils.DATE_FORMAT_PATTERN_EN13
// )
if (mToTimestamp - mFromTimestamp < 0) {
checkCorrectDate(cSelectView)
} else if (mToTimestamp - mFromTimestamp > mOnlySelectDay * 24 * 60 * 60 * 1000) {
checkCorrectDate(cSelectView)
}
getHomeV2Activity()?.getPet()?.apply {
initState()
mDataDetailViewModel.getHistoryByDay(
getHomeV2Activity()!!, deviceId, mFromTimestamp / 1000, mToTimestamp / 1000
)
}
}
}
/**
* 检测符合的日期
*/
private fun checkCorrectDate(cSelectView: TypefaceTextView) {
mViewBinding.apply {
if (cSelectView == tvHomeRouteCalendarFromV3) {
mToTimestamp = Utils.getAfterHowTimestamp(mFromTimestamp, mOnlySelectDay)
if (mToTimestamp > mCurrentTimestamp) {
mToTimestamp = mCurrentTimestamp
}
tvHomeRouteCalendarToV3.text = Utils.formatTime(
mToTimestamp, Utils.DATE_FORMAT_PATTERN_EN13
)
mToCalenderDialog?.setSelectDate(mToTimestamp)
} else {
//结束日期小于开始日期,就默认选择结束日期前一天
mFromTimestamp =
if (mFromTimestamp > mToTimestamp) Utils.getBeforeHowTimestamp(mToTimestamp, 1L)
else Utils.getBeforeHowTimestamp(mToTimestamp, mOnlySelectDay)
tvHomeRouteCalendarFromV3.text = Utils.formatTime(
mFromTimestamp, Utils.DATE_FORMAT_PATTERN_EN13
)
mFromCalenderDialog?.setSelectDate(mFromTimestamp)
}
}
}
override fun onClick(v: View?) {
mViewBinding.apply {
when (v!!) {
ilHomeRoutePetHeadV3.homeDataPetNameSmall, ilHomeRoutePetHeadV3.homeDataPetHeadSmall.root -> {
getHomeV2Activity()?.let {
if (!it.isNotifyRefreshLocation) it.selectPetDialog(false)
}
}
llHomeRouteCalendarFromV3 -> {
getHomeV2Activity()?.getPet()?.let {
//防止执行多次弹窗
if (isLimitClick()) {
return
}
if (Util.checkPackageLimit(getHomeV2Activity()!!, it.deviceId)) {
mRechargeBackType = it.availableOrder
return
}
}
if (null == mFromCalenderDialog) {
mFromCalenderDialog = ShowCalenderAndTimeDialog(
mContext!!,
tvHomeRouteCalendarFromV3,
object : BaseDialog.OnDialogSelectListener {
override fun onSelectClick(dialog: BaseDialog<*>, any: Any) {
mCurrentTimestamp = System.currentTimeMillis()
isSelectCustomDate = true
mFromTimestamp = any as Long
getHistoryDay(tvHomeRouteCalendarFromV3)
}
},
calenderShowTimestamp = mFromTimestamp,
format = Utils.DATE_FORMAT_PATTERN_EN13
)
}
mFromCalenderDialog?.show()
}
llHomeRouteCalendarToV3 -> {
getHomeV2Activity()?.getPet()?.let {
//防止执行多次弹窗
if (isLimitClick()) {
return
}
if (Util.checkPackageLimit(getHomeV2Activity()!!, it.deviceId)) {
mRechargeBackType = it.availableOrder
return
}
}
if (null == mToCalenderDialog) {
mToCalenderDialog = ShowCalenderAndTimeDialog(
mContext!!,
tvHomeRouteCalendarToV3,
object : BaseDialog.OnDialogSelectListener {
override fun onSelectClick(dialog: BaseDialog<*>, any: Any) {
isSelectCustomDate = true
mToTimestamp = any as Long
getHistoryDay(tvHomeRouteCalendarToV3)
}
},
calenderShowTimestamp = mToTimestamp,
format = Utils.DATE_FORMAT_PATTERN_EN13
)
}
mToCalenderDialog?.show()
}
ivHomeRouteMapTypeBtnV3 -> mHistoryDataMapCommon.switchSatelliteAndNormalMapType()
ivMapRouteLineV3LastBtn -> {
if (vsbMapRouteLineV3.progress == 0) {
return
}
vsbMapRouteLineV3.progress -= 1
mHistoryDataMapCommon.slideStopChanged(vsbMapRouteLineV3.progress)
}
ivMapRouteLineV3NextBtn -> {
if (vsbMapRouteLineV3.progress == vsbMapRouteLineV3.max) {
return
}
vsbMapRouteLineV3.progress += 1
mHistoryDataMapCommon.slideStopChanged(vsbMapRouteLineV3.progress)
}
}
}
}
}

View File

@@ -28,17 +28,20 @@ import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.baselibrary.util.Utils
import com.abbidot.tracker.R
import com.abbidot.tracker.adapter.HomeMapDeviceStateAdapter
import com.abbidot.tracker.adapter.SelectMapListDialogAdapter
import com.abbidot.tracker.base.BaseDialog
import com.abbidot.tracker.base.BaseFragment
import com.abbidot.tracker.bean.BleReportDataBean
import com.abbidot.tracker.bean.BleTrackDeviceBean
import com.abbidot.tracker.bean.DataBean
import com.abbidot.tracker.bean.MapDeviceBean
import com.abbidot.tracker.bean.MenuTxtBean
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.constant.ConstantString
import com.abbidot.tracker.constant.GetResultCallback
import com.abbidot.tracker.databinding.FragmentMapV3Binding
import com.abbidot.tracker.dialog.CommonDialog1
import com.abbidot.tracker.dialog.SelectMapListDialog
import com.abbidot.tracker.dialog.SelectMapTypeDialog
import com.abbidot.tracker.ui.activity.HomeV2Activity
import com.abbidot.tracker.ui.activity.map.LiveActivityV3
@@ -104,6 +107,13 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
private var needGpsToGCJ02 = true
private var mAnimatorSet: AnimatorSet? = null
private val mMapGooglePackageName = "com.google.android.apps.maps"
private val mMapGaoDePackageName = "com.autonavi.minimap"
private val mMapBaiduPackageName = "com.baidu.BaiduMap"
private lateinit var mSelectMapListDialogAdapter: SelectMapListDialogAdapter
private var mSelectMapListDialog: SelectMapListDialog? = null
//一键定位开始的时间戳
private var notifyRefreshLocationTimestamp = 0L
@@ -909,28 +919,110 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
}
}
private fun goNavigation() {
mMapDeviceBean?.let { m ->
try {
//自动弹出手机中已经有安装地图的应用
val uri = if (MMKVUtil.getBoolean(MMKVKey.isGpsToGCJ02)) {
val convertLatLon =
LonAndLatUtil.convertFromWGS84ToGCJ02(m.latitude, m.longitude)
"google.navigation:q=${convertLatLon[0]},${convertLatLon[1]}&mode=w".toUri()
} else {
"google.navigation:q=${m.latitude},${m.longitude}&mode=w".toUri()
// private fun goNavigation() {
// mMapDeviceBean?.let { m ->
// try {
// //自动弹出手机中已经有安装地图的应用
// val uri = if (MMKVUtil.getBoolean(MMKVKey.isGpsToGCJ02)) {
// val convertLatLon =
// LonAndLatUtil.convertFromWGS84ToGCJ02(m.latitude, m.longitude)
// "google.navigation:q=${convertLatLon[0]},${convertLatLon[1]}&mode=w".toUri()
// } else {
// "google.navigation:q=${m.latitude},${m.longitude}&mode=w".toUri()
// }
// val intent = Intent(Intent.ACTION_VIEW, uri)
// intent.setPackage("com.google.android.apps.maps")
// startActivity(intent)
// } catch (e: Exception) {
// showToast(getString(R.string.txt_no_install))
// }
// }
// }
private fun showNavigationDialog() {
if (null == mSelectMapListDialog) {
val mapList = mutableListOf<MenuTxtBean>()
ViewUtil.instance.addMenuBean(
mapList,
getString(R.string.map_navigate_map_google),
mMapGooglePackageName,
R.drawable.ico_map_google
)
ViewUtil.instance.addMenuBean(
mapList,
getString(R.string.map_navigate_map_gaode),
mMapGaoDePackageName,
R.drawable.ico_map_gaode
)
ViewUtil.instance.addMenuBean(
mapList,
getString(R.string.map_navigate_map_baidu),
mMapBaiduPackageName,
R.drawable.ico_map_gaode
)
mSelectMapListDialogAdapter = SelectMapListDialogAdapter(mContext!!, mapList)
mSelectMapListDialogAdapter.setOnItemClickListener(object :
BaseRecyclerAdapter.OnItemClickListener {
override fun onItemClick(itemView: View?, pos: Int) {
selectMapNavigation(mapList[pos])
}
})
mSelectMapListDialog = SelectMapListDialog(mContext!!, mSelectMapListDialogAdapter)
}
mSelectMapListDialog!!.show()
}
/**
* 选择地图开始跳转
*/
private fun selectMapNavigation(item: MenuTxtBean) {
mSelectMapListDialog?.dismiss()
mMapDeviceBean?.apply {
try {
when (item.menuValue) {
//谷歌地图
mMapGooglePackageName -> {
var lat = latitude
var lon = longitude
if (MMKVUtil.getBoolean(MMKVKey.isGpsToGCJ02)) {
val convertLatLon =
LonAndLatUtil.convertFromWGS84ToGCJ02(latitude, longitude)
lat = convertLatLon[0]
lon = convertLatLon[1]
}
val uri = "google.navigation:q=${lat},${lon}&mode=w".toUri()
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.setPackage(item.menuValue)
startActivity(intent)
}
//高德地图 https://lbs.amap.com/api/amap-mobile/guide/android/walk-navi
mMapGaoDePackageName -> {
val uri = "amapuri://openFeature?featureName=OnFootNavi&sourceApplication=${
getString(
R.string.app_name
)
}&lat=${latitude}&lon=${longitude}&dev=1".toUri()
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.setPackage(item.menuValue)
startActivity(intent)
}
//百度地图 https://lbs.baidu.com/docs/webapi?title=mapadjustment/uri/andriod
mMapBaiduPackageName -> {
val uri =
"baidumap://map/direction?destination=name:|latlng:${latitude},${longitude}&mode=walking&coord_type=wgs84".toUri()
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.setPackage(item.menuValue)
startActivity(intent)
}
}
val intent = Intent(Intent.ACTION_VIEW, uri)
intent.setPackage("com.google.android.apps.maps")
startActivity(intent)
} catch (e: Exception) {
showToast(getString(R.string.txt_no_install))
showToast(getString(R.string.txt_no_install), gravity = Gravity.CENTER)
}
}
}
override fun onClick(v: View?) {
if (isLimitClick()) return
mViewBinding.apply {
when (v!!) {
homeMapRefreshBtn -> {
@@ -964,6 +1056,7 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
}
homeMapLiveBtn -> {
if (isLimitClick()) return
if (!isCanLive) {
showToast(R.string.txt_live_works_cellular, gravity = Gravity.CENTER)
return
@@ -974,7 +1067,11 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
ivHomeMapRefreshLocation -> notifyRefreshLocation()
llHomeMapTopPet.ivTopPetBtnSmall -> showMapTypeDialog()
homeMapBluetoothBtn -> checkPermissions(1)
homeMapBluetoothBtn -> {
if (isLimitClick()) return
checkPermissions(1)
}
llHomeMapTopPet.homeDataPetNameSmall, llHomeMapTopPet.homeDataPetHeadSmall.root -> {
getHomeV2Activity()?.let {
if (!it.isNotifyRefreshLocation) {
@@ -993,7 +1090,11 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
ilHomeMapDeviceMsg.root.visibility = View.GONE
}
ilHomeMapPetLocation.ivPetLocationNavigationBtn -> goNavigation()
ilHomeMapPetLocation.ivPetLocationNavigationBtn -> {
if (isLimitClick()) return
// goNavigation()
showNavigationDialog()
}
}
}
}

View File

@@ -3,6 +3,7 @@ package com.abbidot.tracker.ui.fragment.map.googlemap
import android.content.Context
import android.graphics.BitmapFactory
import android.graphics.Typeface
import androidx.appcompat.widget.AppCompatSeekBar
import androidx.core.view.isVisible
import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.AppUtils
@@ -15,7 +16,6 @@ import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.util.ViewUtil
import com.abbidot.tracker.vm.GeoCoderViewModel
import com.abbidot.tracker.widget.MapMarkerInfoView
import com.abbidot.tracker.widget.VerticalTopToBottomSeekBar
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory
@@ -32,7 +32,7 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
// private val mScreenshotsViewModel: ScreenshotsViewModel by viewModels()
private lateinit var mGeoCoderViewModel: GeoCoderViewModel
private lateinit var mMarkerInfoView: MapMarkerInfoView
private lateinit var mVerticalTopToBottomSeekBar: VerticalTopToBottomSeekBar
private lateinit var mVerticalTopToBottomSeekBar: AppCompatSeekBar
private val mMarkerOptions = MarkerOptions()
private lateinit var mPetIconDescriptor: BitmapDescriptor
@@ -55,7 +55,7 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
context: Context,
markerInfoView: MapMarkerInfoView,
geoCoderViewModel: GeoCoderViewModel,
verticalTopToBottomSeekBar: VerticalTopToBottomSeekBar,
verticalTopToBottomSeekBar: AppCompatSeekBar,
mapLoadOk: () -> Unit
) = HistoryDataGoogleMapFragment().apply {
mContext = context
@@ -97,9 +97,7 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
if (getGoogleMapZoom() != cameraPosition.zoom) {
setGoogleMapZoom(cameraPosition.zoom)
}
mCurLatLng?.let {
if (mMarkerInfoView.isVisible) setMarkerInfoViewOffset(mMarkerInfoView, it)
}
resetMarkerInfoViewOffset()
}
setOnMarkerClickListener { m ->
//点击数字大头针回到该位置
@@ -123,6 +121,12 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
if (::mMapLoadOk.isInitialized) mMapLoadOk()
}
fun resetMarkerInfoViewOffset() {
mCurLatLng?.let {
if (mMarkerInfoView.isVisible) setMarkerInfoViewOffset(mMarkerInfoView, it)
}
}
suspend fun setPetBean(petBean: PetBean) {
if (petBean == mPetBean) {
return
@@ -153,7 +157,7 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
mGoogleMap?.clear()
if (data.size == 0) return
mLatLngList.clear()
var maxLat = data[0].latitude
var maxLon = data[0].longitude
var minLat = data[0].latitude
@@ -195,7 +199,7 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
* 滑动改变
*/
fun slideChanged(progress: Int) {
setPetMarkerLatLng(mLatLngList[progress])
if (progress < mLatLngList.size) setPetMarkerLatLng(mLatLngList[progress])
}
/**

View File

@@ -206,7 +206,7 @@ class ViewUtil private constructor() {
) {
numberPicker.apply {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
selectionDividerHeight = AppUtils.dpToPx(1)
selectionDividerHeight = AppUtils.dpToPx(0.5f).toInt()
if (tSize > 0) {
textSize = QMUIDisplayHelper.sp2px(context, tSize).toFloat()
}

View File

@@ -6,6 +6,7 @@ import androidx.lifecycle.viewModelScope
import com.abbidot.tracker.base.BaseActivity
import com.abbidot.tracker.bean.ActiveTimeBean
import com.abbidot.tracker.bean.HistoryDataBean
import com.abbidot.tracker.bean.MessageBean
import com.abbidot.tracker.bean.SleepTimeBean
import com.abbidot.tracker.retrofit2.NetworkApi
import kotlinx.coroutines.launch
@@ -20,6 +21,7 @@ class DataDetailViewModel : ViewModel() {
val mActiveDetailLiveData = MutableLiveData<Result<ActiveTimeBean>>()
val mSleepDetailLiveData = MutableLiveData<Result<SleepTimeBean>>()
val mHistoryDetailLive = MutableLiveData<Result<MutableList<HistoryDataBean>>>()
val mHistoryFenceEvents = MutableLiveData<Result<MutableList<MessageBean>>>()
val mSetGoalLiveData = MutableLiveData<Result<String>>()
val mPetActivityInfoLiveData = MutableLiveData<Result<ActiveTimeBean>>()
@@ -40,10 +42,7 @@ class DataDetailViewModel : ViewModel() {
* 获取SleepTime数据
*/
fun getSleepTimeByDay(
activity: BaseActivity<*>,
deviceId: String,
dayTime: String,
petId: String
activity: BaseActivity<*>, deviceId: String, dayTime: String, petId: String
) {
activity.showLoading(true)
viewModelScope.launch {
@@ -60,6 +59,16 @@ class DataDetailViewModel : ViewModel() {
}
}
fun getHistoryFenceList(
activity: BaseActivity<*>, deviceId: String, fromTime: Long, toTime: Long
) {
activity.showLoading(true)
viewModelScope.launch {
val result = NetworkApi.getHistoryFenceList(deviceId, fromTime, toTime)
mHistoryFenceEvents.value = result
}
}
/**
*设置运动目标
* @param goalTime min

View File

@@ -0,0 +1,195 @@
package com.abbidot.tracker.widget;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.coordinatorlayout.widget.CoordinatorLayout;
import androidx.core.view.ViewCompat;
import com.google.android.material.appbar.AppBarLayout;
/**
* 仿饿了么地图滑动特效
*/
public class AppBarBehavior extends AppBarLayout.Behavior {
/**
* nestedScrollview的滑动距离
*/
int ay = 0;
/**
* nestedScrollview 沒有滑动到顶部之前的可滑动最大距离
*/
private int minY = 0;
public AppBarBehavior() {
}
public AppBarBehavior(Context context, AttributeSet attrs) {
super(context, attrs);
}
/**
* AppBarLayout布局时调用
*
* @param parent 父布局CoordinatorLayout
* @param abl 使用此Behavior的AppBarLayout
* @param layoutDirection 布局方向
* @return 返回true表示子View重新布局返回false表示请求默认布局
*/
@Override
public boolean onLayoutChild(CoordinatorLayout parent, AppBarLayout abl, int layoutDirection) {
return super.onLayoutChild(parent, abl, layoutDirection);
}
/**
* 当CoordinatorLayout的子View尝试发起嵌套滚动时调用
*
* @param parent 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param directTargetChild CoordinatorLayout的子View或者是包含嵌套滚动操作的目标View
* @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
* @param nestedScrollAxes 嵌套滚动的方向
* @return 返回true表示接受滚动
*/
@Override
public boolean onStartNestedScroll(CoordinatorLayout parent, AppBarLayout child, View directTargetChild, View target, int nestedScrollAxes, int type) {
return (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0;
}
/**
* 当嵌套滚动已由CoordinatorLayout接受时调用
*
* @param coordinatorLayout 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param directTargetChild CoordinatorLayout的子View或者是包含嵌套滚动操作的目标View
* @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
*/
@Override
public void onNestedScrollAccepted(@NonNull CoordinatorLayout coordinatorLayout, @NonNull AppBarLayout child, @NonNull View directTargetChild, @NonNull View target, int axes, int type) {
super.onNestedScrollAccepted(coordinatorLayout, child, directTargetChild, target, axes, type);
}
/**
* 当准备开始嵌套滚动时调用
*
* @param coordinatorLayout 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
* @param dx 用户在水平方向上滑动的像素数
* @param dy 用户在垂直方向上滑动的像素数
* @param consumed 输出参数consumed[0]为水平方向应该消耗的距离consumed[1]为垂直方向应该消耗的距离
*/
@Override
public void onNestedPreScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dx, int dy, int[] consumed, int type) {
if (minY == 0) {
minY = target.getHeight() + child.getHeight() - coordinatorLayout.getHeight();
}
/**
* 如果nestedScrollview 布局内容少没有内部滑动,这时需要我们进行处理
* 当nestedScrollview 内容很多时,我们不需要任何事情
* */
if (target.getScrollY() <= 0) {
ay += dy;
if (dy > 0 && ay >= minY) {
dy = minY + dy - ay;//这里之所以没有让dy = 0;因为当快速滑动时就会有问题快速滑动时dy的变化很大自行琢磨一下还不懂加群交流
ay = minY;
}
if (dy < 0 && ay <= 0) {
ay = 0;
dy = 0;
}
}
super.onNestedPreScroll(coordinatorLayout, child, target, dx, dy, consumed, type);
}
/**
* 嵌套滚动时调用
*
* @param coordinatorLayout 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
* @param dxConsumed 由目标View滚动操作消耗的水平像素数
* @param dyConsumed 由目标View滚动操作消耗的垂直像素数
* @param dxUnconsumed 由用户请求但是目标View滚动操作未消耗的水平像素数
* @param dyUnconsumed 由用户请求但是目标View滚动操作未消耗的垂直像素数
*/
@Override
public void onNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, int dxConsumed, int dyConsumed, int dxUnconsumed, int dyUnconsumed, int type) {
super.onNestedScroll(coordinatorLayout, child, target, dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, type);
}
/**
* 当嵌套滚动的子View准备快速滚动时调用
*
* @param coordinatorLayout 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
* @param velocityX 水平方向的速度
* @param velocityY 垂直方向的速度
* @return 如果Behavior消耗了快速滚动返回true
*/
@Override
public boolean onNestedPreFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY) {
return super.onNestedPreFling(coordinatorLayout, child, target, velocityX, velocityY);
}
/**
* 当嵌套滚动的子View快速滚动时调用
*
* @param coordinatorLayout 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
* @param velocityX 水平方向的速度
* @param velocityY 垂直方向的速度
* @param consumed 如果嵌套的子View消耗了快速滚动则为true
* @return 如果Behavior消耗了快速滚动返回true
*/
@Override
public boolean onNestedFling(CoordinatorLayout coordinatorLayout, AppBarLayout child, View target, float velocityX, float velocityY, boolean consumed) {
return super.onNestedFling(coordinatorLayout, child, target, velocityX, velocityY, consumed);
}
/**
* 当触摸时调用
*
* @param parent 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param ev 手势事件
*/
@Override
public boolean onTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
return super.onTouchEvent(parent, child, ev);
}
/**
* 当触摸想要拦截时调用 关键所在 true 不拦截 false 拦截AppBarLayout的手势触摸
*
* @param parent 父布局CoordinatorLayout
* @param child 使用此Behavior的AppBarLayout
* @param ev 手势事件
*/
@Override
public boolean onInterceptTouchEvent(CoordinatorLayout parent, AppBarLayout child, MotionEvent ev) {
return false;
}
/**
* 嵌套滚动结束时被调用
*
* @param coordinatorLayout 父布局CoordinatorLayout
* @param abl 使用此Behavior的AppBarLayout
* @param target 发起嵌套滚动的目标View(即AppBarLayout下面的ScrollView或RecyclerView)
*/
@Override
public void onStopNestedScroll(CoordinatorLayout coordinatorLayout, AppBarLayout abl, View target, int type) {
super.onStopNestedScroll(coordinatorLayout, abl, target, type);
}
}

View File

@@ -0,0 +1,39 @@
package com.abbidot.tracker.widget;
import android.content.Context;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
/**
* Created by .yzq on 2026/4/10/周五.
*
* @link
* @description:
*/
public class CustomLayoutManager extends LinearLayoutManager {
public CustomLayoutManager(Context context) {
super(context);
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
super.onLayoutChildren(recycler, state);
// 根据需要修改布局和滚动效果
// 例如调整位置或是控制滑出顶部的限制
}
@Override
public boolean canScrollVertically() {
return true; // 允许垂直滚动
}
@Override
public void scrollToPosition(int position) {
// 自定义滚动位置,确保滑动时不会被提前阻止
super.scrollToPosition(position);
}
}

View File

@@ -0,0 +1,84 @@
package com.abbidot.tracker.widget;
import android.content.Context;
import android.graphics.Rect;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.appcompat.widget.AppCompatSeekBar;
/**
* 可以通过STATUS设置 Seekbar禁止点击禁止滑动等
*/
public class NoClickSlideSeekBar extends AppCompatSeekBar {
private static final int PADDING_DEFAULT = 50;
public enum STATUS {
STATUS_ENABLE, STATUS_CLICK, STATUS_SLIDE, STATUS_UNABLE
}
/**
* 填充大小
*/
private final int mPaddingSize = PADDING_DEFAULT;
private STATUS mStaus = STATUS.STATUS_ENABLE;
private Rect mThumbRect;
private int mX, mY;
private NoClickSlideSeekBar(Context context) {
super(context);
}
public NoClickSlideSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
private NoClickSlideSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (MotionEvent.ACTION_DOWN == event.getAction()) {
mThumbRect = getThumb().getBounds();
}
mX = (int) event.getX();
mY = (int) event.getY();
if (mStaus == STATUS.STATUS_CLICK && !checkBound()) {
return true;
}
if (mStaus == STATUS.STATUS_SLIDE && checkBound()) {
return true;
}
if (mStaus == STATUS.STATUS_UNABLE) {
return true;
}
return super.onTouchEvent(event);
}
/**
* 判断当前触摸点 是否在滑块之类-mPaddingSize是为了提高体验因为有些滑块太小
*
* @return
*/
private boolean checkBound() {
if (mX < mThumbRect.left - mPaddingSize || mX > mThumbRect.right + mPaddingSize || mY < mThumbRect.top - mPaddingSize || mY > mThumbRect.bottom + mPaddingSize) {
return true;
}
return false;
}
public void setStatus(STATUS status) {
this.mStaus = status;
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 428 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 504 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 432 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 252 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 178 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 633 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 546 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 326 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 305 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 186 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 797 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1001 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 713 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 399 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 378 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 201 B

View File

@@ -3,13 +3,13 @@
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape android:shape="rectangle">
<solid android:color="@color/block_color" />
<solid android:color="@color/line_color1" />
<corners android:radius="@dimen/dp_8" />
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="rectangle">
<solid android:color="@color/white" />
<solid android:color="@color/block_color" />
<corners android:radius="@dimen/dp_8" />
</shape>
</item>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="@android:id/background">
<shape>
<corners android:radius="@dimen/dp_20" />
<solid android:color="@color/grey_color_92" />
</shape>
</item>
<item android:id="@android:id/progress">
<clip>
<shape>
<solid android:color="@color/line_stroke_color" />
<corners android:radius="@dimen/dp_20" />
</shape>
</clip>
</item>
</layer-list>

View File

@@ -28,20 +28,20 @@
<NumberPicker
android:id="@+id/np_dialog_calender_hour"
android:layout_width="wrap_content"
android:layout_width="@dimen/dp_47"
android:layout_height="wrap_content" />
<com.abbidot.tracker.widget.TypefaceTextView
android:id="@+id/tv_home_route_calendar_from"
style="@style/my_TextView_style_v2"
android:layout_marginHorizontal="@dimen/dp_18"
android:layout_marginHorizontal="@dimen/dp_10"
android:text=":"
android:textSize="@dimen/textSize24"
android:textSize="@dimen/textSize20"
app:typeface="@string/roboto_bold_font" />
<NumberPicker
android:id="@+id/np_dialog_calender_min"
android:layout_width="wrap_content"
android:layout_width="@dimen/dp_47"
android:layout_height="wrap_content" />
</androidx.appcompat.widget.LinearLayoutCompat>
@@ -56,7 +56,7 @@
<com.abbidot.tracker.widget.TypefaceRoundButton
android:id="@+id/btn_select_calender_hour_min_cancel"
style="@style/my_QMUIRoundButton_small_style_v2"
android:layout_marginEnd="@dimen/dp_4"
android:layout_marginEnd="@dimen/dp_6"
android:text="@string/txt_cancel"
app:qmui_borderColor="@color/block_color2"
app:qmui_borderWidth="0.55dp"
@@ -65,7 +65,7 @@
<com.abbidot.tracker.widget.TypefaceRoundButton
android:id="@+id/btn_select_calender_hour_min_ok"
style="@style/my_QMUIRoundButton_small_style_v2"
android:layout_marginStart="@dimen/dp_4"
android:layout_marginStart="@dimen/dp_6"
android:text="@android:string/ok"
android:textColor="@color/select_color"
app:qmui_backgroundColor="@color/btn_yellow_color"

View File

@@ -0,0 +1,232 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat 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"
android:orientation="vertical"
tools:context=".ui.fragment.data.RouteV3Fragment">
<include
android:id="@+id/il_home_route_pet_head_v3"
layout="@layout/layout_top_pet_data_small"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_8"
android:layout_marginTop="@dimen/dp_8" />
<androidx.cardview.widget.CardView
android:id="@+id/cv_home_route_v3_card"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="@dimen/dp_8"
android:layout_weight="1"
app:cardCornerRadius="@dimen/dp_32">
<androidx.fragment.app.FragmentContainerView
android:id="@+id/fc_home_route_fragment_v3"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<com.abbidot.tracker.widget.MapMarkerInfoView
android:id="@+id/mi_home_route_address_view_v3"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_home_route_map_type_btn_v3"
style="@style/map_image_white_btn_style"
android:layout_gravity="top|end"
android:layout_margin="@dimen/dp_12" />
</androidx.cardview.widget.CardView>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/sv_home_route_map_scroll"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="@+id/rl_home_route_calendar_v3_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginHorizontal="@dimen/dp_8"
android:background="@drawable/shape_home_route_calendar_bg"
android:padding="@dimen/dp_12">
<View
android:id="@+id/v_home_route_calendar_center_v3"
android:layout_width="@dimen/dp_1"
android:layout_height="@dimen/dp_1"
android:layout_centerHorizontal="true" />
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_home_route_calendar_from_v3"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_4"
android:layout_toStartOf="@id/v_home_route_calendar_center_v3"
android:background="@drawable/shape8_white_gray_bg"
android:gravity="center"
android:orientation="horizontal"
android:paddingVertical="@dimen/dp_8">
<com.abbidot.tracker.widget.TypefaceTextView
android:id="@+id/tv_home_route_calendar_from_v3"
style="@style/my_TextView_style_v2"
android:layout_marginStart="@dimen/dp_12"
android:layout_weight="1"
android:gravity="start"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="@dimen/textSize12"
app:typeface="@string/roboto_regular_font" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_8"
android:src="@drawable/icon_date_time_svg" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_home_route_calendar_to_v3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/ll_home_route_calendar_from_v3"
android:layout_alignParentEnd="true"
android:layout_marginStart="@dimen/dp_4"
android:layout_toEndOf="@id/v_home_route_calendar_center_v3"
android:background="@drawable/shape8_white_gray_bg"
android:gravity="center"
android:orientation="horizontal"
android:paddingVertical="@dimen/dp_8">
<com.abbidot.tracker.widget.TypefaceTextView
android:id="@+id/tv_home_route_calendar_to_v3"
style="@style/my_TextView_style_v2"
android:layout_marginStart="@dimen/dp_12"
android:layout_weight="1"
android:gravity="start"
android:text="@string/app_name"
android:textColor="@color/white"
android:textSize="@dimen/textSize12"
app:typeface="@string/roboto_regular_font" />
<androidx.appcompat.widget.AppCompatImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="@dimen/dp_8"
android:src="@drawable/icon_date_time_svg" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_map_route_line_v3_last_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/ll_home_route_calendar_from_v3"
android:layout_alignStart="@id/ll_home_route_calendar_from_v3"
android:layout_marginTop="@dimen/dp_10"
android:background="@drawable/selector_transparent_pressed"
android:paddingVertical="@dimen/dp_8"
android:src="@drawable/icon_left_arrow_image"
android:visibility="gone" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_map_route_line_v3_next_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignTop="@id/iv_map_route_line_v3_last_btn"
android:layout_alignEnd="@id/ll_home_route_calendar_to_v3"
android:background="@drawable/selector_transparent_pressed"
android:paddingVertical="@dimen/dp_8"
android:src="@drawable/icon_right_arrow_image"
android:visibility="gone" />
<com.abbidot.tracker.widget.NoClickSlideSeekBar
android:id="@+id/vsb_map_route_line_v3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/ll_home_route_calendar_from_v3"
android:layout_marginHorizontal="@dimen/dp_2"
android:layout_marginTop="@dimen/dp_12"
android:layout_toStartOf="@id/iv_map_route_line_v3_next_btn"
android:layout_toEndOf="@id/iv_map_route_line_v3_last_btn"
android:maxHeight="@dimen/dp_8"
android:progress="100"
android:progressDrawable="@drawable/shape_seek_bar_style_v3"
android:splitTrack="false"
android:thumb="@drawable/icon_bar_thumb_image"
android:visibility="gone" />
</RelativeLayout>
<RelativeLayout
android:id="@+id/rl_map_route_line_v3_time_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/dp_8"
android:background="@drawable/shape20_white_half_bottom_bg"
android:paddingHorizontal="@dimen/dp_14"
android:paddingBottom="@dimen/dp_22"
android:visibility="visible">
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_map_route_line_v3_zooming_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:orientation="vertical"
android:paddingTop="@dimen/dp_4"
android:paddingBottom="@dimen/dp_10">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/v_map_route_line_v3_zooming"
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_4"
android:layout_centerHorizontal="true"
android:layout_gravity="center"
android:src="@drawable/shape8_gray_bg" />
<com.abbidot.tracker.widget.TypefaceTextView
style="@style/my_TextView_style_v2"
android:layout_below="@id/ll_map_route_line_v3_zooming_layout"
android:layout_gravity="end"
android:text="@string/txt_time_line"
android:textSize="@dimen/textSize12"
app:typeface="@string/roboto_regular_font" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_history_fence_start_image"
android:layout_width="@dimen/dp_18"
android:layout_height="wrap_content"
android:layout_below="@id/ll_map_route_line_v3_zooming_layout"
android:layout_marginTop="@dimen/dp_2"
android:src="@drawable/icon_right_arrow_image"
android:tint="@color/select_color" />
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rv_map_route_line_v3_fences_list"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ll_map_route_line_v3_zooming_layout"
android:layout_marginStart="@dimen/dp_8"
android:layout_toEndOf="@id/iv_history_fence_start_image" />
</RelativeLayout>
</androidx.appcompat.widget.LinearLayoutCompat>
<View
android:layout_width="wrap_content"
android:layout_height="@dimen/dp_40"
android:background="@color/transparent" />
</androidx.appcompat.widget.LinearLayoutCompat>

View File

@@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_history_fence_type_image"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/icon_history_danger_fence_image" />
<com.abbidot.tracker.widget.TypefaceTextView
android:id="@+id/tv_history_fence_type_time"
style="@style/my_TextView_style_v2"
android:layout_alignTop="@id/iv_history_fence_type_image"
android:layout_alignBottom="@id/iv_history_fence_type_image"
android:layout_marginStart="@dimen/dp_8"
android:layout_toEndOf="@id/iv_history_fence_type_image"
android:text="@string/app_name"
android:textSize="@dimen/textSize16"
android:textStyle="bold" />
<com.abbidot.tracker.widget.TypefaceTextView
android:id="@+id/tv_history_fence_type_content"
style="@style/my_TextView_style_v2"
android:layout_below="@id/tv_history_fence_type_time"
android:layout_alignStart="@id/tv_history_fence_type_time"
android:layout_marginTop="@dimen/dp_6"
android:text="@string/app_name"
android:textColor="@color/select_color3"
android:textSize="@dimen/textSize14"
app:typeface="@string/roboto_regular_font" />
<androidx.appcompat.widget.AppCompatImageView
android:id="@+id/iv_history_fence_type_line"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@id/iv_history_fence_type_image"
android:layout_alignStart="@id/iv_history_fence_type_image"
android:layout_alignEnd="@id/iv_history_fence_type_image"
android:layout_marginTop="@dimen/dp_2"
android:src="@drawable/icon_vertical_dot_line_image" />
</RelativeLayout>

View File

@@ -377,4 +377,5 @@
<color name="rote_line_color">#00C478</color>
<color name="light_yellow_color">#F9FFE8</color>
<color name="grey_color_91">#B0B0B0</color>
<color name="grey_color_92">#758E94</color>
</resources>

View File

@@ -262,7 +262,7 @@
<string name="map_navigate_map_google">Google Map</string>
<string name="map_navigate_map_gaode">Gaode Map</string>
<string name="map_navigate_cancel">Cancel</string>
<string name="txt_no_install">The APP is not installed, please download and install the app first</string>
<string name="txt_no_install">Install the map app to navigate</string>
<!-- Data -->
<string name="data_active_time">Active Time</string>
<string name="data_active_goal">Goal</string>
@@ -1076,4 +1076,7 @@
<string name="txt_fully_asleep">Fell asleep %s ago</string>
<string name="txt_move_wake">Move %s to wake up</string>
<string name="map_navigate_map_baidu">Baidu Map</string>
<string name="txt_time_line">Timeline</string>
</resources>