feat: 引入Reaml依赖

This commit is contained in:
2023-03-30 16:13:15 +08:00
135 changed files with 1737 additions and 13944 deletions

View File

@@ -1,6 +1,9 @@
plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-kapt'
id 'com.google.dagger.hilt.android'
id 'io.realm.kotlin'
}
android {
@@ -54,4 +57,22 @@ dependencies {
implementation 'com.github.K1rakishou:Fuck-Storage-Access-Framework:v1.1.3'
// Android工具类库 https://blankj.com/2016/07/31/android-utils-code/
implementation 'com.blankj:utilcodex:1.30.1'
//依赖注入
//hilt
implementation "com.google.dagger:hilt-android:2.44"
kapt "com.google.dagger:hilt-compiler:2.44"
// Retrofit 网络请求相关
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.retrofit2:converter-gson:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.9.0")
implementation("com.squareup.okhttp3:logging-interceptor:4.9.0")
// Realm相关依赖
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4'
implementation 'io.realm.kotlin:library-base:1.7.0'
}
//允许引用生成的代码
kapt {
correctErrorTypes true
}

View File

@@ -27,6 +27,7 @@
<!-- 读取缓存数据 -->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<application
android:name=".OMQSApplication"
android:allowBackup="true"
android:dataExtractionRules="@xml/data_extraction_rules"
android:fullBackupContent="@xml/backup_rules"
@@ -36,10 +37,11 @@
android:theme="@style/Theme.OMQualityInspection"
android:requestLegacyExternalStorage="true">
<activity
android:name=".ui.activity.LoginActivity"
android:name=".ui.activity.login.LoginActivity"
android:exported="true"
android:screenOrientation="landscape"
android:label="@string/app_name"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:theme="@style/Theme.OMQualityInspection">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
@@ -48,15 +50,10 @@
</intent-filter>
</activity>
<activity
android:name=".ui.activity.MainActivity"
android:name=".ui.activity.map.MainActivity"
android:launchMode="singleTask"
android:screenOrientation="landscape"
android:exported="true"
android:theme="@style/Theme.OMQualityInspection"></activity>
<activity
android:name=".ui.activity.MapTestActivity"
android:screenOrientation="landscape"
android:exported="true"
android:theme="@style/Theme.OMQualityInspection"></activity>
android:theme="@style/Theme.OMQualityInspection" />
</application>
</manifest>

View File

@@ -0,0 +1,19 @@
package com.navinfo.omqs
class Constant {
companion object {
/**
* 服务器地址
*/
const val SERVER_ADDRESS = "http://fastmap.navinfo.com/drdc/"
const val DEBUG = true
const val message_status_late = "预约,待发送"
const val message_status_send_over = "已发送"
const val message_version_right_off = "1" //立即发送
const val MESSAGE_PAGE_SIZE = 30 //消息列表一页最多数量
}
}

View File

@@ -0,0 +1,8 @@
package com.navinfo.omqs
import android.app.Application
import dagger.hilt.android.HiltAndroidApp
@HiltAndroidApp
class OMQSApplication : Application() {
}

View File

@@ -1,6 +1,6 @@
package com.navinfo.omqs.model
package com.navinfo.omqs.bean
data class LoginUser(
data class LoginUserBean(
var username: String = "",
var password: String = ""
)

View File

@@ -0,0 +1,36 @@
package com.navinfo.omqs.bean
import io.realm.kotlin.types.RealmObject
import io.realm.kotlin.types.annotations.PrimaryKey
data class OfflineMapCityBean(
@PrimaryKey
val id: String,
val fileName: String,
val name: String,
val url: String,
val version: Long,
val fileSize: Long,
var currentSize:Long = 0,
var status:Int = NONE
): RealmObject {
companion object Status{
const val NONE = 0 //无状态
const val WAITING = 1 //等待中
const val LOADING = 2 //下载中
const val PAUSE = 3 //暂停
const val ERROR = 4 //错误
const val DONE = 5 //完成
const val UPDATE = 6 //有新版本要更新
}
fun getFileSizeText(): String {
return if (fileSize < 1024.0)
"$fileSize B"
else if (fileSize < 1048576.0)
"%.2f K".format(fileSize / 1024.0)
else if (fileSize < 1073741824.0)
"%.2f M".format(fileSize / 1048576.0)
else
"%.2f M".format(fileSize / 1073741824.0)
}
}

View File

@@ -0,0 +1,94 @@
package com.navinfo.omqs.hilt
import android.app.Application
import android.util.Log
import com.google.gson.Gson
import com.navinfo.omqs.Constant
import com.navinfo.omqs.OMQSApplication
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import dagger.Lazy
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
import okhttp3.OkHttpClient
import okhttp3.logging.HttpLoggingInterceptor
import retrofit2.Retrofit
import retrofit2.converter.gson.GsonConverterFactory
import java.util.concurrent.TimeUnit
import javax.inject.Singleton
/**
* 全局单例 注入对象
*/
@Module
@InstallIn(SingletonComponent::class)
class GlobalModule {
@Singleton
@Provides
fun provideApplication(application: Application): OMQSApplication {
return application as OMQSApplication
}
/**
* 注入 网络OKHttp 对象
*/
@Provides
@Singleton
fun provideOkHttpClient(interceptor: HttpLoggingInterceptor): OkHttpClient {
return OkHttpClient.Builder().addInterceptor(interceptor)
.connectTimeout(60, TimeUnit.SECONDS).writeTimeout(5, TimeUnit.MINUTES)
.readTimeout(5, TimeUnit.MINUTES).build()
}
/**
* 注入 OHHttp log
*/
@Provides
@Singleton
fun provideLoggingInterceptor(): HttpLoggingInterceptor {
return HttpLoggingInterceptor {
if (Constant.DEBUG) {
Log.e("jingo", it)
}
}.apply {
level = if (Constant.DEBUG) {
HttpLoggingInterceptor.Level.BODY
} else {
HttpLoggingInterceptor.Level.NONE
}
}
}
@Provides
@Singleton
fun provideRetrofit(
client: Lazy<OkHttpClient>,
converterFactory: GsonConverterFactory,
): Retrofit {
val retrofitBuilder = Retrofit.Builder()
.baseUrl(Constant.SERVER_ADDRESS)
.client(client.get())
.addConverterFactory(converterFactory)
return retrofitBuilder.build()
}
@Provides
@Singleton
fun provideGson(): Gson = Gson()
@Provides
@Singleton
fun provideGsonConverterFactory(gson: Gson): GsonConverterFactory {
return GsonConverterFactory.create(gson)
}
@Provides
@Singleton
fun provideNetworkService(retrofit: Retrofit): RetrofitNetworkServiceAPI {
return retrofit.create(RetrofitNetworkServiceAPI::class.java)
}
}

View File

@@ -0,0 +1,30 @@
package com.navinfo.omqs.hilt
import android.util.Log
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.omqs.ui.activity.map.MainViewModel
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
import dagger.hilt.android.components.ActivityRetainedComponent
import dagger.hilt.android.scopes.ActivityRetainedScoped
@InstallIn(ActivityRetainedComponent::class)
@Module
class MainActivityModule {
@ActivityRetainedScoped
@Provides
fun providesMapController(): NIMapController = NIMapController()
/**
* 实验失败这样创建viewmodel不会在activity销毁的时候同时销毁
*/
// @ActivityRetainedScoped
// @Provides
// fun providesMainViewModel(mapController: NIMapController): MainViewModel {
// Log.e("jingo", "MainViewModel 被创建")
// return MainViewModel(mapController)
// }
}

View File

@@ -0,0 +1,16 @@
package com.navinfo.omqs.hilt
import com.navinfo.omqs.http.NetworkService
import com.navinfo.omqs.http.NetworkServiceImpl
import dagger.Binds
import dagger.Module
import dagger.hilt.InstallIn
import dagger.hilt.components.SingletonComponent
@InstallIn(SingletonComponent::class)
@Module
abstract class NetworkServiceModule {
@Binds
abstract fun bindNetworkService(networkServiceImpl: NetworkServiceImpl): NetworkService
}

View File

@@ -0,0 +1,53 @@
package com.navinfo.omqs.http
/**
* Created by Mayokun Adeniyi on 23/05/2020.
*/
/**
* 网络返回封装
*/
/**
* 在类名之前使用sealed关键字将类声明为密封类。
* 密封类仅在编译时限制类型集来确保类型安全的重要性。
* 密封类隐式是一个无法实例化的抽象类。
*/
/**
* 二:密封类所具有的特性和与别的类具有不同之处
①Sealed class密封类 是一个有特定数量子类的类,看上去和枚举有点类似,所不同的是,在枚举中,我们每个类型只有一个对象(实例);而在密封类中,同一个类可以拥有几个对象。
②Sealed class密封类的所有子类都必须与密封类在同一文件中
③Sealed class密封类的子类的子类可以定义在任何地方并不需要和密封类定义在同一个文件中
④Sealed class密封类没有构造函数不可以直接实例化只能实例化内部的子类
*/
/**
* 如何获取密封类里面的函数方法
* 只能创建密封类子类对象 通过密封类的子类对象调用密封类里的函数方法
*/
sealed class NetResult<out R> {
data class Success<out T>(val data: T?) : NetResult<T>()
data class Failure(val code: Int, val msg: String) : NetResult<Nothing>()
data class Error(val exception: Exception) : NetResult<Nothing>()
object Loading : NetResult<Nothing>()
/**
* 密封类通常与表达when时一起使用。 由于密封类的子类将自身类型作为一种情况。
因此密封类中的when表达式涵盖所有情况从而避免使用else子句。
*/
override fun toString(): String {
return when (this) {
is Success<*> -> "网络访问成功返回正确结果Success[data=$data]"
is Failure -> "网络访问成功返回错误结果Failure[$msg]"
is Error -> "网络访问出错 Error[exception=$exception]"
is Loading -> "网络访问中 Loading"
}
}
}
/**
* 密封类里面可以有若干个子类,这些子类如果要继承密封类,则必须和密封类在同一个文件里
*/

View File

@@ -0,0 +1,13 @@
package com.navinfo.omqs.http
import com.navinfo.omqs.bean.OfflineMapCityBean
/**
* 网络访问 业务接口
*/
interface NetworkService {
/**
* 获取离线地图城市列表
*/
suspend fun getOfflineMapCityList():NetResult<List<OfflineMapCityBean>>
}

View File

@@ -0,0 +1,35 @@
package com.navinfo.omqs.http
import com.navinfo.omqs.bean.OfflineMapCityBean
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import javax.inject.Inject
/**
* 网络访问业务接口的具体实现
*/
class NetworkServiceImpl @Inject constructor(
private val netApi: RetrofitNetworkServiceAPI,
) : NetworkService {
/**
* 获取离线地图城市列表
*/
override suspend fun getOfflineMapCityList(): NetResult<List<OfflineMapCityBean>> =
//在IO线程中运行
withContext(Dispatchers.IO) {
return@withContext try {
val result = netApi.retrofitGetOfflineMapCityList()
if (result.isSuccessful) {
if (result.code() == 200) {
NetResult.Success(result.body())
} else {
NetResult.Failure(result.code(), result.message())
}
} else {
NetResult.Failure(result.code(), result.message())
}
} catch (e: Exception) {
NetResult.Error(e)
}
}
}

View File

@@ -0,0 +1,237 @@
package com.navinfo.omqs.http
import androidx.lifecycle.LiveData
import com.navinfo.omqs.bean.OfflineMapCityBean
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Streaming
import retrofit2.http.Url
import java.util.concurrent.Flow
/**
* retrofit2 网络请求接口
*/
interface RetrofitNetworkServiceAPI {
/**
* 在 Retrofit2 中,可以使用不同类型的返回值来获取 API 调用的结果。以下是 Retrofit2 支持的一些常见返回类型:
1. `Call<T>`:表示一个异步请求,其中 `T` 是 API 响应的类型,通常是一个自定义的数据类。使用 `enqueue` 方法来执行异步请求,并在回调中处理响应。
2. `Response<T>`:表示一个同步请求的响应,其中 `T` 是 API 响应的类型。使用 `execute` 方法来执行同步请求,并返回一个 `Response` 对象,其中包含了响应的状态码、响应头和响应体等信息。
3. `Observable<T>`:表示一个 RxJava 的 Observable 对象,其中 `T` 是 API 响应的类型。使用 `subscribe` 方法来执行请求,并在 onNext 回调中处理响应。
4. `Single<T>`:表示一个 RxJava 的 Single 对象,其中 `T` 是 API 响应的类型。使用 `subscribe` 方法来执行请求,并在 onSuccess 回调中处理响应。
5. `Completable`:表示一个 RxJava 的 Completable 对象,用于执行没有响应体的 API 调用。使用 `subscribe` 方法来执行请求,并在 onComplete 回调中处理响应。
6. `Flowable<T>`:表示一个 RxJava 的 Flowable 对象,其中 `T` 是 API 响应的类型。类似于 Observable但支持背压处理。
7. `Deferred<T>`:表示一个 Kotlin 的 Deferred 对象,其中 `T` 是 API 响应的类型。使用 `await` 方法来执行请求,并返回响应的结果。
8. `LiveData<Response<T>>`:表示一个 LiveData 对象,其中 `T` 是 API 响应的类型。使用 `observe` 方法来执行请求,并在回调中处理响应。
总之Retrofit2 支持多种返回类型,开发者可以根据项目需求选择合适的方式来处理 API 响应结果。
*/
/**
* 获取离线地图城市列表
*/
@GET("/drdc/MapDownload/maplist")
suspend fun retrofitGetOfflineMapCityList(): Response<List<OfflineMapCityBean>>
/**
* 下载文件
*/
@Streaming
@GET
suspend fun retrofitDownLoadFile(@Url url: String):Response<ResponseBody>
/**
* @FormUrlEncoded 请求格式注解请求实体是一个From表单每个键值对需要使用@Field注解
@Field 请求参数注解,提交请求的表单字段,必须要添加,而且需要配合@FormUrlEncoded使用
“token” 参数字段,与后台给的字段需要一致
String 声明的参数类型
token 实际参数表示后面token的取值作为"token"的值
Post请求如果有参数需要在头部添加@FormUrlEncoded注解表示请求实体是一个From表单每个键值对需要使用@Field注解使用@Field添加参数这是发送Post请求时提交请求的表单字段必须要添加的而且需要配合@FormUrlEncoded使用若为在头部添加@FormUrlEncoded注解会抛出如下异常
当有多个不确定参数时,我们可以使用@FieldMap注解@FieldMap与@Field的作用一致可以用于添加多个不确定的参数类似@QueryMapMap的key作为表单的键Map的value作为表单的值。
*/
// @FormUrlEncoded
// @POST("api/dog")
// fun postCall(@Field("token") token: String?): Call<Any?>?
/**
* 当有多个不确定参数时,我们可以使用@FieldMap注解@FieldMap与@Field的作用一致可以用于添加多个不确定的参数类似@QueryMapMap的key作为表单的键Map的value作为表单的值。
*/
// @FormUrlEncoded
// @POST("api/dog")
// open fun postCall(@FieldMap map: Map<String?, Any?>?): Call<Any?>?
/**
* @GET("v7/weather/now")
Call<Wth_now_out> getCall(@QueryMap Map<String, Object> map);
*/
// @GET("v7/weather/now")
// open fun getCall(@QueryMap map: Map<String?, Any?>?): Call<Wth_now_out?>?
/**
*@Query 请求参数注解用于Get请求中的参数
“location”/“key” 参数字段,与后台给的字段需要一致
*/
// @GET("v7/weather/now")
// open fun getCall(@QueryMap map: Map<String?, Any?>?): Call<Wth_now_out?>?
/**
* @FieldMap 请求参数注解,与@Field作用一致用于不确定表单参数
Map<String, Object> map 通过Map将不确定的参数传入相当于多个Field参数
适用于Post请求的还有一个注解@Body@Body可以传递自定义类型数据给服务器多用于post请求发送非表单数据比如用传递Json格式数据它可以注解很多东西比如HashMap、实体类等我们来看看它用法
特别注意:@Body注解不能用于表单或者支持文件上传的表单的编码即不能与@FormUrlEncoded和@Multipart注解同时使用
*/
// @POST("")
// open fun getPsotDataBody(@Body body: RequestBody?): Call<Any?>?
/**
* @Path
使用第一个get请求网址
*/
// @GET("v7/weather/{time}")
// open fun getCall(@Path("time") time: String?,@QueryMap map: Map<String?, Any?>?): Call<Wth_now_out?>?
/**
* @QueryMap get请求方法参数的注解上面已经解释了这里就不重复讲
@Path 请求参数注解用于Url中的占位符{},在网址中的参数
@Path注解用于Url中的占位符{},在网址中的参数,如上面 @GET(“v7/weather/{time}”)的time通过{}占位符来标记time使用@Path注解传入time的值注意有的Url既有占位符又有"?"后面的键值对,其实@Query和@Path两者是可以共用的。在发起请求时{time}会被替换为方法中第二个参数的值time。
*/
// Map<String ,Object> map = new HashMap<>()Map<String ,Object> map = new HashMap<>();
// map.put("location","101010100");
// map.put("key","a5cf6ab782a14013b08fb92a57dd2f72");
// Call<Wth_now_out> call = apiService.getCall("now",map);
/**
* 最终{time}会被替换为now,此时形成的牵绊部分Url为https://devapi.qweather.com/v7/weather/now剩余**?号**后面的Url由@Query传入。
*/
/**
* @HTTP
@HTTP注解的作用是替换@GET、@POST、@PUT、@DELETE、@HEAD以及更多拓展功能
如可通过此注解代替@POST注解实现post请求如下
method 表示请求的方法区分大小写这里的值retrofit不会再做任何处理必须要保证正确
path 网络请求地址路径
hasBody 是否有请求体boolean类型
除请求接口部分的方法有变,别的部分与正常使用@POST请求一致另外需要注意在使用@HTTP注解实现post请求时若含有请求体那么对应的标识注解@FormUrlEncoded一定要加上若为别的请求体那么就添加与之对应的标识注解。
@HTTP注解可以通过method字段灵活设置具体请求方法通过path设置网络请求地址用的比较少。使用方法与设置的请求方法种类一致。
*/
// @FormUrlEncoded
// @POST("api/dog")
// Call<Object> postCall(@FieldMap Map<String,Object> map);
//
// @FormUrlEncoded
// @HTTP(method = "POST",path = "api/dog", hasBody = true)
// Call<Object> postCall1(@FieldMap Map<String,Object> map);
/**
* @Url
如果需要重新地址接口地址,可以使用@Url将地址以参数的形式传入即可。如果有@Url注解时GET传入的Url必须省略。不然会抛出如下异常。
*/
//错误的
// @FormUrlEncoded
// @POST("api/dog")
// open fun postCall(@Url url: String?, @FieldMap map: Map<String?, Any?>?): Call<Any?>?
//正确的
// @FormUrlEncoded
// @POST
// open fun postCall(@Url url: String?, @FieldMap map: Map<String?, Any?>?): Call<Any?>?
/**
* @Streaming
* @Streaming 表示响应体的数据用流的方式返回,使用于返回数据比较大,该注解在下载大文件时特别有用
*/
// @Streaming
// @POST("gists/public")
// open fun getStreamingBig(): Call<ResponseBody?>?
/**
* @Multipart、@part、@PartMap
* @Multipart 表示请求实体是一个支持文件上传的表单,需要配合@Part和@PartMap使用适用于文件上传
@Part 用于表单字段,适用于文件上传的情况,@Part支持三种类型RequestBody、MultipartBody.Part、 任意类型
@PartMap 用于多文件上传, 与@FieldMap和@QueryMap的使用类似
*/
// @Multipart
// @POST("user/followers")
// open fun getPartData(@Part("name") name: RequestBody?, @Part file: Part?): Call<ResponseBody?>?
/**
* 代码使用逻辑:
* 首先声明类型通过MediaType实现类型的声明此处使用的时文本类型然后会根据该类型转化为RequestBody对象此处使用的参数委RequestBody对象相当于讲周润发以文本格式转换委RequestBody对象最终上传时与name形成键值对。
*/
// //声明类型,这里是文字类型
// MediaType textType = MediaType.parse("text/plain");
// //根据声明的类型创建RequestBody,就是转化为RequestBody对象
// RequestBody name = RequestBody.create(textType, "周润发");
////创建文件,这里演示图片上传
// File file = new File("文件路径");
// if (!file.exists()) {
// file.mkdir();
// }
////将文件转化为RequestBody对象
////需要在表单中进行文件上传时就需要使用该格式multipart/form-data
// RequestBody imgBody = RequestBody.create(MediaType.parse("image/png"), file);
////将文件转化为MultipartBody.Part
////第一个参数上传文件的key第二个参数文件名第三个参数RequestBody对象
// MultipartBody.Part filePart = MultipartBody.Part.createFormData("key", file.getName(), imgBody);
/**
* 对于文件类型的上传我们一般使用MultipartBody.Part类型上传在上面讲两个参数设置好后然后调用接口使用请求方法即可
* 此种格式属于文本与文件混合发送的格式。
*/
// var partDataCall: Call<ResponseBody?>? = retrofit.create(Api::class.java).getPartData(name, filePart)
/**
* @PartMap的使用与@FieldMap和@QueryMap的使用类似用于多文件上传我们直接看代码
*/
// @Multipart
// @POST("user/followers")
// open fun getPartMapData(@PartMap map: Map<String?, Part?>?): Call<ResponseBody?>?
// File file1 = new File("文件路径");
// File file2 = new File("文件路径");
// if (!file1.exists()) {
// file1.mkdir();
// }
// if (!file2.exists()) {
// file2.mkdir();
// }
// RequestBody requestBody1 = RequestBody.create(MediaType.parse("image/png"), file1);
// RequestBody requestBody2 = RequestBody.create(MediaType.parse("image/png"), file2);
// MultipartBody.Part filePart1 = MultipartBody.Part.createFormData("file1", file1.getName(), requestBody1);
// MultipartBody.Part filePart2 = MultipartBody.Part.createFormData("file2", file2.getName(), requestBody2);
//
// Map<String,MultipartBody.Part> mapPart = new HashMap<>();
// mapPart.put("file1",filePart1);
// mapPart.put("file2",filePart2);
/**
* 有关MediaType方法
* 概述MediaType即是Internet Media Type互联网媒体类型也叫做MIME类型在Http协议消息头中使用Content-Type来表示具体请求中的媒体类型信息。也就是说MediaType在网络协议的消息头里面叫做Content-Type它使用两部分的标识符来确定一个类型是为了表明我们传的东西是什么类型。
常见的媒体格式类型如下:
text/html HTML格式
text/plain :纯文本格式
text/xml XML格式
image/gif gif图片格式
image/jpeg jpg图片格式
image/pngpng图片格式
以application开头的媒体格式类型
application/xhtml+xml XHTML格式
application/xml XML数据格式
application/atom+xml Atom XML聚合格式
application/json JSON数据格式
application/pdf pdf格式
application/msword Word文档格式
application/octet-stream 二进制流数据(如常见的文件下载、上传)
application/x-www-form-urlencoded 中默认的encTypeform表单数据被编码为key/value格式发送到服务器表单默认的提交数据的格式
另外一种常见的媒体格式是上传文件之时使用的:
multipart/form-data 需要在表单中进行文件上传时,就需要使用该格式
*/
}

View File

@@ -28,9 +28,9 @@ class MainActivity : PermissionsActivity(), FSAFActivityCallbacks {
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
val navController = findNavController(R.id.nav_host_fragment_content_main)
appBarConfiguration = AppBarConfiguration(navController.graph)
setupActionBarWithNavController(navController, appBarConfiguration)
// val navController = findNavController(R.id.nav_host_fragment_content_main)
// appBarConfiguration = AppBarConfiguration(navController.graph)
// setupActionBarWithNavController(navController, appBarConfiguration)
fileChooser.setCallbacks(this@MainActivity)
// binding.fab.setOnClickListener { view ->
@@ -57,27 +57,27 @@ class MainActivity : PermissionsActivity(), FSAFActivityCallbacks {
override fun onPermissionsDenied() {
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
// Inflate the menu; this adds items to the action bar if it is present.
menuInflater.inflate(R.menu.menu_main, menu)
return true
}
// override fun onCreateOptionsMenu(menu: Menu): Boolean {
// // Inflate the menu; this adds items to the action bar if it is present.
// menuInflater.inflate(R.menu.menu_main, menu)
// return true
// }
override fun onOptionsItemSelected(item: MenuItem): Boolean {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
return when (item.itemId) {
R.id.action_settings -> true
else -> super.onOptionsItemSelected(item)
}
}
override fun onSupportNavigateUp(): Boolean {
val navController = findNavController(R.id.nav_host_fragment_content_main)
return navController.navigateUp(appBarConfiguration)
|| super.onSupportNavigateUp()
}
// override fun onOptionsItemSelected(item: MenuItem): Boolean {
// // Handle action bar item clicks here. The action bar will
// // automatically handle clicks on the Home/Up button, so long
// // as you specify a parent activity in AndroidManifest.xml.
// return when (item.itemId) {
// R.id.action_settings -> true
// else -> super.onOptionsItemSelected(item)
// }
// }
//
// override fun onSupportNavigateUp(): Boolean {
// val navController = findNavController(R.id.nav_host_fragment_content_main)
// return navController.navigateUp(appBarConfiguration)
// || super.onSupportNavigateUp()
// }
override fun fsafStartActivityForResult(intent: Intent, requestCode: Int) {
startActivityForResult(intent, requestCode)

View File

@@ -1,23 +0,0 @@
package com.navinfo.omqs.ui.activity
import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.navinfo.omqs.model.LoginUser
class LoginViewModel : ViewModel() {
val loginUser: MutableLiveData<LoginUser> = MutableLiveData()
init {
loginUser.value = LoginUser(username = "admin", password = "123456")
}
/**
* 处理注册按钮
*/
fun onClick(view: View) {
loginUser.value!!.username = "admin2"
loginUser.postValue(loginUser.value)
}
}

View File

@@ -1,45 +0,0 @@
package com.navinfo.omqs.ui.activity
import android.os.Bundle
import androidx.activity.viewModels
import androidx.core.view.WindowCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleEventObserver
import androidx.lifecycle.LifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.ActivityMainBinding
/**
* 地图主页面
*/
class MainActivity : BaseActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel by viewModels<MainViewModel>()
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
//关联生命周期
binding.lifecycleOwner = this
//给xml转递对象
binding.mainActivity = this
//给xml传递viewModel对象
binding.viewModel = viewModel
//初始化地图
viewModel.initMap(this, binding.mapView.mainActivityMap)
//让viewModel监听activity生命周期
lifecycle.addObserver(viewModel)
}
/**
* 打开个人中菜单
*/
fun openMenu() {
binding.mainActivityDrawer.open()
}
}

View File

@@ -1,53 +0,0 @@
package com.navinfo.omqs.ui.activity
import android.app.Application
import android.content.Context
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ViewModel
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.collect.library.map.NIMapView
class MainViewModel(val app: Application) : AndroidViewModel(app), DefaultLifecycleObserver {
/**
* 地图控制器
*/
private lateinit var mapController: NIMapController
/**
* 初始化地图
*/
fun initMap(context: Context, mapView: NIMapView) {
mapController = NIMapController(context = app, mapView = mapView)
}
override fun onStart(owner: LifecycleOwner) {
super.onStart(owner)
//开启定位
mapController.locationLayerHandler.startLocation()
}
override fun onPause(owner: LifecycleOwner) {
mapController.mMapView.onPause()
}
override fun onDestroy(owner: LifecycleOwner) {
mapController.mMapView.onDestroy()
//结束定位
mapController.locationLayerHandler.stopLocation()
}
override fun onResume(owner: LifecycleOwner) {
mapController.mMapView.onResume()
}
/**
* 点击我的位置,回到我的位置
*/
fun onClickLocationButton() {
mapController.locationLayerHandler.animateToCurrentPosition()
}
}

View File

@@ -1,16 +0,0 @@
package com.navinfo.omqs.ui.activity
import android.os.Bundle
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.collect.library.map.NIMapView
import com.navinfo.omqs.R
class MapTestActivity: BaseActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_map_test)
val mapController = NIMapController(context = this, mapView = findViewById<NIMapView>(R.id.main_activity_map1))
mapController.locationLayerHandler.startLocation()
}
}

