diff --git a/app/build.gradle b/app/build.gradle index 0df51c12..dc2f04ca 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -45,12 +45,8 @@ android { dependencies { implementation project(':collect-library') - implementation 'androidx.core:core-ktx:1.9.0' - implementation 'androidx.appcompat:appcompat:1.6.1' - implementation 'com.google.android.material:material:1.8.0' - implementation 'androidx.constraintlayout:constraintlayout:2.1.4' - implementation 'androidx.navigation:navigation-fragment-ktx:2.5.3' - implementation 'androidx.navigation:navigation-ui-ktx:2.5.3' + + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' diff --git a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt index 183c7d55..3c68f02b 100644 --- a/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt +++ b/app/src/main/java/com/navinfo/omqs/OMQSApplication.kt @@ -2,6 +2,7 @@ package com.navinfo.omqs import android.app.Application import android.util.Log +import com.navinfo.omqs.db.MyRealmModule import com.navinfo.omqs.tools.FileManager import dagger.hilt.android.HiltAndroidApp import io.realm.Realm @@ -24,6 +25,8 @@ class OMQSApplication : Application() { .directory(File(Constant.DATA_PATH)) .name("OMQS.realm") .encryptionKey(password) + .modules(Realm.getDefaultModule(), MyRealmModule()) + .schemaVersion(1) .build() Realm.setDefaultConfiguration(config) } diff --git a/app/src/main/java/com/navinfo/omqs/bean/BaseBean.kt b/app/src/main/java/com/navinfo/omqs/bean/BaseBean.kt deleted file mode 100644 index cedc7591..00000000 --- a/app/src/main/java/com/navinfo/omqs/bean/BaseBean.kt +++ /dev/null @@ -1,6 +0,0 @@ -package com.navinfo.omqs.bean - -import java.io.Serializable - -open class BaseBean : Serializable, Cloneable { -} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/bean/QsRecord.kt b/app/src/main/java/com/navinfo/omqs/bean/QsRecord.kt deleted file mode 100644 index ecac7094..00000000 --- a/app/src/main/java/com/navinfo/omqs/bean/QsRecord.kt +++ /dev/null @@ -1,115 +0,0 @@ -package com.navinfo.omqs.bean - -import io.realm.RealmObject -import io.realm.annotations.PrimaryKey - - -/** - * @author zhjch - * @version V1.0 - * @ClassName: Rd_qcRecord - * @Date 2016/1/12 - * @Description: ${TODO}(质检对象) - */ -open class QsRecord @JvmOverloads constructor( - /** - * id 主键 - * - */ - @PrimaryKey var id: String = "", - /** - * linkPid 绑定的道路ID - */ - var linkPid: String = "", - /** - *问题分类 - */ - var classType: String = "", - /** - * 问题类型 - */ - var type: String = "", - - /** - * 问题现象 - */ - var phenomenon: String = "", - /** - * 问题描述 - */ - var description: String = "", - /** - * 设置initial_cause - * @param initial_cause - * initial_cause - */ -// var initial_cause: String? = StringEntity.STRING_DEFAULT -// /** -// * 获取root_cause -// * @return root_cause -// */ -// /** -// * 设置root_cause -// * @param root_cause -// * root_cause -// */ -////根本原因(RCA) -// var root_cause: String? = StringEntity.STRING_DEFAULT -// /** -// * 获取check_userid -// * @return check_userid -// */ -// /** -// * 设置check_userid -// * @param check_userid -// * check_userid -// */ -////质检员 -// var check_userid: String? = StringEntity.STRING_DEFAULT -// /** -// * 获取check_time -// * @return check_time -// */ -// /** -// * 设置check_time -// * @param check_time -// * check_time -// */ -////质检日期 -// var check_time: String? = StringEntity.STRING_DEFAULT -// /** -// * 获取confirm_userid -// * @return confirm_userid -// */ -// /** -// * 设置confirm_userid -// * @param confirm_userid -// * confirm_userid -// */ -////确认人 -// var confirm_userid: String? = StringEntity.STRING_DEFAULT -// /** -// * 获取t_lifecycle -// * @return t_lifecycle -// */ -// /** -// * 设置t_lifecycle -// * @param t_lifecycle -// * t_lifecycle -// */ -////状态 0 无; 1 删除;2 更新;3 新增; -// var t_lifecycle = 0 -// /** -// * 获取t_status -// * @return t_status -// */ -// /** -// * 设置t_status -// * @param t_status -// * t_status -// */ -////问题记录提交状态 0 未提交;1 已提交; -// var t_status = 0 -) : RealmObject( - -) \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt new file mode 100644 index 00000000..f3d98286 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/db/MyRealmModule.kt @@ -0,0 +1,7 @@ +package com.navinfo.omqs.db + +import com.navinfo.collect.library.data.entity.QsRecordBean + +@io.realm.annotations.RealmModule(classes = [QsRecordBean::class]) +class MyRealmModule { +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/dao/ScProblemTypeDao.kt b/app/src/main/java/com/navinfo/omqs/db/dao/ScProblemTypeDao.kt index 5c4b4351..72c049ba 100644 --- a/app/src/main/java/com/navinfo/omqs/db/dao/ScProblemTypeDao.kt +++ b/app/src/main/java/com/navinfo/omqs/db/dao/ScProblemTypeDao.kt @@ -1,8 +1,6 @@ package com.navinfo.omqs.db.dao import androidx.room.* -import androidx.sqlite.db.SupportSQLiteDatabase -import com.navinfo.collect.library.data.dao.impl.MapLifeDataBase.getDatabase import com.navinfo.omqs.bean.ScProblemTypeBean @@ -38,13 +36,13 @@ interface ScProblemTypeDao { /** * 获取问题类型,并去重 */ - @Query("select DISTINCT TYPE from ScProblemType where CLASS_TYPE=:type order by TYPE") - suspend fun findProblemTypeList(type: String): List + @Query("select * from ScProblemType where CLASS_TYPE=:type order by TYPE") + suspend fun findProblemTypeList(type: String): List? - /** - * - */ - @Query("select PHENOMENON from ScProblemType where CLASS_TYPE=:classType and TYPE=:type order by PHENOMENON") - suspend fun getPhenomenonList(classType: String, type: String): List +// /** +// * +// */ +// @Query("select PHENOMENON from ScProblemType where CLASS_TYPE=:classType and TYPE=:type order by PHENOMENON") +// suspend fun getPhenomenonList(classType: String, type: String): List } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/dao/ScRootCauseAnalysisDao.kt b/app/src/main/java/com/navinfo/omqs/db/dao/ScRootCauseAnalysisDao.kt index c10a5b68..9fd40dc8 100644 --- a/app/src/main/java/com/navinfo/omqs/db/dao/ScRootCauseAnalysisDao.kt +++ b/app/src/main/java/com/navinfo/omqs/db/dao/ScRootCauseAnalysisDao.kt @@ -22,4 +22,10 @@ interface ScRootCauseAnalysisDao { insertList(list) } + /** + * 获取问题环节数据 + */ + @Query("select * from ScRootCauseAnalysis order by PROBLEM_LINK") + suspend fun findAllData(): List? + } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt b/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt index a2630bda..f5ba69a6 100644 --- a/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt +++ b/app/src/main/java/com/navinfo/omqs/hilt/MainActivityModule.kt @@ -35,6 +35,7 @@ class MainActivityModule { /** * 实验失败,这样创建,viewmodel不会在activity销毁的时候同时销毁 + * 4-14:因为没有传入activity的 owner,无法检测生命周期, */ // @ActivityRetainedScoped // @Provides diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt index 061fe8dc..b28368ba 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginActivity.kt @@ -58,11 +58,11 @@ class LoginActivity : PermissionsActivity() { loginDialog = null } LoginStatus.LOGIN_STATUS_SUCCESS -> { - val intent = Intent(this@LoginActivity, MainActivity::class.java) - startActivity(intent) -// finish() loginDialog?.dismiss() loginDialog = null + val intent = Intent(this@LoginActivity, MainActivity::class.java) + startActivity(intent) + finish() } LoginStatus.LOGIN_STATUS_CANCEL -> { loginDialog?.dismiss() diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt index e533560a..d0bc2101 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt @@ -141,7 +141,8 @@ class LoginViewModel @Inject constructor( .show() } } - NetResult.Loading -> {} + is NetResult.Loading -> {} + else -> {} } loginStatus.postValue(LoginStatus.LOGIN_STATUS_SUCCESS) } diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt index 3eee52bf..b28d7c4f 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt @@ -6,6 +6,7 @@ import android.util.Log import androidx.activity.viewModels import androidx.core.view.WindowCompat import androidx.databinding.DataBindingUtil +import androidx.lifecycle.lifecycleScope import androidx.lifecycle.viewModelScope import androidx.navigation.findNavController import com.navinfo.collect.library.map.NIMapController @@ -52,7 +53,7 @@ class MainActivity : BaseActivity() { //给xml传递viewModel对象 binding.viewModel = viewModel // lifecycle.addObserver(viewModel) - + lifecycleScope } override fun onStart() { @@ -92,4 +93,8 @@ class MainActivity : BaseActivity() { val naviController = findNavController(R.id.main_activity_right_fragment) naviController.navigate(R.id.EvaluationResultFragment) } + + override fun onBackPressed() { + super.onBackPressed() + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/BaseFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/BaseFragment.kt new file mode 100644 index 00000000..a0882082 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/BaseFragment.kt @@ -0,0 +1,57 @@ +package com.navinfo.omqs.ui.fragment + +import android.os.Bundle +import android.view.KeyEvent +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.activity.OnBackPressedCallback +import androidx.fragment.app.Fragment +import androidx.navigation.fragment.findNavController + +abstract class BaseFragment : Fragment() { +// override fun onCreateView( +// inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? +// ): View { +// val view = OnCreateView(inflater, container, savedInstanceState) +// +// view.isFocusableInTouchMode = true; +// view.requestFocus(); +// view.setOnKeyListener { _, keyCode, event -> +// if (keyCode == KeyEvent.KEYCODE_BACK && event.action == KeyEvent.ACTION_DOWN) { +// onBackPressed() +// } +// false +// } +// +// +// return view +// } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // 获取OnBackPressedDispatcher + val dispatcher = requireActivity().onBackPressedDispatcher + val callback = object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + onBackPressed() + } + } + + // 添加返回键事件处理逻辑 + dispatcher.addCallback(this, callback) + } + +// abstract fun OnCreateView( +// inflater: LayoutInflater, +// container: ViewGroup?, +// savedInstanceState: Bundle? +// ): View + + fun onBackPressed(): Boolean{ + findNavController().navigateUp() + return true + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultAdapter.kt deleted file mode 100644 index 4ac6dc7c..00000000 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultAdapter.kt +++ /dev/null @@ -1,16 +0,0 @@ -package com.navinfo.omqs.ui.fragment.evaluationresult - -import androidx.fragment.app.Fragment -import androidx.fragment.app.FragmentActivity -import androidx.viewpager2.adapter.FragmentStateAdapter - -class EvaluationResultAdapter(activity: FragmentActivity, val fragmentList: List) : - FragmentStateAdapter(activity) { - override fun getItemCount(): Int { - return fragmentList.size - } - - override fun createFragment(position: Int): Fragment { - return fragmentList[position] - } -} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt index 430501f5..4e9013db 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt @@ -4,128 +4,251 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup -import android.widget.AdapterView -import android.widget.ArrayAdapter -import android.widget.Toast -import androidx.fragment.app.Fragment -import androidx.fragment.app.viewModels -import androidx.navigation.fragment.findNavController -import androidx.viewpager2.widget.ViewPager2 -import com.google.android.material.tabs.TabLayoutMediator +import androidx.navigation.NavOptions +import androidx.navigation.findNavController import com.navinfo.omqs.R import com.navinfo.omqs.databinding.FragmentEvaluationResultBinding +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels import dagger.hilt.android.AndroidEntryPoint @AndroidEntryPoint -class EvaluationResultFragment : Fragment() { +class EvaluationResultFragment : BaseFragment(), View.OnClickListener { private var _binding: FragmentEvaluationResultBinding? = null - private val binding get() = _binding!! - private val viewModel by lazy { viewModels().value } - private var phenomenonFragmentAdapter: EvaluationResultAdapter? = null + private val viewModel by shareViewModels("QsRecode") + override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentEvaluationResultBinding.inflate(inflater, container, false) return binding.root - } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - //返回按钮 - binding.evaluationBar.setNavigationOnClickListener { - findNavController().popBackStack() - } - //监听数据变化 - viewModel.classTypeListLiveData.observe(viewLifecycleOwner) { - if (it == null || it.isEmpty()) { - Toast.makeText(requireContext(), "还没有导入元数据!", Toast.LENGTH_SHORT).show() - } else { - binding.evaluationClassType.adapter = - ArrayAdapter(requireContext(), R.layout.text_item_select, it) - } - } - //选择问题分类的回调 - binding.evaluationClassType.onItemSelectedListener = - object : AdapterView.OnItemSelectedListener { - override fun onItemSelected( - parent: AdapterView<*>?, view: View?, position: Int, id: Long - ) { - viewModel.getProblemTypeList(position) - } + liveDataObserve() - override fun onNothingSelected(parent: AdapterView<*>?) {} - } /** - * 监听联动选择的内容 + * 点击监听 */ - viewModel.problemTypeListLiveData.observe(viewLifecycleOwner) { - binding.evaluationClassTabLayout.let { tabLayout -> - tabLayout.removeAllTabs() - val fragmentList = mutableListOf() - for (item in it) { - val tab = tabLayout.newTab() - tab.text = item - tabLayout.addTab(tab) - fragmentList.add(PhenomenonFragment(viewModel.currentClassType,item)) - } - phenomenonFragmentAdapter = - activity?.let { a -> EvaluationResultAdapter(a, fragmentList) } - binding.evaluationViewpager.adapter = phenomenonFragmentAdapter - - TabLayoutMediator( - binding.evaluationClassTabLayout, - binding.evaluationViewpager - ) { tab, position -> - tab.text = it[position] - }.attach() - updateHeight(0) - } + binding.evaluationClassType.setOnClickListener(this) + binding.evaluationProblemType.setOnClickListener(this) + binding.evaluationPhenomenon.setOnClickListener(this) + binding.evaluationLink.setOnClickListener(this) + binding.evaluationCause.setOnClickListener(this) + //返回按钮点击 + binding.evaluationBar.setNavigationOnClickListener { + onBackPressed() } - //获取数据 - viewModel.getClassTypeList() - binding.evaluationViewpager.registerOnPageChangeCallback(object : - ViewPager2.OnPageChangeCallback() { - override fun onPageSelected(position: Int) { - super.onPageSelected(position) - updateHeight(position) + //标题栏按钮 + binding.evaluationBar.setOnMenuItemClickListener { + when (it.itemId) { + R.id.save -> { + viewModel.saveData() + true + } + R.id.delete -> { + viewModel.deleteData() + true + } + else -> true } - }) + } + /** + * 读取元数据 + */ + viewModel.loadMetadata() +// //监听大分类数据变化 +// viewModel.liveDataClassTypeList.observe(viewLifecycleOwner) { +// if (it == null || it.isEmpty()) { +// Toast.makeText(requireContext(), "还没有导入元数据!", Toast.LENGTH_SHORT).show() +// } else { +// binding.evaluationClassType.adapter = +// ArrayAdapter(requireContext(), R.layout.text_item_select, it) +// } +// } +// +// viewModel.liveDataProblemTypeList.observe(viewLifecycleOwner){ +// if (it == null || it.isEmpty()) { +// Toast.makeText(requireContext(), "还没有导入元数据!", Toast.LENGTH_SHORT).show() +// }else{ +// binding.evaluationProblemType.adapter = +// ArrayAdapter(requireContext(), R.layout.text_item_select, it) +// } +// } + +// //选择问题分类的回调 +// binding.evaluationClassType.onItemSelectedListener = +// object : AdapterView.OnItemSelectedListener { +// override fun onItemSelected( +// parent: AdapterView<*>?, view: View?, position: Int, id: Long +// ) { +// viewModel.getProblemTypeList(position) +// } +// +// override fun onNothingSelected(parent: AdapterView<*>?) {} +// } +// /** +// * 监听联动选择的内容 +// */ +// viewModel.problemTypeListLiveData.observe(viewLifecycleOwner) { +// binding.evaluationClassTabLayout.let { tabLayout -> +// tabLayout.removeAllTabs() +// val fragmentList = mutableListOf() +// for (item in it) { +// val tab = tabLayout.newTab() +// tab.text = item +// tabLayout.addTab(tab) +// fragmentList.add(PhenomenonFragment(viewModel.currentClassType, item)) +// } +// phenomenonFragmentAdapter = +// activity?.let { a -> EvaluationResultAdapter(a, fragmentList) } +// binding.evaluationViewpager.adapter = phenomenonFragmentAdapter +// +// TabLayoutMediator( +// binding.evaluationClassTabLayout, +// binding.evaluationViewpager +// ) { tab, position -> +// tab.text = it[position] +// }.attach() +// updateHeight(0) +// } +// +// } + +// binding.evaluationViewpager.registerOnPageChangeCallback(object : +// ViewPager2.OnPageChangeCallback() { +// override fun onPageSelected(position: Int) { +// super.onPageSelected(position) +// updateHeight(position) +// } +// }) } - private fun updateHeight(position: Int) { - phenomenonFragmentAdapter?.let { - if (it.fragmentList.size > position) { - val fragment: Fragment = it.fragmentList[position] - if (fragment.view != null) { - val viewWidth = View.MeasureSpec.makeMeasureSpec( - fragment.requireView().width, View.MeasureSpec.EXACTLY - ) - val viewHeight = - View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) - fragment.requireView().measure(viewWidth, viewHeight) - binding.evaluationViewpager.let { viewpager -> - if (viewpager.layoutParams.height != fragment.requireView().measuredHeight) { - //必须要用对象去接收,然后修改该对象再采用该对象,否则无法生效... - val layoutParams: ViewGroup.LayoutParams = - viewpager.layoutParams - layoutParams.height = fragment.requireView().measuredHeight - viewpager.layoutParams = layoutParams - } - } +// private fun updateHeight(position: Int) { +// phenomenonFragmentAdapter?.let { +// if (it.fragmentList.size > position) { +// val fragment: Fragment = it.fragmentList[position] +// if (fragment.view != null) { +// val viewWidth = View.MeasureSpec.makeMeasureSpec( +// fragment.requireView().width, View.MeasureSpec.EXACTLY +// ) +// val viewHeight = +// View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) +// fragment.requireView().measure(viewWidth, viewHeight) +// binding.evaluationViewpager.let { viewpager -> +// if (viewpager.layoutParams.height != fragment.requireView().measuredHeight) { +// //必须要用对象去接收,然后修改该对象再采用该对象,否则无法生效... +// val layoutParams: ViewGroup.LayoutParams = +// viewpager.layoutParams +// layoutParams.height = fragment.requireView().measuredHeight +// viewpager.layoutParams = layoutParams +// } +// } +// +// } +// } +// } +// +// } - } - } + /** + * 监听liveData + */ + private fun liveDataObserve() { + + //监听问题分类,更新UI + viewModel.liveDataCurrentClassType.observe(viewLifecycleOwner) { + binding.evaluationClassType.text = it + } + //监听问题类型,更新UI + viewModel.liveDataCurrentProblemType.observe(viewLifecycleOwner) { + binding.evaluationProblemType.text = it + } + //监听问题现象,更新UI + viewModel.liveDataCurrentPhenomenon.observe(viewLifecycleOwner) { + binding.evaluationPhenomenon.text = it + } + //监听问题环节,更新UI + viewModel.liveDataCurrentProblemLink.observe(viewLifecycleOwner) { + binding.evaluationLink.text = it + } + //监听问题初步原因,更新UI + viewModel.liveDataCurrentCause.observe(viewLifecycleOwner) { + binding.evaluationCause.text = it + } + //监听是否退出当前页面 + viewModel.liveDataFinish.observe(viewLifecycleOwner) { + onBackPressed() } - } - override fun onDestroyView() { + activity?.apply { + findNavController(R.id.main_activity_middle_fragment).navigateUp() + } super.onDestroyView() _binding = null } + + /** + * 处理点击事件 + */ + override fun onClick(v: View?) { + v?.let { + when (v.id) { + //上三项,打开面板 + R.id.evaluation_class_type, R.id.evaluation_problem_type, R.id.evaluation_phenomenon -> { + activity?.apply { + val controller = findNavController(R.id.main_activity_middle_fragment) + controller.currentDestination?.let { + //如果之前页面是空fragment,直接打开面板 + if (it.id == R.id.EmptyFragment) { + findNavController( + R.id.main_activity_middle_fragment + ).navigate(R.id.PhenomenonFragment) + } else if (it.id != R.id.PhenomenonFragment) {//不是空fragment,先弹出之前的fragment + findNavController( + R.id.main_activity_middle_fragment + ).navigate( + R.id.PhenomenonFragment, + null, + NavOptions.Builder() + .setPopUpTo(it.id, true).build() + ) + } + } + } + } + //下两项,打开面板 + R.id.evaluation_link, R.id.evaluation_cause -> { + activity?.apply { + val controller = findNavController(R.id.main_activity_middle_fragment) + controller.currentDestination?.let { + //如果之前页面是空fragment,直接打开面板 + if (it.id == R.id.EmptyFragment) { + findNavController( + R.id.main_activity_middle_fragment + ).navigate(R.id.ProblemLinkFragment) + } else if (it.id != R.id.ProblemLinkFragment) {//不是空fragment,先弹出之前的fragment + findNavController( + R.id.main_activity_middle_fragment + ).navigate( + R.id.ProblemLinkFragment, + null, + NavOptions.Builder() + .setPopUpTo(it.id, true).build() + ) + } + } + + } + } + else -> {} + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt index cd926fe9..a233fb64 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt @@ -1,73 +1,242 @@ package com.navinfo.omqs.ui.fragment.evaluationresult import android.util.Log -import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.navinfo.collect.library.map.GeoPoint +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.data.entity.QsRecordBean import com.navinfo.omqs.db.RoomAppDatabase import dagger.hilt.android.lifecycle.HiltViewModel +import io.realm.Realm import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import okhttp3.Dispatcher +import java.util.* import javax.inject.Inject @HiltViewModel class EvaluationResultViewModel @Inject constructor( - private val roomAppDatabase: RoomAppDatabase, + private val roomAppDatabase: RoomAppDatabase, private val mapController: NIMapController ) : ViewModel() { + + private val markerTitle = "点选marker" + + /** + * 操作结束,销毁页面 + */ + val liveDataFinish = MutableLiveData() + + /** + * 问题分类 liveData,给[PhenomenonLeftAdapter]展示的数据 + */ + val liveDataClassTypeList = MutableLiveData>() + + /** + * 问题类型 liveData 给[PhenomenonMiddleAdapter]展示的数据 + */ + val liveDataProblemTypeList = MutableLiveData>() + + /** + * 问题现象 liveData 给[PhenomenonRightGroupHeaderAdapter]展示的数据 + */ + val liveDataPhenomenonRightList = MutableLiveData>() + + + /** + * 当前选择问题分类 给[EvaluationResultFragment]中 【问题分类】展示数据 + */ + var liveDataCurrentClassType = MutableLiveData() + + /** + * 当前选择的问题类型 给[EvaluationResultFragment]中 【问题类型】展示数据 + */ + var liveDataCurrentProblemType = MutableLiveData() + + /** + * 当前选择的问题现象 给[EvaluationResultFragment]中 【问题现象】展示数据 + */ + var liveDataCurrentPhenomenon = MutableLiveData() + + + /** + * 当前选择的问题环节 给[EvaluationResultFragment]中 【问题环节】展示数据 + */ + var liveDataCurrentProblemLink = MutableLiveData() + + /** + * 当前选择的问初步原因 给[EvaluationResultFragment]中 【初步原因】展示数据 + */ + var liveDataCurrentCause = MutableLiveData() + + var currentGeoPoint: GeoPoint? = null + + init { Log.e("jingo", "EvaluationResultViewModel 创建了 ${hashCode()}") + mapController.markerHandle.apply { + setOnMapClickListener { + currentGeoPoint = it + addMarker(it, markerTitle) + } + } + val geoPoint = mapController.locationLayerHandler.getCurrentGeoPoint() + geoPoint?.let { + currentGeoPoint = it + mapController.markerHandle.addMarker(geoPoint, markerTitle) + } + } override fun onCleared() { super.onCleared() Log.e("jingo", "EvaluationResultViewModel 销毁了 ${hashCode()}") + mapController.markerHandle.removeMarker(markerTitle) + mapController.markerHandle.removeOnMapClickListener() } - /** - * 问题分类 liveData - */ - - val classTypeListLiveData = MutableLiveData?>() - - /** - * 问题类型 liveData - */ - - val problemTypeListLiveData = MutableLiveData>() - - var currentClassType: String = "" /** * 查询数据库,获取问题分类 */ + fun loadMetadata() { + viewModelScope.launch(Dispatchers.IO) { + getClassTypeList() + getProblemLinkList() + } + } + + /** + * //获取问题分类列表 + */ fun getClassTypeList() { viewModelScope.launch(Dispatchers.IO) { val list = roomAppDatabase.getScProblemTypeDao().findClassTypeList() - classTypeListLiveData.postValue(list) + list?.let { + //通知页面更新 + liveDataClassTypeList.postValue(it) + //如果右侧栏没数据,给个默认值 + if (liveDataCurrentClassType.value == null) { + liveDataCurrentClassType.postValue(it[0]) + } + getProblemList(it[0]) + } + } + + } + + /** + * 获取问题环节列表和初步问题 + */ + fun getProblemLinkList() { + viewModelScope.launch(Dispatchers.IO) { + val list = roomAppDatabase.getScRootCauseAnalysisDao().findAllData() + list?.let { tl -> + if (tl.isNotEmpty()) { + val typeTitleList = mutableListOf() + val phenomenonRightList = mutableListOf() + for (item in tl) { + if (!typeTitleList.contains(item.problemLink)) { + typeTitleList.add(item.problemLink) + } + phenomenonRightList.add( + PhenomenonMiddleBean( + title = item.problemLink, text = item.problemCause, isSelect = false + ) + ) + } + + if (liveDataCurrentProblemLink.value == null) { + liveDataCurrentProblemLink.postValue(phenomenonRightList[0].text) + } + if (liveDataCurrentCause.value == null) { + liveDataCurrentCause.postValue(typeTitleList[0]) + } + liveDataProblemTypeList.postValue(typeTitleList) + liveDataPhenomenonRightList.postValue(phenomenonRightList) + } + } + } + } + + /** + * 获取问题类型列表和问题现象 + */ + private suspend fun getProblemList(classType: String) { + val typeList = roomAppDatabase.getScProblemTypeDao().findProblemTypeList(classType) + typeList?.let { tl -> + if (tl.isNotEmpty()) { + val typeTitleList = mutableListOf() + val phenomenonRightList = mutableListOf() + for (item in tl) { + if (!typeTitleList.contains(item.problemType)) { + typeTitleList.add(item.problemType) + } + phenomenonRightList.add( + PhenomenonMiddleBean( + title = item.problemType, text = item.phenomenon, isSelect = false + ) + ) + } + if (liveDataCurrentPhenomenon.value == null) { + liveDataCurrentPhenomenon.postValue(phenomenonRightList[0].text) + } + if (liveDataCurrentProblemType.value == null) { + liveDataCurrentProblemType.postValue(typeTitleList[0]) + } + liveDataProblemTypeList.postValue(typeTitleList) + liveDataPhenomenonRightList.postValue(phenomenonRightList) + } } } /** * 查询问题类型 */ - fun getProblemTypeList(index: Int) { + fun getProblemTypeList(classType: String) { viewModelScope.launch(Dispatchers.IO) { - classTypeListLiveData.value?.let { - if (index < it.size) { - currentClassType = it[index] - val list = - roomAppDatabase.getScProblemTypeDao().findProblemTypeList(currentClassType) - problemTypeListLiveData.postValue(list) - } - } + liveDataCurrentClassType.postValue(classType) + getProblemList(classType) } } - fun getPhenomenonList() { - viewModelScope.launch (Dispatchers.IO){ + fun setPhenomenonMiddleBean(bean: PhenomenonMiddleBean) { + if (liveDataCurrentPhenomenon.value != bean.text) liveDataCurrentPhenomenon.value = + bean.text + if (liveDataCurrentProblemType.value != bean.title) liveDataCurrentProblemType.value = + bean.title + } + + fun setProblemLinkMiddleBean(bean: PhenomenonMiddleBean) { + if (liveDataCurrentProblemLink.value != bean.text) liveDataCurrentProblemLink.value = + bean.text + if (liveDataCurrentCause.value != bean.title) liveDataCurrentCause.value = bean.title + + } + + fun saveData() { + viewModelScope.launch(Dispatchers.IO) { + val qsRecord = QsRecordBean( + id = UUID.randomUUID().toString(), + classType = liveDataCurrentClassType.value.toString(), + type = liveDataCurrentProblemType.value.toString(), + phenomenon = liveDataCurrentPhenomenon.value.toString(), + problemLink = liveDataCurrentProblemLink.value.toString(), + cause = liveDataCurrentCause.value.toString(), + ) + qsRecord.geometry = currentGeoPoint!!.toGeometry() + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + it.copyToRealmOrUpdate(qsRecord) + } + realm.close() + mapController.mMapView.updateMap() + liveDataFinish.postValue(true) } } + + fun deleteData() { + + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonAdapter.kt deleted file mode 100644 index 4068dd07..00000000 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonAdapter.kt +++ /dev/null @@ -1,24 +0,0 @@ -package com.navinfo.omqs.ui.fragment.evaluationresult - -import android.view.LayoutInflater -import android.view.ViewGroup -import com.navinfo.omqs.R -import com.navinfo.omqs.databinding.TextItemSelectBinding -import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter -import com.navinfo.omqs.ui.other.BaseViewHolder - -class PhenomenonAdapter() : BaseRecyclerViewAdapter() { - override fun getItemViewRes(position: Int): Int { - return R.layout.text_item_select - } - - override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { - val viewBinding = - TextItemSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) - return BaseViewHolder(viewBinding) - } - - override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { - (holder.viewBinding as TextItemSelectBinding).itemId.text = data[position] - } -} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt index 67038c37..9eef25da 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt @@ -6,45 +6,111 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment -import androidx.lifecycle.lifecycleScope +import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.OnScrollListener +import com.navinfo.omqs.R import com.navinfo.omqs.databinding.FragmentPhenomenonBinding -import com.navinfo.omqs.db.RoomAppDatabase +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels import dagger.hilt.android.AndroidEntryPoint -import kotlinx.coroutines.launch -import javax.inject.Inject @AndroidEntryPoint -class PhenomenonFragment(private val classType: String, private val title: String) : - Fragment() { - @Inject - lateinit var roomAppDatabase: RoomAppDatabase +class PhenomenonFragment : + BaseFragment() { private var _binding: FragmentPhenomenonBinding? = null private val binding get() = _binding!! - + private val viewModel: EvaluationResultViewModel by shareViewModels("QsRecode") override fun onCreateView( - inflater: LayoutInflater, container: ViewGroup?, + inflater: LayoutInflater, + container: ViewGroup?, savedInstanceState: Bundle? ): View { _binding = FragmentPhenomenonBinding.inflate(inflater, container, false) Log.e("jingo", "PhenomenonFragment onCreateView ${hashCode()}") return binding.root - } + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - binding.phenomenonRecyclerview.setHasFixedSize(true) - binding.phenomenonRecyclerview.layoutManager = LinearLayoutManager(context) - val adapter = PhenomenonAdapter() - binding.phenomenonRecyclerview.adapter = adapter - lifecycleScope.launch { - Log.e("jingo", "$classType $title ") - val list = roomAppDatabase.getScProblemTypeDao().getPhenomenonList(classType, title) - Log.e("jingo", "${list.toString()}") - adapter.refreshData(list) + + //左侧菜单 + binding.phenomenonLeftRecyclerview.setHasFixedSize(true) + binding.phenomenonLeftRecyclerview.layoutManager = LinearLayoutManager(requireContext()) + val leftAdapter = PhenomenonLeftAdapter { _, text -> + viewModel.getProblemTypeList(text) } + binding.phenomenonLeftRecyclerview.adapter = leftAdapter + //左侧菜单查询结果监听 + viewModel.liveDataClassTypeList.observe(viewLifecycleOwner) { + leftAdapter.refreshData(it) + } + + //右侧菜单 + binding.phenomenonRightRecyclerview.setHasFixedSize(true) + var rightLayoutManager = LinearLayoutManager(requireContext()) + + binding.phenomenonRightRecyclerview.layoutManager = rightLayoutManager + val rightAdapter = PhenomenonRightGroupHeaderAdapter { _, bean -> + viewModel.setPhenomenonMiddleBean(bean) + } + binding.phenomenonRightRecyclerview.adapter = rightAdapter + //右侧菜单增加组标题 + binding.phenomenonRightRecyclerview.addItemDecoration( + PhenomenonRightGroupHeaderDecoration( + requireContext() + ) + ) + //右侧菜单查询数据监听 + viewModel.liveDataPhenomenonRightList.observe(viewLifecycleOwner) { + rightAdapter.refreshData(it) + } + + val middleAdapter = PhenomenonMiddleAdapter { _, title -> + rightLayoutManager.scrollToPositionWithOffset(rightAdapter.getGroupTopIndex(title), 0) + + } + + binding.phenomenonRightRecyclerview.addOnScrollListener(object : + OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + } + + //findLastVisibleItemPosition() :最后一个可见位置 +// findFirstVisibleItemPosition() :第一个可见位置 +// findLastCompletelyVisibleItemPosition() :最后一个完全可见位置 +// findFirstCompletelyVisibleItemPosition() :第一个完全可见位置 + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + val firstIndex = rightLayoutManager.findFirstVisibleItemPosition() + middleAdapter.setRightTitle(rightAdapter.data[firstIndex].title) + } + }) + + + //中间菜单 + binding.phenomenonMiddleRecyclerview.setHasFixedSize(true) + binding.phenomenonMiddleRecyclerview.layoutManager = LinearLayoutManager(requireContext()) + binding.phenomenonMiddleRecyclerview.adapter = middleAdapter + //中间侧菜单查询结果监听 + viewModel.liveDataProblemTypeList.observe(viewLifecycleOwner) { + middleAdapter.refreshData(it) + } + binding.phenomenonDrawer.setOnClickListener { + when (binding.group.visibility) { + View.INVISIBLE, View.GONE -> + binding.group.visibility = View.VISIBLE + else -> + binding.group.visibility = View.GONE + } + } + + viewModel.getClassTypeList() } override fun onDestroyView() { diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonLeftAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonLeftAdapter.kt new file mode 100644 index 00000000..572a3c5f --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonLeftAdapter.kt @@ -0,0 +1,54 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.TextItemSelectBinding +import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter +import com.navinfo.omqs.ui.other.BaseViewHolder + +class PhenomenonLeftAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) : + BaseRecyclerViewAdapter() { + private var selectTitle = "" + + override fun getItemViewRes(position: Int): Int { + return R.layout.text_item_select + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val viewBinding = + TextItemSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BaseViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val bd = holder.viewBinding as TextItemSelectBinding + val title = data[position] + bd.itemId.text = title + if (selectTitle == title) { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.cv_gray_153)) + } else { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.white)) + } + bd.root.setOnClickListener { + if (selectTitle != title) { + selectTitle = title + notifyDataSetChanged() + } + itemListener?.invoke(position, title) + } + } + + override fun refreshData(newData: List) { + data = newData + selectTitle = newData[0] + notifyDataSetChanged() + } + + fun setRightTitle(title: String) { + if (title != selectTitle) { + selectTitle = title + notifyDataSetChanged() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleAdapter.kt new file mode 100644 index 00000000..997b8b83 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleAdapter.kt @@ -0,0 +1,54 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.TextItemSelectBinding +import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter +import com.navinfo.omqs.ui.other.BaseViewHolder + +class PhenomenonMiddleAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) : + BaseRecyclerViewAdapter() { + private var selectTitle = "" + + override fun getItemViewRes(position: Int): Int { + return R.layout.text_item_select + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val viewBinding = + TextItemSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BaseViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val bd = holder.viewBinding as TextItemSelectBinding + val title = data[position] + bd.itemId.text = title + if (selectTitle == title) { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.cv_gray_153)) + } else { + bd.itemId.setBackgroundColor(holder.viewBinding.root.context.getColor(R.color.white)) + } + bd.root.setOnClickListener { + if (selectTitle != title) { + selectTitle = title + notifyDataSetChanged() + } + itemListener?.invoke(position, title) + } + } + + override fun refreshData(newData: List) { + data = newData + selectTitle = newData[0] + notifyDataSetChanged() + } + + fun setRightTitle(title: String) { + if (title != selectTitle) { + selectTitle = title + notifyDataSetChanged() + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleBean.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleBean.kt new file mode 100644 index 00000000..5f77e434 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonMiddleBean.kt @@ -0,0 +1,6 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +/** + * 问题现象列表 + */ +data class PhenomenonMiddleBean(val title: String, val text: String, var isSelect: Boolean = false) diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderAdapter.kt new file mode 100644 index 00000000..1b57d518 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderAdapter.kt @@ -0,0 +1,103 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.view.LayoutInflater +import android.view.ViewGroup +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.TextItemSelectBinding +import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter +import com.navinfo.omqs.ui.other.BaseViewHolder + +class PhenomenonRightGroupHeaderAdapter(private var itemListener: ((Int, PhenomenonMiddleBean) -> Unit?)? = null) : + BaseRecyclerViewAdapter() { + private var groupTitleList = mutableListOf() + override fun getItemViewRes(position: Int): Int { + return R.layout.text_item_select + } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val viewBinding = + TextItemSelectBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BaseViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val bd = holder.viewBinding as TextItemSelectBinding + bd.itemId.text = data[position].text + bd.root.setOnClickListener { + itemListener?.invoke(position, data[position]) + } + } + + /** + * 判断position对应的Item是否是组的第一项 + * + * @param position + * @return + */ + fun isItemHeader(position: Int): Boolean { + if (position >= data.size) + return false + return if (position == 0) { + true + } else { + val lastGroupName = data[position - 1].title + val currentGroupName = data[position].title + //判断上一个数据的组别和下一个数据的组别是否一致,如果不一致则是不同组,也就是为第一项(头部) + lastGroupName != currentGroupName + } + } + + fun isLastGroupTitle(position: Int): Boolean { + if (groupTitleList.isNotEmpty() && data[position].title == groupTitleList.last()) { + return true + } + return false + } + + /** + * 获取position对应的Item组名 + * + * @param position + * @return + */ + fun getGroupName(position: Int): String { + return data[position].title + } + + fun getGroupTopIndex(position: Int): Int { + var nowPosition = position + val title = data[position].title + for (i in data.size - 2 downTo 0) { + if (data[i].title == title) { + nowPosition = i + } else { + break + } + } + return nowPosition + } + + fun getGroupTopIndex(title: String): Int { + for (i in data.indices) { + if (data[i].title == title) + return i + } + return 0 + } + + override fun refreshData(newData: List) { + super.refreshData(newData) + groupTitleList.clear() + for (item in newData) { + if (groupTitleList.size > 0) { + if (groupTitleList.last() != item.title) { + groupTitleList.add(item.title) + } + } else { + groupTitleList.add(item.title) + } + } + } + + +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderDecoration.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderDecoration.kt new file mode 100644 index 00000000..23b60c68 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonRightGroupHeaderDecoration.kt @@ -0,0 +1,205 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Color +import android.graphics.Paint +import android.graphics.Rect +import android.util.Log +import android.view.View +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import androidx.recyclerview.widget.RecyclerView.ItemDecoration + + +/** + * 自定义装饰器(实现分组+吸顶效果) + */ +class PhenomenonRightGroupHeaderDecoration(context: Context) : ItemDecoration() { + //头部的高 + private val mItemHeaderHeight: Int + private val mTextPaddingLeft: Int + + //画笔,绘制头部和分割线 + private val mItemHeaderPaint: Paint + private val mTextPaint: Paint + private val mLinePaint: Paint + private val mTextRect: Rect + private var lastGroupView: View? = null + + init { + mItemHeaderHeight = dp2px(context, 40f) + mTextPaddingLeft = dp2px(context, 6f) + mTextRect = Rect() + mItemHeaderPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mItemHeaderPaint.color = Color.GRAY + mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG) + mTextPaint.textSize = 46f + mTextPaint.color = Color.WHITE + mLinePaint = Paint(Paint.ANTI_ALIAS_FLAG) + mLinePaint.color = Color.GRAY + } + + /** + * 绘制Item的分割线和组头 + * + * @param c + * @param parent + * @param state + */ + override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + if (parent.adapter is PhenomenonRightGroupHeaderAdapter) { + val adapter = parent.adapter as PhenomenonRightGroupHeaderAdapter + val count = parent.childCount //获取可见范围内Item的总数 + for (i in 0 until count) { + val view: View = parent.getChildAt(i) + + val position = parent.getChildLayoutPosition(view) + val isHeader: Boolean = adapter.isItemHeader(position) + val left = parent.paddingLeft + val right = parent.width - parent.paddingRight + if (isHeader) { + c.drawRect( + left.toFloat(), + (view.top - mItemHeaderHeight).toFloat(), + right.toFloat(), + view.top.toFloat(), + mItemHeaderPaint + ) + val text = adapter.getGroupName(position) + mTextPaint.getTextBounds( + text, + 0, + text.length, + mTextRect + ) + c.drawText( + adapter.getGroupName(position), + (left + mTextPaddingLeft).toFloat(), + (view.top - mItemHeaderHeight + mItemHeaderHeight / 2 + mTextRect.height() / 2).toFloat(), + mTextPaint + ) + } else { + c.drawRect( + left.toFloat(), + (view.top - 1).toFloat(), right.toFloat(), + view.top.toFloat(), mLinePaint + ) + + } + } + } + } + + /** + * 绘制Item的顶部布局(吸顶效果) + * + * @param c + * @param parent + * @param state + */ + override fun onDrawOver(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { + if (parent.adapter is PhenomenonRightGroupHeaderAdapter) { + val adapter = parent.adapter as PhenomenonRightGroupHeaderAdapter + val position = + (parent.layoutManager as LinearLayoutManager?)!!.findFirstVisibleItemPosition() + parent.findViewHolderForAdapterPosition(position)?.let { + val view: View = it.itemView + val isHeader: Boolean = adapter.isItemHeader(position + 1) + val top = parent.paddingTop + val left = parent.paddingLeft + val right = parent.width - parent.paddingRight + if (isHeader) { + val bottom = mItemHeaderHeight.coerceAtMost(view.bottom) + c.drawRect( + left.toFloat(), + (top + view.top - mItemHeaderHeight).toFloat(), + right.toFloat(), + (top + bottom).toFloat(), + mItemHeaderPaint + ) + val text = adapter.getGroupName(position) + mTextPaint.getTextBounds( + text, + 0, + text.length, + mTextRect + ) + c.drawText( + adapter.getGroupName(position), + (left + mTextPaddingLeft).toFloat(), + (top + mItemHeaderHeight / 2 + mTextRect.height() / 2 - (mItemHeaderHeight - bottom)).toFloat(), + mTextPaint + ) + } else { + c.drawRect( + left.toFloat(), + top.toFloat(), right.toFloat(), + (top + mItemHeaderHeight).toFloat(), mItemHeaderPaint + ) + val text = adapter.getGroupName(position) + mTextPaint.getTextBounds( + text, + 0, + text.length, + mTextRect + ) + c.drawText( + adapter.getGroupName(position), (left + mTextPaddingLeft).toFloat(), + (top + mItemHeaderHeight / 2 + mTextRect.height() / 2).toFloat(), mTextPaint + ) + + } + } + c.save() + } + } + + /** + * 设置Item的间距 + * + * @param outRect + * @param view + * @param parent + * @param state + */ + override fun getItemOffsets( + outRect: Rect, + view: View, + parent: RecyclerView, + state: RecyclerView.State + ) { + if (parent.adapter is PhenomenonRightGroupHeaderAdapter) { + val adapter = parent.adapter as PhenomenonRightGroupHeaderAdapter + //获取当前view在整个列表中的位置 + val position = parent.getChildLayoutPosition(view) + //是不是改组的第一个 + val isHeader: Boolean = adapter.isItemHeader(position) + if (isHeader) { + outRect.top = mItemHeaderHeight + if (adapter.isLastGroupTitle(position)) { + lastGroupView = view + } + } else if (position == (parent.adapter as PhenomenonRightGroupHeaderAdapter).itemCount - 1) { + //判断这条是不是最后一条 + //如果是最后一个,找到他所在组的第一个 + lastGroupView?.let { + if (it.top > 0) { + outRect.bottom = it.top - it.height * 2 + } + } + outRect.top = 1 + } else { + outRect.top = 1 + } + } + } + + /** + * dp转换成px + */ + private fun dp2px(context: Context, dpValue: Float): Int { + val scale: Float = context.resources.displayMetrics.density + return (dpValue * scale + 0.5f).toInt() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt new file mode 100644 index 00000000..d10fe1ef --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt @@ -0,0 +1,99 @@ +package com.navinfo.omqs.ui.fragment.evaluationresult + +import android.os.Bundle +import android.util.Log +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.recyclerview.widget.LinearLayoutManager +import androidx.recyclerview.widget.RecyclerView +import com.navinfo.omqs.databinding.FragmentProblemLinkBinding +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels + +class ProblemLinkFragment : BaseFragment() { + private var _binding: FragmentProblemLinkBinding? = null + private val binding get() = _binding!! + private val viewModel: EvaluationResultViewModel by shareViewModels("QsRecode") + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View { + _binding = FragmentProblemLinkBinding.inflate(inflater, container, false) + Log.e("jingo", "linkFragment onCreateView ${hashCode()}") + return binding.root + } + + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + //右侧菜单 + binding.linkRightRecyclerview.setHasFixedSize(true) + var rightLayoutManager = LinearLayoutManager(requireContext()) + + binding.linkRightRecyclerview.layoutManager = rightLayoutManager + val rightAdapter = PhenomenonRightGroupHeaderAdapter { _, bean -> + viewModel.setProblemLinkMiddleBean(bean) + } + binding.linkRightRecyclerview.adapter = rightAdapter + //右侧菜单增加组标题 + binding.linkRightRecyclerview.addItemDecoration( + PhenomenonRightGroupHeaderDecoration( + requireContext() + ) + ) + //右侧菜单查询数据监听 + viewModel.liveDataPhenomenonRightList.observe(viewLifecycleOwner) { + rightAdapter.refreshData(it) + } + + val middleAdapter = PhenomenonMiddleAdapter { _, title -> + rightLayoutManager.scrollToPositionWithOffset(rightAdapter.getGroupTopIndex(title), 0) + } + + binding.linkRightRecyclerview.addOnScrollListener(object : + RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + } + + override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { + super.onScrolled(recyclerView, dx, dy) + val firstIndex = rightLayoutManager.findFirstVisibleItemPosition() + middleAdapter.setRightTitle(rightAdapter.data[firstIndex].title) + } + }) + + //中间菜单 + binding.linkMiddleRecyclerview.setHasFixedSize(true) + binding.linkMiddleRecyclerview.layoutManager = LinearLayoutManager(requireContext()) + binding.linkMiddleRecyclerview.adapter = middleAdapter + //中间侧菜单查询结果监听 + viewModel.liveDataProblemTypeList.observe(viewLifecycleOwner) { + middleAdapter.refreshData(it) + } + binding.linkDrawer.setOnClickListener { + when (binding.group.visibility) { + View.INVISIBLE, View.GONE -> + binding.group.visibility = View.VISIBLE + else -> + binding.group.visibility = View.GONE + } + } + + viewModel.getProblemLinkList() + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + Log.e("jingo", "linkFragment onDestroyView ${hashCode()}") + } + + override fun onResume() { + super.onResume() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/other/AdapterItemClickListener.kt b/app/src/main/java/com/navinfo/omqs/ui/other/AdapterItemClickListener.kt new file mode 100644 index 00000000..ce53ad6e --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/other/AdapterItemClickListener.kt @@ -0,0 +1,5 @@ +package com.navinfo.omqs.ui.other + +interface AdapterItemClickListener { + fun onItemClick(position: Int) +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt index 3f60af13..aed4ced8 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/other/BaseRecyclerViewAdapter.kt @@ -37,27 +37,28 @@ abstract class BaseRecyclerViewAdapter(var data: List = listOf()) : return data.size } - fun refreshData(newData: List) { - this.data = newData - this.notifyDataSetChanged() - } - - override fun onViewAttachedToWindow(holder: BaseViewHolder) { super.onViewAttachedToWindow(holder) holder.onStart() } + override fun onViewDetachedFromWindow(holder: BaseViewHolder) { super.onViewDetachedFromWindow(holder) holder.apply { onStop() } } + // -// override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + + // this.recyclerView = recyclerView // super.onAttachedToRecyclerView(recyclerView) -// this.recyclerView = recyclerView +// override fun onAttachedToRecyclerView(recyclerView: RecyclerView) { + open fun refreshData(newData: List) { + this.data = newData + this.notifyDataSetChanged() + } // } // // override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) { diff --git a/app/src/main/java/com/navinfo/omqs/ui/other/ShareViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/other/ShareViewModel.kt new file mode 100644 index 00000000..57ffe9cc --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/other/ShareViewModel.kt @@ -0,0 +1,107 @@ +package com.navinfo.omqs.ui.other + +import androidx.annotation.MainThread +import androidx.fragment.app.Fragment +import androidx.fragment.app.createViewModelLazy +import androidx.lifecycle.* +import androidx.lifecycle.viewmodel.CreationExtras + +/** + * 用来共享的viewModel + */ +val vMStores = HashMap() + + +@MainThread +inline fun Fragment.shareViewModels( + scopeName: String, + noinline ownerProducer: () -> ViewModelStoreOwner = { this }, + noinline factoryProducer: (() -> ViewModelProvider.Factory)? = null +): Lazy { + val owner by lazy(LazyThreadSafetyMode.NONE) { ownerProducer() } + + val store: ViewModelStoreOwner + if (vMStores.keys.contains(scopeName)) { + store = vMStores[scopeName]!! + } else { + vMStores[scopeName] = owner + store = owner + this.let { fragment -> + fragment.lifecycle.addObserver(object : LifecycleEventObserver { + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + if (event == Lifecycle.Event.ON_DESTROY) { + fragment.lifecycle.removeObserver(this) + store.viewModelStore.clear() + vMStores.remove(scopeName) + } + } + }) + } + + + } +// store.register(this) + + return createViewModelLazy( + VM::class, + { store.viewModelStore }, + { + (store as? HasDefaultViewModelProviderFactory)?.defaultViewModelCreationExtras + ?: CreationExtras.Empty + }, + factoryProducer ?: { + (store as? HasDefaultViewModelProviderFactory)?.defaultViewModelProviderFactory + ?: defaultViewModelProviderFactory + }) +} + +// +//@MainThread +//inline fun LifecycleOwner.shareViewModels( +// scopeName: String, +// factory: ViewModelProvider.Factory? = null +//): Lazy { +// val store: VMStore +// if (vMStores.keys.contains(scopeName)) { +// store = vMStores[scopeName]!! +// } else { +// store = VMStore() +// vMStores[scopeName] = store +// } +// store.register(this) +// return ViewModelLazy(VM::class, +// { store.viewModelStore }, +// { factory ?: MyViewModelFactory() }) +//} + + +class MyViewModelFactory( +) : ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return modelClass.getConstructor().newInstance() + } +} + +class VMStore(val owner: ViewModelStoreOwner) { + +// private val bindTargets = ArrayList() +// fun register(host: LifecycleOwner) { +// if (!bindTargets.contains(host)) { +// bindTargets.add(host) +// host.lifecycle.addObserver(object : LifecycleEventObserver { +// override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { +// if (event == Lifecycle.Event.ON_DESTROY) { +// host.lifecycle.removeObserver(this) +// bindTargets.remove(host) +// if (bindTargets.isEmpty()) {//如果当前商店没有关联对象,则释放资源 +// vMStores.entries.find { it.value == this@VMStore }?.also { +// owner.viewModelStore.clear() +// vMStores.remove(it.key) +// } +// } +// } +// } +// }) +// } +// } +} \ No newline at end of file diff --git a/app/src/main/res/layout/activity_login.xml b/app/src/main/res/layout/activity_login.xml index 1d88e9f5..fba45e93 100644 --- a/app/src/main/res/layout/activity_login.xml +++ b/app/src/main/res/layout/activity_login.xml @@ -35,13 +35,14 @@ @@ -54,10 +55,12 @@ + @@ -27,8 +28,12 @@ + android:layout_width="0dp" + android:layout_height="0dp" + app:layout_constraintBottom_toBottomOf="parent" + app:layout_constraintLeft_toLeftOf="parent" + app:layout_constraintRight_toRightOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_evaluation_result.xml b/app/src/main/res/layout/fragment_evaluation_result.xml index d53445ad..c9829571 100644 --- a/app/src/main/res/layout/fragment_evaluation_result.xml +++ b/app/src/main/res/layout/fragment_evaluation_result.xml @@ -45,24 +45,52 @@ android:layout_height="wrap_content" android:text="问题分类" /> - - + android:layout_marginTop="5dp" + android:background="@drawable/fm_card_map_down_status_bg" /> - + android:layout_height="wrap_content" + android:layout_marginTop="5dp" + android:background="@drawable/fm_card_map_down_status_bg" /> + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/fragment_phenomenon.xml b/app/src/main/res/layout/fragment_phenomenon.xml index 17f0dfea..4a789a65 100644 --- a/app/src/main/res/layout/fragment_phenomenon.xml +++ b/app/src/main/res/layout/fragment_phenomenon.xml @@ -1,15 +1,67 @@ - - - \ No newline at end of file + android:background="@drawable/progress_bg" + android:paddingLeft="10dp" + android:paddingTop="30dp" + android:paddingRight="10dp" + android:paddingBottom="30dp" + android:src="@drawable/btn_back_xml" /> + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_problem_link.xml b/app/src/main/res/layout/fragment_problem_link.xml new file mode 100644 index 00000000..c0862ccb --- /dev/null +++ b/app/src/main/res/layout/fragment_problem_link.xml @@ -0,0 +1,60 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/navigation/middle_fragment_nav_graph.xml b/app/src/main/res/navigation/middle_fragment_nav_graph.xml new file mode 100644 index 00000000..6fd2b7f6 --- /dev/null +++ b/app/src/main/res/navigation/middle_fragment_nav_graph.xml @@ -0,0 +1,24 @@ + + + + + + + + \ No newline at end of file diff --git a/collect-library/build.gradle b/collect-library/build.gradle index 0f109e87..3cca075f 100644 --- a/collect-library/build.gradle +++ b/collect-library/build.gradle @@ -63,10 +63,19 @@ android { dependencies { api fileTree(dir: 'libs', include: ['*.jar', '*.aar']) api files('libs/BaiduLBS_Android.jar') + + // + api 'androidx.core:core-ktx:1.9.0' + api 'androidx.appcompat:appcompat:1.6.1' + api 'com.google.android.material:material:1.8.0' + api 'androidx.constraintlayout:constraintlayout:2.1.4' + api 'androidx.navigation:navigation-fragment-ktx:2.5.3' + api 'androidx.navigation:navigation-ui-ktx:2.5.3' + testImplementation 'junit:junit:4.13.2' androidTestImplementation 'androidx.test.ext:junit:1.1.5' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1' - implementation 'com.yanzhenjie:kalle:0.1.7' +// implementation 'com.yanzhenjie:kalle:0.1.7' // VTM依赖 implementation "net.sf.kxml:kxml2:2.3.0" diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt new file mode 100644 index 00000000..ed24f345 --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt @@ -0,0 +1,102 @@ +package com.navinfo.collect.library.data.entity + +import com.navinfo.collect.library.utils.GeometryToolsKt +import io.realm.RealmObject +import io.realm.RealmSet +import io.realm.annotations.PrimaryKey +import io.realm.annotations.RealmClass + + +/** + * @author zhjch + * @version V1.0 + * @ClassName: Rd_qcRecord + * @Date 2016/1/12 + * @Description: ${TODO}(质检对象) + */ +@RealmClass +open class QsRecordBean @JvmOverloads constructor( + /** + * id 主键 + * + */ + @PrimaryKey + var id: String = "", + /** + * 关联要素id + */ + var elementId: String = "", + /** + * linkPid 绑定的道路ID + */ + var linkId: String = "", + /** + *问题分类 + */ + var classType: String = "", + /** + * 问题类型 + */ + var type: String = "", + + /** + * 问题现象 + */ + var phenomenon: String = "", + /** + * 描述信息 + */ + var description: String = "", + /** + * 问题环节 + */ + var problemLink: String = "", + /** + * 问题原因 + * 根本原因(RCA) + */ + + var cause: String = "", + /** + * 质检员ID + */ + var checkUserId: String = "", + /** + * 质检日期 + */ + var checkTime: String = "", + /** + * 确认人 + */ + var confirmUserId: String = "", + /** + * 状态 0 无; 1 删除;2 更新;3 新增; + */ + + var t_lifecycle: Int = 3, + /** + * 问题记录提交状态 0 未提交;1 已提交; + */ + var t_status: Int = 0, + /** + * 显示坐标 + */ +// var geometry: String = "", + /** + * 显示坐标 + */ + var guideGeometry: String = "", + + ) : RealmObject() { + + + private val tileX = RealmSet() // x方向的tile编码 + private val tileY = RealmSet() // y方向的tile编码 + var geometry: String = "" + set(value) { + field = value + // 根据geometry自动计算当前要素的x-tile和y-tile + GeometryToolsKt.getTileXByGeometry(value, tileX) + GeometryToolsKt.getTileYByGeometry(value, tileY) + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/GeoPoint.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/GeoPoint.kt new file mode 100644 index 00000000..ed7115f8 --- /dev/null +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/GeoPoint.kt @@ -0,0 +1,14 @@ +package com.navinfo.collect.library.map + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class GeoPoint( + var latitude: Double = 0.0, + var longitude: Double = 0.0 +) : Parcelable { + fun toGeometry(): String { + return "POINT($longitude $latitude)" + } +} \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt index 7b0b244d..30613a8f 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapController.kt @@ -2,6 +2,7 @@ package com.navinfo.collect.library.map import android.content.Context import android.util.Log +import androidx.appcompat.app.AppCompatActivity import com.navinfo.collect.library.map.handler.* import com.navinfo.collect.library.map.maphandler.MeasureLayerHandler import com.navinfo.collect.library.map.handler.ViewportHandler @@ -23,7 +24,7 @@ class NIMapController { lateinit var measureLayerHandler: MeasureLayerHandler - fun init(context: Context, mapView: NIMapView, options: NIMapOptions? = null, mapPath: String) { + fun init(context: AppCompatActivity, mapView: NIMapView, options: NIMapOptions? = null, mapPath: String) { Constant.MAP_PATH = mapPath layerManagerHandler = LayerManagerHandler(context, mapView) locationLayerHandler = LocationLayerHandler(context, mapView) @@ -38,5 +39,6 @@ class NIMapController { mMapView.vtmMap.viewport().maxZoomLevel = Constant.MAX_ZOOM // 设置地图的最大级别 } + } diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java index 02a0daea..d8fa38ca 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/NIMapView.java @@ -3,7 +3,6 @@ package com.navinfo.collect.library.map; import android.content.Context; import android.os.Bundle; import android.util.AttributeSet; -import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; @@ -15,37 +14,21 @@ import androidx.annotation.Nullable; import com.navinfo.collect.library.R; import com.navinfo.collect.library.map.layers.NaviMapScaleBar; -import com.navinfo.collect.library.map.source.MapLifeDBTileSource; -import com.navinfo.collect.library.map.source.MapLifeNiLocationTileDataSource; -import com.navinfo.collect.library.map.source.MapLifeNiLocationTileSource; -import com.navinfo.collect.library.system.Constant; import org.oscim.android.MapPreferences; import org.oscim.android.MapView; import org.oscim.core.GeoPoint; import org.oscim.android.theme.AssetsRenderTheme; import org.oscim.core.MapPosition; -import org.oscim.core.Tile; import org.oscim.event.Event; import org.oscim.event.Gesture; import org.oscim.event.GestureListener; import org.oscim.layers.GroupLayer; import org.oscim.layers.Layer; -import org.oscim.layers.TileGridLayer; -import org.oscim.layers.tile.buildings.BuildingLayer; -import org.oscim.layers.tile.vector.OsmTileLayer; import org.oscim.layers.tile.vector.VectorTileLayer; -import org.oscim.layers.tile.vector.labeling.LabelLayer; -import org.oscim.layers.tile.vector.labeling.LabelTileLoaderHook; import org.oscim.map.Map; import org.oscim.renderer.GLViewport; -import org.oscim.theme.IRenderTheme; -import org.oscim.theme.ThemeLoader; import org.oscim.theme.VtmThemes; -import org.oscim.tiling.source.mapfile.MapFileTileSource; -import org.oscim.tiling.source.mapfile.MultiMapFileTileSource; - -import java.io.File; /** @@ -125,14 +108,14 @@ public final class NIMapView extends RelativeLayout { * * @param point */ - void onMapClick(GeoPoint point); + void onMapClick(com.navinfo.collect.library.map.GeoPoint point); /** * 地图内 Poi 单击事件回调函数 * * @param poi */ - void onMapPoiClick(GeoPoint poi); +// void onMapPoiClick(GeoPoint poi); } /** @@ -218,10 +201,8 @@ public final class NIMapView extends RelativeLayout { this.mContext = context; mapView = rootView.findViewById(R.id.base_map_view); -// map = new NIMap(this); compassImage = rootView.findViewById(R.id.navinfo_map_compass); initMapGroup(); // 初始化图层组 -// mLayerManager = new NILayerManager(context, getVtmMap()); logoImage = rootView.findViewById(R.id.navinfo_map_logo); mRotateAnimation = new NIRotateAnimation(compassImage); @@ -251,6 +232,7 @@ public final class NIMapView extends RelativeLayout { } }); + // 增加比例尺图层 NaviMapScaleBar naviMapScaleBar = new NaviMapScaleBar(getVtmMap()); naviMapScaleBar.initScaleBarLayer(GLViewport.Position.BOTTOM_LEFT, 25, 60); @@ -302,6 +284,9 @@ public final class NIMapView extends RelativeLayout { zoomLayout = rootView.findViewById(R.id.navinfo_map_zoom_layer); switchTileVectorLayerTheme(MAP_THEME.DEFAULT); + + MapEventsReceiver mapEventReceiver = new MapEventsReceiver(mapView.map()); + getVtmMap().layers().add(mapEventReceiver); } @@ -923,7 +908,7 @@ public final class NIMapView extends RelativeLayout { GeoPoint geoPoint = mMap.viewport().fromScreenPoint(e.getX(), e.getY()); if (g instanceof Gesture.Tap) { // 单击事件 if (mapClickListener != null) { - mapClickListener.onMapClick(geoPoint); + mapClickListener.onMapClick(new com.navinfo.collect.library.map.GeoPoint(geoPoint.getLatitude(), geoPoint.getLongitude())); } } else if (g instanceof Gesture.DoubleTap) { // 双击 if (mapDoubleClickListener != null) { @@ -934,17 +919,6 @@ public final class NIMapView extends RelativeLayout { mapLongClickListener.onMapLongClick(geoPoint); } } - setOnMapClickListener(new OnMapClickListener() { - @Override - public void onMapClick(GeoPoint point) { - - } - - @Override - public void onMapPoiClick(GeoPoint poi) { - - } - }); return false; } } @@ -952,7 +926,7 @@ public final class NIMapView extends RelativeLayout { /** * 设置地图的点击事件 */ - public void setOnMapClickListener(@Nullable OnMapClickListener listener) { + public void setOnMapClickListener(OnMapClickListener listener) { this.mapClickListener = listener; } diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt index b909f5b4..2d5bf2e5 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/BaseHandler.kt @@ -1,11 +1,12 @@ package com.navinfo.collect.library.map.handler import android.content.Context +import androidx.appcompat.app.AppCompatActivity import com.navinfo.collect.library.map.NIMapView import org.oscim.layers.Layer -abstract class BaseHandler(context: Context, mapView: NIMapView) { - protected val mContext: Context = context +abstract class BaseHandler(context: AppCompatActivity, mapView: NIMapView) { + protected val mContext: AppCompatActivity = context protected val mMapView: NIMapView = mapView fun addLayer(layer: Layer, groupType: NIMapView.LAYER_GROUPS) { @@ -19,4 +20,11 @@ abstract class BaseHandler(context: Context, mapView: NIMapView) { mMapView.vtmMap.layers().remove(layer) } + fun setOnMapClickListener(listener: NIMapView.OnMapClickListener) { + mMapView.setOnMapClickListener(listener) + } + + fun removeOnMapClickListener() { + mMapView.setOnMapClickListener(null) + } } \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt index f8f64bab..aa709a97 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LayerManagerHandler.kt @@ -1,15 +1,21 @@ package com.navinfo.collect.library.map.handler -import android.content.Context +import android.graphics.Color +import androidx.appcompat.app.AppCompatActivity +import androidx.lifecycle.lifecycleScope +import com.navinfo.collect.library.data.entity.QsRecordBean import com.navinfo.collect.library.map.NIMapView -import com.navinfo.collect.library.map.source.NavinfoMapRastorTileSource import com.navinfo.collect.library.map.source.NavinfoMultiMapFileTileSource import com.navinfo.collect.library.system.Constant +import io.realm.Realm +import io.realm.kotlin.where +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import okhttp3.Cache import okhttp3.OkHttpClient +import org.oscim.backend.CanvasAdapter +import org.oscim.backend.canvas.Paint import org.oscim.layers.GroupLayer -import org.oscim.layers.Layer -import org.oscim.layers.tile.bitmap.BitmapTileLayer import org.oscim.layers.tile.buildings.BuildingLayer import org.oscim.layers.tile.vector.VectorTileLayer import org.oscim.layers.tile.vector.labeling.LabelLayer @@ -20,11 +26,22 @@ import java.io.File /** * Layer 操作 */ -class LayerManagerHandler(context: Context, mapView: NIMapView) : +class LayerManagerHandler(context: AppCompatActivity, mapView: NIMapView) : BaseHandler(context, mapView) { private var baseGroupLayer // 用于盛放所有基础底图的图层组,便于统一管理 : GroupLayer? = null + /** + * 默认文字颜色 + */ + private val mDefaultTextColor = "#4E55AF" + + /** + * 文字画笔 + */ + + private lateinit var paint: Paint + init { initMap() } @@ -36,8 +53,9 @@ class LayerManagerHandler(context: Context, mapView: NIMapView) : loadBaseMap() mMapView.switchTileVectorLayerTheme(NIMapView.MAP_THEME.DEFAULT) + //初始化之间数据图层 + initQsRecordDataLayer() mMapView.vtmMap.updateMap() -// initVectorTileLayer() // initMapLifeSource() } @@ -46,7 +64,7 @@ class LayerManagerHandler(context: Context, mapView: NIMapView) : * 切换基础底图样式 */ fun loadBaseMap() { - + //给地图layer分组 if (baseGroupLayer == null) { baseGroupLayer = GroupLayer(mMapView.vtmMap) addLayer(baseGroupLayer!!, NIMapView.LAYER_GROUPS.BASE) @@ -90,8 +108,11 @@ class LayerManagerHandler(context: Context, mapView: NIMapView) : } } baseLayer.tileSource = urlTileSource + //增加基础路网图层 it.layers.add(baseLayer) + //增加建筑图层 it.layers.add(BuildingLayer(mMapView.vtmMap, baseLayer)) + //增加文字图层 it.layers.add(LabelLayer(mMapView.vtmMap, baseLayer)) for (layer in it.layers) { addLayer(layer, NIMapView.LAYER_GROUPS.BASE) @@ -101,26 +122,45 @@ class LayerManagerHandler(context: Context, mapView: NIMapView) : } } - private fun getRasterTileLayer( - url: String?, - tilePath: String?, - useCache: Boolean - ): Layer { - val builder = OkHttpClient.Builder() - val mTileSource = - NavinfoMapRastorTileSource.builder(url).tilePath(tilePath) - .httpFactory(OkHttpFactory(builder)).build() - // 如果使用缓存 - if (useCache) { - val cacheDirectory = - File(Constant.MAP_PATH, "cache") - val cacheSize = 300 * 1024 * 1024 // 300 MB - val cache = Cache(cacheDirectory, cacheSize.toLong()) - builder.cache(cache) - } + private fun initQsRecordDataLayer() { + paint = CanvasAdapter.newPaint() + paint.setTypeface(Paint.FontFamily.DEFAULT, Paint.FontStyle.NORMAL) + paint.setTextSize(13 * CanvasAdapter.getScale()) + paint.strokeWidth = 2 * CanvasAdapter.getScale() + paint.color = Color.parseColor(mDefaultTextColor) - return BitmapTileLayer(mMapView.vtmMap, mTileSource) + mContext.lifecycleScope.launch(Dispatchers.IO) { + + + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + val list = realm.where().findAll() + paint.setColor(Color.parseColor(mDefaultTextColor)) + } + realm.close() + } } + +// private fun getRasterTileLayer( +// url: String?, +// tilePath: String?, +// useCache: Boolean +// ): Layer { +// val builder = OkHttpClient.Builder() +// val mTileSource = +// NavinfoMapRastorTileSource.builder(url).tilePath(tilePath) +// .httpFactory(OkHttpFactory(builder)).build() +// // 如果使用缓存 +// if (useCache) { +// val cacheDirectory = +// File(Constant.MAP_PATH, "cache") +// val cacheSize = 300 * 1024 * 1024 // 300 MB +// val cache = Cache(cacheDirectory, cacheSize.toLong()) +// builder.cache(cache) +// } +// +// return BitmapTileLayer(mMapView.vtmMap, mTileSource) +// } } /** diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt index 19134fe2..e6558e75 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/LocationLayerHandler.kt @@ -8,6 +8,7 @@ import com.baidu.location.BDLocation import com.baidu.location.LocationClient import com.baidu.location.LocationClientOption import com.baidu.location.LocationClientOption.LocationMode +import com.navinfo.collect.library.map.GeoPoint import com.navinfo.collect.library.map.NIMapView import org.oscim.layers.LocationLayer @@ -142,6 +143,13 @@ class LocationLayerHandler(context: Context, mapView: NIMapView) : BaseHandler(c mMapView.vtmMap.animator().animateTo(300, mapPosition) } } + + fun getCurrentGeoPoint(): GeoPoint? { + mCurrentLocation?.let { + return GeoPoint(it.latitude, it.longitude) + } + return null + } } /** diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt index 8c86e51b..7fbee159 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/MarkHandler.kt @@ -1,75 +1,101 @@ package com.navinfo.collect.library.map.handler import android.content.Context +import android.graphics.BitmapFactory +import com.navinfo.collect.library.R +import com.navinfo.collect.library.map.GeoPoint import com.navinfo.collect.library.map.NIMapView import com.navinfo.collect.library.utils.StringUtil -import org.oscim.core.GeoPoint +import org.oscim.android.canvas.AndroidBitmap +import org.oscim.layers.marker.ItemizedLayer import org.oscim.layers.marker.ItemizedLayer.OnItemGestureListener import org.oscim.layers.marker.MarkerInterface import org.oscim.layers.marker.MarkerItem +import org.oscim.layers.marker.MarkerSymbol /** * marker 操作 */ -open class MarkHandler(context: Context, mapView:NIMapView) : - BaseHandler(context, mapView), OnItemGestureListener { +class MarkHandler(context: Context, mapView: NIMapView) : + BaseHandler(context, mapView) { -// //增加marker -// fun addMarker( -// geoPoint: GeoPoint, -// title: String?, -// description: String? = "" -// ): MarkerItem { -// var marker: MarkerItem? = null -// for (e in mMapView.layerManager.defaultMarkerLayer.itemList) { -// if (e is MarkerItem && e.title == title) { -// marker = e; -// break; -// } -// } -// if (marker == null) { -// var tempTitle = title; -// if (tempTitle.isNullOrBlank()) { -// tempTitle = StringUtil.createUUID(); -// } -// val marker = MarkerItem( -// tempTitle, -// description, -// geoPoint -// ) -// mMapView.layerManager.defaultMarkerLayer.addItem(marker); -// mMapView.vtmMap.updateMap(true) -// return marker -// } else { -// marker.description = description -// marker.geoPoint = geoPoint -// mMapView.layerManager.defaultMarkerLayer.removeItem(marker) -// mMapView.layerManager.defaultMarkerLayer.addItem(marker) -// mMapView.vtmMap.updateMap(true) -// return marker -// } -// } -// -// fun removeMarker(title: String) { -// var marker: MarkerItem? = null -// for (e in mMapView.layerManager.defaultMarkerLayer.itemList) { -// if (e is MarkerItem && e.title == title) { -// marker = e; -// break; -// } -// } -// if (marker != null) { -// mMapView.layerManager.defaultMarkerLayer.removeItem(marker) -// mMapView.vtmMap.updateMap(true) -// } -// } -// - override fun onItemSingleTapUp(index: Int, item: MarkerInterface): Boolean { - return false + // //默认marker图层 + private var mDefaultMarkerLayer: ItemizedLayer + + init { + //新增marker图标样式 + val mDefaultBitmap = + AndroidBitmap(BitmapFactory.decodeResource(context.resources, R.mipmap.marker)); + + val markerSymbol = MarkerSymbol( + mDefaultBitmap, + MarkerSymbol.HotspotPlace.BOTTOM_CENTER + ); + //新增marker图层 + mDefaultMarkerLayer = ItemizedLayer( + mapView.vtmMap, + ArrayList(), + markerSymbol, + object : OnItemGestureListener { + override fun onItemSingleTapUp(index: Int, item: MarkerInterface?): Boolean { + return false + } + + override fun onItemLongPress(index: Int, item: MarkerInterface?): Boolean { + return false + } + + } + ) + addLayer(mDefaultMarkerLayer, NIMapView.LAYER_GROUPS.OPERATE); } - override fun onItemLongPress(index: Int, item: MarkerInterface): Boolean { - return false + //增加marker + fun addMarker( + geoPoint: GeoPoint, + title: String?, + description: String? = "" + ) { + var marker: MarkerItem? = null + for (e in mDefaultMarkerLayer.itemList) { + if (e is MarkerItem && e.title == title) { + marker = e + break + } + } + if (marker == null) { + var tempTitle = title; + if (tempTitle.isNullOrBlank()) { + tempTitle = StringUtil.createUUID(); + } + val marker = MarkerItem( + tempTitle, + description, + org.oscim.core.GeoPoint(geoPoint.latitude, geoPoint.longitude) + ) + mDefaultMarkerLayer.addItem(marker); + mMapView.vtmMap.updateMap(true) + } else { + marker.description = description + marker.geoPoint = org.oscim.core.GeoPoint(geoPoint.latitude, geoPoint.longitude) + mDefaultMarkerLayer.removeItem(marker) + mDefaultMarkerLayer.addItem(marker) + mMapView.vtmMap.updateMap(true) + } + } + + fun removeMarker(title: String) { + var marker: MarkerItem? = null + for (e in mDefaultMarkerLayer.itemList) { + if (e is MarkerItem && e.title == title) { + marker = e + break + } + } + if (marker != null) { + mDefaultMarkerLayer.removeItem(marker) + mMapView.vtmMap.updateMap(true) + } } } \ No newline at end of file diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt index d2463cf2..dd3ef9d7 100644 --- a/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt +++ b/collect-library/src/main/java/com/navinfo/collect/library/map/handler/ViewportHandler.kt @@ -7,7 +7,6 @@ import org.oscim.core.GeoPoint import org.oscim.core.Point open class ViewportHandler(context: Context, mapView: NIMapView) : BaseHandler(context, mapView) { - /** * Set pivot horizontal / vertical relative to view center in [-1, 1]. * e.g. pivotY 0.5 is usually preferred for navigation, moving center to 25% of view height. @@ -73,7 +72,7 @@ open class ViewportHandler(context: Context, mapView: NIMapView) : BaseHandler(c px: Float, py: Float ): Map { - val geo = mMapView.vtmMap.viewport().fromScreenPoint(px,py) + val geo = mMapView.vtmMap.viewport().fromScreenPoint(px, py) return mapOf( "latitude" to geo.latitude,