View File

@@ -1,4 +1,4 @@
package com.navinfo.omqs.ui.activity
package com.navinfo.omqs.ui.activity.login
import android.content.Intent
import android.os.Bundle
@@ -6,6 +6,8 @@ import androidx.activity.viewModels
import androidx.databinding.DataBindingUtil
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.ActivityLoginBinding
import com.navinfo.omqs.ui.activity.map.MainActivity
import com.navinfo.omqs.ui.activity.PermissionsActivity
/**
* 登陆页面
@@ -24,6 +26,8 @@ class LoginActivity : PermissionsActivity() {
}
override fun onPermissionsGranted() {
// 获取权限后初始化Realm设置
viewModel
}
override fun onPermissionsDenied() {
@@ -35,6 +39,6 @@ class LoginActivity : PermissionsActivity() {
fun onClickLoginButton() {
val intent = Intent(this@LoginActivity, MainActivity::class.java)
startActivity(intent)
finish()
// finish()
}
}

View File

@@ -0,0 +1,30 @@
package com.navinfo.omqs.ui.activity.login
import android.view.View
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.navinfo.omqs.bean.LoginUserBean
import com.navinfo.omqs.bean.OfflineMapCityBean
import io.realm.kotlin.RealmConfiguration
class LoginViewModel : ViewModel() {
val loginUser: MutableLiveData<LoginUserBean> = MutableLiveData()
init {
loginUser.value = LoginUserBean(username = "admin", password = "123456")
}
fun initRealm() {
val config = RealmConfiguration.Builder(schema = setOf(OfflineMapCityBean::class)).directory(
)
}
/**
* 处理注册按钮
*/
fun onClick(view: View) {
loginUser.value!!.username = "admin2"
loginUser.postValue(loginUser.value)
}
}

View File

@@ -0,0 +1,77 @@
package com.navinfo.omqs.ui.activity.map
import android.os.Bundle
import android.util.Log
import androidx.activity.viewModels
import androidx.core.view.WindowCompat
import androidx.databinding.DataBindingUtil
import androidx.lifecycle.viewModelScope
import com.navinfo.collect.library.map.NIMapController
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.ActivityMainBinding
import com.navinfo.omqs.ui.activity.BaseActivity
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
/**
* 地图主页面
*/
@AndroidEntryPoint
class MainActivity : BaseActivity() {
private lateinit var binding: ActivityMainBinding
private val viewModel by viewModels<MainViewModel>()
//注入地图控制器
@Inject
lateinit var mapController: NIMapController
override fun onCreate(savedInstanceState: Bundle?) {
WindowCompat.setDecorFitsSystemWindows(window, false)
super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this, R.layout.activity_main)
//初始化地图
mapController.init(this, binding.mapView.mainActivityMap)
//关联生命周期
binding.lifecycleOwner = this
//给xml转递对象
binding.mainActivity = this
//给xml传递viewModel对象
binding.viewModel = viewModel
// lifecycle.addObserver(viewModel)
}
override fun onStart() {
super.onStart()
//开启定位
mapController.locationLayerHandler.startLocation()
}
override fun onPause() {
super.onPause()
mapController.mMapView.onPause()
}
override fun onDestroy() {
super.onDestroy()
mapController.mMapView.onDestroy()
mapController.locationLayerHandler.stopLocation()
Log.e("jingo","MainActivity 销毁")
}
override fun onResume() {
super.onResume()
mapController.mMapView.onResume()
}
/**
* 打开个人中菜单
*/
fun openMenu() {
binding.mainActivityDrawer.open()
}
}

View File

@@ -0,0 +1,31 @@
package com.navinfo.omqs.ui.activity.map
import android.util.Log
import androidx.lifecycle.ViewModel
import com.navinfo.collect.library.map.NIMapController
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.scopes.ActivityRetainedScoped
import javax.inject.Inject
import javax.inject.Singleton
/**
* 创建Activity全局viewmode
*/
@HiltViewModel
class MainViewModel @Inject constructor(
private val mapController: NIMapController,
) : ViewModel() {
/**
* 点击我的位置,回到我的位置
*/
fun onClickLocationButton() {
mapController.locationLayerHandler.animateToCurrentPosition()
}
override fun onCleared() {
Log.e("jingo","MainViewModel 被释放了")
super.onCleared()
}
}

View File

@@ -1,45 +0,0 @@
package com.navinfo.omqs.ui.fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.FragmentFirstBinding
/**
* A simple [Fragment] subclass as the default destination in the navigation.
*/
class FirstFragment : Fragment() {
private var _binding: FragmentFirstBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
_binding = FragmentFirstBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.buttonFirst.setOnClickListener {
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@@ -1,45 +0,0 @@
package com.navinfo.omqs.ui.fragment
import android.os.Bundle
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.navigation.fragment.findNavController
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.FragmentSecondBinding
/**
* A simple [Fragment] subclass as the second destination in the navigation.
*/
class SecondFragment : Fragment() {
private var _binding: FragmentSecondBinding? = null
// This property is only valid between onCreateView and
// onDestroyView.
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentSecondBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
binding.buttonSecond.setOnClickListener {
findNavController().navigate(R.id.action_SecondFragment_to_FirstFragment)
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
}

View File

@@ -0,0 +1,25 @@
package com.navinfo.omqs.ui.fragment.offlinemap
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
/**
* 离线地图主页面viewpage适配器
*/
class OfflineMapAdapter(activity: FragmentActivity) :
FragmentStateAdapter(activity) {
private val stateFragment = OfflineMapStateListFragment()
private val cityListFragment = OfflineMapCityListFragment()
override fun getItemCount(): Int {
return 2
}
override fun createFragment(position: Int): Fragment {
return when (position) {
0 -> stateFragment
else ->
cityListFragment
}
}
}

View File

@@ -0,0 +1,33 @@
package com.navinfo.omqs.ui.fragment.offlinemap
import androidx.databinding.ViewDataBinding
import com.navinfo.omqs.R
import com.navinfo.omqs.BR
import com.navinfo.omqs.bean.OfflineMapCityBean
import com.navinfo.omqs.databinding.AdapterOfflineMapCityBinding
import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter
import com.navinfo.omqs.ui.other.BaseViewHolder
import javax.inject.Inject
/**
* 离线地图城市列表 RecyclerView 适配器
*/
class OfflineMapCityListAdapter @Inject constructor() :
BaseRecyclerViewAdapter<OfflineMapCityBean>() {
override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
var binding: ViewDataBinding = holder.dataBinding
//立刻刷新UI解决闪烁
// binding.executePendingBindings()
binding.setVariable(BR.cityBean, data[position])
(binding as AdapterOfflineMapCityBinding).offlineMapDownloadBtn.setOnClickListener {
}
}
override fun getItemViewType(position: Int): Int {
return R.layout.adapter_offline_map_city
}
}

View File

@@ -0,0 +1,49 @@
package com.navinfo.omqs.ui.fragment.offlinemap
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import com.navinfo.omqs.databinding.FragmentOfflineMapCityListBinding
import dagger.hilt.android.AndroidEntryPoint
/**
* 离线地图城市列表
*/
@AndroidEntryPoint
class OfflineMapCityListFragment : Fragment() {
private var _binding: FragmentOfflineMapCityListBinding? = null
private val viewModel by viewModels<OfflineMapCityListViewModel>()
private val binding get() = _binding!!
private val adapter: OfflineMapCityListAdapter by lazy { OfflineMapCityListAdapter() }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentOfflineMapCityListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val layoutManager = LinearLayoutManager(context)
_binding!!.offlineMapCityListRecyclerview.layoutManager = layoutManager
_binding!!.offlineMapCityListRecyclerview.adapter = adapter
viewModel.cityListLiveData.observe(viewLifecycleOwner) {
adapter.refreshData(it)
}
viewModel.getCityList()
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
Log.e("jingo","OfflineMapCityListFragment onDestroyView")
}
}

View File

@@ -0,0 +1,49 @@
package com.navinfo.omqs.ui.fragment.offlinemap
import android.app.Application
import android.content.Context
import android.widget.Toast
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.navinfo.omqs.http.NetResult
import com.navinfo.omqs.http.NetworkService
import com.navinfo.omqs.bean.OfflineMapCityBean
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.launch
import javax.inject.Inject
/**
* 离线地图城市列表viewModel
*/
@HiltViewModel
class OfflineMapCityListViewModel @Inject constructor(
private val networkService: NetworkService,
@ApplicationContext val context: Context
) : ViewModel() {
val cityListLiveData = MutableLiveData<List<OfflineMapCityBean>>()
/**
* 去获取离线地图列表
*/
fun getCityList() {
viewModelScope.launch {
when (val result = networkService.getOfflineMapCityList()) {
is NetResult.Success -> {
cityListLiveData.postValue(result.data!!)
}
is NetResult.Error -> {
Toast.makeText(context, "${result.exception.message}", Toast.LENGTH_SHORT)
.show()
}
is NetResult.Failure -> {
Toast.makeText(context, "${result.code}:${result.msg}", Toast.LENGTH_SHORT)
.show()
}
NetResult.Loading -> {}
}
}
}
}

View File

@@ -0,0 +1,66 @@
package com.navinfo.omqs.ui.fragment.offlinemap
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.lifecycle.lifecycleScope
import androidx.navigation.fragment.findNavController
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.google.android.material.tabs.TabLayoutMediator
import com.navinfo.omqs.databinding.FragmentOfflineMapBinding
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* 离线地图
*/
class OfflineMapFragment : Fragment() {
private var _binding: FragmentOfflineMapBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentOfflineMapBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
//禁止滑动,因为页面在抽屉里,和抽屉的滑动有冲突
binding.offlineMapViewpager.isUserInputEnabled = false
//创建viewpager2的适配器
binding.offlineMapViewpager.adapter = activity?.let { OfflineMapAdapter(it) }
//绑定viewpager2与tabLayout
TabLayoutMediator(
binding.offlineMapTabLayout,
binding.offlineMapViewpager
) { tab, position ->
when (position) {
0 -> tab.text = "下载管理"
1 -> tab.text = "城市列表"
}
}.attach()
//处理返回按钮
binding.offlineMapBack.setOnClickListener {
findNavController().popBackStack()
}
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
Log.e("jingo","OfflineMapFragment onDestroyView")
}
}

View File

@@ -0,0 +1,38 @@
package com.navinfo.omqs.ui.fragment.offlinemap
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayoutMediator
import com.navinfo.omqs.databinding.FragmentOfflineMapBinding
import com.navinfo.omqs.databinding.FragmentOfflineMapStateListBinding
class OfflineMapStateListFragment : Fragment() {
private var _binding: FragmentOfflineMapStateListBinding? = null
private val binding get() = _binding!!
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentOfflineMapStateListBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
Log.e("jingo","OfflineMapStateListFragment onDestroyView")
}
}

View File

@@ -0,0 +1,83 @@
package com.navinfo.omqs.ui.fragment.personalcenter
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.activity.viewModels
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.get
import androidx.navigation.fragment.findNavController
import com.blankj.utilcode.util.UriUtils
import com.github.k1rakishou.fsaf.FileChooser
import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks
import com.github.k1rakishou.fsaf.callback.FileChooserCallback
import com.google.android.material.snackbar.Snackbar
import com.navinfo.omqs.R
import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding
/**
* 个人中心
*/
class PersonalCenterFragment : Fragment(), FSAFActivityCallbacks {
private var _binding: FragmentPersonalCenterBinding? = null
private val binding get() = _binding!!
private val fileChooser by lazy { FileChooser(requireContext()) }
private val viewModel by lazy { viewModels<PersonalCenterViewModel>().value }
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View {
_binding = FragmentPersonalCenterBinding.inflate(inflater, container, false)
return binding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
Log.e("jingo", "NIMapController PersonalCenterFragment onViewCreated")
binding.root.setNavigationItemSelectedListener {
when (it.itemId) {
R.id.personal_center_menu_offline_map ->
findNavController().navigate(R.id.action_FirstFragment_to_SecondFragment)
R.id.personal_center_menu_import_data -> {
// 用户选中导入数据,打开文件选择器,用户选择导入的数据文件目录
fileChooser.openChooseFileDialog(object: FileChooserCallback() {
override fun onCancel(reason: String) {
}
override fun onResult(uri: Uri) {
val file = UriUtils.uri2File(uri)
// 开始导入数据
viewModel.importOmdbData(file)
}
})
}
}
true
}
fileChooser.setCallbacks(this@PersonalCenterFragment)
}
override fun onDestroyView() {
super.onDestroyView()
_binding = null
}
override fun fsafStartActivityForResult(intent: Intent, requestCode: Int) {
startActivityForResult(intent, requestCode)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
fileChooser.onActivityResult(requestCode, resultCode, data)
}
}

View File

@@ -0,0 +1,16 @@
package com.navinfo.omqs.ui.fragment.personalcenter
import androidx.lifecycle.ViewModel
import java.io.File
class PersonalCenterViewModel: ViewModel() {
fun importOmdbData(omdbFile: File) {
// 检查File是否为sqlite数据库
if (omdbFile == null || omdbFile.exists()) {
throw Exception("文件不存在")
}
if (!omdbFile.name.endsWith(".sqlite") and !omdbFile.name.endsWith("db")) {
throw Exception("文件不存在")
}
}
}

View File

@@ -0,0 +1,32 @@
package com.navinfo.omqs.ui.other
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.RecyclerView
/**
* RecyclerView 适配器基础类
*/
abstract class BaseRecyclerViewAdapter<T>(var data: List<T> = listOf()) :
RecyclerView.Adapter<BaseViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
return BaseViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.context),
viewType,
parent,
false
)
)
}
override fun getItemCount(): Int {
return data.size
}
fun refreshData(newData:List<T>){
this.data = newData
this.notifyDataSetChanged()
}
}

View File

@@ -0,0 +1,11 @@
package com.navinfo.omqs.ui.other
import androidx.databinding.ViewDataBinding
import androidx.recyclerview.widget.RecyclerView
/**
* dataBinding viewHolder 基类
*/
open class BaseViewHolder(var dataBinding: ViewDataBinding) :
RecyclerView.ViewHolder(dataBinding.root) {
}

View File

@@ -0,0 +1,64 @@
package com.navinfo.omqs.ui.widget
import android.content.Context
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.Rect
import android.opengl.ETC1.getHeight
import android.opengl.ETC1.getWidth
import android.util.AttributeSet
import android.widget.ProgressBar
/**
* 带文字提示的进度条
*/
class MyProgressBar : ProgressBar {
private lateinit var mPaint: Paint
private var text: String = ""
private var rate = 0f
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs) {
initView()
}
constructor(context: Context?) : super(context) {
initView()
}
private fun initView() {
mPaint = Paint()
mPaint.isAntiAlias = true
mPaint.color = Color.BLUE
}
@Synchronized
override fun setProgress(progress: Int) {
setText(progress)
super.setProgress(progress)
}
private fun setText(progress: Int) {
rate = progress * 1.0f / this.getMax()
val i = (rate * 100).toInt()
text = "$i%"
}
@Synchronized
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
val rect = Rect()
mPaint.getTextBounds(text, 0, text.length, rect)
// int x = (getWidth()/2) - rect.centerX();
// int y = (getHeight()/2) - rect.centerY();
var x = (width * rate).toInt()
if (x == width) {
// 如果为百分之百则在左边绘制。
x = width - rect.right
}
val y: Int = 0 - rect.top
mPaint.textSize = 22f
canvas.drawText(text, x.toFloat(), y.toFloat(), mPaint)
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#8cc1ff"/>
<item android:state_checked="true" android:color="@color/white"></item>
<item android:state_selected="true" android:color="@color/white"></item>
<item android:state_pressed="true" android:color="@color/white"></item>
<item android:color="#108ee9"></item>
</selector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#100F0F"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M20.5,3l-0.16,0.03L15,5.1 9,3 3.36,4.9c-0.21,0.07 -0.36,0.25 -0.36,0.48V20.5c0,0.28 0.22,0.5 0.5,0.5l0.16,-0.03L9,18.9l6,2.1 5.64,-1.9c0.21,-0.07 0.36,-0.25 0.36,-0.48V3.5c0,-0.28 -0.22,-0.5 -0.5,-0.5zM15,19l-6,-2.11V5l6,2.11V19z"/>
</vector>

View File

@@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android" >
<item android:state_enabled="true" android:state_pressed="false" android:drawable="@mipmap/icon_back_n"/>
<item android:state_pressed="true" android:drawable="@mipmap/icon_back_p"/>
<item android:state_enabled="false" android:drawable="@mipmap/icon_back_n"/>
</selector>

View File

@@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="true"><shape android:shape="rectangle">
<gradient android:type="linear" android:useLevel="true" android:startColor="#03bef3" android:endColor="#0c5ce8" android:angle="45" />
<corners android:topLeftRadius="2dp" android:topRightRadius="0dp" android:bottomLeftRadius="2dp" android:bottomRightRadius="0dp" />
</shape></item>
<item android:state_focused="true"><shape android:shape="rectangle">
<gradient android:type="linear" android:useLevel="true" android:startColor="#03bef3" android:endColor="#0c5ce8" android:angle="45" />
<corners android:topLeftRadius="2dp" android:topRightRadius="0dp" android:bottomLeftRadius="2dp" android:bottomRightRadius="0dp" />
</shape></item>
<item android:state_checked="true"><shape android:shape="rectangle">
<gradient android:type="linear" android:useLevel="true" android:startColor="#03bef3" android:endColor="#0c5ce8" android:angle="45" />
<corners android:topLeftRadius="2dp" android:topRightRadius="0dp" android:bottomLeftRadius="2dp" android:bottomRightRadius="0dp" />
</shape></item>
<item android:state_selected="true"><shape android:shape="rectangle">
<gradient android:type="linear" android:useLevel="true" android:startColor="#03bef3" android:endColor="#0c5ce8" android:angle="45" />
<corners android:topLeftRadius="2dp" android:topRightRadius="0dp" android:bottomLeftRadius="2dp" android:bottomRightRadius="0dp" />
</shape></item>
<item>
<shape android:shape="rectangle">
<solid android:color="#ff043683" />
<corners android:topLeftRadius="0dp" android:topRightRadius="2dp" android:bottomLeftRadius="0dp" android:bottomRightRadius="2dp" />
</shape></item>
</selector>

View File

@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false">
<shape>
<stroke android:width="1.1dp" android:color="@color/cv_gray_153" />
<solid android:color="@color/cv_gray_153" />
<padding android:bottom="@dimen/default_widget_padding" android:left="@dimen/default_widget_padding" android:right="@dimen/default_widget_padding" android:top="@dimen/default_widget_padding"></padding>
</shape>
</item>
<item android:state_pressed="true">
<shape>
<stroke android:width="1.1dp" android:color="@color/white" />
<solid android:color="@color/white" />
<padding android:bottom="@dimen/default_widget_padding" android:left="@dimen/default_widget_padding" android:right="@dimen/default_widget_padding" android:top="@dimen/default_widget_padding"></padding>
</shape>
</item>
<item android:state_selected="true">
<shape>
<stroke android:width="1.1dp" android:color="@color/cvm_red" />
<solid android:color="@color/cvm_red" />
<padding android:bottom="@dimen/default_widget_padding" android:left="@dimen/default_widget_padding" android:right="@dimen/default_widget_padding" android:top="@dimen/default_widget_padding"></padding>
</shape>
</item>
<item>
<shape>
<stroke android:width="1.1dp" android:color="@color/btn_blue_solid" />
<solid android:color="@color/transp" />
<padding android:bottom="@dimen/default_widget_padding" android:left="@dimen/default_widget_padding" android:right="@dimen/default_widget_padding" android:top="@dimen/default_widget_padding"></padding>
</shape>
</item>
</selector>

View File

@@ -0,0 +1,5 @@
<vector android:height="24dp" android:tint="#747D8C"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/white" android:pathData="M9,3L5,6.99h3L8,14h2L10,6.99h3L9,3zM16,17.01L16,10h-2v7.01h-3L15,21l4,-3.99h-3z"/>
</vector>

View File

@@ -2,17 +2,17 @@
<layout 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"
tools:context=".ui.activity.LoginActivity">
tools:context=".ui.activity.login.LoginActivity">
<data>
<variable
name="activity"
type="com.navinfo.omqs.ui.activity.LoginActivity" />
type="com.navinfo.omqs.ui.activity.login.LoginActivity" />
<variable
name="loginUserModel"
type="com.navinfo.omqs.ui.activity.LoginViewModel" />
type="com.navinfo.omqs.ui.activity.login.LoginViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout

View File

@@ -2,17 +2,17 @@
<layout 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"
tools:context=".ui.activity.MainActivity">
tools:context=".ui.activity.map.MainActivity">
<data>
<variable
name="mainActivity"
type="com.navinfo.omqs.ui.activity.MainActivity" />
type="com.navinfo.omqs.ui.activity.map.MainActivity" />
<variable
name="viewModel"
type="com.navinfo.omqs.ui.activity.MainViewModel" />
type="com.navinfo.omqs.ui.activity.map.MainViewModel" />
</data>
<androidx.drawerlayout.widget.DrawerLayout
@@ -27,15 +27,14 @@
android:layout_width="match_parent"
android:layout_height="match_parent"
app:mainActivity="@{mainActivity}"
app:viewModel="@{viewModel}"/>
app:viewModel="@{viewModel}" />
<com.google.android.material.navigation.NavigationView
android:id="@+id/nav_view"
android:layout_width="wrap_content"
<fragment
android:id="@+id/main_activity_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="300dp"
android:layout_height="match_parent"
android:layout_gravity="left"
android:fitsSystemWindows="true"
app:headerLayout="@layout/nav_header_main"
app:menu="@menu/activity_main_drawer" />
app:navGraph="@navigation/nav_graph" />
</androidx.drawerlayout.widget.DrawerLayout>
</layout>

View File

@@ -0,0 +1,82 @@
<?xml version="1.0" encoding="utf-8"?>
<layout 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"
tools:context="com.navinfo.omqs.ui.fragment.offlinemap.OfflineMapCityListAdapter">
<data>
<import type="com.navinfo.omqs.R" />
<variable
name="cityBean"
type="com.navinfo.omqs.bean.OfflineMapCityBean" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="10dp"
android:background="@color/cv_bg_color">
<TextView
android:id="@+id/offline_map_city_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@{cityBean.name}"
android:textColor="@color/white"
android:textSize="@dimen/default_font_size"
app:layout_constraintLeft_toLeftOf="@id/offline_map_city_size"
app:layout_constraintRight_toRightOf="@id/offline_map_city_size"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/offline_map_city_size"
style="@style/map_size_font_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="@mipmap/point_blue"
android:textSize="@dimen/card_title_font_3size"
android:text="@{cityBean.getFileSizeText()}"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toBottomOf="@id/offline_map_city_name" />
<TextView
android:id="@+id/tv_city_list_status"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:clickable="true"
android:focusable="false"
android:shadowColor="@android:color/transparent"
android:textColor="@color/white"
android:textSize="@dimen/card_title_font_2size"
app:layout_constraintBottom_toBottomOf="@id/offline_map_download_btn"
app:layout_constraintRight_toLeftOf="@id/offline_map_download_btn"
app:layout_constraintTop_toTopOf="@id/offline_map_download_btn" />
<TextView
android:id="@+id/offline_map_download_btn"
style="@style/map_download_style_btn"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:shadowColor="@android:color/transparent"
android:text="下载"
android:textColor="@color/btn_blue_solid"
android:textSize="@dimen/card_title_font_2size"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintBottom_toTopOf="@id/offline_map_progress"
app:layout_constraintTop_toTopOf="parent" />
<com.navinfo.omqs.ui.widget.MyProgressBar
android:layout_marginTop="5dp"
android:visibility="gone"
android:id="@+id/offline_map_progress"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="100"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout>

View File

@@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="@string/appbar_scrolling_view_behavior">
<fragment
android:id="@+id/nav_host_fragment_content_main"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:defaultNavHost="true"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph="@navigation/nav_graph" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -1,36 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.FirstFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<Button
android:id="@+id/button_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/next"
app:layout_constraintBottom_toTopOf="@id/textview_first"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
/>
<TextView
android:id="@+id/textview_first"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/lorem_ipsum"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_first" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -0,0 +1,99 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#000000"
android:orientation="vertical">
<RelativeLayout
style="@style/title_default_style"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#553C3F41">
<ImageView
android:id="@+id/btn_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:paddingBottom="@dimen/default_widget_padding"
android:paddingLeft="@dimen/default_widget_padding"
android:paddingRight="@dimen/default_widget_padding"
android:paddingTop="@dimen/default_widget_padding"
android:src="@drawable/btn_back_xml" />
<RadioGroup
android:id="@+id/rg_city"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:gravity="center"
android:orientation="horizontal">
<RadioButton
android:id="@+id/rb_manager"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/fm_card_map_btn_bg_line"
android:button="@null"
android:drawableLeft="@null"
android:gravity="center"
android:paddingBottom="@dimen/default_widget_padding"
android:paddingLeft="@dimen/default_widget_padding"
android:paddingRight="@dimen/default_widget_padding"
android:paddingTop="@dimen/default_widget_padding"
android:text="下载管理"
android:textColor="@color/btn_blue_white"
android:textSize="18sp" />
<RadioButton
android:id="@+id/rb_city_list"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@drawable/fm_card_map_btn_bg_line"
android:button="@null"
android:checked="true"
android:drawableLeft="@null"
android:gravity="center"
android:paddingBottom="@dimen/default_widget_padding"
android:paddingLeft="@dimen/default_widget_padding"
android:paddingRight="@dimen/default_widget_padding"
android:paddingTop="@dimen/default_widget_padding"
android:text="城市列表"
android:textColor="@color/btn_blue_white"
android:textSize="18sp" />
</RadioGroup>
</RelativeLayout>
<EditText
android:id="@+id/edt_search"
android:layout_width="match_parent"
android:layout_height="wrap_content"
style="@style/input_blue_type"
android:hint="搜索"></EditText>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="@+id/tv_map_download_network_hint"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:layout_gravity="center"
android:textColor="#FFFFFF"
android:visibility="gone"
android:text="网络错误,请检查网络后重试"></TextView>
<!-- <com.yanzhenjie.recyclerview.SwipeRecyclerView-->
<!-- android:id="@+id/lv_map_manager"-->
<!-- android:layout_width="match_parent"-->
<!-- android:visibility="invisible"-->
<!-- android:layout_height="match_parent">-->
<!-- </com.yanzhenjie.recyclerview.SwipeRecyclerView>-->
<!-- <com.yanzhenjie.recyclerview.SwipeRecyclerView-->
<!-- android:id="@+id/lv_map_city"-->
<!-- android:visibility="visible"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="match_parent">-->
<!-- </com.yanzhenjie.recyclerview.SwipeRecyclerView>-->
</FrameLayout>
</LinearLayout>

View File

@@ -0,0 +1,54 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/design_default_color_background"
tools:context=".ui.fragment.offlinemap.OfflineMapFragment">
<com.google.android.material.tabs.TabLayout
android:id="@+id/offline_map_tab_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/default_blue"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="下载管理" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="城市列表" />
</com.google.android.material.tabs.TabLayout>
<EditText
android:id="@+id/offline_map_search"
style="@style/input_blue_type"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="搜索"
app:layout_constraintTop_toBottomOf="@id/offline_map_tab_layout" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/offline_map_viewpager"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="@id/offline_map_search" />
<ImageView
android:id="@+id/offline_map_back"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dp"
android:padding="10dp"
android:src="@mipmap/icon_back_n"
app:layout_constraintBottom_toBottomOf="@id/offline_map_tab_layout"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="@id/offline_map_tab_layout" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.offlinemap.OfflineMapCityListFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/offline_map_city_list_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/default_red"
tools:context=".ui.fragment.offlinemap.OfflineMapStateListFragment">
<TextView
android:text="第一页"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<com.google.android.material.navigation.NavigationView 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:layout_gravity="left"
android:fitsSystemWindows="true"
app:headerLayout="@layout/personal_center_head"
app:menu="@menu/personal_center_menu" />

View File

@@ -1,35 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.core.widget.NestedScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.fragment.SecondFragment">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<Button
android:id="@+id/button_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/previous"
app:layout_constraintBottom_toTopOf="@id/textview_second"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/textview_second"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:text="@string/lorem_ipsum"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/button_second" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.core.widget.NestedScrollView>

View File

@@ -6,17 +6,17 @@
<data>
<variable
name="mainActivity"
type="com.navinfo.omqs.ui.activity.MainActivity" />
type="com.navinfo.omqs.ui.activity.map.MainActivity" />
<variable
name="viewModel"
type="com.navinfo.omqs.ui.activity.MainViewModel" />
type="com.navinfo.omqs.ui.activity.map.MainViewModel" />
</data>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".ui.activity.MainActivity">
tools:context=".ui.activity.map.MainActivity">
<com.navinfo.collect.library.map.NIMapView
android:id="@+id/main_activity_map"

View File

@@ -1,20 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group android:checkableBehavior="single">
<item
android:id="@+id/nav_home"
android:icon="@drawable/icon_map_zoom_in"
android:title="menu_home" />
<item
android:id="@+id/nav_gallery"
android:icon="@drawable/icon_map_zoom_in"
android:title="menu_gallery" />
<item
android:id="@+id/nav_slideshow"
android:icon="@drawable/icon_map_zoom_in"
android:title="menu_slideshow" />
</group>
</menu>

View File

@@ -1,10 +0,0 @@
<menu 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"
tools:context="com.navinfo.omqs.ui.activity.MainActivity">
<item
android:id="@+id/action_settings"
android:orderInCategory="100"
android:title="@string/action_settings"
app:showAsAction="never" />
</menu>

View File

@@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
tools:showIn="navigation_view">
<group
android:id="@+id/group1"
android:checkableBehavior="single">
<item
android:id="@+id/personal_center_menu_offline_map"
android:icon="@drawable/baseline_map_24"
android:title="离线地图" />
<item
android:id="@+id/personal_center_menu_import_data"
android:icon="@drawable/ic_baseline_import_export_24"
android:title="导入数据" />
<item
android:id="@+id/personal_center_menu_offline_map2"
android:icon="@drawable/baseline_person_24"
android:title="menu_slideshow" />
</group>
<group
android:id="@+id/group2"
android:checkableBehavior="single">
<item android:title="小标题">
<menu>
<item
android:id="@+id/personal_center_menu_offline_map3"
android:icon="@drawable/baseline_person_24"
android:title="menu_home" />
<item
android:id="@+id/personal_center_menu_offline_map4"
android:icon="@drawable/baseline_person_24"
android:title="menu_gallery" />
<item
android:id="@+id/personal_center_menu_offline_map5"
android:icon="@drawable/baseline_person_24"
android:title="menu_slideshow" />
</menu>
</item>
</group>
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

View File

@@ -3,13 +3,13 @@
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/nav_graph"
app:startDestination="@id/FirstFragment">
app:startDestination="@id/PersonalCenterFragment">
<fragment
android:id="@+id/FirstFragment"
android:name="com.navinfo.omqs.ui.fragment.FirstFragment"
android:label="@string/first_fragment_label"
tools:layout="@layout/fragment_first">
android:id="@+id/PersonalCenterFragment"
android:name="com.navinfo.omqs.ui.fragment.personalcenter.PersonalCenterFragment"
android:label="@string/personal_center"
tools:layout="@layout/fragment_personal_center">
<action
android:id="@+id/action_FirstFragment_to_SecondFragment"
@@ -17,12 +17,12 @@
</fragment>
<fragment
android:id="@+id/SecondFragment"
android:name="com.navinfo.omqs.ui.fragment.SecondFragment"
android:name="com.navinfo.omqs.ui.fragment.offlinemap.OfflineMapFragment"
android:label="@string/second_fragment_label"
tools:layout="@layout/fragment_second">
tools:layout="@layout/fragment_offline_map">
<action
android:id="@+id/action_SecondFragment_to_FirstFragment"
app:destination="@id/FirstFragment" />
app:destination="@id/PersonalCenterFragment" />
</fragment>
</navigation>

View File

@@ -2,4 +2,8 @@
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="cv_gray_153">#999999</color>
<color name="cvm_red">#FF3B30</color>
<color name="cv_bg_color">#553C3F41</color>
<color name="btn_blue_solid">#108ee9</color>
</resources>

View File

@@ -5,4 +5,8 @@
<dimen name="activity_vertical_margin">16dp</dimen>
<dimen name="nav_header_vertical_spacing">8dp</dimen>
<dimen name="nav_header_height">176dp</dimen>
<dimen name="default_widget_padding">10dp</dimen>
<dimen name="default_font_size" comment="默认字体大小style中父最顶层">15dp</dimen>
<dimen name="card_title_font_2size">13sp</dimen>
<dimen name="card_title_font_3size">10sp</dimen>
</resources>

View File

@@ -2,47 +2,11 @@
<string name="app_name">OMQualityInspection</string>
<string name="action_settings">Settings</string>
<!-- Strings used for fragments for navigation -->
<string name="first_fragment_label">First Fragment</string>
<string name="personal_center">个人中心</string>
<string name="second_fragment_label">Second Fragment</string>
<string name="next">Next</string>
<string name="previous">Previous</string>
<string name="lorem_ipsum">
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nam in scelerisque sem. Mauris
volutpat, dolor id interdum ullamcorper, risus dolor egestas lectus, sit amet mattis purus
dui nec risus. Maecenas non sodales nisi, vel dictum dolor. Class aptent taciti sociosqu ad
litora torquent per conubia nostra, per inceptos himenaeos. Suspendisse blandit eleifend
diam, vel rutrum tellus vulputate quis. Aliquam eget libero aliquet, imperdiet nisl a,
ornare ex. Sed rhoncus est ut libero porta lobortis. Fusce in dictum tellus.\n\n
Suspendisse interdum ornare ante. Aliquam nec cursus lorem. Morbi id magna felis. Vivamus
egestas, est a condimentum egestas, turpis nisl iaculis ipsum, in dictum tellus dolor sed
neque. Morbi tellus erat, dapibus ut sem a, iaculis tincidunt dui. Interdum et malesuada
fames ac ante ipsum primis in faucibus. Curabitur et eros porttitor, ultricies urna vitae,
molestie nibh. Phasellus at commodo eros, non aliquet metus. Sed maximus nisl nec dolor
bibendum, vel congue leo egestas.\n\n
Sed interdum tortor nibh, in sagittis risus mollis quis. Curabitur mi odio, condimentum sit
amet auctor at, mollis non turpis. Nullam pretium libero vestibulum, finibus orci vel,
molestie quam. Fusce blandit tincidunt nulla, quis sollicitudin libero facilisis et. Integer
interdum nunc ligula, et fermentum metus hendrerit id. Vestibulum lectus felis, dictum at
lacinia sit amet, tristique id quam. Cras eu consequat dui. Suspendisse sodales nunc ligula,
in lobortis sem porta sed. Integer id ultrices magna, in luctus elit. Sed a pellentesque
est.\n\n
Aenean nunc velit, lacinia sed dolor sed, ultrices viverra nulla. Etiam a venenatis nibh.
Morbi laoreet, tortor sed facilisis varius, nibh orci rhoncus nulla, id elementum leo dui
non lorem. Nam mollis ipsum quis auctor varius. Quisque elementum eu libero sed commodo. In
eros nisl, imperdiet vel imperdiet et, scelerisque a mauris. Pellentesque varius ex nunc,
quis imperdiet eros placerat ac. Duis finibus orci et est auctor tincidunt. Sed non viverra
ipsum. Nunc quis augue egestas, cursus lorem at, molestie sem. Morbi a consectetur ipsum, a
placerat diam. Etiam vulputate dignissim convallis. Integer faucibus mauris sit amet finibus
convallis.\n\n
Phasellus in aliquet mi. Pellentesque habitant morbi tristique senectus et netus et
malesuada fames ac turpis egestas. In volutpat arcu ut felis sagittis, in finibus massa
gravida. Pellentesque id tellus orci. Integer dictum, lorem sed efficitur ullamcorper,
libero justo consectetur ipsum, in mollis nisl ex sed nisl. Donec maximus ullamcorper
sodales. Praesent bibendum rhoncus tellus nec feugiat. In a ornare nulla. Donec rhoncus
libero vel nunc consequat, quis tincidunt nisl eleifend. Cras bibendum enim a justo luctus
vestibulum. Fusce dictum libero quis erat maximus, vitae volutpat diam dignissim.
</string>
<string name="input_user_name">请输入用户名</string>
<string name="input_password">请输入密码</string>
<string name="login">登录</string>

View File

@@ -0,0 +1,57 @@
<resources>
<style name="title_default_style" comment="默认顶标题样式">
<item name="android:layout_width">match_parent</item>
<item name="android:layout_height">20dp</item>
<item name="android:clickable">true</item>
<item name="android:focusable">true</item>
<item name="android:layout_alignParentTop">true</item>
<item name="android:background">@color/default_blue</item>
<item name="android:paddingBottom">6dp</item>
<item name="android:paddingLeft">14dp</item>
<item name="android:paddingRight">14dp</item>
<item name="android:paddingTop">6dp</item>
<item name="android:gravity">center_vertical</item>
</style>
<!-- 默认字体 -->
<style name="content_font_default">
<item name="android:gravity">center_vertical</item>
<item name="android:textSize">15dp</item>
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
</style>
<!-- 输入框基本样式 -->
<style name="input_blue_type" parent="content_font_default">
<item name="android:textSize">16sp</item>
<item name="android:background">#FFFFFF</item>
<item name="android:textColor">#1890ff</item>
<item name="android:padding">10dp</item>
<item name="android:imeOptions">actionDone</item>
<item name="android:singleLine">true</item>
</style>
<style name="map_size_font_style" comment="离线地图gdb/nds大小字体样式">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:drawablePadding">4dp</item>
<item name="android:textColor">@color/white</item>
<item name="android:textSize">9sp</item>
<item name="android:focusable">false</item>
<item name="android:clickable">false</item>
</style>
<style name="map_download_style_btn" comment="离线地图下载的按钮样式">
<item name="android:layout_width">wrap_content</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_gravity">center_vertical</item>
<item name="android:padding">@dimen/default_widget_padding</item>
<item name="android:background">@drawable/fm_card_map_down_status_bg</item>
<item name="android:gravity">center_horizontal</item>
<item name="android:textColor">@color/btn_blue_white</item>
<item name="android:textSize">15sp</item>
</style>
</resources>