部分问题修复

这个提交包含在:
徐勤民 2024-08-25 22:13:45 +08:00
父节点 194d27a6e7
当前提交 d780cb9660
共有 184 个文件被更改,包括 9837 次插入6 次删除

2
base/.gitattributes vendored 普通文件
查看文件

@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto

83
base/.gitignore vendored 普通文件
查看文件

@ -0,0 +1,83 @@
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
# Uncomment the following line in case you need and you don't have the release build type files in your app
# release/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# IntelliJ
*.iml
.idea/workspace.xml
.idea/tasks.xml
.idea/gradle.xml
.idea/assetWizardSettings.xml
.idea/dictionaries
.idea/libraries
# Android Studio 3 in .gitignore file.
.idea/caches
.idea/modules.xml
# Comment next line if keeping position of elements in Navigation Editor is relevant for you
.idea/navEditor.xml
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/

184
base/README.md 普通文件
查看文件

@ -0,0 +1,184 @@
[TOC]
# WebSocket
``````kotlin
WebSocketHandler.getInstance("ws://192.168.3.20:8765")
``````
# 线程
## UI线程执行
````kotlin
runOnUiThread { "提示信息".showMessage() }
````
````kotlin
App.getInstance().runOnUiThread() {}
````
## 延时执行
```kotlin
App.getInstance().runOnUiThreadDelay({},1100)
```
# 常用工具
## Toast
````kotlin
"连接完成".showMessage()
````
````kotlin
ToolsHelper.showMessage("")
````
## Log
````kotlin
"".loge()
````
````kotlin
"".log()
````
````kotlin
LogHelper.d("")
````
# 常用方法
## 双击退出
```kotlin
private var oldTime = 0L
override fun onBackPressed() {
val newTime = System.currentTimeMillis()
if (newTime - oldTime < 1500 && oldTime != 0L)
AppManager.getInstance().exit()
else {
oldTime = newTime
ToolsHelper.showMessage("双击退出")
}
}
```
# 界面
> 所有界面继承`BaseFragment`,`BaseActivity`,`BaseListActivity`等
>
> 页面`layout`跟节点必须为`layout`
```xml
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
</layout>
```
## 列表页面
### 纯列表
> `BaseListActivity`
### 自定义布局列表
> `BaseListFormLayoutActivity`
>
> 布局列表部分必须使用下面的方法和id
```xml
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
android:id="@+id/baseRefreshLayout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.xuqm.base.view.EmptyView
android:id="@+id/baseEmptyView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/baseRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:overScrollMode="never" />
</com.xuqm.base.view.EmptyView>
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
```
## 界面控件使用
```kotlin
binding.btn1.setOnClickListener {
}
```
## 导航栏
> 使用base自带导航栏的情况下,可以操控对应控件
```kotlin
baseBinding.baseToolbar.backBtn.setOnClickListener {}
```

94
base/build.gradle 普通文件
查看文件

@ -0,0 +1,94 @@
apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'maven-publish'
// aar包的版本号
def aarVersion = "0.0.1.101"
android {
compileSdkVersion versions.compileSdk
buildToolsVersion versions.buildTools
defaultConfig {
minSdkVersion versions.minSdk
targetSdkVersion versions.targetSdk
flavorDimensions "versioncode"
buildConfigField("String", "APP_ID", "\"" + apps.applicationId + "\"")
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
productFlavors productF
namespace 'com.xuqm.base'
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
api androidxDependencies
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
implementation 'org.jetbrains.anko:anko-commons:0.10.5'
annotationProcessor compilerDependencies
commonDependencies.each { k, v ->
api(v) {
//support库
exclude group: 'com.android.support'
}
}
//
api 'com.huawei.hms:scanplus:1.1.1.301'
}
// aar包中的任务
task sourceJar(type: Jar) {
archiveClassifier.set('sources')
from android.sourceSets.main.java.srcDirs
}
afterEvaluate {
publishing {
publications {
// debug名字是自己起的
release(MavenPublication) {
groupId = 'cn.org.bjca.trust.android'
artifactId = 'base'
version = aarVersion
// debug release
from components.release
//
artifact sourceJar
}
}
//
repositories {
//
// mavenLocal()
//
// maven {
// allowInsecureProtocol true
// url("http://nexus.51trust.net/repository/android-hosted/")
// credentials {
// username = "deployment"
// password = "deployment123"
// }
// }
maven {
allowInsecureProtocol true
url("http://xuqinmin.com.cn:8081/repository/android-releases/")
credentials {
username = "admin"
password = "xuqinmin1022"
}
}
}
}
}

0
base/consumer-rules.pro 普通文件
查看文件

21
base/proguard-rules.pro vendored 普通文件
查看文件

@ -0,0 +1,21 @@
# Add project specific ProGuard rules here.
# You can control the set of applied configuration files using the
# proguardFiles setting in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile

查看文件

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
<!-- 7.0之后安装apk、需要权限 -->
<uses-permission android:name="android.permission.READ_PRIVILEGED_PHONE_STATE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.webkit.PermissionRequest" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.webkit.resource.VIDEO_CAPTURE" />
<application>
<activity android:name=".web.XWebViewActivity" />
</application>
</manifest>

查看文件

@ -0,0 +1,155 @@
package com.livinglifetechway.quickpermissions_kotlin
import android.content.Context
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import com.livinglifetechway.quickpermissions_kotlin.util.PermissionCheckerFragment
import com.livinglifetechway.quickpermissions_kotlin.util.PermissionsUtil
import com.livinglifetechway.quickpermissions_kotlin.util.QuickPermissionsRequest
import com.livinglifetechway.quickpermissions_kotlin.util.QuickPermissionsOptions
private const val TAG = "runWithPermissions"
/**
* Injects code to ask for permissions before executing any code that requires permissions
* defined in the annotation
*/
fun Context?.runWithPermissions(
vararg permissions: String,
options: QuickPermissionsOptions = QuickPermissionsOptions(),
callback: () -> Unit
): Any? {
return runWithPermissionsHandler(this, permissions, callback, options)
}
/**
* Injects code to ask for permissions before executing any code that requires permissions
* defined in the annotation
*/
fun Fragment?.runWithPermissions(
vararg permissions: String,
options: QuickPermissionsOptions = QuickPermissionsOptions(),
callback: () -> Unit
): Any? {
return runWithPermissionsHandler(this, permissions, callback, options)
}
private fun runWithPermissionsHandler(target: Any?, permissions: Array<out String>, callback: () -> Unit, options: QuickPermissionsOptions): Nothing? {
Log.d(TAG, "runWithPermissions: start")
// get the permissions defined in annotation
Log.d(TAG, "runWithPermissions: permissions to check: $permissions")
// get target
if (target is AppCompatActivity || target is Fragment) {
Log.d(TAG, "runWithPermissions: context found")
val context = when (target) {
is Context -> target
is Fragment -> target.context
else -> null
}
// check if we have the permissions
if (PermissionsUtil.hasSelfPermission(context, arrayOf(*permissions))) {
Log.d(TAG, "runWithPermissions: already has required permissions. Proceed with the execution.")
callback()
} else {
// we don't have required permissions
// begin the permission request flow
Log.d(TAG, "runWithPermissions: doesn't have required permissions")
// check if we have permission checker fragment already attached
// support for AppCompatActivity and Activity
var permissionCheckerFragment = when (context) {
// for app compat activity
is AppCompatActivity -> context.supportFragmentManager?.findFragmentByTag(PermissionCheckerFragment::class.java.canonicalName) as PermissionCheckerFragment?
// for support fragment
is Fragment -> context.childFragmentManager.findFragmentByTag(PermissionCheckerFragment::class.java.canonicalName) as PermissionCheckerFragment?
// else return null
else -> null
}
// check if permission check fragment is added or not
// if not, add that fragment
if (permissionCheckerFragment == null) {
Log.d(TAG, "runWithPermissions: adding headless fragment for asking permissions")
permissionCheckerFragment = PermissionCheckerFragment.newInstance()
when (context) {
is AppCompatActivity -> {
context.supportFragmentManager.beginTransaction().apply {
add(permissionCheckerFragment, PermissionCheckerFragment::class.java.canonicalName)
commit()
}
// make sure fragment is added before we do any context based operations
context.supportFragmentManager?.executePendingTransactions()
}
is Fragment -> {
// this does not work at the moment
context.childFragmentManager.beginTransaction().apply {
add(permissionCheckerFragment, PermissionCheckerFragment::class.java.canonicalName)
commit()
}
// make sure fragment is added before we do any context based operations
context.childFragmentManager.executePendingTransactions()
}
}
}
// set callback to permission checker fragment
permissionCheckerFragment.setListener(object : PermissionCheckerFragment.QuickPermissionsCallback {
override fun onPermissionsGranted(quickPermissionsRequest: QuickPermissionsRequest?) {
Log.d(TAG, "runWithPermissions: got permissions")
try {
callback()
} catch (throwable: Throwable) {
throwable.printStackTrace()
}
}
override fun onPermissionsDenied(quickPermissionsRequest: QuickPermissionsRequest?) {
quickPermissionsRequest?.permissionsDeniedMethod?.invoke(quickPermissionsRequest)
}
override fun shouldShowRequestPermissionsRationale(quickPermissionsRequest: QuickPermissionsRequest?) {
quickPermissionsRequest?.rationaleMethod?.invoke(quickPermissionsRequest)
}
override fun onPermissionsPermanentlyDenied(quickPermissionsRequest: QuickPermissionsRequest?) {
quickPermissionsRequest?.permanentDeniedMethod?.invoke(quickPermissionsRequest)
}
})
// create permission request instance
val permissionRequest = QuickPermissionsRequest(permissionCheckerFragment, arrayOf(*permissions))
permissionRequest.handleRationale = options.handleRationale
permissionRequest.handlePermanentlyDenied = options.handlePermanentlyDenied
permissionRequest.rationaleMessage = if (options.rationaleMessage.isBlank())
"These permissions are required to perform this feature. Please allow us to use this feature. "
else
options.rationaleMessage
permissionRequest.permanentlyDeniedMessage = if (options.permanentlyDeniedMessage.isBlank())
"Some permissions are permanently denied which are required to perform this operation. Please open app settings to grant these permissions."
else
options.permanentlyDeniedMessage
permissionRequest.rationaleMethod = options.rationaleMethod
permissionRequest.permanentDeniedMethod = options.permanentDeniedMethod
permissionRequest.permissionsDeniedMethod = options.permissionsDeniedMethod
// begin the flow by requesting permissions
permissionCheckerFragment.setRequestPermissionsRequest(permissionRequest)
// start requesting permissions for the first time
permissionCheckerFragment.requestPermissionsFromUser()
}
} else {
// context is null
// cannot handle the permission checking from the any class other than AppCompatActivity/Fragment
// crash the app RIGHT NOW!
throw IllegalStateException("Found " + target!!::class.java.canonicalName + " : No support from any classes other than AppCompatActivity/Fragment")
}
return null
}

查看文件

@ -0,0 +1,237 @@
package com.livinglifetechway.quickpermissions_kotlin.util
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri.fromParts
import android.os.Bundle
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
import android.util.Log
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
import org.jetbrains.anko.alert
/**
* This fragment holds the single permission request and holds it until the flow is completed
*/
class PermissionCheckerFragment : Fragment() {
private var quickPermissionsRequest: QuickPermissionsRequest? = null
interface QuickPermissionsCallback {
fun shouldShowRequestPermissionsRationale(quickPermissionsRequest: QuickPermissionsRequest?)
fun onPermissionsGranted(quickPermissionsRequest: QuickPermissionsRequest?)
fun onPermissionsPermanentlyDenied(quickPermissionsRequest: QuickPermissionsRequest?)
fun onPermissionsDenied(quickPermissionsRequest: QuickPermissionsRequest?)
}
companion object {
private const val TAG = "QuickPermissionsKotlin"
private const val PERMISSIONS_REQUEST_CODE = 199
fun newInstance(): PermissionCheckerFragment = PermissionCheckerFragment()
}
private var mListener: QuickPermissionsCallback? = null
fun setListener(listener: QuickPermissionsCallback) {
mListener = listener
Log.d(TAG, "onCreate: listeners set")
}
private fun removeListener() {
mListener = null
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "onCreate: permission fragment created")
}
fun setRequestPermissionsRequest(quickPermissionsRequest: QuickPermissionsRequest?) {
this.quickPermissionsRequest = quickPermissionsRequest
}
private fun removeRequestPermissionsRequest() {
quickPermissionsRequest = null
}
fun clean() {
if (quickPermissionsRequest != null) {
// permission request flow is finishing
// let the caller receive callback about it
if (quickPermissionsRequest?.deniedPermissions?.size ?: 0 > 0)
mListener?.onPermissionsDenied(quickPermissionsRequest)
removeRequestPermissionsRequest()
removeListener()
} else {
Log.w(
TAG, "clean: QuickPermissionsRequest has already completed its flow. " +
"No further callbacks will be called for the current flow."
)
}
}
fun requestPermissionsFromUser() {
if (quickPermissionsRequest != null) {
Log.d(TAG, "requestPermissionsFromUser: requesting permissions")
requestPermissions(
quickPermissionsRequest?.permissions.orEmpty(),
PERMISSIONS_REQUEST_CODE
)
} else {
Log.w(
TAG,
"requestPermissionsFromUser: QuickPermissionsRequest has already completed its flow. " +
"Cannot request permissions again from the request received from the callback. " +
"You can start the new flow by calling runWithPermissions() { } again."
)
}
}
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<String>,
grantResults: IntArray
) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
Log.d(TAG, "passing callback")
// check if permissions granted
handlePermissionResult(permissions, grantResults)
}
/**
* Checks and takes the action based on permission results retrieved from onRequestPermissionsResult
* and from the settings activity
*
* @param permissions List of Permissions
* @param grantResults A list of permission result <b>Granted</b> or <b>Denied</b>
*/
private fun handlePermissionResult(permissions: Array<String>, grantResults: IntArray) {
// add a check with the permissions list
// if the permissions list is empty, that means system has told that permissions request
// is invalid somehow or discarded the previous request
// this can happen in case when the multiple permissions requests are sent
// simultaneously to the system
if (permissions.isEmpty()) {
Log.w(
TAG,
"handlePermissionResult: Permissions result discarded. You might have called multiple permissions request simultaneously"
)
return
}
if (PermissionsUtil.hasSelfPermission(context, permissions)) {
// set the denied permissions to empty as all the permissions are granted
// this is required as clean will be called which can invoke on permissions denied
// if it finds some permissions in the denied list
quickPermissionsRequest?.deniedPermissions = emptyArray()
// we are good to go!
mListener?.onPermissionsGranted(quickPermissionsRequest)
// flow complete
clean()
} else {
// we are still missing permissions
val deniedPermissions = PermissionsUtil.getDeniedPermissions(permissions, grantResults)
quickPermissionsRequest?.deniedPermissions = deniedPermissions
// check if rationale dialog should be shown or not
var shouldShowRationale = true
var isPermanentlyDenied = false
for (i in 0 until deniedPermissions.size) {
val deniedPermission = deniedPermissions[i]
val rationale = shouldShowRequestPermissionRationale(deniedPermission)
if (!rationale) {
shouldShowRationale = false
isPermanentlyDenied = true
break
}
}
if (quickPermissionsRequest?.handlePermanentlyDenied == true && isPermanentlyDenied) {
quickPermissionsRequest?.permanentDeniedMethod?.let {
// get list of permanently denied methods
quickPermissionsRequest?.permanentlyDeniedPermissions =
PermissionsUtil.getPermanentlyDeniedPermissions(
this,
permissions,
grantResults
)
mListener?.onPermissionsPermanentlyDenied(quickPermissionsRequest)
return
}
activity?.alert {
message = quickPermissionsRequest?.permanentlyDeniedMessage.orEmpty()
positiveButton("SETTINGS") {
openAppSettings()
}
negativeButton("CANCEL") {
clean()
}
}?.apply { isCancelable = false }?.show()
return
}
// if should show rationale dialog
if (quickPermissionsRequest?.handleRationale == true && shouldShowRationale) {
quickPermissionsRequest?.rationaleMethod?.let {
mListener?.shouldShowRequestPermissionsRationale(quickPermissionsRequest)
return
}
activity?.alert {
message = quickPermissionsRequest?.rationaleMessage.orEmpty()
positiveButton("TRY AGAIN") {
requestPermissionsFromUser()
}
negativeButton("CANCEL") {
clean()
}
}?.apply { isCancelable = false }?.show()
return
}
// if handlePermanentlyDenied = false and handleRationale = false
// This will call permissionsDenied method
clean()
}
}
fun openAppSettings() {
if (quickPermissionsRequest != null) {
val intent = Intent(
ACTION_APPLICATION_DETAILS_SETTINGS,
fromParts("package", activity?.packageName, null)
)
// intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivityForResult(intent, PERMISSIONS_REQUEST_CODE)
} else {
Log.w(
TAG,
"openAppSettings: QuickPermissionsRequest has already completed its flow. Cannot open app settings"
)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == PERMISSIONS_REQUEST_CODE) {
val permissions = quickPermissionsRequest?.permissions ?: emptyArray()
val grantResults = IntArray(permissions.size)
permissions.forEachIndexed { index, s ->
grantResults[index] = context?.let { ActivityCompat.checkSelfPermission(it, s) }
?: PackageManager.PERMISSION_DENIED
}
handlePermissionResult(permissions, grantResults)
}
}
}

查看文件

@ -0,0 +1,54 @@
package com.livinglifetechway.quickpermissions_kotlin.util
import android.app.Activity
import android.content.Context
import android.content.pm.PackageManager
import androidx.core.app.ActivityCompat
import androidx.fragment.app.Fragment
/**
* Utility class that wraps access to the runtime permissions API in M and provides basic helper
* methods.
*/
object PermissionsUtil {
fun getDeniedPermissions(permissions: Array<String>, grantResults: IntArray): Array<String> =
permissions.filterIndexed { index, s ->
grantResults[index] == PackageManager.PERMISSION_DENIED
}.toTypedArray()
fun getPermanentlyDeniedPermissions(
fragment: Fragment,
permissions: Array<String>,
grantResults: IntArray
): Array<String> =
permissions.filterIndexed { index, s ->
grantResults[index] == PackageManager.PERMISSION_DENIED && !fragment.shouldShowRequestPermissionRationale(
s
)
}.toTypedArray()
/**
* Returns true if the Activity has access to all given permissions.
* Always returns true on platforms below M.
*
* @see Activity.checkSelfPermission
*/
fun hasSelfPermission(activity: Context?, permissions: Array<String>): Boolean {
// Verify that all required permissions have been granted
activity?.let {
for (permission in permissions) {
if (ActivityCompat.checkSelfPermission(
activity,
permission
) != PackageManager.PERMISSION_GRANTED
) {
return false
}
}
}
return true
}
}

查看文件

@ -0,0 +1,11 @@
package com.livinglifetechway.quickpermissions_kotlin.util
data class QuickPermissionsOptions(
var handleRationale: Boolean = true,
var rationaleMessage: String = "",
var handlePermanentlyDenied: Boolean = true,
var permanentlyDeniedMessage: String = "",
var rationaleMethod: ((QuickPermissionsRequest) -> Unit)? = null,
var permanentDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null,
var permissionsDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null
)

查看文件

@ -0,0 +1,30 @@
package com.livinglifetechway.quickpermissions_kotlin.util
data class QuickPermissionsRequest(
private var target: PermissionCheckerFragment,
var permissions: Array<String> = emptyArray(),
var handleRationale: Boolean = true,
var rationaleMessage: String = "",
var handlePermanentlyDenied: Boolean = true,
var permanentlyDeniedMessage: String = "",
internal var rationaleMethod: ((QuickPermissionsRequest) -> Unit)? = null,
internal var permanentDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null,
internal var permissionsDeniedMethod: ((QuickPermissionsRequest) -> Unit)? = null,
var deniedPermissions: Array<String> = emptyArray(),
var permanentlyDeniedPermissions: Array<String> = emptyArray()
) {
/**
* Proceed with requesting permissions again with user request
*/
fun proceed() = target.requestPermissionsFromUser()
/**
* Cancels the current permissions request flow
*/
fun cancel() = target.clean()
/**
* In case of permissions permanently denied, request user to enable from app settings
*/
fun openAppSettings() = target.openAppSettings()
}

查看文件

@ -0,0 +1,111 @@
package com.xuqm.base;
import android.app.Application;
import android.content.Context;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import androidx.annotation.Nullable;
import com.orhanobut.logger.AndroidLogAdapter;
import com.orhanobut.logger.FormatStrategy;
import com.orhanobut.logger.Logger;
import com.orhanobut.logger.PrettyFormatStrategy;
import com.xuqm.base.di.component.AppComponent;
import com.xuqm.base.di.manager.HttpManager;
public class App extends Application {
public AppComponent appComponent;
// 宽高
private int width = 0, height = 0;
private static App instance;
public static App getInstance() {
if (null == instance) {
synchronized (App.class) {
if (null == instance)
instance = new App();
}
}
return instance;
}
public App() {
instance = this;
handler = new Handler();
appComponent = HttpManager.getAppComponent("");
}
//https://www.wanandroid.com/wxarticle/list/408/1/json
@Override
public void onCreate() {
super.onCreate();
DisplayMetrics dm = new DisplayMetrics();
WindowManager wm = (WindowManager) this.getSystemService(Context.WINDOW_SERVICE);
if (null != wm) {
wm.getDefaultDisplay().getMetrics(dm);
width = dm.widthPixels;// 屏幕宽度
height = dm.heightPixels;// 屏幕高度
}
FormatStrategy formatStrategy = PrettyFormatStrategy.newBuilder()
.showThreadInfo(false) // (Optional) Whether to show thread info or not. Default true
.methodCount(0) // (Optional) How many method line to show. Default 2
.methodOffset(2) // (Optional) Hides internal method calls up to offset. Default 5
//.logStrategy(customLog) // (Optional) Changes the log strategy to print out. Default LogCat
.tag("LogHttpInfo") // (Optional) Global tag for every log. Default PRETTY_LOGGER
.build();
Logger.addLogAdapter(new AndroidLogAdapter(formatStrategy) {
@Override
public boolean isLoggable(int priority, @Nullable String tag) {
return showLog();
}
});
}
/**
* 是否打印日志
*
* @return true-开启日志
*/
public boolean showLog() {
return true;
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
private final Handler handler;
/**
* 提交主线程处理
*
* @param runnable runnable
*/
public void runOnUiThread(final Runnable runnable) {
handler.post(runnable);
}
/**
* 提交主线程延时后处理
*
* @param runnable runnable
* @param delayMillis 延时时间
*/
public void runOnUiThreadDelay(final Runnable runnable, long delayMillis) {
handler.postDelayed(runnable, delayMillis);
}
}

查看文件

@ -0,0 +1,222 @@
package com.xuqm.base;
import android.content.Context;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.os.Build;
import android.os.Environment;
import android.os.Looper;
import android.util.Log;
import android.widget.Toast;
import com.xuqm.base.common.FileHelper;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.io.Writer;
import java.lang.Thread.UncaughtExceptionHandler;
import java.lang.reflect.Field;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
/**
* UncaughtException处理类,当程序发生Uncaught异常的时候,有该类来接管程序,并记录发送错误报告.
*
* @author user
*/
public class CrashHandler implements UncaughtExceptionHandler {
public static final String TAG = "CrashHandler";
// CrashHandler 实例
private static CrashHandler INSTANCE = new CrashHandler();
// 程序的 Context 对象
private Context mContext;
// 系统默认的 UncaughtException 处理类
private UncaughtExceptionHandler mDefaultHandler;
// 用来存储设备信息和异常信息
private Map<String, String> infos = new HashMap<String, String>();
// 用于格式化日期,作为日志文件名的一部分
private DateFormat formatter = new SimpleDateFormat("yyyy-MM-dd-HH-mm-ss", Locale.getDefault());
/**
* 保证只有一个 CrashHandler 实例
*/
private CrashHandler() {
}
/**
* 获取 CrashHandler 实例 ,单例模式
*/
public static CrashHandler getInstance() {
return INSTANCE;
}
/**
* 初始化
*
* @param context
*/
public void init(Context context) {
mContext = context;
// 获取系统默认的 UncaughtException 处理器
mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler();
// 设置该 CrashHandler 为程序的默认处理器
Thread.setDefaultUncaughtExceptionHandler(this);
}
/**
* UncaughtException 发生时会转入该函数来处理
*/
@Override
public void uncaughtException(Thread thread, Throwable ex) {
if (!handleException(ex) && mDefaultHandler != null) {
// 如果用户没有处理则让系统默认的异常处理器来处理
mDefaultHandler.uncaughtException(thread, ex);
} else {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
Log.e(TAG, "error : ", e);
Thread.currentThread().interrupt();
}
// Intent mIntent = new Intent(mContext, WelcomeActivity.class);
// mIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// mContext.startActivity(mIntent);
android.os.Process.killProcess(android.os.Process.myPid());
}
}
/**
* 自定义错误处理收集错误信息发送错误报告等操作均在此完成
*
* @param ex
* @return true如果处理了该异常信息否则返回 false
*/
private boolean handleException(Throwable ex) {
if (ex == null) {
return false;
}
// 使用 Toast 来显示异常信息
new Thread() {
@Override
public void run() {
Looper.prepare();
Toast.makeText(mContext, "好像出了点问题~~~", Toast.LENGTH_LONG).show();
Looper.loop();
}
}.start();
// 收集设备参数信息
collectDeviceInfo(mContext);
// 保存日志文件
saveCrashInfo2File(ex);
return true;
}
/**
* 收集设备参数信息
*
* @param ctx
*/
public void collectDeviceInfo(Context ctx) {
try {
PackageManager pm = ctx.getPackageManager();
PackageInfo pi = pm.getPackageInfo(ctx.getPackageName(), PackageManager.GET_ACTIVITIES);
if (pi != null) {
String versionName = pi.versionName == null ? "null" : pi.versionName;
String versionCode = pi.versionCode + "";
infos.put("versionName", versionName);
infos.put("versionCode", versionCode);
}
} catch (NameNotFoundException e) {
Log.e(TAG, "an error occured when collect package info", e);
}
Field[] fields = Build.class.getDeclaredFields();
for (Field field : fields) {
try {
field.setAccessible(true);
infos.put(field.getName(), field.get(null).toString());
Log.d(TAG, field.getName() + " : " + field.get(null));
} catch (Exception e) {
Log.e(TAG, "an error occured when collect crash info", e);
}
}
}
/**
* 保存错误信息到文件中 *
*
* @param ex
* @return 返回文件名称, 便于将文件传送到服务器
*/
private String saveCrashInfo2File(Throwable ex) {
StringBuffer sb = new StringBuffer();
for (Map.Entry<String, String> entry : infos.entrySet()) {
String key = entry.getKey();
String value = entry.getValue();
sb.append(String.format("%s=%s\n", key, value));
}
Writer writer = new StringWriter();
PrintWriter printWriter = new PrintWriter(writer);
ex.printStackTrace(printWriter);
Throwable cause = ex.getCause();
while (cause != null) {
cause.printStackTrace(printWriter);
cause = cause.getCause();
}
printWriter.close();
String result = writer.toString();
sb.append(result);
long timestamp = System.currentTimeMillis();
String time = formatter.format(new Date());
String fileName = "crash-" + time + "-" + timestamp + ".log";
if (Environment.getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) {
String path = String.format("%scrash/", FileHelper.getRootFilePath());
File dir = new File(path);
if (!dir.exists()) {
dir.mkdirs();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(path + fileName);
fos.write(sb.toString().getBytes());
} catch (Exception e) {
Log.e(TAG, "an error occured while writing file...", e);
} finally {
try {
if (fos != null) {
fos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
return fileName;
}
}

查看文件

@ -0,0 +1,17 @@
package com.xuqm.base.adapter;
/**
* 所有用到{@link com.xuqm.base.ui.BaseListActivity}来做的列表页
* 数据model都要继承BaseItem
*/
public class BaseItem {
private int s_id;
public int getS_id() {
return s_id;
}
public void setS_id(int s_id) {
this.s_id = s_id;
}
}

查看文件

@ -0,0 +1,186 @@
package com.xuqm.base.adapter;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.xuqm.base.adapter.callback.AdapterItemClickListener;
import com.xuqm.base.adapter.callback.AdapterItemLongClickListener;
import java.util.ArrayList;
import java.util.List;
/**
* 不用{@link BasePagedAdapter}的时候可以用这个
* <p>
*
* @param <T> 数据各式
*/
public abstract class BaseNormalAdapter<T> extends RecyclerView.Adapter<ViewHolder> {
private Context context;
private AdapterItemClickListener<T> itemClickListener;//item的点击事件
private AdapterItemLongClickListener<T> itemLongClickListener;//item的长按事件
private ItemViewDelegateManager<T> mItemViewDelegateManager;//ItemViewDelegate的管理类
private List<T> list;
private AdapterItemClickListener<T> listener;
public BaseNormalAdapter() {
this.list = new ArrayList<>();
mItemViewDelegateManager = new ItemViewDelegateManager<>();
}
public BaseNormalAdapter(List<T> list) {
this.list = null == list ? new ArrayList<>() : list;
mItemViewDelegateManager = new ItemViewDelegateManager<>();
}
public void setmDatas(List<T> mDatas) {
this.list.clear();
this.addmDatas(null == mDatas ? new ArrayList<>() : mDatas);
}
public List<T> getDatas() {
return this.list;
}
public void addmDatas(List<T> mDatas) {
this.list.addAll(mDatas);
notifyDataSetChanged();
}
public void addItem(T item) {
this.list.add(item);
notifyDataSetChanged();
}
public void removeItem(T item) {
this.list.remove(item);
notifyDataSetChanged();
}
@Override
public int getItemViewType(int position) {
if (!useItemViewDelegateManager()) return super.getItemViewType(position);
return mItemViewDelegateManager.getItemViewType(list.get(position), position);
}
/**
* 判断是否有多种ItemViewType
* 根据mItemViewDelegateManager 里面存储的数量决定
*
* @return true 有多种ItemViewType
*/
private boolean useItemViewDelegateManager() {
return mItemViewDelegateManager.getItemViewDelegateCount() > 0;
}
/**
* 添加不同的item样式
*
* @param itemViewDelegate 自定义的item
* @return this
*/
public BaseNormalAdapter addItemViewDelegate(ItemViewDelegate<T> itemViewDelegate) {
mItemViewDelegateManager.addDelegate(itemViewDelegate);
return this;
}
/**
* 添加不同的item样式
*
* @param viewType 自定义的item type 不能重复
* @param itemViewDelegate 自定义的item
* @return this
*/
public BaseNormalAdapter addItemViewDelegate(int viewType, ItemViewDelegate<T> itemViewDelegate) {
mItemViewDelegateManager.addDelegate(viewType, itemViewDelegate);
return this;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemViewDelegate itemViewDelegate = mItemViewDelegateManager.getItemViewDelegate(viewType);
int layoutId = itemViewDelegate.getItemViewLayoutId();//这里拿到自定义的layoutId
context = parent.getContext();//context没用传递过来这里自己获取到
return new ViewHolder(context, parent, layoutId);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
if (null != itemClickListener) {
holder.itemView.setOnClickListener(v -> itemClickListener.onClick(holder.itemView, list.get(position), position));
}
if (null != itemLongClickListener) {
holder.itemView.setOnLongClickListener(v -> itemLongClickListener.onClick(holder.itemView, list.get(position), position));
}
convert(holder, list.get(position));
}
@Override
public int getItemCount() {
return list.size();
}
/**
* 设置item点击监听
*
* @param itemClickListener item的点击事件
*/
public void setItemClickListener(AdapterItemClickListener<T> itemClickListener) {
this.itemClickListener = itemClickListener;
}
/**
* 设置item长按监听
*
* @param itemLongClickListener item的长按事件
*/
public void setItemLongClickListener(AdapterItemLongClickListener<T> itemLongClickListener) {
this.itemLongClickListener = itemLongClickListener;
}
/**
* 部分情况可以需要用到这个比如item里面元素想要和item使用同一个回调处理
*
* @return
*/
protected AdapterItemClickListener<T> getItemClickListener() {
return itemClickListener;
}
/**
* ui绘制的事件分发给ItemViewDelegate自己处理
* 比如settext() setOnClickListener()这些
*
* @param holder holder
* @param item item
*/
public void convert(ViewHolder holder, T item) {
mItemViewDelegateManager.convert(holder, item, holder.getAdapterPosition());
}
/**
* 刷新知道item
*
* @param position position
*/
public void changeItem(int position) {
if (0 <= position && position < getItemCount()) {
notifyItemChanged(position);
}
}
public void changeItem(int position, Object payload) {
if (0 <= position && position < getItemCount()) {
notifyItemChanged(position, payload);
}
}
}

查看文件

@ -0,0 +1,157 @@
package com.xuqm.base.adapter;
import android.content.Context;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.paging.PagedListAdapter;
import com.xuqm.base.adapter.callback.AdapterItemClickListener;
import com.xuqm.base.adapter.callback.AdapterItemLongClickListener;
import java.util.List;
/**
* 如果用到了{@link com.xuqm.base.ui.BaseListActivity}来展示列表页面的话需要adapter继承这个
* <p>
* 如果item只有一种类型可以使用{@link CommonPagedAdapter}来展示
* <p>
* 如果不用{@link CommonPagedAdapter}的话继承后需要使用{@link #addItemViewDelegate(ItemViewDelegate)}
* 来设置展示的页面
*
* @param <T>
*/
public class BasePagedAdapter<T extends BaseItem> extends PagedListAdapter<T, ViewHolder> {
private Context context;
private AdapterItemClickListener<T> itemClickListener;//item的点击事件
private AdapterItemLongClickListener<T> itemLongClickListener;//item的长按事件
private ItemViewDelegateManager<T> mItemViewDelegateManager;//ItemViewDelegate的管理类
public BasePagedAdapter() {
super(new Diff<>());
mItemViewDelegateManager = new ItemViewDelegateManager<>();
}
@Override
public int getItemViewType(int position) {
if (!useItemViewDelegateManager()) return super.getItemViewType(position);
return mItemViewDelegateManager.getItemViewType(getItem(position), position);
}
/**
* 判断是否有多种ItemViewType
* 根据mItemViewDelegateManager 里面存储的数量决定
*
* @return true 有多种ItemViewType
*/
private boolean useItemViewDelegateManager() {
return mItemViewDelegateManager.getItemViewDelegateCount() > 0;
}
/**
* 添加不同的item样式
*
* @param itemViewDelegate 自定义的item
* @return this
*/
public BasePagedAdapter addItemViewDelegate(ItemViewDelegate<T> itemViewDelegate) {
mItemViewDelegateManager.addDelegate(itemViewDelegate);
return this;
}
/**
* 添加不同的item样式
*
* @param viewType 自定义的item type 不能重复
* @param itemViewDelegate 自定义的item
* @return this
*/
public BasePagedAdapter addItemViewDelegate(int viewType, ItemViewDelegate<T> itemViewDelegate) {
mItemViewDelegateManager.addDelegate(viewType, itemViewDelegate);
return this;
}
@NonNull
@Override
public ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
ItemViewDelegate itemViewDelegate = mItemViewDelegateManager.getItemViewDelegate(viewType);
int layoutId = itemViewDelegate.getItemViewLayoutId();//这里拿到自定义的layoutId
context = parent.getContext();//context没用传递过来这里自己获取到
return new ViewHolder(context, parent, layoutId);
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
convert(holder, getItem(position));
}
@Override
public void onBindViewHolder(@NonNull ViewHolder holder, int position, @NonNull List<Object> payloads) {
if (null != itemClickListener) {
holder.itemView.setOnClickListener(v -> itemClickListener.onClick(holder.itemView, getItem(position), position));
}
if (null != itemLongClickListener) {
holder.itemView.setOnLongClickListener(v -> itemLongClickListener.onClick(holder.itemView, getItem(position), position));
}
bindViewHolder(holder, getItem(position), position, payloads);
}
private void bindViewHolder(ViewHolder holder, T item, int position, List<Object> payloads) {
convert(holder, item);
}
/**
* 设置item点击监听
*
* @param itemClickListener item的点击事件
*/
public void setItemClickListener(AdapterItemClickListener<T> itemClickListener) {
this.itemClickListener = itemClickListener;
}
/**
* 设置item长按监听
*
* @param itemLongClickListener item的长按事件
*/
public void setItemLongClickListener(AdapterItemLongClickListener<T> itemLongClickListener) {
this.itemLongClickListener = itemLongClickListener;
}
/**
* 部分情况可以需要用到这个比如item里面元素想要和item使用同一个回调处理
* @return
*/
protected AdapterItemClickListener<T> getItemClickListener() {
return itemClickListener;
}
/**
* ui绘制的事件分发给ItemViewDelegate自己处理
* 比如settext() setOnClickListener()这些
* @param holder holder
* @param item item
*/
public void convert(ViewHolder holder, T item) {
mItemViewDelegateManager.convert(holder, item, holder.getAdapterPosition());
}
/**
* 刷新知道item
* @param position position
*/
public void changeItem(int position) {
if (0 <= position && position < getItemCount()) {
notifyItemChanged(position);
}
}
public void changeItem(int position, Object payload) {
if (0 <= position && position < getItemCount()) {
notifyItemChanged(position, payload);
}
}
}

查看文件

@ -0,0 +1,59 @@
package com.xuqm.base.adapter;
import java.util.List;
/**
* 这个adapter主要是用来简化通用adapter
* 如果item只有一种样式或者说不需要用到itemViewType可以直接使用这个
* <p>
* 构造函数直接传入对应的layoutId然后重写convert方法就可以了
* list不传的话后面使用{@link #setmDatas(List)} 添加就可以了
*
* @param <T> item用到的数据类型
*/
public abstract class CommonAdapter<T> extends BaseNormalAdapter<T> {
protected CommonAdapter(final int layoutId) {
super();
addItemViewDelegate(new ItemViewDelegate<T>() {
@Override
public int getItemViewLayoutId() {
return layoutId;
}
@Override
public boolean isForViewType(T item, int position) {
return true;
}
@Override
public void convert(ViewHolder holder, T t, int position) {
CommonAdapter.this.convert(holder, t, position);
}
});
}
protected CommonAdapter(final int layoutId, List<T> list) {
super(list);
addItemViewDelegate(new ItemViewDelegate<T>() {
@Override
public int getItemViewLayoutId() {
return layoutId;
}
@Override
public boolean isForViewType(T item, int position) {
return true;
}
@Override
public void convert(ViewHolder holder, T t, int position) {
CommonAdapter.this.convert(holder, t, position);
}
});
}
protected abstract void convert(ViewHolder holder, T item, int position);
}

查看文件

@ -0,0 +1,36 @@
package com.xuqm.base.adapter;
/**
* 这个adapter主要是用来简化通用列表页的绘制
* 如果item只有一种样式或者说不需要用到itemViewType可以直接使用这个
* <p>
* 构造函数直接传入对应的layoutId然后重写convert方法就可以了
*
* @param <T> item用到的数据类型
*/
public abstract class CommonPagedAdapter<T extends BaseItem> extends BasePagedAdapter<T> {
protected CommonPagedAdapter(final int layoutId) {
super();
addItemViewDelegate(new ItemViewDelegate<T>() {
@Override
public int getItemViewLayoutId() {
return layoutId;
}
@Override
public boolean isForViewType(T item, int position) {
return true;
}
@Override
public void convert(ViewHolder holder, T t, int position) {
CommonPagedAdapter.this.convert(holder, t, position);
}
});
}
protected abstract void convert(ViewHolder holder, T item, int position);
}

查看文件

@ -0,0 +1,16 @@
package com.xuqm.base.adapter;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.DiffUtil;
public class Diff<T extends BaseItem> extends DiffUtil.ItemCallback<T> {
@Override
public boolean areItemsTheSame(@NonNull T oldItem, @NonNull T newItem) {
return oldItem.getS_id() == newItem.getS_id();
}
@Override
public boolean areContentsTheSame(@NonNull T oldItem, @NonNull T newItem) {
return oldItem.getS_id() == newItem.getS_id();
}
}

查看文件

@ -0,0 +1,72 @@
package com.xuqm.base.adapter;
import android.content.Context;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.widget.HorizontalScrollView;
public class ElasticHorizontalScrollView extends HorizontalScrollView {
private float x;
private DisplayMetrics metrics;
private int threshold = 0;
public ElasticHorizontalScrollView(Context context, AttributeSet attrs) {
super(context, attrs);
metrics = getResources().getDisplayMetrics();
}
public ElasticHorizontalScrollView(Context context) {
this(context, null);
}
public void setThreshold(int threshold) {
this.threshold = threshold;
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
if (ev == null) {
return super.onTouchEvent(ev);
} else {
return commOnTouchEvent(ev);
}
}
public void reset() {
scrollTo(0, 0);
}
private boolean commOnTouchEvent(MotionEvent ev) {
int action = ev.getAction();
int length = threshold;
switch (action) {
case MotionEvent.ACTION_DOWN:
x = ev.getX();
break;
case MotionEvent.ACTION_UP:
//复原位置
if ((ev.getX() - x) > 0) {
if (getScrollX() > length / 2) {
smoothScrollTo(length, 0);
} else {
smoothScrollTo(0, 0);
}
} else {
if (getScrollX() > length / 2) {
smoothScrollTo(length, 0);
} else {
smoothScrollTo(0, 0);
}
}
return true;
case MotionEvent.ACTION_MOVE:
return super.onTouchEvent(ev);
default:
return true;
}
return true;
}
}

查看文件

@ -0,0 +1,30 @@
package com.xuqm.base.adapter;
import androidx.annotation.NonNull;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.lifecycle.Lifecycle;
import androidx.viewpager2.adapter.FragmentStateAdapter;
import java.util.List;
public class FragmentAdapter extends FragmentStateAdapter {
private List<Fragment> fragments;
public FragmentAdapter(@NonNull FragmentManager fragmentManager, @NonNull Lifecycle lifecycle, List<Fragment> fragments) {
super(fragmentManager, lifecycle);
this.fragments = fragments;
}
@NonNull
@Override
public Fragment createFragment(int position) {
return fragments.get(position);
}
@Override
public int getItemCount() {
return fragments.size();
}
}

查看文件

@ -0,0 +1,15 @@
package com.xuqm.base.adapter;
/**
* Created by zhy on 16/6/22.
*/
public interface ItemViewDelegate<T> {
int getItemViewLayoutId();//这个 ItemViewDelegate 将要展示的页面
boolean isForViewType(T item, int position); //条件判断用来判断什么时候展示这个ItemViewDelegate
void convert(ViewHolder holder, T item, int position);//UI绘制与事件添加
}

查看文件

@ -0,0 +1,95 @@
package com.xuqm.base.adapter;
import androidx.collection.SparseArrayCompat;
/**
* Created by zhy on 16/6/22.
*/
public class ItemViewDelegateManager<T> {
private SparseArrayCompat<ItemViewDelegate<T>> delegates = new SparseArrayCompat<>();
public int getItemViewDelegateCount() {
return delegates.size();
}
public ItemViewDelegateManager<T> addDelegate(ItemViewDelegate<T> delegate) {
int viewType = delegates.size();
if (delegate != null) {
delegates.put(viewType, delegate);
}
return this;
}
public ItemViewDelegateManager<T> addDelegate(int viewType, ItemViewDelegate<T> delegate) {
if (delegates.get(viewType) != null) {
throw new IllegalArgumentException(
"An ItemViewDelegate is already registered for the viewType = "
+ viewType
+ ". Already registered ItemViewDelegate is "
+ delegates.get(viewType));
}
delegates.put(viewType, delegate);
return this;
}
public ItemViewDelegateManager<T> removeDelegate(ItemViewDelegate<T> delegate) {
if (delegate == null) {
throw new NullPointerException("ItemViewDelegate is null");
}
int indexToRemove = delegates.indexOfValue(delegate);
if (indexToRemove >= 0) {
delegates.removeAt(indexToRemove);
}
return this;
}
public ItemViewDelegateManager<T> removeDelegate(int itemType) {
int indexToRemove = delegates.indexOfKey(itemType);
if (indexToRemove >= 0) {
delegates.removeAt(indexToRemove);
}
return this;
}
int getItemViewType(T item, int position) {
int delegatesCount = delegates.size();
for (int i = 0; i < delegatesCount; i++) {
ItemViewDelegate<T> delegate = delegates.valueAt(i);
if (delegate.isForViewType(item, position)) {
return delegates.keyAt(i);
}
}
throw new IllegalArgumentException(
"No ItemViewDelegate added that matches position=" + position + " in data source");
}
void convert(ViewHolder holder, T item, int position) {
int delegatesCount = delegates.size();
for (int i = 0; i < delegatesCount; i++) {
ItemViewDelegate<T> delegate = delegates.valueAt(i);
if (delegate.isForViewType(item, position)) {
delegate.convert(holder, item, position);
return;
}
}
throw new IllegalArgumentException(
"No ItemViewDelegateManager added that matches position=" + position + " in data source");
}
public ItemViewDelegate getItemViewDelegate(int viewType) {
return delegates.get(viewType);
}
public int getItemViewLayoutId(int viewType) {
return getItemViewDelegate(viewType).getItemViewLayoutId();
}
public int getItemViewType(ItemViewDelegate<T> itemViewDelegate) {
return delegates.indexOfValue(itemViewDelegate);
}
}

查看文件

@ -0,0 +1,178 @@
package com.xuqm.base.adapter;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.LinearLayout.LayoutParams;
import androidx.recyclerview.widget.RecyclerView;
import com.xuqm.base.R;
/**
* @author jose.han
* @date 2019/7/19 0019
* @description 包装器再原有的adpter 基础上分装测滑功能基于
*/
public class SlipReAdapter extends RecyclerView.Adapter<SlipReAdapter.RViewHolder> {
private RecyclerView.Adapter mAdapter;
private ISlipClickAction mISlipClickAction;
private int mSlipViewId;
public final static int MODE_DELETE = 0;
public final static int MODE_CLICK = 0;
private int mMode = MODE_DELETE;
private int mSlipWidth = 0;
public static class Builder {
private RecyclerView.Adapter mAdapter;
private ISlipClickAction mISlipClickAction;
private int mSlipViewId;
private int mMode;
private int mSlipWidth;
public Builder setAdapter(RecyclerView.Adapter adapter) {
mAdapter = adapter;
return this;
}
public Builder setISlipClickAction(
ISlipClickAction ISlipClickAction) {
mISlipClickAction = ISlipClickAction;
return this;
}
public Builder setSlipViewId(int slipViewId) {
mSlipViewId = slipViewId;
return this;
}
public Builder setMode(int mode) {
mMode = mode;
return this;
}
public Builder setSlipWidth(float slipWidth) {
mSlipWidth = (int) slipWidth;
return this;
}
public SlipReAdapter build() {
SlipReAdapter slipReAdapter = new SlipReAdapter();
slipReAdapter.setAdapter(mAdapter);
slipReAdapter.setISlipClickAction(mISlipClickAction);
slipReAdapter.setMode(mMode);
slipReAdapter.setSlipViewId(mSlipViewId);
slipReAdapter.setSlipWidth(mSlipWidth);
return slipReAdapter;
}
}
public SlipReAdapter() {
}
public void setAdapter(RecyclerView.Adapter adapter) {
mAdapter = adapter;
}
public void setISlipClickAction(
ISlipClickAction ISlipClickAction) {
mISlipClickAction = ISlipClickAction;
}
public void setSlipViewId(int slipViewId) {
mSlipViewId = slipViewId;
}
public void setMode(int mode) {
mMode = mode;
}
public void setSlipWidth(int slipWidth) {
mSlipWidth = slipWidth;
}
@Override
public RViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_slip, parent, false);
LinearLayout contentLL = view.findViewById(R.id.content_ll);
LinearLayout deleteLl = view.findViewById(R.id.delete_ll);
View delete = LayoutInflater.from(parent.getContext()).inflate(mSlipViewId, null, false);
deleteLl.addView(delete);
LayoutParams layoutParams = new LayoutParams(
parent.getResources().getDisplayMetrics().widthPixels,
ViewGroup.LayoutParams.WRAP_CONTENT);
RecyclerView.ViewHolder viewHolder = mAdapter.onCreateViewHolder(parent, viewType);
viewHolder.itemView.setLayoutParams(layoutParams);
contentLL.addView(viewHolder.itemView);
return new RViewHolder(view, viewHolder, mSlipWidth);
}
@Override
public void onBindViewHolder(final RViewHolder holder, final int position) {
mAdapter.onBindViewHolder(holder.mViewHolder, position);
holder.deleteLl.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
mISlipClickAction.onAction(holder.getAdapterPosition());
holder.mElasticHorizontalScrollView.reset();
Log.i("SlipReAdapter", "slip action and the pos is:" + holder.getAdapterPosition());
if (mMode == MODE_DELETE) {
notifyItemRemoved(holder.getAdapterPosition());
} else if (mMode == MODE_CLICK) {
notifyItemChanged(holder.getAdapterPosition());
}
}
});
}
@Override
public int getItemCount() {
return mAdapter != null ? mAdapter.getItemCount() : 0;
}
public static class RViewHolder extends RecyclerView.ViewHolder {
private View deleteLl;
private ElasticHorizontalScrollView mElasticHorizontalScrollView;
private RecyclerView.ViewHolder mViewHolder;
public RViewHolder(View itemView, RecyclerView.ViewHolder viewHolder, int threshold) {
super(itemView);
mViewHolder = viewHolder;
deleteLl = itemView.findViewById(R.id.delete_ll);
mElasticHorizontalScrollView = itemView.findViewById(R.id.ElasticHorizontalScrollView);
if (threshold != 0) {
LayoutParams layoutParams = new LayoutParams(threshold,
ViewGroup.LayoutParams.WRAP_CONTENT);
deleteLl.setLayoutParams(layoutParams);
mElasticHorizontalScrollView.setThreshold(threshold);
} else {
deleteLl.post(new Runnable() {
@Override
public void run() {
int width = deleteLl.getWidth();
mElasticHorizontalScrollView.setThreshold(width);
}
});
}
}
}
public interface ISlipClickAction {
public void onAction(int position);
}
}

查看文件

@ -0,0 +1,171 @@
package com.xuqm.base.adapter;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.util.SparseArray;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.ColorInt;
import androidx.annotation.DrawableRes;
import androidx.annotation.IdRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.StringRes;
import androidx.recyclerview.widget.RecyclerView;
import com.xuqm.base.adapter.callback.AdapterClickListener;
import com.xuqm.base.common.ImageHelper;
import java.util.List;
public class ViewHolder extends RecyclerView.ViewHolder {
private Context context;
private ViewGroup parent;
private int layoutId;
private SparseArray<View> views = new SparseArray<>();
public ViewHolder(Context context, ViewGroup parent, @LayoutRes int layoutId) {
super(LayoutInflater.from(context).inflate(layoutId, parent, false));
this.context = context;
this.parent = parent;
this.layoutId = layoutId;
}
public <T extends View> T getView(int viewId) {
View view = views.get(viewId);
if (null == view) {
view = itemView.findViewById(viewId);
if (null == view) throw new IllegalArgumentException("not found id");
views.put(viewId, view);
}
return (T) view;
}
public ViewHolder setText(@IdRes int viewId, CharSequence text) {
TextView textView = getView(viewId);
textView.setText(text);
return this;
}
public ViewHolder setTypeface(@IdRes int viewId, Typeface typeface) {
TextView textView = getView(viewId);
textView.setTypeface(typeface);
return this;
}
public ViewHolder setEnabled(@IdRes int viewId, boolean enabled) {
View view = getView(viewId);
view.setEnabled(enabled);
return this;
}
public ViewHolder setBackgroundResource(@IdRes int viewId, @DrawableRes int resId) {
View textView = getView(viewId);
textView.setBackgroundResource(resId);
return this;
}
public ViewHolder setBackgroundColor(@IdRes int viewId, @ColorInt int color) {
View textView = getView(viewId);
textView.setBackgroundColor(color);
return this;
}
public ViewHolder setTextColor(@IdRes int viewId, @ColorInt int color) {
TextView textView = getView(viewId);
textView.setTextColor(color);
return this;
}
public ViewHolder setText(@IdRes int viewId, @StringRes int resId) {
TextView textView = getView(viewId);
textView.setText(context.getString(resId));
return this;
}
public ViewHolder setImageResource(@IdRes int viewId, @DrawableRes int resId) {
ImageView imageView = getView(viewId);
imageView.setImageResource(resId);
return this;
}
public ViewHolder setImage(@IdRes int viewId, String url) {
ImageView imageView = getView(viewId);
ImageHelper.load(imageView, url);
return this;
}
public ViewHolder setImage(@IdRes int viewId, @DrawableRes int resourceId, String url) {
ImageView imageView = getView(viewId);
ImageHelper.load(imageView, resourceId, url);
return this;
}
public ViewHolder setImage(@IdRes int viewId, @DrawableRes int placeholder, @DrawableRes int error, String url) {
ImageView imageView = getView(viewId);
ImageHelper.load(imageView, placeholder, error, url);
return this;
}
public ViewHolder setImage(@IdRes int viewId, Bitmap bitmap) {
ImageView imageView = getView(viewId);
ImageHelper.load(imageView, bitmap);
return this;
}
public ViewHolder gone(@IdRes int viewId) {
View view = getView(viewId);
view.setVisibility(View.GONE);
return this;
}
public ViewHolder invisible(@IdRes int viewId) {
View view = getView(viewId);
view.setVisibility(View.INVISIBLE);
return this;
}
public ViewHolder gone(View view) {
view.setVisibility(View.GONE);
return this;
}
public ViewHolder visible(@IdRes int viewId) {
View view = getView(viewId);
view.setVisibility(View.VISIBLE);
return this;
}
public ViewHolder setVisibility(@IdRes int viewId, boolean isVisible) {
View view = getView(viewId);
if (isVisible) view.setVisibility(View.VISIBLE);
else view.setVisibility(View.GONE);
return this;
}
public ViewHolder visible(View view) {
view.setVisibility(View.VISIBLE);
return this;
}
public ViewHolder setClickListener(@IdRes int viewId, AdapterClickListener adapterClickListener) {
View view = getView(viewId);
if (null != view) view.setOnClickListener(adapterClickListener::onClick);
return this;
}
public ViewHolder setClickListener(List<Integer> viewIds, AdapterClickListener adapterClickListener) {
for (Integer viewId : viewIds) {
View view = getView(viewId);
if (null != view) view.setOnClickListener(adapterClickListener::onClick);
}
return this;
}
}

查看文件

@ -0,0 +1,10 @@
package com.xuqm.base.adapter.callback;
import android.view.View;
/**
* adapter中为item元素设置点击时间时候用到的监听
*/
public interface AdapterClickListener {
void onClick(View view);
}

查看文件

@ -0,0 +1,11 @@
package com.xuqm.base.adapter.callback;
import android.view.View;
/**
* item设置点击事件的监听
* @param <T>
*/
public interface AdapterItemClickListener<T> {
void onClick(View view, T item, int position);
}

查看文件

@ -0,0 +1,11 @@
package com.xuqm.base.adapter.callback;
import android.view.View;
/**
* item设置长按事件的监听
* @param <T>
*/
public interface AdapterItemLongClickListener<T> {
boolean onClick(View view, T item, int position);
}

查看文件

@ -0,0 +1,143 @@
package com.xuqm.base.common;
import android.text.TextUtils;
import android.util.Base64;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Locale;
import javax.crypto.Cipher;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
public class AESCBCUtil { //密码
private static final String key = "12345678901234567890123456789012";
//iv偏移量
private static final String iv = "1234567890123456";
/**
* 加密对字符串进行加密并返回十六进制字符串(hex)
*
* @param encryptStr 需要加密的字符串
* @return 加密后的十六进制字符串(hex)
*/
public static String encrypt(String encryptStr, String key, String iv) {
try {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
cipher.init(Cipher.ENCRYPT_MODE, skeySpec, ivParameterSpec);
byte[] encrypted = cipher.doFinal(encryptStr.getBytes());
byte[] encode = Base64.encode(encrypted, Base64.DEFAULT);
return new String(encode).replace("\n","");
// return byte2HexStr(encrypted);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 解密对加密后的十六进制字符串(hex)进行解密并返回字符串
*
* @param encryptedStr 需要解密的加密后的十六进制字符串
* @return 解密后的字符串
*/
public static String decrypt(String encryptedStr, String key, String iv) {
try {
IvParameterSpec ivParameterSpec = new IvParameterSpec(iv.getBytes(StandardCharsets.UTF_8));
SecretKeySpec skeySpec = new SecretKeySpec(key.getBytes(StandardCharsets.UTF_8), "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7PADDING");
cipher.init(Cipher.DECRYPT_MODE, skeySpec, ivParameterSpec);
byte[] decode = Base64.decode(encryptedStr, Base64.DEFAULT);
// byte[] bytes = hexStr2Bytes(encryptedStr);
byte[] original = cipher.doFinal(decode);
return new String(original);
} catch (Exception ex) {
ex.printStackTrace();
}
return null;
}
/**
* 十六进制字符串转换为byte[]
*
* @param hexStr 需要转换为byte[]的字符串
* @return 转换后的byte[]
*/
public static byte[] hexStr2Bytes(String hexStr) {
/*对输入值进行规范化整理*/
hexStr = hexStr.trim().replace(" ", "").toUpperCase(Locale.US);
//处理值初始化
int m = 0, n = 0;
int iLen = hexStr.length() / 2; //计算长度
byte[] ret = new byte[iLen]; //分配存储空间
for (int i = 0; i < iLen; i++) {
m = i * 2 + 1;
n = m + 1;
ret[i] = (byte) (Integer.decode("0x" + hexStr.substring(i * 2, m) + hexStr.substring(m, n)) & 0xFF);
}
return ret;
}
/**
* byte[]转换为十六进制字符串
*
* @param bytes 需要转换为字符串的byte[]
* @return 转换后的十六进制字符串
*/
public static String byte2HexStr(byte[] bytes) {
String hs = "";
String stmp = "";
for (int n = 0; n < bytes.length; n++) {
stmp = (Integer.toHexString(bytes[n] & 0XFF));
if (stmp.length() == 1)
hs = hs + "0" + stmp;
else
hs = hs + stmp;
}
return hs;
}
public static String md5(String string) {
if (TextUtils.isEmpty(string)) {
return "";
}
MessageDigest md5 = null;
try {
md5 = MessageDigest.getInstance("MD5");
byte[] bytes = md5.digest(string.getBytes());
String result = "";
for (byte b : bytes) {
String temp = Integer.toHexString(b & 0xff);
if (temp.length() == 1) {
temp = "0" + temp;
}
result += temp;
}
return result.toUpperCase();
} catch (NoSuchAlgorithmException e) {
e.printStackTrace();
}
return "";
}
}

查看文件

@ -0,0 +1,106 @@
package com.xuqm.base.common;
import android.app.Activity;
import java.util.Stack;
/**
* activity的管理栈
*/
public class AppManager {
public static AppManager getInstance() {
return APPHolder.INSTANCE;
}
private static class APPHolder {
private static final AppManager INSTANCE = new AppManager();
}
private AppManager() {
activityStack = new Stack<>();
}
private final Stack<Activity> activityStack;
//添加一个新的act
public void pushActivity(Activity activity) {
activityStack.add(activity);
}
/**
* 推出一个activity 其实toolbar的返回按钮可以直接使用这个方法
*
* @param activity 需要退出的activity
*/
public void popActivity(Activity activity) {
if (activityStack != null && activityStack.size() > 0) {
if (activity != null) {
activity.finish();
activityStack.remove(activity);
}
}
}
/**
* 获取当前最上面的那个activity
*
* @return
*/
public Activity getActivity() {
return activityStack.lastElement();
}
/**
* finish最后一个
*
* @return
*/
public void finish() {
this.popActivity(this.getActivity());
}
/**
* finish最后一个之外的所有页面
*
* @return
*/
public void logout() {
if (activityStack.size() < 1)
return;
for (int i = 0; i < activityStack.size() - 1; i++) {
Activity activity = activityStack.firstElement();
if (activity == null) break;
popActivity(activity);
}
}
/**
* 退出app
*/
public void exit() {
if (activityStack != null) {
while (activityStack.size() > 0) {
Activity activity = getActivity();
if (activity == null) break;
popActivity(activity);
}
}
android.os.Process.killProcess(android.os.Process.myPid());
}
/**
* 退出app
*/
public void exitWithOutLast() {
if (activityStack != null) {
int size = activityStack.size();
for (int i = 0; i < size - 1; i++) {
popActivity(activityStack.get(0));
}
}
}
}

查看文件

@ -0,0 +1,87 @@
package com.xuqm.base.common;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings.Secure;
import android.telephony.TelephonyManager;
import java.io.UnsupportedEncodingException;
import java.util.UUID;
public class DeviceUuidFactory {
protected static final String PREFS_FILE = "device_id.xml";
protected static final String PREFS_DEVICE_ID = "device_id";
protected static volatile UUID uuid;
public DeviceUuidFactory(Context context) {
if( uuid ==null ) {
synchronized (DeviceUuidFactory.class) {
if( uuid == null) {
final SharedPreferences prefs = context.getSharedPreferences( PREFS_FILE, 0);
final String id = prefs.getString(PREFS_DEVICE_ID, null );
if (id != null) {
// Use the ids previously computed and stored in the prefs file
uuid = UUID.fromString(id);
} else {
final String androidId = Secure.getString(context.getContentResolver(), Secure.ANDROID_ID);
// Use the Android ID unless it's broken, in which case fallback on deviceId,
// unless it's not available, then fallback on a random number which we store
// to a prefs file
try {
if (!"9774d56d682e549c".equals(androidId)) {
uuid = UUID.nameUUIDFromBytes(androidId.getBytes("utf8"));
} else {
final String deviceId = ((TelephonyManager) context.getSystemService( Context.TELEPHONY_SERVICE )).getDeviceId();
uuid = deviceId!=null ? UUID.nameUUIDFromBytes(deviceId.getBytes("utf8")) : UUID.randomUUID();
}
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
// Write the value out to the prefs file
prefs.edit().putString(PREFS_DEVICE_ID, uuid.toString() ).commit();
}
}
}
}
}
/**
* Returns a unique UUID for the current android device. As with all UUIDs, this unique ID is "very highly likely"
* to be unique across all Android devices. Much more so than ANDROID_ID is.
*
* The UUID is generated by using ANDROID_ID as the base key if appropriate, falling back on
* TelephonyManager.getDeviceID() if ANDROID_ID is known to be incorrect, and finally falling back
* on a random UUID that's persisted to SharedPreferences if getDeviceID() does not return a
* usable value.
*
* In some rare circumstances, this ID may change. In particular, if the device is factory reset a new device ID
* may be generated. In addition, if a user upgrades their phone from certain buggy implementations of Android 2.2
* to a newer, non-buggy version of Android, the device ID may change. Or, if a user uninstalls your app on
* a device that has neither a proper Android ID nor a Device ID, this ID may change on reinstallation.
*
* Note that if the code falls back on using TelephonyManager.getDeviceId(), the resulting ID will NOT
* change after a factory reset. Something to be aware of.
*
* Works around a bug in Android 2.2 for many devices when using ANDROID_ID directly.
*
* @see http://code.google.com/p/android/issues/detail?id=10603
*
* @return a UUID that may be used to uniquely identify your device for most purposes.
*/
public UUID getDeviceUuid() {
return uuid;
}
}

查看文件

@ -0,0 +1,10 @@
package com.xuqm.base.common;
import android.content.Context;
import android.util.TypedValue;
public class DimensHelper {
public static int pxToDp(Context context, int px) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, px, context.getResources().getDisplayMetrics());
}
}

查看文件

@ -0,0 +1,218 @@
package com.xuqm.base.common;
import android.content.Context;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import androidx.annotation.NonNull;
import androidx.core.content.FileProvider;
import com.xuqm.base.App;
import com.xuqm.base.BuildConfig;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.UUID;
public class FileHelper {
public static String getRootFilePath() {
return App.getInstance().getExternalFilesDir(null).getPath() +
File.separator +
BuildConfig.APP_ID +
File.separator;
}
/**
* 根据路径创建文件夹
*
* @param filePath
* @return
*/
public static boolean createDirectory(String filePath) {
if (ToolsHelper.isNull(filePath)) {
return false;
}
File file = new File(filePath);
if (file.exists() && file.isDirectory()) {
return true;
}
return file.mkdirs();
}
public static void delete(String path) {
LogHelper.e(String.format("开始删除文件::%s", path));
File file = new File(path);
if (!file.exists()
|| !file.isFile())
return;
file.delete();
}
public static void delete(File file) {
LogHelper.e(String.format("开始删除文件::%s", file.getAbsoluteFile()));
if (!file.exists()
|| !file.isFile())
return;
file.delete();
}
public static String getVoicePath() {
String path = getRootFilePath() +
"voice" +
File.separator;
return createDirectory(path) ? path : "";
}
public static String getImagePath() {
String path = getRootFilePath() +
"image" +
File.separator;
return createDirectory(path) ? path : "";
}
public static String getAppPath() {
String path = getRootFilePath() +
"apps" +
File.separator;
LogHelper.e(path);
return createDirectory(path) ? path : "";
}
public static String getDownloadPath() {
String path = getRootFilePath() +
"download" +
File.separator;
LogHelper.e(path);
return createDirectory(path) ? path : "";
}
/**
* 读取assets文件夹的文件
*
* @param strFileName 文件名包含assets后面的路径
* @return 文件内容
*/
public static String readJSON(String strFileName) {
String strResult = "";
try (InputStream is = App.getInstance().getAssets().open(strFileName)) {
int size = is.available();
byte[] buffer = new byte[size];
is.read(buffer);
strResult = new String(buffer, StandardCharsets.UTF_8);
} catch (Exception ex) {
LogHelper.e("readJson", ex);
}
return strResult;
}
public static String getBitmapFilePath(String path, String suffix) {
String pathStr = getRootFilePath() + "images" + File.separator + path + File.separator;
createDirectory(pathStr);
return pathStr + UUID.randomUUID() + "." + suffix;
}
/**
* 保存图片到本地
*
* @param bitmap 图片
* @param filePath 保存到的文件 png
* @return 状态
*/
public static String saveBitmap(Bitmap bitmap, String filePath) {
if (bitmap == null)
return "";
FileOutputStream fos = null;
try {
fos = new FileOutputStream(new File(filePath));
bitmap.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
return filePath;
} catch (Exception e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return "";
}
public void installAPK(String filePath) {
Intent intent = new Intent();
intent.setAction("android.intent.action.VIEW");
intent.addCategory("android.intent.category.DEFAULT");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);// 广播里面操作需要加上这句存在于一个独立的栈里
intent.setDataAndType(Uri.fromFile(new File(filePath)), "application/vnd.android.package-archive");
App.getInstance().startActivity(intent);
}
public static void openFile(Context activity, File file) {
Intent intent = new Intent();
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 设置intent的Action属性
intent.setAction(Intent.ACTION_VIEW);
// 获取文件file的MIME类型
String type = getMIMEType(file);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
}
// 设置intent的data和Type属性
intent.setDataAndType(/* uri */FileHelper.getFileUri(file), type);
activity.startActivity(intent);
}
public static Uri getFileUri(@NonNull File file) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
return FileProvider.getUriForFile(AppManager.getInstance().getActivity(), BuildConfig.APP_ID + ".fileprovider", file);
} else {
return Uri.fromFile(file);
}
}
private static String[][] MIME_MapTable = new String[][]{{".3gp", "video/3gpp"}, {".apk", "application/vnd.android.package-archive"}, {".asf", "video/x-ms-asf"}, {".avi", "video/x-msvideo"}, {".bin", "application/octet-stream"}, {".bmp", "image/bmp"}, {".c", "text/plain"}, {".class", "application/octet-stream"}, {".conf", "text/plain"}, {".cpp", "text/plain"}, {".doc", "application/msword"}, {".docx", "application/vnd.openxmlformats-officedocument.wordprocessingml.document"}, {".xls", "application/vnd.ms-excel"}, {".xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"}, {".exe", "application/octet-stream"}, {".gif", "image/gif"}, {".gtar", "application/x-gtar"}, {".gz", "application/x-gzip"}, {".h", "text/plain"}, {".htm", "text/html"}, {".html", "text/html"}, {".jar", "application/java-archive"}, {".java", "text/plain"}, {".jpeg", "image/jpeg"}, {".jpg", "image/jpeg"}, {".eucppic", "image/jpeg"}, {".js", "application/x-javascript"}, {".log", "text/plain"}, {".m3u", "audio/x-mpegurl"}, {".m4a", "audio/mp4a-latm"}, {".m4b", "audio/mp4a-latm"}, {".m4p", "audio/mp4a-latm"}, {".m4u", "video/vnd.mpegurl"}, {".m4v", "video/x-m4v"}, {".mov", "video/quicktime"}, {".mp2", "audio/x-mpeg"}, {".mp3", "audio/x-mpeg"}, {".mp4", "video/mp4"}, {".mpc", "application/vnd.mpohun.certificate"}, {".mpe", "video/mpeg"}, {".mpeg", "video/mpeg"}, {".mpg", "video/mpeg"}, {".mpg4", "video/mp4"}, {".mpga", "audio/mpeg"}, {".msg", "application/vnd.ms-outlook"}, {".ogg", "audio/ogg"}, {".pdf", "application/pdf"}, {".png", "image/png"}, {".pps", "application/vnd.ms-powerpoint"}, {".ppt", "application/vnd.ms-powerpoint"}, {".pptx", "application/vnd.openxmlformats-officedocument.presentationml.presentation"}, {".prop", "text/plain"}, {".rc", "text/plain"}, {".rmvb", "audio/x-pn-realaudio"}, {".rtf", "application/rtf"}, {".sh", "text/plain"}, {".tar", "application/x-tar"}, {".tgz", "application/x-compressed"}, {".txt", "text/plain"}, {".wav", "audio/x-wav"}, {".wma", "audio/x-ms-wma"}, {".wmv", "audio/x-ms-wmv"}, {".wps", "application/vnd.ms-works"}, {".xml", "text/plain"}, {".z", "application/x-compress"}, {".zip", "application/x-zip-compressed"}, {"", "*/*"}};
public static String getMIMEType(File file) {
String type = "*/*";
String fName = file.getName();
int dotIndex = fName.lastIndexOf(".");
if (dotIndex < 0) {
return type;
} else {
String end = fName.substring(dotIndex, fName.length()).toLowerCase();
if (end == "") {
return type;
} else {
for (int i = 0; i < MIME_MapTable.length; ++i) {
if (end.equals(MIME_MapTable[i][0])) {
type = MIME_MapTable[i][1];
}
}
return type;
}
}
}
}

查看文件

@ -0,0 +1,112 @@
package com.xuqm.base.common;
import android.content.Context;
import android.widget.ImageView;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.resource.bitmap.CenterCrop;
import com.bumptech.glide.load.resource.bitmap.RoundedCorners;
import com.luck.picture.lib.engine.ImageEngine;
import com.luck.picture.lib.utils.ActivityCompatHelper;
import com.xuqm.base.R;
public class GlideEngine implements ImageEngine {
/**
* 加载图片
*
* @param context 上下文
* @param url 资源url
* @param imageView 图片承载控件
*/
@Override
public void loadImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.into(imageView);
}
@Override
public void loadImage(Context context, ImageView imageView, String url, int maxWidth, int maxHeight) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(maxWidth, maxHeight)
.into(imageView);
}
/**
* 加载相册目录封面
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadAlbumCover(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.asBitmap()
.load(url)
.override(180, 180)
.sizeMultiplier(0.5f)
.transform(new CenterCrop(), new RoundedCorners(8))
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
/**
* 加载图片列表图片
*
* @param context 上下文
* @param url 图片路径
* @param imageView 承载图片ImageView
*/
@Override
public void loadGridImage(Context context, String url, ImageView imageView) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context)
.load(url)
.override(200, 200)
.centerCrop()
.placeholder(R.drawable.ps_image_placeholder)
.into(imageView);
}
@Override
public void pauseRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).pauseRequests();
}
@Override
public void resumeRequests(Context context) {
if (!ActivityCompatHelper.assertValidRequest(context)) {
return;
}
Glide.with(context).resumeRequests();
}
private GlideEngine() {
}
private static final class InstanceHolder {
static final GlideEngine instance = new GlideEngine();
}
public static GlideEngine createGlideEngine() {
return InstanceHolder.instance;
}
}

查看文件

@ -0,0 +1,61 @@
package com.xuqm.base.common;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonParser;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* Created by xuqm on 2016/6/3.
*/
public class GsonImplHelp extends Json {
private Gson gson = new Gson();
@Override
public String toJson(Object src) {
return gson.toJson(src);
}
@Override
public <T> T toObject(String json, Class<T> claxx) {
return gson.fromJson(json, claxx);
}
@Override
public <T> T toObject(String json, Type typeOfT) {
return gson.fromJson(json, typeOfT);
}
@Override
public <T> T toObject(byte[] bytes, Class<T> claxx) {
return gson.fromJson(new String(bytes), claxx);
}
public <T> List<T> toList(String json, Class<T> clazz) {
JsonArray jsonArray = new JsonParser().parse(json).getAsJsonArray();
List<T> list = new ArrayList<>();
for (JsonElement jsonElement : jsonArray) {
list.add(gson.fromJson(jsonElement, clazz)); //cls
}
return list;
}
public static <T> List<T> stringToArray(String s, Class<T[]> cls) {
T[] array = new Gson().fromJson(s, cls);
return Arrays.asList(array);
}
}

查看文件

@ -0,0 +1,53 @@
package com.xuqm.base.common;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.util.Base64;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
public class ImageHelp {
/*
* bitmap转base64
* */
public static String bitmapToBase64(Bitmap bitmap) {
String result = null;
ByteArrayOutputStream baos = null;
try {
if (bitmap != null) {
baos = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);
baos.flush();
baos.close();
byte[] bitmapBytes = baos.toByteArray();
result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
try {
if (baos != null) {
baos.flush();
baos.close();
}
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
/**
* base64转为bitmap
*
* @param base64Data
* @return
*/
public static Bitmap base64ToBitmap(String base64Data) {
byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT);
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
}
}

查看文件

@ -0,0 +1,34 @@
package com.xuqm.base.common;
import android.widget.ImageView;
import androidx.annotation.DrawableRes;
import com.bumptech.glide.Glide;
import com.bumptech.glide.load.engine.DiskCacheStrategy;
import com.bumptech.glide.request.RequestOptions;
/**
* 一个image相关的工具类
*
* @author xuqm
*/
public class ImageHelper {
/**
* 给imageView添加图片的方法
*
* @param imageView 需要添加图片的控件
* @param url url地址可以是path draw等
*/
public static void load(ImageView imageView, Object url) {
Glide.with(imageView).load(url).into(imageView);
}
public static void load(ImageView imageView, @DrawableRes int resourceId, Object url) {
Glide.with(imageView).applyDefaultRequestOptions(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.AUTOMATIC)).load(url).placeholder(resourceId).error(resourceId).into(imageView);
}
public static void load(ImageView imageView, @DrawableRes int placeholder, @DrawableRes int error, Object url) {
Glide.with(imageView).applyDefaultRequestOptions(RequestOptions.diskCacheStrategyOf(DiskCacheStrategy.AUTOMATIC)).load(url).placeholder(placeholder).error(error).into(imageView);
}
}

查看文件

@ -0,0 +1,31 @@
package com.xuqm.base.common;
import java.lang.reflect.Type;
import java.util.List;
/**
* Created by xuqm on 2016/6/3.
*/
public abstract class Json {
private static Json json;
Json() {
}
public static Json get() {
if (json == null) {
json = new GsonImplHelp();
}
return json;
}
public abstract String toJson(Object src);
public abstract <T> T toObject(String json, Class<T> claxx);
public abstract <T> T toObject(String json, Type typeOfT);
public abstract <T> T toObject(byte[] bytes, Class<T> claxx);
public abstract <T> List<T> toList(String json, Class<T> claxx);
}

查看文件

@ -0,0 +1,75 @@
package com.xuqm.base.common;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.orhanobut.logger.Logger;
import java.util.Locale;
/**
* 日志库用的是 KLog
* 平常自己调试时候不想用那么多就随便写了个类
*/
public class LogHelper {
public static void d(String tag, Object object) {
Logger.t(tag).d(object);
}
public static void d(Object object) {
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
Logger.t(tag).d(object);
}
public static void d(@NonNull String message, @Nullable Object... args) {
Logger.t(message).d(args);
}
public static void e(String tag, Object object) {
Logger.t(tag).e(object.toString());
}
public static void e(Object object) {
if (null == object){
return;
}
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
Logger.t(tag).e("=====>" + object.toString());
}
public static void e(String msg, Throwable tr) {
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
Logger.t(tag).e(tr, msg);
}
public static void e(String tag, String msg, Throwable tr) {
Logger.t(tag).e(tr, msg);
}
public static void json(String msg) {
StackTraceElement caller = getCallerStackTraceElement();
String tag = generateTag(caller);
Logger.t(tag).json(msg);
}
public static void json(String tag, String msg) {
Logger.t(tag).json(msg);
}
private static String generateTag(StackTraceElement caller) {
String tag = "%s.%s(L:%d)";
String callerClazzName = caller.getClassName();
callerClazzName = callerClazzName.substring(callerClazzName.lastIndexOf(".") + 1);
return String.format(Locale.getDefault(), tag, callerClazzName, caller.getMethodName(), caller.getLineNumber());
}
private static StackTraceElement getCallerStackTraceElement() {
return Thread.currentThread().getStackTrace()[4];
}
}

查看文件

@ -0,0 +1,8 @@
package com.xuqm.base.common;
/**
* 下拉刷新的状态码表
*/
public enum RefreshResult {
SUCCEED, FAILED, NO_DATA, NO_MORE
}

查看文件

@ -0,0 +1,18 @@
package com.xuqm.base.common;
import android.content.Context;
public class ScreenUtils {
/**
* 获取屏幕高度(px)
*/
public static int getScreenHeight(Context context) {
return context.getResources().getDisplayMetrics().heightPixels;
}
/**
* 获取屏幕宽度(px)
*/
public static int getScreenWidth(Context context) {
return context.getResources().getDisplayMetrics().widthPixels;
}
}

查看文件

@ -0,0 +1,13 @@
package com.xuqm.base.common
const val SHARE_UESR_NAME = "share_user_name"
const val SHARE_UESR_PASSWORD = "share_user_password"
const val SHARE_LOGIN_OBJ = "SHARE_LOGIN_OBJ"
const val SHARE_UESR_TOKEN = "share_user_token"
const val SHARE_UESR_ID = "share_user_id"
const val SHARE_UESR_TOKEN_TIME = "share_user_token_time"
const val MSG_COUNT = "msg_count"
const val SHARE_COUNTRY = "share_user_country"

查看文件

@ -0,0 +1,94 @@
package com.xuqm.base.common;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
public class TimeHelper {
/**
* 获取当前时间戳
*
* @return 时间戳
*/
public static long getTimeMillis() {
return System.currentTimeMillis();
}
/**
* 获取当前时间指定返回样式
*
* @param formats 指定样式
* @return 时间字符串
*/
public static String getTimeString(String formats) {
return getStringFormMillis(System.currentTimeMillis(), formats);
}
/**
* 根据给定时间戳和样式返回字符串
*
* @param millis 时间戳
* @param formats 指定字符串格式
* @return 时间字符串
*/
public static String getStringFormMillis(long millis, String formats) {
Date date = new Date(millis);
return new SimpleDateFormat(formats, Locale.CHINESE).format(date);
}
public static String getStringFormMillisForGMT(long millis, String formats) {
Date date = new Date(millis);
SimpleDateFormat simpleDateFormat = new SimpleDateFormat(formats);//格式
simpleDateFormat.setTimeZone(TimeZone.getTimeZone("GMT")); // 设置时区为GMT
return simpleDateFormat.format(date);
}
/**
* 根据Date 返回指定格式的时字符串
*
* @param date 数据
* @param formats 格式
* @return 指定格式的字符串
*/
public static String getStringFromDate(Date date, String formats) {
SimpleDateFormat formatter = new SimpleDateFormat(formats, Locale.getDefault());
return formatter.format(date);
}
/**
* 根据给定字符串和格式获取时间戳
*
* @param dateString 时间字符串
* @param formats 时间格式
* @return 时间戳
*/
public static long getTimeMillisForType(String dateString, String formats) {
SimpleDateFormat format = new SimpleDateFormat(formats, Locale.getDefault());
Date date = null;
try {
date = format.parse(dateString);
} catch (ParseException e) {
e.printStackTrace();
}
return date != null ? date.getTime() : 0;
}
/**
* 获取以秒为单位的时间戳
*
* @return
*/
public static long getTimeFromSecond() {
return getTimeMillis() / 1000;
}
}

查看文件

@ -0,0 +1,274 @@
package com.xuqm.base.common;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;
import com.google.android.material.snackbar.Snackbar;
import com.google.android.material.textfield.TextInputLayout;
import com.google.gson.internal.$Gson$Types;
import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
public class ToolsHelper {
public static boolean isNull(Object obj) {
if (null == obj) return true;
String str = obj.toString();
if (str.isEmpty()) return true;
return str.equalsIgnoreCase("null");
}
public static Long toLong(Object obj) {
if (isNull(obj)) return 0L;
try {
return Long.parseLong(obj.toString());
} catch (Exception e) {
return 0L;
}
}
public static int toInt(Object obj) {
if (isNull(obj)) return 0;
try {
return (int)(ToolsHelper.toDouble(obj.toString()));
} catch (Exception e) {
return 0;
}
}
public static double toDouble(Object obj) {
if (isNull(obj)) return 0.0;
try {
return Double.parseDouble(obj.toString());
} catch (Exception e) {
return 0D;
}
}
public static String toString(Object obj) {
if (isNull(obj)) return "";
return obj.toString();
}
/**
* 格式化json字符串
*
* @param jsonStr 需要格式化的json串
* @return 格式化后的json串
*/
public static String formatJson(String jsonStr) {
if (null == jsonStr || "".equals(jsonStr)) return "";
StringBuilder sb = new StringBuilder();
char last = '\0';
char current = '\0';
int indent = 0;
for (int i = 0; i < jsonStr.length(); i++) {
last = current;
current = jsonStr.charAt(i);
//遇到{ [换行且下一行缩进
switch (current) {
case '{':
case '[':
sb.append(current);
sb.append('\n');
indent++;
addIndentBlank(sb, indent);
break;
//遇到} ]换行当前行缩进
case '}':
case ']':
sb.append('\n');
indent--;
addIndentBlank(sb, indent);
sb.append(current);
break;
//遇到,换行
case ',':
sb.append(current);
if (last != '\\') {
sb.append('\n');
addIndentBlank(sb, indent);
}
break;
default:
sb.append(current);
}
}
return sb.toString();
}
/**
* 添加space
*/
private static void addIndentBlank(StringBuilder sb, int indent) {
for (int i = 0; i < indent; i++) {
sb.append('\t');
}
}
/**
* http 请求数据返回 json 中中文字符为 unicode 编码转汉字转码
*
* @param theString
* @return 转化后的结果.
*/
public static String decodeUnicode(String theString) {
char aChar;
int len = theString.length();
StringBuilder outBuffer = new StringBuilder(len);
for (int x = 0; x < len; ) {
aChar = theString.charAt(x++);
if (aChar == '\\') {
aChar = theString.charAt(x++);
if (aChar == 'u') {
int value = 0;
for (int i = 0; i < 4; i++) {
aChar = theString.charAt(x++);
switch (aChar) {
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
value = (value << 4) + aChar - '0';
break;
case 'a':
case 'b':
case 'c':
case 'd':
case 'e':
case 'f':
value = (value << 4) + 10 + aChar - 'a';
break;
case 'A':
case 'B':
case 'C':
case 'D':
case 'E':
case 'F':
value = (value << 4) + 10 + aChar - 'A';
break;
default:
throw new IllegalArgumentException("Malformed \\uxxxx encoding.");
}
}
outBuffer.append((char) value);
} else {
if (aChar == 't') {
aChar = '\t';
} else if (aChar != 'r') {
if (aChar == 'n') {
aChar = '\n';
} else if (aChar == 'f') {
aChar = '\f';
}
} else {
aChar = '\r';
}
outBuffer.append(aChar);
}
} else {
outBuffer.append(aChar);
}
}
return outBuffer.toString();
}
/**
* 弹出提示信息 感觉比Toast好看点 不过Toast不需要依赖view
*
* @param view 绑定一个view才能展示
* @param content 需要展示的内容
*/
public static void snack(View view, CharSequence content) {
Snackbar.make(view, content, Snackbar.LENGTH_SHORT).show();
}
public static void showMessage(CharSequence content) {
Toast.makeText(AppManager.getInstance().getActivity(), content, Toast.LENGTH_SHORT).show();
}
/**
* EditText绑定TextInputLayout处理一下
*
* @param editText editText
* @param textInputLayout textInputLayout
*/
public static void addTextChangedListener(EditText editText, TextInputLayout textInputLayout) {
editText.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
if (!TextUtils.isEmpty(textInputLayout.getError())) {//输入的时候不提示错误信息
textInputLayout.setErrorEnabled(true);
textInputLayout.setError("");
textInputLayout.setErrorEnabled(false);
}
}
});
}
/**
* 使用 TextInputLayout 提示错误信息
*
* @param textInputLayout TextInputLayout
* @param msg 错判的内容
*/
public static void showError(TextInputLayout textInputLayout, String msg) {
textInputLayout.setErrorEnabled(true);
textInputLayout.setError(msg);
}
/**
* 将Object对象里面的属性和值转化成Map对象
*
* @param obj
* @return
* @throws IllegalAccessException
*/
public static <T> Map<String, T> objectToMap(Object obj) throws IllegalAccessException {
Map<String, T> map = new HashMap<>();
Class<?> clazz = obj.getClass();
for (Field field : clazz.getDeclaredFields()) {
field.setAccessible(true);
String fieldName = field.getName();
T value = (T) field.get(obj);
if (null != value) map.put(fieldName, value);
}
return map;
}
public static Type getSuperclassTypeParameter(Class<?> subclass) {
Type superClass = subclass.getGenericSuperclass();
// if (superClass instanceof Class) {
// System.out.println("superClass=" + superClass);
// throw new RuntimeException("Missing type parameter.");
// }
ParameterizedType parameterized = (ParameterizedType) superClass;
return $Gson$Types.canonicalize(parameterized.getActualTypeArguments()[0]);
}
}

查看文件

@ -0,0 +1,23 @@
package com.xuqm.base.datasource;
import androidx.annotation.NonNull;
import androidx.lifecycle.MutableLiveData;
import androidx.paging.DataSource;
public class DataSourceFactory<T> extends DataSource.Factory<Integer, T> {
private PagedDataLoader<T> dataLoader;
public DataSourceFactory(PagedDataLoader<T> dataLoader) {
this.dataLoader = dataLoader;
}
public MutableLiveData<PagedDataSource<T>> sourceLiveData = new MutableLiveData<>();
@NonNull
@Override
public DataSource<Integer, T> create() {
PagedDataSource<T> dataSource = new PagedDataSource<>(dataLoader);
sourceLiveData.postValue(dataSource);
return dataSource;
}
}

查看文件

@ -0,0 +1,13 @@
package com.xuqm.base.datasource;
import androidx.paging.PageKeyedDataSource;
public interface PagedDataLoader<T> {
void loadInitial(PageKeyedDataSource.LoadInitialParams<Integer> params, PageKeyedDataSource.LoadInitialCallback<Integer, T> callback);
void loadAfter(PageKeyedDataSource.LoadParams<Integer> params, PageKeyedDataSource.LoadCallback<Integer, T> callback);
void refresh();
void loadMore();
}

查看文件

@ -0,0 +1,26 @@
package com.xuqm.base.datasource;
import androidx.annotation.NonNull;
import androidx.paging.PageKeyedDataSource;
public class PagedDataSource<T> extends PageKeyedDataSource<Integer, T> {
private PagedDataLoader<T> dataLoader;
public PagedDataSource(PagedDataLoader<T> dataLoader) {
this.dataLoader = dataLoader;
}
@Override
public void loadInitial(@NonNull LoadInitialParams<Integer> params, @NonNull LoadInitialCallback<Integer, T> callback) {
this.dataLoader.loadInitial(params, callback);
}
@Override
public void loadBefore(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, T> callback) {
}
@Override
public void loadAfter(@NonNull LoadParams<Integer> params, @NonNull LoadCallback<Integer, T> callback) {
this.dataLoader.loadAfter(params, callback);
}
}

查看文件

@ -0,0 +1,28 @@
package com.xuqm.base.di.component;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.xuqm.base.di.module.NetworkModule;
import java.util.List;
import javax.inject.Singleton;
import dagger.Component;
import okhttp3.Cookie;
import okhttp3.OkHttpClient;
import retrofit2.Retrofit;
/**
* 可以获取到RetrofitOkHttpClientPersistentCookieJarcookies
*/
@Singleton
@Component(modules = NetworkModule.class)
public interface AppComponent {
Retrofit retrofit();
OkHttpClient okHttpClient();
PersistentCookieJar persistentCookieJar();
List<Cookie> cookies();
}

查看文件

@ -0,0 +1,28 @@
package com.xuqm.base.di.interceptor;
import com.xuqm.base.common.LogHelper;
import com.xuqm.base.common.ToolsHelper;
import okhttp3.logging.HttpLoggingInterceptor;
public class HttpLogger implements HttpLoggingInterceptor.Logger {
private StringBuilder mMessage = new StringBuilder();
@Override
public void log(String message) {
// 请求或者响应开始
if (message.startsWith("--> POST")) {
mMessage.setLength(0);
}
// {}或者[]形式的说明是响应结果的json数据需要进行格式化
if ((message.startsWith("{") && message.endsWith("}"))
|| (message.startsWith("[") && message.endsWith("]"))) {
message = ToolsHelper.formatJson(ToolsHelper.decodeUnicode(message));
}
mMessage.append(message.concat("\n"));
// 响应结束打印整条日志
if (message.startsWith("<-- END HTTP")) {
LogHelper.d("___http",mMessage.toString());
}
}
}

查看文件

@ -0,0 +1,57 @@
package com.xuqm.base.di.interceptor;
import com.xuqm.base.common.LogHelper;
import java.io.IOException;
import java.nio.charset.Charset;
import okhttp3.Interceptor;
import okhttp3.MediaType;
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
import okio.Buffer;
import static okhttp3.internal.Util.UTF_8;
public class LoggingInterceptor implements Interceptor {
String TAG = "_____Http";
@Override
public Response intercept(Chain chain) throws IOException {
Request request = chain.request();
long startTime = System.currentTimeMillis();
Response response = chain.proceed(chain.request());
long endTime = System.currentTimeMillis();
long duration = endTime - startTime;
ResponseBody responseBody = response.body();
if (responseBody == null) {
return response;
}
okhttp3.MediaType mediaType = responseBody.contentType();
String content = response.body().string();
LogHelper.e(TAG, request.toString());
LogHelper.e(TAG, response.code() + " : " + response.message());
String method = request.method();
if ("POST".equals(method)) {
Buffer buffer = new Buffer();
try {
request.body().writeTo(buffer);
Charset charset = Charset.forName("UTF-8");
MediaType contentType = request.body().contentType();
if (contentType != null) {
charset = contentType.charset(UTF_8);
}
String params = buffer.readString(charset);
LogHelper.e(TAG, params);
} catch (IOException e) {
e.printStackTrace();
}
}
LogHelper.json(TAG, content);
LogHelper.e(TAG, "耗时: " + duration + "毫秒");
return response.newBuilder().body(okhttp3.ResponseBody.create(mediaType, content)).build();
}
}

查看文件

@ -0,0 +1,82 @@
package com.xuqm.base.di.manager;
import com.xuqm.base.App;
import com.xuqm.base.di.component.AppComponent;
import com.xuqm.base.di.component.DaggerAppComponent;
import com.xuqm.base.di.module.NetworkModule;
import java.util.HashMap;
import java.util.Map;
import okhttp3.Interceptor;
/**
* 网络访问的管理类
*/
public class HttpManager {
private static Map<String, Object> apis = new HashMap<>();
private static Map<String, AppComponent> appComponentMap = new HashMap<>();
/**
* 使用默认的appComponent和给定的service获取一个service实例
* appComponent {@link App #appComponent}定义
* service 可以参照retrofit的使用方法
* <p>
* appComponent {@link #getAppComponent(String)}
*
* @param service service
* @param <T> service实例class类型
* @return service实例
*/
public static <T> T getApi(final Class<T> service) {
return getApi(App.getInstance().appComponent, service);
}
/**
* 根据给定的appComponent和service获取一个service实例
* appComponent可以使用{@link #getAppComponent(String)} 方法获得
* service 可以参照retrofit的使用方法
*
* @param appComponent {@link #getAppComponent(String)}
* @param service service
* @param <T> service实例class类型
* @return service实例
*/
public static <T> T getApi(AppComponent appComponent, final Class<T> service) {
String key = appComponent.hashCode() + service.getCanonicalName();
if (!apis.containsKey(key))
synchronized (HttpManager.class) {
if (!apis.containsKey(key))
apis.put(key, appComponent.retrofit().create(service));
}
return (T) apis.get(key);
}
/**
* 根据指定的baseUrl 获取一个{@link AppComponent} 用来做后续事件
*
* @param baseUrl 换地地址 例如
* @return AppComponent
*/
// public static AppComponent getAppComponent(String baseUrl) {
// return getAppComponent(baseUrl, null);
// }
/**
* 根据指定的baseUrl 获取一个{@link AppComponent} 用来做后续事件
*
* @param baseUrl 换地地址
* @param interceptor 自定义拦截器
* @return AppComponent
*/
public static AppComponent getAppComponent(String baseUrl, Interceptor... interceptor) {
if (!appComponentMap.containsKey(baseUrl))
synchronized (HttpManager.class) {
if (!appComponentMap.containsKey(baseUrl))
appComponentMap.put(baseUrl, DaggerAppComponent.builder().networkModule(new NetworkModule(baseUrl, interceptor)).build());
}
return appComponentMap.get(baseUrl);
}
}

查看文件

@ -0,0 +1,97 @@
package com.xuqm.base.di.module;
import com.franmontiel.persistentcookiejar.PersistentCookieJar;
import com.franmontiel.persistentcookiejar.cache.SetCookieCache;
import com.franmontiel.persistentcookiejar.persistence.SharedPrefsCookiePersistor;
import com.xuqm.base.App;
import com.xuqm.base.di.interceptor.HttpLogger;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;
import javax.inject.Singleton;
import dagger.Module;
import dagger.Provides;
import okhttp3.Cookie;
import okhttp3.Interceptor;
import okhttp3.OkHttpClient;
import okhttp3.logging.HttpLoggingInterceptor;
import retrofit2.Retrofit;
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory;
import retrofit2.converter.gson.GsonConverterFactory;
@Module
public class NetworkModule {
private String BaseUrl = "https://xuqinmin.com/";
private final List<Interceptor> interceptor = new ArrayList<>();
public NetworkModule() {
}
public NetworkModule(String baseUrl, Interceptor... interceptor) {
BaseUrl = baseUrl;
this.interceptor.clear();
this.interceptor.addAll(Arrays.asList(interceptor));
}
@Provides
@Singleton
Retrofit provideRetrofit(OkHttpClient okHttpClient) {
return new Retrofit.Builder()
.baseUrl(BaseUrl)
.client(okHttpClient)
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build();
}
@Provides
@Singleton
OkHttpClient provideOkHttpClient(HttpLoggingInterceptor httpLoggingInterceptor, PersistentCookieJar persistentCookieJar) {
OkHttpClient.Builder builder = new OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.pingInterval(5, TimeUnit.SECONDS)
.readTimeout(60, TimeUnit.SECONDS)
.writeTimeout(60, TimeUnit.SECONDS);
// builder.addNetworkInterceptor(httpLoggingInterceptor);
if (0 != interceptor.size()) {
for (Interceptor interceptor1 : this.interceptor) {
builder.addInterceptor(interceptor1);
}
}
return builder.cookieJar(persistentCookieJar)
.build();
}
@Provides
@Singleton
HttpLoggingInterceptor provideHttpLoggingInterceptor() {
return new HttpLoggingInterceptor(new HttpLogger()).setLevel(HttpLoggingInterceptor.Level.BASIC);
}
@Provides
@Singleton
PersistentCookieJar providePersistentCookieJar(SharedPrefsCookiePersistor sharedPrefsCookiePersistor) {
return new PersistentCookieJar(new SetCookieCache(), sharedPrefsCookiePersistor);
}
@Provides
@Singleton
SharedPrefsCookiePersistor provideSharedPrefsCookiePersistor() {
return new SharedPrefsCookiePersistor(App.getInstance());
}
@Provides
@Singleton
List<Cookie> provideCookies(SharedPrefsCookiePersistor sharedPrefsCookiePersistor) {
return sharedPrefsCookiePersistor.loadAll();
}
}

查看文件

@ -0,0 +1,83 @@
package com.xuqm.base.dialog
import android.app.Dialog
import android.content.Context
import android.os.Bundle
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.DialogFragment
class AlertDialogFragment : DialogFragment() {
companion object {
fun newInstance(
title: String,
message: String,
confirm: String,
cancel: String
): AlertDialogFragment {
val args = Bundle()
args.putString("title", title)
args.putString("message", message)
args.putString("confirm", confirm)
args.putString("cancel", cancel)
val fragment = AlertDialogFragment()
fragment.arguments = args
return fragment
}
}
private var title: String? = null
private var message: String? = null
private var confirm: String? = null
private var cancel: String? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
title = arguments?.getString("title")
message = arguments?.getString("message")
confirm = arguments?.getString("confirm")
cancel = arguments?.getString("cancel")
}
private lateinit var listener: NoticeDialogListener
interface NoticeDialogListener {
fun onDialogPositiveClick(dialog: DialogFragment)
fun onDialogNegativeClick(dialog: DialogFragment)
}
override fun onAttach(context: Context) {
super.onAttach(context)
try {
listener = context as NoticeDialogListener
} catch (e: ClassCastException) {
throw ClassCastException(
(context.toString() +
" must implement NoticeDialogListener")
)
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return activity?.let {
val builder = AlertDialog.Builder(it)
title?.let { it1 -> builder.setTitle(it1) }
message?.let { it1 -> builder.setMessage(it1) }
confirm?.let { it1 ->
builder.setPositiveButton(
it1
) { _, _ ->
listener.onDialogPositiveClick(this)
}
}
cancel?.let { it1 ->
builder.setNegativeButton(
it1
) { _, _ ->
listener.onDialogNegativeClick(this)
}
}
builder.create()
} ?: throw IllegalStateException("Activity cannot be null")
}
}

查看文件

@ -0,0 +1,164 @@
package com.xuqm.base.dialog.loading;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.LinearGradient;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.View;
import com.xuqm.base.R;
/**
* Created by qiqi on 15/11/3.
*/
public class ColorfulRingProgressView extends View {
private float mPercent = 75;
private float mStrokeWidth;
private int mBgColor = 0xffe1e1e1;
private float mStartAngle = 0;
private int mFgColorStart = 0xffffe400;
private int mFgColorEnd = 0xffff4800;
private LinearGradient mShader;
private Context mContext;
private RectF mOval;
private Paint mPaint;
public ColorfulRingProgressView(Context context, AttributeSet attrs) {
super(context, attrs);
this.mContext = context;
TypedArray a = context.getTheme().obtainStyledAttributes(attrs,
R.styleable.ColorfulRingProgressView,
0, 0);
try {
mBgColor = a.getColor(R.styleable.ColorfulRingProgressView_bgColor, 0xffe1e1e1);
mFgColorEnd = a.getColor(R.styleable.ColorfulRingProgressView_fgColorEnd, 0xffff4800);
mFgColorStart = a.getColor(R.styleable.ColorfulRingProgressView_fgColorStart, 0xffffe400);
mPercent = a.getFloat(R.styleable.ColorfulRingProgressView_percent, 75);
mStartAngle = a.getFloat(R.styleable.ColorfulRingProgressView_startAngle, 0) + 270;
mStrokeWidth = a.getDimensionPixelSize(R.styleable.ColorfulRingProgressView_strokeWidths, dp2px(21));
System.out.println("**** m" + mStrokeWidth);
} finally {
a.recycle();
}
init();
}
private void init() {
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setStyle(Paint.Style.STROKE);
mPaint.setStrokeWidth(mStrokeWidth);
mPaint.setStrokeCap(Paint.Cap.ROUND);
}
private int dp2px(float dp) {
return (int) (mContext.getResources().getDisplayMetrics().density * dp + 0.5f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
mPaint.setShader(null);
mPaint.setColor(0xffFFE93C);
canvas.drawArc(mOval, 0, 360, false, mPaint);
mPaint.setShader(mShader);
canvas.drawArc(mOval, mStartAngle, mPercent * 3.6f, false, mPaint);
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
updateOval();
mShader = new LinearGradient(mOval.left, mOval.top,
mOval.left, mOval.bottom, mFgColorStart, mFgColorEnd, Shader.TileMode.MIRROR);
}
public float getPercent() {
return mPercent;
}
public void setPercent(float mPercent) {
this.mPercent = mPercent;
refreshTheLayout();
}
public float getStrokeWidth() {
return mStrokeWidth;
}
public void setStrokeWidth(float mStrokeWidth) {
this.mStrokeWidth = mStrokeWidth;
mPaint.setStrokeWidth(mStrokeWidth);
updateOval();
refreshTheLayout();
}
private void updateOval() {
int xp = getPaddingLeft() + getPaddingRight();
int yp = getPaddingBottom() + getPaddingTop();
mOval = new RectF(getPaddingLeft() + mStrokeWidth, getPaddingTop() + mStrokeWidth,
getPaddingLeft() + (getWidth() - xp) - mStrokeWidth,
getPaddingTop() + (getHeight() - yp) - mStrokeWidth);
}
public void setStrokeWidthDp(float dp) {
this.mStrokeWidth = dp2px(dp);
mPaint.setStrokeWidth(mStrokeWidth);
updateOval();
refreshTheLayout();
}
public void refreshTheLayout() {
invalidate();
requestLayout();
}
public int getFgColorStart() {
return mFgColorStart;
}
public void setFgColorStart(int mFgColorStart) {
this.mFgColorStart = mFgColorStart;
mShader = new LinearGradient(mOval.left, mOval.top,
mOval.left, mOval.bottom, mFgColorStart, mFgColorEnd, Shader.TileMode.MIRROR);
refreshTheLayout();
}
public int getFgColorEnd() {
return mFgColorEnd;
}
public void setFgColorEnd(int mFgColorEnd) {
this.mFgColorEnd = mFgColorEnd;
mShader = new LinearGradient(mOval.left, mOval.top,
mOval.left, mOval.bottom, mFgColorStart, mFgColorEnd, Shader.TileMode.MIRROR);
refreshTheLayout();
}
public float getStartAngle() {
return mStartAngle;
}
public void setStartAngle(float mStartAngle) {
this.mStartAngle = mStartAngle + 270;
refreshTheLayout();
}
}

查看文件

@ -0,0 +1,238 @@
/**
* com.leadingsoft.leaderaide.activity
*
* @ClassName: LoadingDialog
* @Description: LoadingDialog等待页面
* @author: macq macq@leadingsoft.cn
* @date: 2014-06-16
*/
package com.xuqm.base.dialog.loading;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnKeyListener;
import android.os.Handler;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.xuqm.base.R;
import com.xuqm.base.common.AppManager;
import com.xuqm.base.common.LogHelper;
import com.xuqm.base.common.ToolsHelper;
public class LoadingDialog {
private static Dialog mDialog;
public static void showDialog(String msg) {
showDialog(AppManager.getInstance().getActivity(), msg);
}
public static void showDialog(Context context, String msg) {
if (isShowing()) {
return;
}
try {
OnKeyListener keyListener = (dialog, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_DOWN) {
mDialog.dismiss();
}
return false;
};
mDialog = new Dialog(context, R.style.dialog);
mDialog.setOnKeyListener(keyListener);
mDialog.setCancelable(false);
mDialog.show();
mDialog.setContentView(R.layout.loading_process_dialog_icon);
TextView TvLoading = (TextView) mDialog
.findViewById(R.id.loading_process_dialog_text);
TvLoading.setText(msg);
if (ToolsHelper.isNull(msg)) {
TvLoading.setVisibility(View.GONE);
}
Window window = mDialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.gravity = Gravity.CENTER;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;//宽高可设置具体大小
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
mDialog.getWindow().setAttributes(lp);
} catch (Exception e) {
LogHelper.e(LoadingDialog.class.getSimpleName(), e);
}
}
public static void showDialog(Context context, String msg, boolean isShowBackGround) {
OnKeyListener keyListener = new OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_DOWN) {
mDialog.dismiss();
}
return false;
}
};
mDialog = new Dialog(context, R.style.dialog);
mDialog.setOnKeyListener(keyListener);
mDialog.setCancelable(false);
mDialog.show();
mDialog.setContentView(R.layout.loading_process_dialog_icon);
LinearLayout ll_loading_process_dialog = (LinearLayout) mDialog.findViewById(R.id.ll_loading_process_dialog);
// 清空背景
if (!isShowBackGround) {
ll_loading_process_dialog.setBackgroundResource(0);
}
TextView TvLoading = (TextView) mDialog.findViewById(R.id.loading_process_dialog_text);
TvLoading.setText(msg);
if (ToolsHelper.isNull(msg)) {
TvLoading.setVisibility(View.GONE);
}
Window window = mDialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.gravity = Gravity.CENTER;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;//宽高可设置具体大小
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
mDialog.getWindow().setAttributes(lp);
}
public static void updataTV(String text) {
if (null == mDialog)
return;
TextView TvLoading = (TextView) mDialog.findViewById(R.id.loading_process_dialog_text);
TvLoading.setText(text);
}
public static void dismissDialog() {
if (mDialog != null) {
if (mDialog.isShowing()) {
try {
mDialog.dismiss();
} catch (Exception e) {
// TODO: handle exception
}
}
}
}
public static boolean isShowing() {
if (mDialog != null) {
return mDialog.isShowing();
}
return false;
}
/**
* 获取进度条的Dialog
*/
public static Dialog getCircleProgressDialog(Context context) {
return new Dialog(context, R.style.dialog);
}
/**
* 获取进度条的VIEW
*/
public static View getCircleProgressView(Context context) {
LayoutInflater inflater = LayoutInflater.from(context);
return inflater.inflate(R.layout.loading_circle_process_dialog_icon, null);
}
/**
* 显示带进度条的dialog
*/
public static void showCircleProgressDialog(Context context, Dialog dialog, View view) {
OnKeyListener keyListener = new OnKeyListener() {
@Override
public boolean onKey(DialogInterface dialog, int keyCode,
KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_DOWN) {
dialog.dismiss();
}
return false;
}
};
try {
//dialog = new AlertDialog.Builder(context).create();
dialog.setOnKeyListener(keyListener);
dialog.setCancelable(false);
dialog.show();
dialog.setContentView(view);
} catch (Exception ex) {
LogHelper.e("创建圆形进度条", ex);
}
}
/**
* 显示带进度条的dialog
*/
public static void updateCircleProgressDialog(int percent) {
if (null== mView)return;
TextView tvPercent = (TextView) mView.findViewById(R.id.tvPercent);
ColorfulRingProgressView isDownload = (ColorfulRingProgressView) mView.findViewById(R.id.crpv);
isDownload.setPercent(percent);
tvPercent.setText("" + percent);
}
/********************************* APK下载圆形进度条 ***********************************************/
/**
* 获取进度条的VIEW
*/
public static View getProgressViewByTextView(Context context) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.loading_circle_process_dialog_icon_by_textview, null);
return view;
}
private static View mView;
/**
* 显示带进度条的dialog
*/
public static void showCircleProgressDialog(final Context context, final Handler handler, String msg) {
mDialog=getCircleProgressDialog(context);
mView=getProgressViewByTextView(context);
OnKeyListener keyListener = (dialog, keyCode, event) -> {
if (keyCode == KeyEvent.KEYCODE_BACK
&& event.getAction() == KeyEvent.ACTION_DOWN) {
handler.obtainMessage(9999).sendToTarget();
}
return false;
};
try {
//dialog = new AlertDialog.Builder(context).create();
mDialog.setOnKeyListener(keyListener);
mDialog.setCancelable(false);
mDialog.show();
TextView textView = (TextView) mView.findViewById(R.id.tip_msg);
textView.setText(msg);
TextView tvPercent = (TextView) mView.findViewById(R.id.tvPercent);
tvPercent.setText("0");
mDialog.setContentView(mView);
Window window = mDialog.getWindow();
WindowManager.LayoutParams lp = window.getAttributes();
lp.gravity = Gravity.CENTER;
lp.width = WindowManager.LayoutParams.WRAP_CONTENT;//宽高可设置具体大小
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
mDialog.getWindow().setAttributes(lp);
} catch (Exception ex) {
LogHelper.e("创建圆形进度条", ex);
}
}
}

查看文件

@ -0,0 +1,232 @@
package com.xuqm.base.extensions
import android.app.Activity
import android.content.Context
import android.view.inputmethod.InputMethodManager
import androidx.annotation.NonNull
import androidx.appcompat.app.AlertDialog
import androidx.fragment.app.Fragment
import com.livinglifetechway.quickpermissions_kotlin.runWithPermissions
import com.livinglifetechway.quickpermissions_kotlin.util.QuickPermissionsOptions
import com.xuqm.base.BuildConfig
import com.xuqm.base.common.LogHelper
import com.xuqm.base.common.ToolsHelper
import kotlin.experimental.and
import kotlin.properties.ReadWriteProperty
import kotlin.reflect.KProperty
private class NotNullSingleValue<T> : ReadWriteProperty<Any?, T> {
private var value: T? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T {
return value ?: throw IllegalStateException("not initialized")
}
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
if (this.value == null) {
this.value = value
} else {
throw IllegalStateException("already initialized")
}
}
}
/**
* 不为空且唯一
*/
fun <T> notNullSingleValue(): ReadWriteProperty<Any?, T> = NotNullSingleValue()
/**
* 打印log
*/
//fun Any.log(message: CharSequence) {
// KLog.d(this.javaClass.simpleName, message)
//}
fun Any.log(message: Any) {
LogHelper.d(this.javaClass.simpleName, message)
}
fun Any.loge(message: Any) {
LogHelper.e("=====>" + this.javaClass.simpleName, message)
}
fun Any.log() {
LogHelper.d("=====>" + this.javaClass.simpleName, this)
}
fun Any.loge() {
LogHelper.e("=====>" + this.javaClass.simpleName, this)
}
fun Any.showMessage(content: CharSequence) {
ToolsHelper.showMessage(content)
}
fun Any.showMessage() {
ToolsHelper.showMessage(this.toString())
}
fun ByteArray.toHexString(): String {
val sb = StringBuffer()
for (i in this.indices) {
val hex = Integer.toHexString((this[i] and 0xFF.toByte()).toInt())
if (hex.length < 2) {
sb.append(0)
}
sb.append(hex)
}
return sb.toString()
}
/**
* 弹出窗口
* @param title 标题
* @param message 提示信息
* @param confirm 点击确认按钮
* @param cancel 点击取消按钮的事件
*/
fun Activity.showDialog(title: String, message: String, confirm: () -> Unit, cancel: () -> Unit) {
AlertDialog.Builder(this).setTitle(title)
.setMessage(message)
.setPositiveButton(
"Confirm"
) { _, _ ->
confirm()
}
.setNegativeButton(
"Cancel"
) { _, _ ->
cancel()
}.create().show()
}
/**
* 弹出窗口---只有一个确认按钮
* @param title 标题
* @param message 提示信息
* @param confirm 点击确认按钮
*/
fun Activity.showDialog(title: String, message: String, confirm: () -> Unit) {
AlertDialog.Builder(this).setTitle(title)
.setMessage(message)
.setPositiveButton(
"Confirm"
) { _, _ ->
confirm()
}.create().show()
}
/**
* 权限检测--申请
* @param rationaleMethod 权限被拒绝时候的回调提供重新申请打开设置界面等方法
* @param permanentDeniedMethod 拒绝权限并且选中了不在提示选项的回调可以指导用户打开设置页面授予权限
*/
fun Context?.runWithPermission(
vararg permissions: String,
callback: () -> Unit
): Any? = this.runWithPermissions(
*permissions
) { callback() }
fun Context?.runWithPermission(
vararg permissions: String,
rationaleMethod: ((com.livinglifetechway.quickpermissions_kotlin.util.QuickPermissionsRequest) -> Unit),
permanentDeniedMethod: ((com.livinglifetechway.quickpermissions_kotlin.util.QuickPermissionsRequest) -> Unit),
callback: () -> Unit
): Any? {
val quickPermissionsOption = QuickPermissionsOptions(
rationaleMethod = rationaleMethod,
permanentDeniedMethod = permanentDeniedMethod
)
return this.runWithPermissions(
*permissions, options = quickPermissionsOption
) { callback() }
}
/*
隐藏软键盘
*/
fun Activity.hideSoftInput() {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(window.decorView.windowToken, 0)
}
fun Fragment.hideSoftInput() {
activity?.hideSoftInput()
}
fun Context.putInt(key: String, value: Int) {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE) ?: return
with(sharedPref.edit()) {
putInt(key, value)
apply()
}
}
fun Context.getIntForPreferences(key: String): Int {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE)
return sharedPref?.getInt(key, -1) ?: -1
}
fun Context.getIntForPreferences(key: String, @NonNull defValue: Int): Int {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE)
return sharedPref?.getInt(key, defValue) ?: defValue
}
fun Context.putLong(key: String, value: Long) {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE) ?: return
with(sharedPref.edit()) {
putLong(key, value)
apply()
}
}
fun Context.getLongForPreferences(key: String): Long {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE)
return sharedPref?.getLong(key, -1) ?: -1
}
fun Context.getLongForPreferences(key: String, @NonNull defValue: Long): Long {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE)
return sharedPref?.getLong(key, defValue) ?: defValue
}
fun Context.putString(key: String, value: String) {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE) ?: return
with(sharedPref.edit()) {
putString(key, value)
apply()
}
}
fun Context.getStringForPreferences(key: String): String {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE)
return sharedPref?.getString(key, "") ?: ""
}
fun Context.getStringForPreferences(key: String, @NonNull defValue: String): String {
val sharedPref =
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE)
return sharedPref?.getString(key, defValue) ?: defValue
}
fun Context.clearForPreferences() {
getSharedPreferences(BuildConfig.APP_ID, Context.MODE_PRIVATE).edit().clear().apply()
}

查看文件

@ -0,0 +1,10 @@
package com.xuqm.base.extensions
import android.content.Context
import android.graphics.Typeface
import android.widget.TextView
fun TextView.setFont(context: Context, font: String) {
this.typeface = Typeface.createFromAsset(context.assets, "fonts/${font}.ttf")
}

查看文件

@ -0,0 +1,25 @@
package com.xuqm.base.extensions
class Fonts {
companion object {
const val Black = "Montserrat-Black"
const val BlackItalic = "Montserrat-BlackItalic"
const val Bold = "Montserrat-Bold"
const val BoldItalic = "Montserrat-BoldItalic"
const val ExtraBold = "Montserrat-ExtraBold"
const val ExtraBoldItalic = "Montserrat-ExtraBoldItalic"
const val ExtraLight = "Montserrat-ExtraLight"
const val ExtraLightItalic = "Montserrat-ExtraLightItalic"
const val Italic = "Montserrat-Italic"
const val Light = "Montserrat-Light"
const val LightItalic = "Montserrat-LightItalic"
const val Medium = "Montserrat-Medium"
const val MediumItalic = "Montserrat-MediumItalic"
const val Regular = "Montserrat-Regular"
const val SemiBold = "Montserrat-SemiBold"
const val SemiBoldItalic = "Montserrat-SemiBoldItalic"
const val Thin = "Montserrat-Thin"
const val ThinItalic = "Montserrat-ThinItalic"
}
}

查看文件

@ -0,0 +1,22 @@
package com.xuqm.base.file;
import java.io.IOException;
import okhttp3.Interceptor;
import okhttp3.Response;
public class DownloadInterceptor implements Interceptor {
private final DownloadListener downloadListener;
public DownloadInterceptor(DownloadListener downloadListener) {
this.downloadListener = downloadListener;
}
@Override
public Response intercept(Chain chain) throws IOException {
Response response = chain.proceed(chain.request());
return response.newBuilder().body(
new DownloadResponseBody(response.body(), downloadListener)).build();
}
}

查看文件

@ -0,0 +1,11 @@
package com.xuqm.base.file;
public interface DownloadListener {
void onStartDownload();
void onProgress(int progress);
void onFinishDownload();
void onFail(String errorInfo);
}

查看文件

@ -0,0 +1,70 @@
package com.xuqm.base.file;
import android.util.Log;
import com.xuqm.base.common.LogHelper;
import java.io.IOException;
import okhttp3.MediaType;
import okhttp3.ResponseBody;
import okio.Buffer;
import okio.BufferedSource;
import okio.ForwardingSource;
import okio.Okio;
import okio.Source;
public class DownloadResponseBody extends ResponseBody {
private final ResponseBody responseBody;
private final DownloadListener downloadListener;
// BufferedSource 是okio库中的输入流这里就当作inputStream来使用
private BufferedSource bufferedSource;
public DownloadResponseBody(ResponseBody responseBody, DownloadListener downloadListener) {
this.responseBody = responseBody;
this.downloadListener = downloadListener;
}
@Override
public MediaType contentType() {
return responseBody.contentType();
}
@Override
public long contentLength() {
return responseBody.contentLength();
}
@Override
public BufferedSource source() {
if (bufferedSource == null) {
bufferedSource = Okio.buffer(source(responseBody.source()));
}
return bufferedSource;
}
private Source source(Source source) {
return new ForwardingSource(source) {
long totalBytesRead = 0L;
@Override
public long read(Buffer sink, long byteCount) throws IOException {
long bytesRead = super.read(sink, byteCount);
// read() returns the number of bytes read, or -1 if this source is exhausted.
totalBytesRead += bytesRead != -1 ? bytesRead : 0;
Log.e("download", "read: "+ (int) (totalBytesRead * 100 / responseBody.contentLength()));
if (null != downloadListener) {
if (bytesRead != -1) {
downloadListener.onProgress((int) (totalBytesRead * 100 / responseBody.contentLength()));
}
}
return bytesRead;
}
};
}
}

查看文件

@ -0,0 +1,80 @@
package com.xuqm.base.file;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import io.reactivex.observers.DefaultObserver;
import okhttp3.ResponseBody;
public abstract class FileDownLoadObserver<T> extends DefaultObserver<T> {
@Override
public void onNext(T t) {
onDownLoadSuccess(t);
}
@Override
public void onError(Throwable e) {
onDownLoadFail(e);
}
//可以重写具体可由子类实现
@Override
public void onComplete() {
}
//下载成功的回调
public abstract void onDownLoadSuccess(T t);
//下载失败回调
public abstract void onDownLoadFail(Throwable throwable);
//下载进度监听
public abstract void onProgress(int progress,long total);
/**
* 将文件写入本地
* @param responseBody 请求结果全体
* @param destFileDir 目标文件夹
* @param destFileName 目标文件名
* @return 写入完成的文件
* @throws IOException IO异常
*/
public File saveFile(ResponseBody responseBody, String destFileDir, String destFileName) throws IOException {
InputStream is = null;
byte[] buf = new byte[2048];
int len = 0;
FileOutputStream fos = null;
try {
is = responseBody.byteStream();
final long total = responseBody.contentLength();
long sum = 0;
File dir = new File(destFileDir);
if (!dir.exists()) {
dir.mkdirs();
}
File file = new File(dir, destFileName);
fos = new FileOutputStream(file);
while ((len = is.read(buf)) != -1) {
sum += len;
fos.write(buf, 0, len);
final long finalSum = sum;
//这里就是对进度的监听回调
onProgress((int) (finalSum * 100 / total),total);
}
fos.flush();
return file;
} finally {
try {
if (is != null) is.close();
} catch (IOException e) {
e.printStackTrace();
}
try {
if (fos != null) fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}

查看文件

@ -0,0 +1,178 @@
package com.xuqm.base.file;
import com.xuqm.base.common.LogHelper;
import com.xuqm.base.di.component.AppComponent;
import com.xuqm.base.di.manager.HttpManager;
import com.xuqm.base.repository.CommonService;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Map;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.annotations.NonNull;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
public class FileDownloadAndUploadManager {
private static FileDownloadAndUploadManager instance;
public static FileDownloadAndUploadManager getInstance() {
if (null == instance) {
synchronized (FileDownloadAndUploadManager.class) {
if (null == instance) {
instance = new FileDownloadAndUploadManager();
}
}
}
return instance;
}
private final AppComponent appComponent;
protected CompositeDisposable compositeDisposable;
private FileDownloadAndUploadManager() {
appComponent = HttpManager.getAppComponent("https://xuqinmin.com/");
compositeDisposable = new CompositeDisposable();
}
public void download(@NonNull String url, final String filePath) {
LogHelper.e(url);
thisListener.onStartDownload();
Disposable d = HttpManager.getApi(appComponent, CommonService.class)
.download(url)
.subscribeOn(Schedulers.io())
.unsubscribeOn(Schedulers.io())
.map(ResponseBody::byteStream)
.observeOn(Schedulers.computation())
.doOnNext(inputStream -> writeFile(inputStream, filePath))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(body -> {
thisListener.onFinishDownload();
}, err -> {
LogHelper.e("=============>", err);
thisListener.onFail(err.getMessage());
});
compositeDisposable.add(d);
}
public void cancel() {
compositeDisposable.dispose();
compositeDisposable.clear();
}
public void download(@NonNull String url, final String destDir, final String fileName, final FileDownLoadObserver<File> fileDownLoadObserver) {
HttpManager.getApi(appComponent, CommonService.class)
.download(url)
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map(responseBody -> fileDownLoadObserver.saveFile(responseBody, destDir, fileName))
.observeOn(AndroidSchedulers.mainThread())
.subscribe(fileDownLoadObserver);
}
// public void upload(@NonNull String url, @NonNull String filePath, final Map<String, String> formDataPart) {
// upload(url, new File(filePath), formDataPart);
// }
public void upload(@NonNull String url, @NonNull File file, final String contentType) {
MediaType mediaType = MediaType.parse(contentType);
RequestBody body =
RequestBody.create(mediaType, file);
Disposable d = HttpManager.getApi(appComponent, CommonService.class)
.upload(url, body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(b -> {
LogHelper.e("=============>上传完成:::" + b);
if (null != listener)
listener.onFinishDownload();
}, err -> {
LogHelper.e("=============>", err);
if (null != listener)
listener.onFail(err.getMessage());
});
compositeDisposable.add(d);
}
/**
* 将输入流写入文件
*
* @param inputString 文件流
* @param filePath 文件地址
*/
private void writeFile(InputStream inputString, String filePath) {
File file = new File(filePath);
if (file.exists()) {
file.delete();
}
FileOutputStream fos = null;
try {
fos = new FileOutputStream(file);
byte[] b = new byte[1024];
int len;
while ((len = inputString.read(b)) != -1) {
fos.write(b, 0, len);
}
inputString.close();
fos.close();
} catch (FileNotFoundException e) {
thisListener.onFail("FileNotFoundException");
} catch (IOException e) {
thisListener.onFail("IOException");
}
}
private DownloadListener listener;
private final DownloadListener thisListener = new DownloadListener() {
@Override
public void onStartDownload() {
if (null != listener)
listener.onStartDownload();
LogHelper.e("=========开始下载");
}
@Override
public void onProgress(int progress) {
if (null != listener)
listener.onProgress(progress);
LogHelper.e("=========下载进度" + progress);
}
@Override
public void onFinishDownload() {
if (null != listener)
listener.onFinishDownload();
LogHelper.e("=========下载完成");
}
@Override
public void onFail(String errorInfo) {
if (null != listener)
listener.onFail(errorInfo);
LogHelper.e("=========下载失败" + errorInfo);
}
};
public void setListener(DownloadListener listener) {
this.listener = listener;
}
}

查看文件

@ -0,0 +1,48 @@
package com.xuqm.base.model;
import androidx.annotation.NonNull;
/**
* 通用的HttpResult封装app应该根据接口情况定制化配置
*
* @param <T>
*/
public class HttpResult<T> {
private String status;
private String maxNumber;
private T data;
public String getStatus() {
return status;
}
public void setStatus(String status) {
this.status = status;
}
public String getMaxNumber() {
return maxNumber;
}
public void setMaxNumber(String maxNumber) {
this.maxNumber = maxNumber;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
@Override
@NonNull
public String toString() {
return "HttpResult{" +
"status='" + status + '\'' +
", maxNumber='" + maxNumber + '\'' +
", data=" + data +
'}';
}
}

查看文件

@ -0,0 +1,38 @@
package com.xuqm.base.model;
public class KeyValueData<K, V> {
private K key;
private V value;
public KeyValueData() {
}
public KeyValueData(K key, V value) {
this.key = key;
this.value = value;
}
public K getKey() {
return key;
}
public void setKey(K key) {
this.key = key;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
@Override
public String toString() {
return "KeyValueData{" +
"key=" + key +
", value=" + value +
'}';
}
}

查看文件

@ -0,0 +1,21 @@
package com.xuqm.base.repository;
import io.reactivex.Observable;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.http.Body;
import retrofit2.http.GET;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Streaming;
import retrofit2.http.Url;
public interface CommonService {
@Streaming
@GET
Observable<ResponseBody> download(@Url String url);
@PUT
@Streaming
Observable<ResponseBody> upload(@Url String url, @Body RequestBody body);
}

查看文件

@ -0,0 +1,382 @@
package com.xuqm.base.ui;
import android.app.Activity;
import android.content.Context;
import android.graphics.Typeface;
import android.os.Bundle;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import android.widget.EditText;
import androidx.annotation.ColorRes;
import androidx.annotation.DrawableRes;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import com.gyf.immersionbar.BarHide;
import com.gyf.immersionbar.ImmersionBar;
import com.xuqm.base.R;
import com.xuqm.base.common.AppManager;
import com.xuqm.base.databinding.ActivityBaseBinding;
import com.xuqm.base.ui.callback.UiCallback;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
public abstract class BaseActivity<V extends ViewDataBinding> extends AppCompatActivity implements UiCallback {
protected String TAG = this.getClass().getSimpleName();
protected Activity mContext;
private V binding;
private ActivityBaseBinding baseBinding;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
AppManager.getInstance().pushActivity(this);
getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN);
mContext = this;
editText.clear();
views.clear();
if (getLayoutId() == 0) {//没有layout的时候act自己写布局
ImmersionBar.with(this)
.init();
setContentView();
} else if (showStatus() != -1) {//没有toolbar但是有状态栏的情况
bindUi(getLayoutId());
ImmersionBar.with(this)
.titleBar(binding.getRoot()) //指定标题栏view
.statusBarColor(showStatus())
.autoStatusBarDarkModeEnable(true)
.init();
} else if (transparentStatusBar()) {//透明状态栏但是显示状态栏的内容
bindUi(getLayoutId());
ImmersionBar.with(this).transparentBar()
.titleBar(binding.getRoot().findViewWithTag("top_status"))
.statusBarDarkFont(true)
// .hideBar(BarHide.FLAG_HIDE_NAVIGATION_BAR)
.init();
} else if (fullscreen()) {//全屏什么都不显示
bindUi(getLayoutId());
ImmersionBar.with(this)
.fullScreen(true)
.statusBarDarkFont(true)
// .hideBar(BarHide.FLAG_HIDE_NAVIGATION_BAR)
.init();
} else {//使用base提供的toolbar
baseBinding = DataBindingUtil.setContentView(mContext, R.layout.activity_base);
ImmersionBar.with(this)
// .hideBar(BarHide.FLAG_HIDE_NAVIGATION_BAR)
.statusBarColor(R.color.white)
.statusBarDarkFont(true)
.titleBar(baseBinding.baseToolbar) //指定标题栏view
.init();
binding = DataBindingUtil.inflate(getLayoutInflater(), getLayoutId(), baseBinding.activityRootView, true);
getWindow().getDecorView().setSystemUiVisibility(View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR);
}
if (null != baseBinding) {
baseBinding.baseToolbar.backBtnPressed(this::backBtnPressed);
baseBinding.baseToolbar.getTitleView().setTypeface(Typeface.createFromAsset(this.getAssets(), "fonts/Montserrat-Bold.ttf"));
}
initView(savedInstanceState);
initData();
lateInitView();
}
@NonNull
public V getBinding() {
return binding;
}
public ActivityBaseBinding getBaseBinding() {
return baseBinding;
}
/**
* 导航栏展示的内容
*
* @param titleId 标题
*/
public void setTitleText(@StringRes int titleId) {
setTitleText(getText(titleId));
}
/**
* 导航栏展示的内容
*
* @param title 标题
*/
public void setTitleText(CharSequence title) {
if (null == baseBinding) {
return;
}
baseBinding.baseToolbar.setTitle(title);
}
/**
* 导航栏右上角按钮
*
* @param title 标题
*/
public void setConfirmText(CharSequence title, View.OnClickListener listener) {
if (null == baseBinding) {
return;
}
baseBinding.baseToolbar.getConfirmBtn().setVisibility(View.VISIBLE);
baseBinding.baseToolbar.getConfirmBtn().setText(title);
baseBinding.baseToolbar.getConfirmBtn().setOnClickListener(listener);
}
/**
* 设置标题颜色
*
* @param color 标题颜色
*/
public void setTextColor(int color) {
if (null == baseBinding) {
return;
}
baseBinding.baseToolbar.setTextColor(color);
}
/**
* 设置返回图标颜色
*
* @param iconTintColor 返回图标颜色
*/
public void setIconTintColor(int iconTintColor) {
if (null == baseBinding) {
return;
}
baseBinding.baseToolbar.setIconTintColor(iconTintColor);
}
public void setIconDraw(@DrawableRes int resId) {
if (null == baseBinding) {
return;
}
baseBinding.baseToolbar.getBackBtn().setImageResource(resId);
}
/**
* 是否展示返回按钮
*
* @param showBack 是否展示返回按钮
*/
public void showBack(boolean showBack) {
if (null == baseBinding) {
return;
}
baseBinding.baseToolbar.setShowBack(showBack);
}
/**
* 是否显示导航栏下面的线
*
* @param showLine 是否显示导航栏下面的线
*/
public void showLine(boolean showLine) {
if (null == baseBinding) {
return;
}
baseBinding.baseToolbar.setShowLine(showLine);
}
public void backBtnPressed() {
finish();
}
protected void bindUi(@LayoutRes int layoutResID) {
binding = DataBindingUtil.setContentView(mContext, layoutResID);
}
/**
* 如果不提供layoutId{@link #getLayoutId()} 则可以重写这个方法自己布局
*/
@Override
public void setContentView() {
}
@Override
public void initView(Bundle savedInstanceState) {
}
@Override
public void initData() {
}
/**
* 这个init方法在initData之后执行有一些view相关的设置需要数据信息
*/
public void lateInitView() {
}
/**
* 是否需要展示statusBar默认为false使用默认布局展示toolbar
* false的话可以自定义toolbar
*
* @return statusBar 的颜色
*/
@Override
@ColorRes
public int showStatus() {
return -1;
}
/**
* 如果返回true则状态栏变成透明状态但是状态栏的文字还显示
* 使用透明状态栏 布局必须指定一个 android:tag="top_status"
* 最好是第一个子元素指定可以自动设置padding
*
* @return 是否需要设置状态栏为透明状态
*/
@Override
public boolean transparentStatusBar() {
return false;
}
/**
* 是不是黑色主题
*
* @return 全屏展示
*/
@Override
public boolean fullscreen() {
return false;
}
@Override
protected void onDestroy() {
super.onDestroy();
AppManager.getInstance().popActivity(this);
}
/**
* 点击空白处软键盘是否消失
*/
protected boolean canTouch = true;
/**
* 指定控件点击软键盘不消失需要addView相关控件
*/
protected boolean viewCanTouch = true;
/**
* editText注册-不在这里注册一下软件盘消失时光标还会闪烁
*/
private final List<EditText> editText = new ArrayList<>();
/**
* 指定控件点击软键盘不消失
*/
private final List<View> views = new ArrayList<>();
/**
* EditText与软键盘的控制
* 如果点击空白处(非EditText)关闭软键盘隐藏光标
*
* @param editText
*/
public void addEditText(EditText... editText) {
this.editText.addAll(Arrays.asList(editText));
}
/**
* 部分页面存在点击按钮软键盘不取消的需求把对应view添加到这里就好了
*
* @param view
*/
public void addView(View view) {
this.views.add(view);
}
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
for (View v : views) {
int[] leftTop = {0, 0};
//获取输入框当前的location位置
v.getLocationInWindow(leftTop);
int left = leftTop[0];
int top = leftTop[1];
int bottom = top + v.getHeight();
int right = left + v.getWidth();
if ((ev.getX() > left && ev.getX() < right
&& ev.getY() > top && ev.getY() < bottom
&& v.getVisibility() == View.VISIBLE)) {
viewCanTouch = false;
}
}
if (!canTouch || !viewCanTouch) {
viewCanTouch = true;
return super.dispatchTouchEvent(ev);
}
if (ev.getAction() == MotionEvent.ACTION_DOWN) {
View v = getCurrentFocus();
if (isShouldHideInput(v, ev)) {
hideKeyboard(v);
}
return super.dispatchTouchEvent(ev);
}
// 必不可少否则所有的组件都不会有TouchEvent了
if (getWindow().superDispatchTouchEvent(ev)) {
return true;
}
return onTouchEvent(ev);
}
/**
* 强制隐藏键盘
*
* @param v
*/
public void hideKeyboard(View v) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
if (null == v || null == v.getWindowToken()) {
} else {
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
this.onHideKeyboard();
for (EditText et : editText) {
et.clearFocus();
}
}
}
public void onHideKeyboard() {
}
public boolean isShouldHideInput(View v, MotionEvent event) {
if (v != null && (v instanceof EditText)) {
int[] leftTop = {0, 0};
//获取输入框当前的location位置
v.getLocationInWindow(leftTop);
int left = leftTop[0];
int top = leftTop[1];
int bottom = top + v.getHeight();
int right = left + v.getWidth();
if ((event.getX() > left && event.getX() < right
&& event.getY() > top && event.getY() < bottom)) {
// 点击的是输入框区域保留点击EditText的事件
return false;
} else {
return true;
}
}
return false;
}
}

查看文件

@ -0,0 +1,61 @@
package com.xuqm.base.ui;
import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ViewDataBinding;
import androidx.fragment.app.Fragment;
import com.xuqm.base.common.ToolsHelper;
public abstract class BaseFragment<V extends ViewDataBinding> extends Fragment {
private V binding;
protected Context mContext;
@Nullable
@Override
public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
binding = DataBindingUtil.inflate(getLayoutInflater(), getLayoutId(), container, false);
mContext = getActivity();
return binding.getRoot();
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
initView();
initData();
}
@NonNull
protected V getBinding() {
return binding;
}
@LayoutRes
protected abstract int getLayoutId();
protected void initView() {
}
protected void initData() {
}
/**
* 收起键盘
*/
protected void hideSoftInput() {
InputMethodManager imm = (InputMethodManager) getActivity().getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(getActivity().getWindow().getDecorView().getWindowToken(), 0);
}
}

查看文件

@ -0,0 +1,161 @@
package com.xuqm.base.ui;
import android.os.Bundle;
import android.view.View;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.xuqm.base.R;
import com.xuqm.base.adapter.BaseItem;
import com.xuqm.base.adapter.BasePagedAdapter;
import com.xuqm.base.common.RefreshResult;
import com.xuqm.base.common.ToolsHelper;
import com.xuqm.base.databinding.ActivityBaseListBinding;
import com.xuqm.base.view.enu.Status;
import com.xuqm.base.viewmodel.BaseListViewModel;
import com.xuqm.base.viewmodel.callback.AdapterObserverCallback;
import com.xuqm.base.viewmodel.callback.DataObserverCallback;
import java.lang.reflect.ParameterizedType;
/**
* 列表页面的activity继承这个只需要指定一个adapter就可以展示数据了
* 例子参考
* MainActivity extends BaseListActivity<User, MainViewModel>
*
* User需要继承{@link BaseItem}
* MainViewModel 需要继承{@link BaseListViewModel}
*
* @param <T>
* @param <VM>
*/
public abstract class BaseListActivity<T extends BaseItem, VM extends BaseListViewModel<T>>
extends BaseActivity<ActivityBaseListBinding> {
private ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
private Class<VM> cal = (Class<VM>) parameterizedType.getActualTypeArguments()[1];
private VM viewModel;
private BasePagedAdapter<T> adapter;
@Override
public int getLayoutId() {
return R.layout.activity_base_list;
}
/**
* 获取到viewModel可以做其它事情比如item的增删改查等
*
* @return viewModel
*/
public VM getViewModel() {
return viewModel;
}
@Override
public void initView(Bundle savedInstanceState) {
getBaseBinding().activityRootView.setBackgroundResource(R.color.bg_window);
viewModel = new ViewModelProvider(this).get(cal);
adapter = adapter();
adapter.setItemClickListener(this::itemClicked);
adapter.setItemLongClickListener(this::itemLongClicked);
getBinding().baseRecyclerView.setAdapter(adapter);
getBinding().baseRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
getBinding().baseRefreshLayout.setOnRefreshListener(() -> viewModel.invalidate());
}
@Override
public void initData() {
/*
数据更新更新事件的观察者
*/
viewModel.observeDataObserver(this, new DataObserverCallback<T>() {
@Override
public void data(PagedList<T> data) {
adapter.submitList(data);//数据加载
}
@Override
public void refreshResult(RefreshResult refreshResult) {
refreshFinished(refreshResult);//刷新状态处理
}
@Override
public void loadMoreResult(RefreshResult refreshResult) {
loadMoreFinished(refreshResult);//加载更多的处理
}
});
//数据更新处理观察者
viewModel.observeAdapterObserver(this, new AdapterObserverCallback() {
@Override
public void notifyItem(int position, Object payload) {
adapter.notifyItemChanged(position, payload);
}
@Override
public void removeItem(int position) {
adapter.notifyItemRemoved(position);
}
});
}
/**
* 如果需要对item的点击事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
*/
public void itemClicked(View view, T item, int position) {
}
/**
* 如果需要对item的长按事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
* @return true
*/
public boolean itemLongClicked(View view, T item, int position) {
return false;
}
private void refreshFinished(RefreshResult result) {
getBinding().baseRefreshLayout.setRefreshing(false);
if (result == RefreshResult.SUCCEED)
getBinding().baseEmptyView.setStatus(Status.DISMISS);
else if (result == RefreshResult.FAILED)
getBinding().baseEmptyView.setStatus(Status.LOAD_FAILED);
else if (result == RefreshResult.NO_DATA)
getBinding().baseEmptyView.setStatus(Status.NO_DATA);
else if (result == RefreshResult.NO_MORE) {
getBinding().baseEmptyView.setStatus(Status.DISMISS);
ToolsHelper.snack(getBinding().baseEmptyView, "All loads completed");
}
}
private void loadMoreFinished(RefreshResult result) {
// if (result == RefreshResult.SUCCEED) {
// } else if (result == RefreshResult.FAILED) {
// } else
if (result == RefreshResult.NO_MORE) {
ToolsHelper.snack(getBinding().baseEmptyView, "All loads completed");
}
}
/**
* 需要指定一个adapteritem只有一种类型的使用{@link com.xuqm.base.adapter.CommonPagedAdapter}
* 需要指定一个adapteritem有多种类型的使用{@link com.xuqm.base.adapter.BasePagedAdapter}
*
* @return 自定义的adapter
*/
public abstract BasePagedAdapter<T> adapter();
}

查看文件

@ -0,0 +1,174 @@
package com.xuqm.base.ui;
import android.os.Bundle;
import android.view.View;
import androidx.annotation.LayoutRes;
import androidx.annotation.Nullable;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import com.google.android.material.appbar.AppBarLayout;
import com.xuqm.base.R;
import com.xuqm.base.adapter.BaseItem;
import com.xuqm.base.adapter.BasePagedAdapter;
import com.xuqm.base.common.RefreshResult;
import com.xuqm.base.common.ToolsHelper;
import com.xuqm.base.databinding.ActivityBaseListAppBarBinding;
import com.xuqm.base.view.enu.Status;
import com.xuqm.base.viewmodel.BaseListViewModel;
import com.xuqm.base.viewmodel.callback.AdapterObserverCallback;
import com.xuqm.base.viewmodel.callback.DataObserverCallback;
import java.lang.reflect.ParameterizedType;
public abstract class BaseListAppBarFragment<T extends BaseItem, VM extends BaseListViewModel<T>> extends BaseFragment<ActivityBaseListAppBarBinding> {
private ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
private Class<VM> cal = (Class<VM>) parameterizedType.getActualTypeArguments()[1];
private VM viewModel;
private BasePagedAdapter<T> adapter;
@Override
public int getLayoutId() {
return R.layout.activity_base_list_app_bar;
}
@LayoutRes
protected int getAppBarView() {
return 0;
}
@Override
public void onActivityCreated(@Nullable Bundle savedInstanceState) {
if (0 != getAppBarView()) {
getLayoutInflater().inflate(getAppBarView(), getBinding().appBarLayout,true);
}
super.onActivityCreated(savedInstanceState);
}
/**
* 获取到viewModel可以做其它事情比如item的增删改查等
*
* @return viewModel
*/
public VM getViewModel() {
return viewModel;
}
@Override
protected void initView() {
viewModel = new ViewModelProvider(this).get(cal);
adapter = adapter();
adapter.setItemClickListener(this::itemClicked);
adapter.setItemLongClickListener(this::itemLongClicked);
getBinding().baseRecyclerView.setAdapter(adapter);
getBinding().baseRecyclerView.setLayoutManager(new LinearLayoutManager(mContext));
getBinding().baseRefreshLayout.setOnRefreshListener(() -> viewModel.invalidate());
getBinding().appBarLayout.addOnOffsetChangedListener((appBarLayout, verticalOffset) -> {
if (verticalOffset >= 0) {
getBinding().baseRefreshLayout.setEnabled(true);
} else {
getBinding().baseRefreshLayout.setEnabled(false);
}
});
}
@Override
protected void initData() {
/*
数据更新更新事件的观察者
*/
viewModel.observeDataObserver(this, new DataObserverCallback<T>() {
@Override
public void data(PagedList<T> data) {
adapter.submitList(data);//数据加载
}
@Override
public void refreshResult(RefreshResult refreshResult) {
refreshFinished(refreshResult);//刷新状态处理
}
@Override
public void loadMoreResult(RefreshResult refreshResult) {
loadMoreFinished(refreshResult);//加载更多的处理
}
});
//数据更新处理观察者
viewModel.observeAdapterObserver(this, new AdapterObserverCallback() {
@Override
public void notifyItem(int position, Object payload) {
adapter.notifyItemChanged(position, payload);
}
@Override
public void removeItem(int position) {
adapter.notifyItemRemoved(position);
}
});
}
/**
* 如果需要对item的点击事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
*/
public void itemClicked(View view, T item, int position) {
}
/**
* 如果需要对item的长按事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
* @return true
*/
public boolean itemLongClicked(View view, T item, int position) {
return false;
}
public void refreshFinished(RefreshResult result) {
getBinding().baseRefreshLayout.setRefreshing(false);
if (result == RefreshResult.SUCCEED)
getBinding().baseEmptyView.setStatus(Status.DISMISS);
else if (result == RefreshResult.FAILED)
getBinding().baseEmptyView.setStatus(Status.LOAD_FAILED);
else if (result == RefreshResult.NO_DATA)
getBinding().baseEmptyView.setStatus(Status.NO_DATA);
else if (result == RefreshResult.NO_MORE) {
getBinding().baseEmptyView.setStatus(Status.DISMISS);
ToolsHelper.snack(getBinding().baseEmptyView, "All loads completed");
}
}
private void loadMoreFinished(RefreshResult result) {
// if (result == RefreshResult.SUCCEED) {
// } else if (result == RefreshResult.FAILED) {
// } else
if (result == RefreshResult.NO_MORE) {
ToolsHelper.snack(getBinding().baseEmptyView, "All loads completed");
}
}
/**
* 需要指定一个adapteritem只有一种类型的使用{@link com.xuqm.base.adapter.CommonPagedAdapter}
* 需要指定一个adapteritem有多种类型的使用{@link BasePagedAdapter}
*
* @return 自定义的adapter
*/
public abstract BasePagedAdapter<T> adapter();
}

查看文件

@ -0,0 +1,174 @@
package com.xuqm.base.ui;
import android.os.Bundle;
import android.view.View;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.xuqm.base.R;
import com.xuqm.base.adapter.BaseItem;
import com.xuqm.base.adapter.BasePagedAdapter;
import com.xuqm.base.common.RefreshResult;
import com.xuqm.base.common.ToolsHelper;
import com.xuqm.base.view.EmptyView;
import com.xuqm.base.view.enu.Status;
import com.xuqm.base.viewmodel.BaseListViewModel;
import com.xuqm.base.viewmodel.callback.AdapterObserverCallback;
import com.xuqm.base.viewmodel.callback.DataObserverCallback;
import java.lang.reflect.ParameterizedType;
/**
* 列表页面的activity继承这个只需要指定一个adapter就可以展示数据了
* 例子参考
* MainActivity extends BaseListActivity<User, MainViewModel>
* <p>
* User需要继承{@link BaseItem}
* MainViewModel 需要继承{@link BaseListViewModel}
*
* @param <T>
* @param <VM>
*/
public abstract class BaseListFormLayoutActivity<T extends BaseItem, VM extends BaseListViewModel<T>, V extends ViewDataBinding>
extends BaseActivity<V> {
private ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
private Class<VM> cal = (Class<VM>) parameterizedType.getActualTypeArguments()[1];
private VM viewModel;
private BasePagedAdapter<T> adapter;
@Override
public int getLayoutId() {
return R.layout.activity_base_list;
}
/**
* 获取到viewModel可以做其它事情比如item的增删改查等
*
* @return viewModel
*/
public VM getViewModel() {
return viewModel;
}
public BasePagedAdapter<T> getAdapter() {
return adapter;
}
public RecyclerView recyclerView;
private SwipeRefreshLayout swipeRefreshLayout;
private EmptyView baseEmptyView;
@Override
public void initView(Bundle savedInstanceState) {
viewModel = new ViewModelProvider(this).get(cal);
adapter = adapter();
adapter.setItemClickListener(this::itemClicked);
adapter.setItemLongClickListener(this::itemLongClicked);
recyclerView = findViewById(R.id.baseRecyclerView);
swipeRefreshLayout = findViewById(R.id.baseRefreshLayout);
baseEmptyView = findViewById(R.id.baseEmptyView);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
swipeRefreshLayout.setOnRefreshListener(() -> viewModel.invalidate());
}
@Override
public void initData() {
/*
数据更新更新事件的观察者
*/
viewModel.observeDataObserver(this, new DataObserverCallback<T>() {
@Override
public void data(PagedList<T> data) {
adapter.submitList(data);//数据加载
}
@Override
public void refreshResult(RefreshResult refreshResult) {
refreshFinished(refreshResult);//刷新状态处理
}
@Override
public void loadMoreResult(RefreshResult refreshResult) {
loadMoreFinished(refreshResult);//加载更多的处理
}
});
//数据更新处理观察者
viewModel.observeAdapterObserver(this, new AdapterObserverCallback() {
@Override
public void notifyItem(int position, Object payload) {
adapter.notifyItemChanged(position, payload);
}
@Override
public void removeItem(int position) {
// adapter.notifyItemRemoved(position);
adapter.notifyItemRangeRemoved(position,adapter.getItemCount()-1);
}
});
}
/**
* 如果需要对item的点击事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
*/
public void itemClicked(View view, T item, int position) {
}
/**
* 如果需要对item的长按事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
* @return true
*/
public boolean itemLongClicked(View view, T item, int position) {
return false;
}
public void refreshFinished(RefreshResult result) {
swipeRefreshLayout.setRefreshing(false);
if (result == RefreshResult.SUCCEED)
baseEmptyView.setStatus(Status.DISMISS);
else if (result == RefreshResult.FAILED)
baseEmptyView.setStatus(Status.LOAD_FAILED);
else if (result == RefreshResult.NO_DATA)
baseEmptyView.setStatus(Status.NO_DATA);
else if (result == RefreshResult.NO_MORE) {
baseEmptyView.setStatus(Status.DISMISS);
ToolsHelper.snack(baseEmptyView, "All loads completed");
}
}
public void loadMoreFinished(RefreshResult result) {
// if (result == RefreshResult.SUCCEED) {
// } else if (result == RefreshResult.FAILED) {
// } else
if (result == RefreshResult.NO_MORE) {
ToolsHelper.snack(baseEmptyView, "All loads completed");
}
}
/**
* 需要指定一个adapteritem只有一种类型的使用{@link com.xuqm.base.adapter.CommonPagedAdapter}
* 需要指定一个adapteritem有多种类型的使用{@link BasePagedAdapter}
*
* @return 自定义的adapter
*/
public abstract BasePagedAdapter<T> adapter();
}

查看文件

@ -0,0 +1,163 @@
package com.xuqm.base.ui;
import android.view.View;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.xuqm.base.R;
import com.xuqm.base.adapter.BaseItem;
import com.xuqm.base.adapter.BasePagedAdapter;
import com.xuqm.base.common.RefreshResult;
import com.xuqm.base.common.ToolsHelper;
import com.xuqm.base.databinding.ActivityBaseListBinding;
import com.xuqm.base.view.enu.Status;
import com.xuqm.base.viewmodel.BaseListViewModel;
import com.xuqm.base.viewmodel.callback.AdapterObserverCallback;
import com.xuqm.base.viewmodel.callback.DataObserverCallback;
import java.lang.reflect.ParameterizedType;
public abstract class BaseListFragment<T extends BaseItem, VM extends BaseListViewModel<T>> extends BaseFragment<ActivityBaseListBinding> {
private ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
private Class<VM> cal = (Class<VM>) parameterizedType.getActualTypeArguments()[1];
protected VM viewModel;
private BasePagedAdapter<T> adapter;
@Override
public int getLayoutId() {
return R.layout.activity_base_list;
}
/**
* 获取到viewModel可以做其它事情比如item的增删改查等
*
* @return viewModel
*/
public VM getViewModel() {
return viewModel;
}
@Override
protected void initView() {
if (getFactory() == null)
viewModel = new ViewModelProvider(this).get(cal);
else
viewModel = new ViewModelProvider(this, getFactory()).get(cal);
adapter = adapter();
adapter.setItemClickListener(this::itemClicked);
adapter.setItemLongClickListener(this::itemLongClicked);
getBinding().baseRecyclerView.setAdapter(adapter);
getBinding().baseRecyclerView.setLayoutManager(layoutManager());
getBinding().baseRefreshLayout.setOnRefreshListener(() -> viewModel.invalidate());
}
protected ViewModelProvider.Factory getFactory() {
return null;
}
@Override
protected void initData() {
/*
数据更新更新事件的观察者
*/
viewModel.observeDataObserver(this, new DataObserverCallback<T>() {
@Override
public void data(PagedList<T> data) {
adapter.submitList(data);//数据加载
}
@Override
public void refreshResult(RefreshResult refreshResult) {
refreshFinished(refreshResult);//刷新状态处理
}
@Override
public void loadMoreResult(RefreshResult refreshResult) {
loadMoreFinished(refreshResult);//加载更多的处理
}
});
//数据更新处理观察者
viewModel.observeAdapterObserver(this, new AdapterObserverCallback() {
@Override
public void notifyItem(int position, Object payload) {
adapter.notifyItemChanged(position, payload);
}
@Override
public void removeItem(int position) {
adapter.notifyItemRemoved(position);
}
});
}
/**
* 如果需要对item的点击事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
*/
public void itemClicked(View view, T item, int position) {
}
/**
* 如果需要对item的长按事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
* @return true
*/
public boolean itemLongClicked(View view, T item, int position) {
return false;
}
public void refreshFinished(RefreshResult result) {
getBinding().baseRefreshLayout.setRefreshing(false);
if (result == RefreshResult.SUCCEED)
getBinding().baseEmptyView.setStatus(Status.DISMISS);
else if (result == RefreshResult.FAILED)
getBinding().baseEmptyView.setStatus(Status.LOAD_FAILED);
else if (result == RefreshResult.NO_DATA)
getBinding().baseEmptyView.setStatus(Status.NO_DATA);
else if (result == RefreshResult.NO_MORE) {
getBinding().baseEmptyView.setStatus(Status.DISMISS);
// ToolsHelper.snack(getBinding().baseEmptyView, "All loads completed");
}
}
private void loadMoreFinished(RefreshResult result) {
// if (result == RefreshResult.SUCCEED) {
// } else if (result == RefreshResult.FAILED) {
// } else
if (result == RefreshResult.NO_MORE) {
// ToolsHelper.snack(getBinding().baseEmptyView, "All loads completed");
}
}
/**
* 需要指定一个adapteritem只有一种类型的使用{@link com.xuqm.base.adapter.CommonPagedAdapter}
* 需要指定一个adapteritem有多种类型的使用{@link com.xuqm.base.adapter.BasePagedAdapter}
*
* @return 自定义的adapter
*/
public abstract BasePagedAdapter<T> adapter();
public RecyclerView.LayoutManager layoutManager() {
return new LinearLayoutManager(mContext);
}
;
}

查看文件

@ -0,0 +1,185 @@
package com.xuqm.base.ui;
import android.view.View;
import androidx.databinding.ViewDataBinding;
import androidx.lifecycle.ViewModelProvider;
import androidx.paging.PagedList;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
import com.xuqm.base.R;
import com.xuqm.base.adapter.BaseItem;
import com.xuqm.base.adapter.BasePagedAdapter;
import com.xuqm.base.common.RefreshResult;
import com.xuqm.base.common.ToolsHelper;
import com.xuqm.base.view.EmptyView;
import com.xuqm.base.view.enu.Status;
import com.xuqm.base.viewmodel.BaseListViewModel;
import com.xuqm.base.viewmodel.callback.AdapterObserverCallback;
import com.xuqm.base.viewmodel.callback.DataObserverCallback;
import java.lang.reflect.ParameterizedType;
public abstract class BaseListFromLayoutFragment<T extends BaseItem, VM extends BaseListViewModel<T>, V extends ViewDataBinding> extends BaseFragment<V> {
private ParameterizedType parameterizedType = (ParameterizedType) getClass().getGenericSuperclass();
private Class<VM> cal = (Class<VM>) parameterizedType.getActualTypeArguments()[1];
protected VM viewModel;
private BasePagedAdapter<T> adapter;
@Override
public int getLayoutId() {
return R.layout.activity_base_list;
}
/**
* 获取到viewModel可以做其它事情比如item的增删改查等
*
* @return viewModel
*/
public VM getViewModel() {
return viewModel;
}
public RecyclerView recyclerView;
private SwipeRefreshLayout swipeRefreshLayout;
private EmptyView baseEmptyView;
@Override
protected void initView() {
if (getFactory() == null)
viewModel = new ViewModelProvider(this).get(cal);
else
viewModel = new ViewModelProvider(this, getFactory()).get(cal);
adapter = adapter();
recyclerView = recyclerView();
swipeRefreshLayout = swipeRefreshLayout();
baseEmptyView = baseEmptyView();
adapter.setItemClickListener(this::itemClicked);
adapter.setItemLongClickListener(this::itemLongClicked);
recyclerView.setLayoutManager(layoutManager());
recyclerView.setAdapter(adapter);
swipeRefreshLayout.setOnRefreshListener(() -> viewModel.invalidate());
}
protected ViewModelProvider.Factory getFactory() {
return null;
}
@Override
protected void initData() {
/*
数据更新更新事件的观察者
*/
viewModel.observeDataObserver(this, new DataObserverCallback<T>() {
@Override
public void data(PagedList<T> data) {
adapter.submitList(data);//数据加载
}
@Override
public void refreshResult(RefreshResult refreshResult) {
refreshFinished(refreshResult);//刷新状态处理
}
@Override
public void loadMoreResult(RefreshResult refreshResult) {
loadMoreFinished(refreshResult);//加载更多的处理
}
});
//数据更新处理观察者
viewModel.observeAdapterObserver(this, new AdapterObserverCallback() {
@Override
public void notifyItem(int position, Object payload) {
adapter.notifyItemChanged(position, payload);
}
@Override
public void removeItem(int position) {
adapter.notifyItemRemoved(position);
}
});
}
/**
* 如果需要对item的点击事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
*/
public void itemClicked(View view, T item, int position) {
}
/**
* 如果需要对item的长按事件做处理直接重写这个方法就可以了
*
* @param view view
* @param item item
* @param position position
* @return true
*/
public boolean itemLongClicked(View view, T item, int position) {
return false;
}
public void refreshFinished(RefreshResult result) {
swipeRefreshLayout.setRefreshing(false);
if (result == RefreshResult.SUCCEED)
baseEmptyView.setStatus(Status.DISMISS);
else if (result == RefreshResult.FAILED)
baseEmptyView.setStatus(Status.LOAD_FAILED);
else if (result == RefreshResult.NO_DATA)
baseEmptyView.setStatus(Status.NO_DATA);
else if (result == RefreshResult.NO_MORE) {
baseEmptyView.setStatus(Status.DISMISS);
ToolsHelper.snack(baseEmptyView, "All loads completed");
}
}
private void loadMoreFinished(RefreshResult result) {
// if (result == RefreshResult.SUCCEED) {
// } else if (result == RefreshResult.FAILED) {
// } else
if (result == RefreshResult.NO_MORE) {
// ToolsHelper.snack(getBinding().baseEmptyView, "All loads completed");
}
}
/**
* 需要指定一个adapteritem只有一种类型的使用{@link com.xuqm.base.adapter.CommonPagedAdapter}
* 需要指定一个adapteritem有多种类型的使用{@link BasePagedAdapter}
*
* @return 自定义的adapter
*/
public abstract BasePagedAdapter<T> adapter();
public RecyclerView recyclerView() {
return getActivity().findViewById(R.id.baseRecyclerView);
}
public SwipeRefreshLayout swipeRefreshLayout() {
return getActivity().findViewById(R.id.baseRefreshLayout);
}
public EmptyView baseEmptyView() {
return getActivity().findViewById(R.id.baseEmptyView);
}
public RecyclerView.LayoutManager layoutManager() {
return new LinearLayoutManager(mContext);
}
;
}

查看文件

@ -0,0 +1,44 @@
package com.xuqm.base.ui.adapter;
import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import androidx.databinding.BindingAdapter;
import com.bumptech.glide.Glide;
import com.xuqm.base.R;
import com.xuqm.base.common.ImageHelper;
public class ImageViewAdapter {
@BindingAdapter("android:src")
public static void setSrc(ImageView view, Bitmap bitmap) {
view.setImageBitmap(bitmap);
}
@BindingAdapter("android:src")
public static void setSrc(ImageView view, int resId) {
view.setImageResource(resId);
}
@BindingAdapter("imageUrl")
public static void setSrc(ImageView imageView, String url) {
Glide.with(imageView.getContext()).load(url)
.placeholder(R.drawable.ic_loading)
.into(imageView);
// ImageHelper.load(imageView,url);
}
@BindingAdapter({"imageUrl", "placeHolder", "error"})
public static void loadImage(ImageView imageView, String url, Drawable holderDrawable, Drawable errorDrawable) {
Glide.with(imageView.getContext())
.load(url)
.placeholder(holderDrawable)
.error(errorDrawable)
.into(imageView);
}
}

查看文件

@ -0,0 +1,5 @@
package com.xuqm.base.ui.callback;
public interface ToolBarListener {
void back();
}

查看文件

@ -0,0 +1,23 @@
package com.xuqm.base.ui.callback;
import android.os.Bundle;
import androidx.annotation.LayoutRes;
public interface UiCallback {
@LayoutRes
int getLayoutId();
void setContentView();
void initView(Bundle savedInstanceState);
void initData();
int showStatus();
boolean transparentStatusBar();
boolean fullscreen();
}

查看文件

@ -0,0 +1,100 @@
package com.xuqm.base.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.xuqm.base.R;
import com.xuqm.base.view.enu.Status;
/**
* 自定义的view
* 展示不同的数据加载状态
*/
public class EmptyMsgView extends FrameLayout {
private View contentView;
private Status status = Status.DISMISS;
public EmptyMsgView(@NonNull Context context) {
super(context);
init();
}
public EmptyMsgView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
View.inflate(getContext(), R.layout.empty_msg_view, this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 5) {
throw new IllegalStateException("EmptyView can only have one child view");
}
if (getChildCount() == 5) {
contentView = getChildAt(4);
}
emptyViewLoading = findViewById(R.id.emptyViewLoading);
emptyViewNoData = findViewById(R.id.emptyViewNoData);
emptyViewLoadFailed = findViewById(R.id.emptyViewLoadFailed);
emptyViewNetworkUnavailable = findViewById(R.id.emptyViewNetworkUnavailable);
setStatus(Status.DISMISS);
}
private LinearLayout emptyViewLoading;
private LinearLayout emptyViewNoData;
private LinearLayout emptyViewLoadFailed;
private LinearLayout emptyViewNetworkUnavailable;
public void setStatus(Status status) {
// LogHelper.e(contentView.getVisibility());
if (status != this.status) {
if (status == Status.DISMISS) {
if (null != contentView) contentView.setVisibility(VISIBLE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.LOADING) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(VISIBLE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.NO_DATA) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(VISIBLE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.LOAD_FAILED) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(VISIBLE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.NETWORK_UNAVAILABLE) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(VISIBLE);
}
this.status = status;
}
}
}

查看文件

@ -0,0 +1,112 @@
package com.xuqm.base.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.xuqm.base.R;
import com.xuqm.base.common.ImageHelper;
import com.xuqm.base.view.enu.Status;
/**
* 自定义的view
* 展示不同的数据加载状态
*/
public class EmptyView extends FrameLayout {
private View contentView;
private Status status = Status.DISMISS;
public EmptyView(@NonNull Context context) {
super(context);
init();
}
public EmptyView(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init();
}
private void init() {
View.inflate(getContext(), R.layout.empty_view, this);
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (getChildCount() > 5) {
throw new IllegalStateException("EmptyView can only have one child view");
}
if (getChildCount() == 5) {
contentView = getChildAt(4);
}
emptyViewLoading = findViewById(R.id.emptyViewLoading);
emptyViewNoData = findViewById(R.id.emptyViewNoData);
emptyViewLoadFailed = findViewById(R.id.emptyViewLoadFailed);
emptyViewNetworkUnavailable = findViewById(R.id.emptyViewNetworkUnavailable);
emptyIv = findViewById(R.id.empty_iv);
emptyTv = findViewById(R.id.empty_tv);
setStatus(Status.DISMISS);
}
private LinearLayout emptyViewLoading;
private LinearLayout emptyViewNoData;
private LinearLayout emptyViewLoadFailed;
private LinearLayout emptyViewNetworkUnavailable;
private ImageView emptyIv;
private TextView emptyTv;
public void setEmptyView(String content, Object url) {
emptyTv.setText(content);
ImageHelper.load(emptyIv, url);
}
public void setStatus(Status status) {
// LogHelper.e(contentView.getVisibility());
if (status != this.status) {
if (status == Status.DISMISS) {
if (null != contentView) contentView.setVisibility(VISIBLE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.LOADING) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(VISIBLE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.NO_DATA) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(VISIBLE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.LOAD_FAILED) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(VISIBLE);
emptyViewNetworkUnavailable.setVisibility(GONE);
} else if (status == Status.NETWORK_UNAVAILABLE) {
if (null != contentView) contentView.setVisibility(GONE);
emptyViewLoading.setVisibility(GONE);
emptyViewNoData.setVisibility(GONE);
emptyViewLoadFailed.setVisibility(GONE);
emptyViewNetworkUnavailable.setVisibility(VISIBLE);
}
this.status = status;
}
}
}

查看文件

@ -0,0 +1,120 @@
package com.xuqm.base.view;
import android.content.Context;
import android.util.AttributeSet;
import android.view.View;
import android.widget.RadioGroup;
public class FlowRadioGroup extends RadioGroup {
public FlowRadioGroup(Context context) {
super(context);
}
public FlowRadioGroup(Context context, AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSize = MeasureSpec.getSize(widthMeasureSpec);
int widthMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSize = MeasureSpec.getSize(heightMeasureSpec);
int heightMode = MeasureSpec.getMode(heightMeasureSpec);
//调用ViewGroup的方法测量子view
measureChildren(widthMeasureSpec, heightMeasureSpec);
//最大的宽
int maxWidth = 0;
//累计的高
int totalHeight = 0;
//当前这一行的累计行宽
int lineWidth = 0;
//当前这行的最大行高
int maxLineHeight = 0;
//用于记录换行前的行宽和行高
int oldHeight;
int oldWidth;
int count = getChildCount();
//假设 widthMode和heightMode都是AT_MOST
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
oldHeight = maxLineHeight;
//当前最大宽度
oldWidth = maxWidth;
int deltaX = child.getMeasuredWidth() + params.leftMargin + params.rightMargin;
if (lineWidth + deltaX + getPaddingLeft() + getPaddingRight() > widthSize) {//如果折行,height增加
//和目前最大的宽度比较,得到最宽不能加上当前的child的宽,所以用的是oldWidth
maxWidth = Math.max(lineWidth, oldWidth);
//重置宽度
lineWidth = deltaX;
//累加高度
totalHeight += oldHeight;
//重置行高,当前这个View属于下一行因此当前最大行高为这个child的高度加上margin
maxLineHeight = child.getMeasuredHeight() + params.topMargin + params.bottomMargin;
} else {
//不换行累加宽度
lineWidth += deltaX;
//不换行计算行最高
int deltaY = child.getMeasuredHeight() + params.topMargin + params.bottomMargin;
maxLineHeight = Math.max(maxLineHeight, deltaY);
}
if (i == count - 1) {
//前面没有加上下一行的搞如果是最后一行还要再叠加上最后一行的最高的值
totalHeight += maxLineHeight;
//计算最后一行和前面的最宽的一行比较
maxWidth = Math.max(lineWidth, oldWidth);
}
}
//加上当前容器的padding值
maxWidth += getPaddingLeft() + getPaddingRight();
totalHeight += getPaddingTop() + getPaddingBottom();
setMeasuredDimension(widthMode == MeasureSpec.EXACTLY ? widthSize : maxWidth,
heightMode == MeasureSpec.EXACTLY ? heightSize : totalHeight);
}
@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
int count = getChildCount();
//pre为前面所有的child的相加后的位置
int preLeft = getPaddingLeft();
int preTop = getPaddingTop();
//记录每一行的最高值
int maxHeight = 0;
for (int i = 0; i < count; i++) {
View child = getChildAt(i);
MarginLayoutParams params = (MarginLayoutParams) child.getLayoutParams();
//r-l为当前容器的宽度如果子view的累积宽度大于容器宽度就换行
if (preLeft + params.leftMargin + child.getMeasuredWidth() + params.rightMargin + getPaddingRight() > (r - l)) {
//重置
preLeft = getPaddingLeft();
//要选择child的height最大的作为设置
preTop = preTop + maxHeight;
maxHeight = getChildAt(i).getMeasuredHeight() + params.topMargin + params.bottomMargin;
} else { //不换行,计算最大高度
maxHeight = Math.max(maxHeight, child.getMeasuredHeight() + params.topMargin + params.bottomMargin);
}
//left坐标
int left = preLeft + params.leftMargin;
//top坐标
int top = preTop + params.topMargin;
int right = left + child.getMeasuredWidth();
int bottom = top + child.getMeasuredHeight();
//为子view布局
child.layout(left, top, right, bottom);
//计算布局结束后preLeft的值
preLeft += params.leftMargin + child.getMeasuredWidth() + params.rightMargin;
}
}
}

查看文件

@ -0,0 +1,35 @@
package com.xuqm.base.view
import android.view.View
import androidx.viewpager2.widget.ViewPager2
class GalleryTransformer : ViewPager2.PageTransformer {
companion object {
private const val TARGET_ALPHA = 0.5f
private const val TARGET_SCALE = 0.9f
}
override fun transformPage(page: View, position: Float) {
if (position < -1 || position > 1) {
//当前页面左侧以及右侧的页面效果
page.alpha = TARGET_ALPHA
page.scaleX = TARGET_SCALE
page.scaleY = TARGET_SCALE
} else {
//从不可见变为可见效果
//透明度效果
if (position <= 0) {
page.alpha =
TARGET_ALPHA + TARGET_ALPHA * (1 + position)
} else {
page.alpha =
TARGET_ALPHA + TARGET_ALPHA * (1 - position)
}
//缩放效果
val scale = Math.max(TARGET_SCALE, 1 - Math.abs(position))
page.scaleX = scale
page.scaleY = scale
}
}
}

查看文件

@ -0,0 +1,69 @@
package com.xuqm.base.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.util.AttributeSet;
import androidx.annotation.Nullable;
import androidx.recyclerview.widget.RecyclerView;
import com.xuqm.base.R;
/**
* max limit-able RecyclerView
*/
public class MaxLimitRecyclerView extends RecyclerView {
private int mMaxHeight;
private int mMaxWidth;
public MaxLimitRecyclerView(Context context) {
this(context, null);
}
public MaxLimitRecyclerView(Context context, @Nullable AttributeSet attrs) {
this(context, attrs, 0);
}
public MaxLimitRecyclerView(Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
inti(attrs);
}
private void inti(AttributeSet attrs) {
if (getContext() != null && attrs != null) {
TypedArray typedArray = null;
try {
typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.MaxLimitRecyclerView);
if (typedArray.hasValue(R.styleable.MaxLimitRecyclerView_limit_maxHeight)) {
mMaxHeight = typedArray.getDimensionPixelOffset(R.styleable.MaxLimitRecyclerView_limit_maxHeight, -1);
}
if (typedArray.hasValue(R.styleable.MaxLimitRecyclerView_limit_maxWidth)) {
mMaxWidth = typedArray.getDimensionPixelOffset(R.styleable.MaxLimitRecyclerView_limit_maxWidth, -1);
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (typedArray != null) {
typedArray.recycle();
}
}
}
}
@Override
protected void onMeasure(int widthSpec, int heightSpec) {
super.onMeasure(widthSpec, heightSpec);
boolean needLimit = mMaxHeight >= 0 || mMaxWidth >= 0;
if (needLimit) {
int limitHeight = getMeasuredHeight();
int limitWith = getMeasuredWidth();
if (getMeasuredHeight() > mMaxHeight) {
limitHeight = mMaxHeight;
}
// if (getMeasuredWidth() > mMaxWidth) {
// limitWith = mMaxWidth;
// }
setMeasuredDimension(limitWith, limitHeight);
}
}
}

查看文件

@ -0,0 +1,657 @@
package com.xuqm.base.view;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.util.AttributeSet;
import android.widget.ImageView;
import com.xuqm.base.R;
/**
* Created by JiYaRuo on 2019/10/11.
*
* @BeUsedFor 自定义ImageView 随心所欲的改变ImageView的四个圆角角度
*/
@SuppressLint("AppCompatCustomView")
public class SelectableRoundedImageView extends ImageView {
public static final String TAG = "SelectableRoundedImageView";
private int mResource = 0;
private static final ScaleType[] sScaleTypeArray = {
ScaleType.MATRIX,
ScaleType.FIT_XY,
ScaleType.FIT_START,
ScaleType.FIT_CENTER,
ScaleType.FIT_END,
ScaleType.CENTER,
ScaleType.CENTER_CROP,
ScaleType.CENTER_INSIDE
};
// Set default scale type to FIT_CENTER, which is default scale type of
// original ImageView.
private ScaleType mScaleType = ScaleType.FIT_CENTER;
private float mLeftTopCornerRadius = 0.0f;
private float mRightTopCornerRadius = 0.0f;
private float mLeftBottomCornerRadius = 0.0f;
private float mRightBottomCornerRadius = 0.0f;
private float mBorderWidth = 0.0f;
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
private boolean isOval = false;
private Drawable mDrawable;
private float[] mRadii = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
public SelectableRoundedImageView(Context context) {
super(context);
}
public SelectableRoundedImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public SelectableRoundedImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SelectableRoundedImageView, defStyle, 0);
final int index = a.getInt(R.styleable.SelectableRoundedImageView_android_scaleType, -1);
if (index >= 0) {
setScaleType(sScaleTypeArray[index]);
}
mLeftTopCornerRadius = a.getDimensionPixelSize(
R.styleable.SelectableRoundedImageView_sriv_left_top_corner_radius, 0);
mRightTopCornerRadius = a.getDimensionPixelSize(
R.styleable.SelectableRoundedImageView_sriv_right_top_corner_radius, 0);
mLeftBottomCornerRadius = a.getDimensionPixelSize(
R.styleable.SelectableRoundedImageView_sriv_left_bottom_corner_radius, 0);
mRightBottomCornerRadius = a.getDimensionPixelSize(
R.styleable.SelectableRoundedImageView_sriv_right_bottom_corner_radius, 0);
if (mLeftTopCornerRadius < 0.0f || mRightTopCornerRadius < 0.0f
|| mLeftBottomCornerRadius < 0.0f || mRightBottomCornerRadius < 0.0f) {
throw new IllegalArgumentException("radius values cannot be negative.");
}
mRadii = new float[]{
mLeftTopCornerRadius, mLeftTopCornerRadius,
mRightTopCornerRadius, mRightTopCornerRadius,
mRightBottomCornerRadius, mRightBottomCornerRadius,
mLeftBottomCornerRadius, mLeftBottomCornerRadius
};
mBorderWidth = a.getDimensionPixelSize(R.styleable.SelectableRoundedImageView_sriv_border_width, 0);
if (mBorderWidth < 0) {
throw new IllegalArgumentException("border width cannot be negative.");
}
mBorderColor = a.getColorStateList(R.styleable.SelectableRoundedImageView_sriv_border_color);
if (mBorderColor == null) {
mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
}
isOval = a.getBoolean(R.styleable.SelectableRoundedImageView_sriv_oval, false);
a.recycle();
updateDrawable();
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
invalidate();
}
@Override
public ScaleType getScaleType() {
return mScaleType;
}
@Override
public void setScaleType(ScaleType scaleType) {
super.setScaleType(scaleType);
mScaleType = scaleType;
updateDrawable();
}
@Override
public void setImageDrawable(Drawable drawable) {
mResource = 0;
mDrawable = SelectableRoundedCornerDrawable.fromDrawable(drawable, getResources());
super.setImageDrawable(mDrawable);
updateDrawable();
}
@Override
public void setImageBitmap(Bitmap bm) {
mResource = 0;
mDrawable = SelectableRoundedCornerDrawable.fromBitmap(bm, getResources());
super.setImageDrawable(mDrawable);
updateDrawable();
}
@Override
public void setImageResource(int resId) {
if (mResource != resId) {
mResource = resId;
mDrawable = resolveResource();
super.setImageDrawable(mDrawable);
updateDrawable();
}
}
@Override
public void setImageURI(Uri uri) {
super.setImageURI(uri);
setImageDrawable(getDrawable());
}
private Drawable resolveResource() {
Resources rsrc = getResources();
if (rsrc == null) {
return null;
}
Drawable d = null;
if (mResource != 0) {
try {
d = rsrc.getDrawable(mResource);
} catch (Exception e) {
// Log.w(TAG, "Unable to find resource: " + mResource, e);
// Don't try again.
mResource = 0;
}
}
return SelectableRoundedCornerDrawable.fromDrawable(d, getResources());
}
private void updateDrawable() {
if (mDrawable == null) {
return;
}
((SelectableRoundedCornerDrawable) mDrawable).setScaleType(mScaleType);
((SelectableRoundedCornerDrawable) mDrawable).setCornerRadii(mRadii);
((SelectableRoundedCornerDrawable) mDrawable).setBorderWidth(mBorderWidth);
((SelectableRoundedCornerDrawable) mDrawable).setBorderColor(mBorderColor);
((SelectableRoundedCornerDrawable) mDrawable).setOval(isOval);
}
public float getCornerRadius() {
return mLeftTopCornerRadius;
}
/**
* Set radii for each corner.
*
* @param leftTop The desired radius for left-top corner in dip.
* @param rightTop The desired desired radius for right-top corner in dip.
* @param leftBottom The desired radius for left-bottom corner in dip.
* @param rightBottom The desired radius for right-bottom corner in dip.
*/
public void setCornerRadiiDP(float leftTop, float rightTop, float leftBottom, float rightBottom) {
final float density = getResources().getDisplayMetrics().density;
final float lt = leftTop * density;
final float rt = rightTop * density;
final float lb = leftBottom * density;
final float rb = rightBottom * density;
mRadii = new float[]{lt, lt, rt, rt, rb, rb, lb, lb};
updateDrawable();
}
public float getBorderWidth() {
return mBorderWidth;
}
/**
* Set border width.
*
* @param width The desired width in dip.
*/
public void setBorderWidthDP(float width) {
float scaledWidth = getResources().getDisplayMetrics().density * width;
if (mBorderWidth == scaledWidth) {
return;
}
mBorderWidth = scaledWidth;
updateDrawable();
invalidate();
}
public int getBorderColor() {
return mBorderColor.getDefaultColor();
}
public void setBorderColor(int color) {
setBorderColor(ColorStateList.valueOf(color));
}
public ColorStateList getBorderColors() {
return mBorderColor;
}
public void setBorderColor(ColorStateList colors) {
if (mBorderColor.equals(colors)) {
return;
}
mBorderColor = (colors != null) ? colors : ColorStateList
.valueOf(DEFAULT_BORDER_COLOR);
updateDrawable();
if (mBorderWidth > 0) {
invalidate();
}
}
public boolean isOval() {
return isOval;
}
public void setOval(boolean oval) {
isOval = oval;
updateDrawable();
invalidate();
}
static class SelectableRoundedCornerDrawable extends Drawable {
private static final String TAG = "SelectableRoundedCornerDrawable";
private static final int DEFAULT_BORDER_COLOR = Color.BLACK;
private RectF mBounds = new RectF();
private RectF mBorderBounds = new RectF();
private final RectF mBitmapRect = new RectF();
private final int mBitmapWidth;
private final int mBitmapHeight;
private final Paint mBitmapPaint;
private final Paint mBorderPaint;
private BitmapShader mBitmapShader;
private float[] mRadii = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
private float[] mBorderRadii = new float[]{0, 0, 0, 0, 0, 0, 0, 0};
private boolean mOval = false;
private float mBorderWidth = 0;
private ColorStateList mBorderColor = ColorStateList.valueOf(DEFAULT_BORDER_COLOR);
// Set default scale type to FIT_CENTER, which is default scale type of
// original ImageView.
private ScaleType mScaleType = ScaleType.FIT_CENTER;
private Path mPath = new Path();
private Bitmap mBitmap;
private boolean mBoundsConfigured = false;
public SelectableRoundedCornerDrawable(Bitmap bitmap, Resources r) {
mBitmap = bitmap;
mBitmapShader = new BitmapShader(bitmap, Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
if (bitmap != null) {
mBitmapWidth = bitmap.getScaledWidth(r.getDisplayMetrics());
mBitmapHeight = bitmap.getScaledHeight(r.getDisplayMetrics());
} else {
mBitmapWidth = mBitmapHeight = -1;
}
mBitmapRect.set(0, 0, mBitmapWidth, mBitmapHeight);
mBitmapPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBitmapPaint.setStyle(Paint.Style.FILL);
mBitmapPaint.setShader(mBitmapShader);
mBorderPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mBorderPaint.setStyle(Paint.Style.STROKE);
mBorderPaint.setColor(mBorderColor.getColorForState(getState(), DEFAULT_BORDER_COLOR));
mBorderPaint.setStrokeWidth(mBorderWidth);
}
public static SelectableRoundedCornerDrawable fromBitmap(Bitmap bitmap, Resources r) {
if (bitmap != null) {
return new SelectableRoundedCornerDrawable(bitmap, r);
} else {
return null;
}
}
public static Drawable fromDrawable(Drawable drawable, Resources r) {
if (drawable != null) {
if (drawable instanceof SelectableRoundedCornerDrawable) {
return drawable;
} else if (drawable instanceof LayerDrawable) {
LayerDrawable ld = (LayerDrawable) drawable;
final int num = ld.getNumberOfLayers();
for (int i = 0; i < num; i++) {
Drawable d = ld.getDrawable(i);
ld.setDrawableByLayerId(ld.getId(i), fromDrawable(d, r));
}
return ld;
}
Bitmap bm = drawableToBitmap(drawable);
if (bm != null) {
return new SelectableRoundedCornerDrawable(bm, r);
} else {
// Log.w(TAG, "Failed to create bitmap from drawable!");
}
}
return drawable;
}
public static Bitmap drawableToBitmap(Drawable drawable) {
if (drawable == null) {
return null;
}
if (drawable instanceof BitmapDrawable) {
return ((BitmapDrawable) drawable).getBitmap();
}
Bitmap bitmap;
int width = Math.max(drawable.getIntrinsicWidth(), 2);
int height = Math.max(drawable.getIntrinsicHeight(), 2);
try {
bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
} catch (IllegalArgumentException e) {
e.printStackTrace();
bitmap = null;
}
return bitmap;
}
@Override
public boolean isStateful() {
return mBorderColor.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
int newColor = mBorderColor.getColorForState(state, 0);
if (mBorderPaint.getColor() != newColor) {
mBorderPaint.setColor(newColor);
return true;
} else {
return super.onStateChange(state);
}
}
private void configureBounds(Canvas canvas) {
// I have discovered a truly marvelous explanation of this,
// which this comment space is too narrow to contain. :)
// If you want to understand what's going on here,
// See http://www.joooooooooonhokim.com/?p=289
Rect clipBounds = canvas.getClipBounds();
Matrix canvasMatrix = canvas.getMatrix();
if (ScaleType.CENTER == mScaleType) {
mBounds.set(clipBounds);
} else if (ScaleType.CENTER_CROP == mScaleType) {
applyScaleToRadii(canvasMatrix);
mBounds.set(clipBounds);
} else if (ScaleType.FIT_XY == mScaleType) {
Matrix m = new Matrix();
m.setRectToRect(mBitmapRect, new RectF(clipBounds), Matrix.ScaleToFit.FILL);
mBitmapShader.setLocalMatrix(m);
mBounds.set(clipBounds);
} else if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType
|| ScaleType.FIT_CENTER == mScaleType || ScaleType.CENTER_INSIDE == mScaleType) {
applyScaleToRadii(canvasMatrix);
mBounds.set(mBitmapRect);
} else if (ScaleType.MATRIX == mScaleType) {
applyScaleToRadii(canvasMatrix);
mBounds.set(mBitmapRect);
}
}
private void applyScaleToRadii(Matrix m) {
float[] values = new float[9];
m.getValues(values);
for (int i = 0; i < mRadii.length; i++) {
mRadii[i] = mRadii[i] / values[0];
}
}
private void adjustCanvasForBorder(Canvas canvas) {
Matrix canvasMatrix = canvas.getMatrix();
final float[] values = new float[9];
canvasMatrix.getValues(values);
final float scaleFactorX = values[0];
final float scaleFactorY = values[4];
final float translateX = values[2];
final float translateY = values[5];
final float newScaleX = mBounds.width()
/ (mBounds.width() + mBorderWidth + mBorderWidth);
final float newScaleY = mBounds.height()
/ (mBounds.height() + mBorderWidth + mBorderWidth);
canvas.scale(newScaleX, newScaleY);
if (ScaleType.FIT_START == mScaleType || ScaleType.FIT_END == mScaleType
|| ScaleType.FIT_XY == mScaleType || ScaleType.FIT_CENTER == mScaleType
|| ScaleType.CENTER_INSIDE == mScaleType || ScaleType.MATRIX == mScaleType) {
canvas.translate(mBorderWidth, mBorderWidth);
} else if (ScaleType.CENTER == mScaleType || ScaleType.CENTER_CROP == mScaleType) {
// First, make translate values to 0
canvas.translate(
-translateX / (newScaleX * scaleFactorX),
-translateY / (newScaleY * scaleFactorY));
// Then, set the final translate values.
canvas.translate(-(mBounds.left - mBorderWidth), -(mBounds.top - mBorderWidth));
}
}
private void adjustBorderWidthAndBorderBounds(Canvas canvas) {
Matrix canvasMatrix = canvas.getMatrix();
final float[] values = new float[9];
canvasMatrix.getValues(values);
final float scaleFactor = values[0];
float viewWidth = mBounds.width() * scaleFactor;
mBorderWidth = (mBorderWidth * mBounds.width()) / (viewWidth - (2 * mBorderWidth));
mBorderPaint.setStrokeWidth(mBorderWidth);
mBorderBounds.set(mBounds);
mBorderBounds.inset(-mBorderWidth / 2, -mBorderWidth / 2);
}
private void setBorderRadii() {
for (int i = 0; i < mRadii.length; i++) {
if (mRadii[i] > 0) {
mBorderRadii[i] = mRadii[i];
mRadii[i] = mRadii[i] - mBorderWidth;
}
}
}
@Override
public void draw(Canvas canvas) {
canvas.save();
if (!mBoundsConfigured) {
configureBounds(canvas);
if (mBorderWidth > 0) {
adjustBorderWidthAndBorderBounds(canvas);
setBorderRadii();
}
mBoundsConfigured = true;
}
if (mOval) {
if (mBorderWidth > 0) {
adjustCanvasForBorder(canvas);
mPath.addOval(mBounds, Path.Direction.CW);
canvas.drawPath(mPath, mBitmapPaint);
mPath.reset();
mPath.addOval(mBorderBounds, Path.Direction.CW);
canvas.drawPath(mPath, mBorderPaint);
} else {
mPath.addOval(mBounds, Path.Direction.CW);
canvas.drawPath(mPath, mBitmapPaint);
}
} else {
if (mBorderWidth > 0) {
adjustCanvasForBorder(canvas);
mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);
canvas.drawPath(mPath, mBitmapPaint);
mPath.reset();
mPath.addRoundRect(mBorderBounds, mBorderRadii, Path.Direction.CW);
canvas.drawPath(mPath, mBorderPaint);
} else {
mPath.addRoundRect(mBounds, mRadii, Path.Direction.CW);
canvas.drawPath(mPath, mBitmapPaint);
}
}
canvas.restore();
}
public void setCornerRadii(float[] radii) {
if (radii == null)
return;
if (radii.length != 8) {
throw new ArrayIndexOutOfBoundsException("radii[] needs 8 values");
}
for (int i = 0; i < radii.length; i++) {
mRadii[i] = radii[i];
}
}
@Override
public int getOpacity() {
return (mBitmap == null || mBitmap.hasAlpha() || mBitmapPaint.getAlpha() < 255) ? PixelFormat.TRANSLUCENT
: PixelFormat.OPAQUE;
}
@Override
public void setAlpha(int alpha) {
mBitmapPaint.setAlpha(alpha);
invalidateSelf();
}
@Override
public void setColorFilter(ColorFilter cf) {
mBitmapPaint.setColorFilter(cf);
invalidateSelf();
}
@Override
public void setDither(boolean dither) {
mBitmapPaint.setDither(dither);
invalidateSelf();
}
@Override
public void setFilterBitmap(boolean filter) {
mBitmapPaint.setFilterBitmap(filter);
invalidateSelf();
}
@Override
public int getIntrinsicWidth() {
return mBitmapWidth;
}
@Override
public int getIntrinsicHeight() {
return mBitmapHeight;
}
public float getBorderWidth() {
return mBorderWidth;
}
public void setBorderWidth(float width) {
mBorderWidth = width;
mBorderPaint.setStrokeWidth(width);
}
public int getBorderColor() {
return mBorderColor.getDefaultColor();
}
public void setBorderColor(int color) {
setBorderColor(ColorStateList.valueOf(color));
}
public ColorStateList getBorderColors() {
return mBorderColor;
}
/**
* Controls border color of this ImageView.
*
* @param colors The desired border color. If it's null, no border will be
* drawn.
*/
public void setBorderColor(ColorStateList colors) {
if (colors == null) {
mBorderWidth = 0;
mBorderColor = ColorStateList.valueOf(Color.TRANSPARENT);
mBorderPaint.setColor(Color.TRANSPARENT);
} else {
mBorderColor = colors;
mBorderPaint.setColor(mBorderColor.getColorForState(getState(),
DEFAULT_BORDER_COLOR));
}
}
public boolean isOval() {
return mOval;
}
public void setOval(boolean oval) {
mOval = oval;
}
public ScaleType getScaleType() {
return mScaleType;
}
public void setScaleType(ScaleType scaleType) {
if (scaleType == null) {
return;
}
mScaleType = scaleType;
}
}
}

查看文件

@ -0,0 +1,83 @@
package com.xuqm.base.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import android.view.ViewGroup;
import android.widget.TextView;
public class StrokeTextView extends androidx.appcompat.widget.AppCompatTextView {
private final TextView outlineTextView;
private TextPaint strokePaint;
public StrokeTextView(Context context) {
super(context);
outlineTextView = new TextView(context);
}
public StrokeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
outlineTextView = new TextView(context, attrs);
}
public StrokeTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
outlineTextView = new TextView(context, attrs, defStyle);
}
@Override
public void setLayoutParams(ViewGroup.LayoutParams params) {
super.setLayoutParams(params);
outlineTextView.setLayoutParams(params);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//设置轮廓文字
CharSequence outlineText = outlineTextView.getText();
if (outlineText == null || !outlineText.equals(this.getText())) {
outlineTextView.setText(getText());
postInvalidate();
}
outlineTextView.measure(widthMeasureSpec, heightMeasureSpec);
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
outlineTextView.layout(left, top, right, bottom);
}
@Override
protected void onDraw(Canvas canvas) {
if (strokePaint == null) {
strokePaint = new TextPaint();
}
//复制原来TextViewg画笔中的一些参数
TextPaint paint = getPaint();
strokePaint.setTextSize(paint.getTextSize()+3);
strokePaint.setFlags(paint.getFlags());
strokePaint.setAlpha(paint.getAlpha());
//自定义描边效果
strokePaint.setStyle(Paint.Style.STROKE);
strokePaint.setColor(Color.parseColor("#66000000"));
strokePaint.setStrokeWidth(6);
String text = getText().toString();
//在文本底层画出带描边的文本
canvas.drawText(text, (getWidth() - strokePaint.measureText(text)) / 2,
getBaseline(), strokePaint);
super.onDraw(canvas);
}
}

查看文件

@ -0,0 +1,98 @@
package com.xuqm.base.view
import android.annotation.SuppressLint
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.ViewGroup
import android.widget.RelativeLayout
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.xuqm.base.R
class SuperViewPager : RelativeLayout {
val mViewPager: ViewPager2 by lazy {
findViewById<ViewPager2>(R.id.mViewPager)
.apply {
//设置关闭过度滑动的效果
getChildAt(0).overScrollMode = View.OVER_SCROLL_NEVER
}
}
//自己定义了一个比率,来调整画廊效果最左侧和最右侧占用的宽度
var edgeRatio = 0.25
set(value) {
field = value
refreshPageSize()
}
//为了保证画廊效果,可见的Page处理为单数
var visibleItem: Int = 1
set(value) {
field = if (value.rem(2) == 0) {
value - 1
} else {
value
}
refreshPageSize()
}
//刷新页面大小
private fun refreshPageSize() {
//使用post为了保证获取根布局width的时候结果不为0
mViewPager.post {
mViewPager.offscreenPageLimit = visibleItem
//根据想要显示的页面个数,动态给ViewPager2计算一个大小
val mPageWidth = if (visibleItem == 1) {
width
} else {
width.toDouble().div(visibleItem.minus(2).plus(edgeRatio)).toInt()
}
mViewPager.layoutParams = LayoutParams(
LayoutParams(
mPageWidth,
ViewGroup.LayoutParams.MATCH_PARENT
).apply { gravity = Gravity.CENTER })
}
}
/**
* 将根布局的触摸事件直接传递给ViewPager
*/
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(event: MotionEvent?): Boolean {
return mViewPager.getChildAt(0).onTouchEvent(event)
}
constructor(context: Context?) : super(context)
constructor(context: Context?, attrs: AttributeSet?) : super(context, attrs)
constructor(context: Context?, attrs: AttributeSet?, defStyleAttr: Int) : super(
context,
attrs,
defStyleAttr
)
init {
clipChildren = false
LayoutInflater.from(context).inflate(R.layout.super_viewpager_layout, this, true)
}
/**
* 为ViewPager2设置一个适配器ViewPager2的适配器不再是PagerAdapter,而是RecyclerView.Adapter类型
*/
fun setAdapter(adapter: RecyclerView.Adapter<*>) {
mViewPager.adapter = adapter
}
/**
* 设置页面切换的效果
*/
fun setPageTransformer(pageTransformer: ViewPager2.PageTransformer) {
mViewPager.setPageTransformer(pageTransformer)
}
}

查看文件

@ -0,0 +1,150 @@
package com.xuqm.base.view;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.content.ContextCompat;
import androidx.core.graphics.drawable.DrawableCompat;
import com.xuqm.base.R;
import com.xuqm.base.ui.callback.ToolBarListener;
/**
* 自定义的toolbar
*/
public class ToolbarLayout extends FrameLayout {
private CharSequence title = "";
private int textColor = 0xFFFFFF;
private int iconTintColor = 0xFFFFFF;
private boolean showBack = true;
private boolean showLine = false;
public void setTitle(CharSequence title) {
this.title = title;
getTitleView().setText(title);
}
public void setTextColor(int textColor) {
this.textColor = textColor;
getTitleView().setTextColor(textColor);
}
public void setIconTintColor(int iconTintColor) {
this.iconTintColor = iconTintColor;
tintIcon(iconTintColor);
}
public void setShowBack(boolean showBack) {
this.showBack = showBack;
getBackBtn().setVisibility(showBack ? VISIBLE : GONE);
}
public void setShowLine(boolean showLine) {
this.showLine = showLine;
findViewById(R.id.toolbarLine).setVisibility(showLine ? VISIBLE : GONE);
}
private ToolBarListener backListener;
public void backBtnPressed(ToolBarListener listener) {
backListener = listener;
}
public ToolbarLayout(@NonNull Context context) {
super(context);
init(null);
}
public ToolbarLayout(@NonNull Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
private void init(AttributeSet attrs) {
Context mContext = getContext();
if (null == attrs) return;
View.inflate(mContext, R.layout.toolbar, this);
TypedArray typedArray = mContext.obtainStyledAttributes(attrs, R.styleable.ToolbarLayout);
title = typedArray.getString(R.styleable.ToolbarLayout_title);
showBack = typedArray.getBoolean(R.styleable.ToolbarLayout_showBack, true);
showLine = typedArray.getBoolean(R.styleable.ToolbarLayout_showLine, false);
textColor = typedArray.getColor(R.styleable.ToolbarLayout_textColor, ContextCompat.getColor(mContext, R.color.title));
// iconTintColor = typedArray.getColor(R.styleable.ToolbarLayout_iconTint, ContextCompat.getColor(mContext, R.color.white));
typedArray.recycle();
}
@Override
protected void onFinishInflate() {
super.onFinishInflate();
if (null != getBackBtn()) {
getBackBtn().setVisibility(showBack ? VISIBLE : GONE);
if (showBack) {
// tintIcon(iconTintColor);
getBackBtn().setOnClickListener(v -> backListener.back());
}
}
if (null != getTitleView()) {
getTitleView().setText(title);
getTitleView().setTextColor(textColor);
}
if (null != getConfirmBtn()) {
getTitleView().setText(title);
getConfirmBtn().setTextColor(textColor);
}
findViewById(R.id.toolbarLine).setVisibility(showLine ? VISIBLE : GONE);
}
private void tintIcon(int colors) {
Drawable wrappedDrawable = DrawableCompat.wrap(getBackBtn().getDrawable());
DrawableCompat.setTintList(wrappedDrawable, ColorStateList.valueOf(colors));
getBackBtn().setImageDrawable(wrappedDrawable);
}
private ImageView backBtn;
public ImageView getBackBtn() {
if (null == backBtn)
backBtn = findViewById(R.id.toolbarBack);
return backBtn;
}
private TextView titleView;
public TextView getTitleView() {
if (null == titleView)
titleView = findViewById(R.id.toolbarTitle);
return titleView;
}
private TextView confirmBtn;
public TextView getConfirmBtn() {
if (null == confirmBtn)
confirmBtn = findViewById(R.id.toolbarConfirm);
return confirmBtn;
}
private ImageView closeBtn;
public ImageView getCloseBtn() {
if (null == closeBtn)
closeBtn = findViewById(R.id.toolbarClose);
return closeBtn;
}
private ImageView toolbarMenu;
public ImageView getToolbarMenu() {
if (null == toolbarMenu)
toolbarMenu = findViewById(R.id.toolbarMenu);
return toolbarMenu;
}
}

查看文件

@ -0,0 +1,5 @@
package com.xuqm.base.view.enu;
public enum Status {
DISMISS, LOADING, NO_DATA, LOAD_FAILED, NETWORK_UNAVAILABLE
}

查看文件

@ -0,0 +1,132 @@
package com.xuqm.base.viewmodel;
import androidx.annotation.NonNull;
import androidx.core.util.Pair;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.paging.LivePagedListBuilder;
import androidx.paging.PageKeyedDataSource;
import androidx.paging.PagedList;
import com.xuqm.base.common.RefreshResult;
import com.xuqm.base.datasource.DataSourceFactory;
import com.xuqm.base.datasource.PagedDataLoader;
import com.xuqm.base.viewmodel.callback.AdapterObserverCallback;
import com.xuqm.base.viewmodel.callback.DataObserverCallback;
import com.xuqm.base.viewmodel.callback.Response;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* @param <T>
*/
public abstract class BaseListViewModel<T> extends BaseViewModel implements PagedDataLoader<T> {
public int pageSize() {
return 10;
}
private final ArrayList<T> data = new ArrayList<>();
private final DataSourceFactory<T> dataSourceFactory = new DataSourceFactory<>(this);
private final LiveData<PagedList<T>> loadLiveData = new LivePagedListBuilder<>(dataSourceFactory, this.pageSize()).build();
private final MutableLiveData<RefreshResult> refreshLiveData = new MutableLiveData<>();
private final MutableLiveData<RefreshResult> loadMoreLiveData = new MutableLiveData<>();
private final MutableLiveData<Pair<Integer, Object>> notifyItemLiveData = new MutableLiveData<>();
private final MutableLiveData<Integer> removeItemLiveData = new MutableLiveData<>();
public void invalidate() {
Objects.requireNonNull(dataSourceFactory.sourceLiveData.getValue()).invalidate();
}
public void observeDataObserver(@NonNull LifecycleOwner owner, DataObserverCallback<T> dataObserverCallback) {
loadLiveData.observe(owner, dataObserverCallback::data);
refreshLiveData.observe(owner, dataObserverCallback::refreshResult);
loadMoreLiveData.observe(owner, dataObserverCallback::loadMoreResult);
}
public void observeAdapterObserver(@NonNull LifecycleOwner owner, AdapterObserverCallback observerCallback) {
notifyItemLiveData.observe(owner, integerObjectPair -> observerCallback.notifyItem(integerObjectPair.first, integerObjectPair.second));
removeItemLiveData.observe(owner, observerCallback::removeItem);
}
@Override
public void loadInitial(PageKeyedDataSource.LoadInitialParams<Integer> params, PageKeyedDataSource.LoadInitialCallback<Integer, T> callback) {
refresh();
data.clear();
loadData(1, onResponse -> {
if (null == onResponse) {
refreshLiveData.postValue(RefreshResult.FAILED);
} else if (onResponse.isEmpty()) {
refreshLiveData.postValue(RefreshResult.NO_DATA);
} else if (onResponse.size() < pageSize()) {
data.addAll(onResponse);
callback.onResult(onResponse, null, null);
refreshLiveData.postValue(RefreshResult.NO_MORE);
} else {
data.addAll(onResponse);
callback.onResult(onResponse, null, 2);
refreshLiveData.postValue(RefreshResult.SUCCEED);
}
});
}
@Override
public void loadAfter(PageKeyedDataSource.LoadParams<Integer> params, PageKeyedDataSource.LoadCallback<Integer, T> callback) {
loadMore();
loadData(params.key, onResponse -> {
if (null == onResponse) loadMoreLiveData.postValue(RefreshResult.FAILED);
else if (onResponse.size() < pageSize()) {
data.addAll(onResponse);
callback.onResult(onResponse, null);
loadMoreLiveData.postValue(RefreshResult.NO_MORE);
} else {
data.addAll(onResponse);
callback.onResult(onResponse, params.key + 1);
loadMoreLiveData.postValue(RefreshResult.SUCCEED);
}
});
}
public void setData(List<T> data) {
this.data.clear();
this.data.addAll(data);
refreshLiveData.postValue(RefreshResult.SUCCEED);
}
public ArrayList<T> getData() {
return data;
}
public void notifyItem(int position) {
notifyItemLiveData.postValue(new Pair<>(position, null));
}
public void notifyItem(int position, Object payload) {
notifyItemLiveData.postValue(new Pair<>(position, payload));
}
public void removeItem(int position) {
removeItemLiveData.postValue(position);
}
@Override
public void refresh() {
}
@Override
public void loadMore() {
}
public abstract void loadData(int page, Response<T> onResponse);
}

查看文件

@ -0,0 +1,67 @@
package com.xuqm.base.viewmodel;
import androidx.annotation.NonNull;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.lifecycle.ViewModel;
import com.xuqm.base.common.LogHelper;
import io.reactivex.disposables.CompositeDisposable;
import io.reactivex.disposables.Disposable;
/**
* ViewModel 基类
*/
public abstract class BaseViewModel extends ViewModel implements DefaultLifecycleObserver {
private final String TAG = getClass().getSimpleName();
protected CompositeDisposable compositeDisposable = new CompositeDisposable();
/**
* Disposable 的管理自动处理网络请求
*
* @param d Disposable
*/
protected void add(Disposable d) {
compositeDisposable.add(d);
}
@Override
public void onCreate(@NonNull LifecycleOwner owner) {
LogHelper.d(TAG, "onCreate");
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
LogHelper.d(TAG, "onStart");
}
@Override
public void onResume(@NonNull LifecycleOwner owner) {
LogHelper.d(TAG, "onResume");
}
@Override
public void onPause(@NonNull LifecycleOwner owner) {
LogHelper.d(TAG, "onPause");
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
LogHelper.d(TAG, "onStop");
}
@Override
public void onDestroy(@NonNull LifecycleOwner owner) {
LogHelper.d(TAG, "onDestroy");
}
@Override
protected void onCleared() {
super.onCleared();
LogHelper.d(TAG, "onCleared");
if (!compositeDisposable.isDisposed()) {
compositeDisposable.dispose();
}
}
}

查看文件

@ -0,0 +1,7 @@
package com.xuqm.base.viewmodel.callback;
public interface AdapterObserverCallback {
void notifyItem(int position, Object payload);
void removeItem(int position);
}

查看文件

@ -0,0 +1,13 @@
package com.xuqm.base.viewmodel.callback;
import androidx.paging.PagedList;
import com.xuqm.base.common.RefreshResult;
public interface DataObserverCallback<T> {
void data(PagedList<T> data);
void refreshResult(RefreshResult refreshResult);
void loadMoreResult(RefreshResult refreshResult);
}

查看文件

@ -0,0 +1,7 @@
package com.xuqm.base.viewmodel.callback;
import java.util.ArrayList;
public interface Response<T> {
void onResponse(ArrayList<T> onResponse);
}

查看文件

@ -0,0 +1,41 @@
package com.xuqm.base.web
import android.content.Context
import android.webkit.JavascriptInterface
import com.xuqm.base.common.AppManager
import com.xuqm.base.common.ToolsHelper
import com.xuqm.base.extensions.log
class XAndroidInterface constructor(val mContext: Context, val viewModel: XWebViewModel) {
@JavascriptInterface
fun showToast(callBackId: String, toast: String) {
ToolsHelper.showMessage(toast)
viewModel.callBackSuccess(callBackId)
}
@JavascriptInterface
fun setTitle(callBackId: String, title: String) {
viewModel.setTitle(title)
viewModel.callBackSuccess(callBackId)
}
@JavascriptInterface
fun scan(callBackId: String) {
viewModel.scan(callBackId)
}
@JavascriptInterface
fun closed() {
AppManager.getInstance().finish()
}
@JavascriptInterface
fun setTopMenu(callBackId: String, date: String) {
date.split(",").log()
viewModel.setTopMenu(callBackId,date.split(","))
}
@JavascriptInterface
fun getUserInfo(callBackId: String) {
viewModel.getUserInfo(callBackId)
}
}

查看文件

@ -0,0 +1,25 @@
package com.xuqm.base.web
import android.webkit.WebView
import com.xuqm.base.App
import com.xuqm.base.common.GsonImplHelp
import com.xuqm.base.extensions.log
class XWebCallBack(val webView: WebView) {
fun error(callBackId: String, msg: String) {
sendCallBack(XWebJsCallModel(callBackId, -1, msg, null))
}
fun success(callBackId: String, data: Any?) {
sendCallBack(XWebJsCallModel(callBackId, 0, null, data))
}
private fun sendCallBack(model: XWebJsCallModel) {
App.getInstance().runOnUiThread {
GsonImplHelp.get().toJson(model).log()
webView.loadUrl(
"javascript:__callbackMessageFromNative("+GsonImplHelp.get().toJson(model)+")"
)
}
}
}

查看文件

@ -0,0 +1,115 @@
package com.xuqm.base.web
import android.Manifest
import android.net.Uri
import android.webkit.*
import com.bigkoo.alertview.AlertView
import com.xuqm.base.App
import com.xuqm.base.extensions.log
import com.xuqm.base.extensions.loge
import com.xuqm.base.extensions.runWithPermission
class XWebChromeClient(val mContext: XWebViewActivity, val viewModel: XWebViewModel) :
WebChromeClient() {
override fun onProgressChanged(view: WebView?, newProgress: Int) {
super.onProgressChanged(view, newProgress)
viewModel.setProgress(newProgress)
}
override fun onJsAlert(
view: WebView,
url: String,
message: String,
result: JsResult
): Boolean {
App.getInstance().runOnUiThread {
AlertView(
"", message, null, arrayOf("Confirm"), null, mContext,
AlertView.Style.Alert
) { _, _ ->
result.confirm()
}.show()
}
return true
}
override fun onJsConfirm(
view: WebView,
url: String,
message: String,
result: JsResult
): Boolean {
AlertView(
"", message, null, arrayOf("Confirm", "Cancel"), null, mContext,
AlertView.Style.Alert
) { _, position ->
when (position) {
0 -> result.confirm()
1 -> result.cancel()
}
}.show()
return true
}
override fun onShowFileChooser(
webView: WebView,
filePathCallback: ValueCallback<Array<Uri>>,
fileChooserParams: FileChooserParams
): Boolean {
var types = "*/*"
if (fileChooserParams.acceptTypes.isNotEmpty())
types = if (fileChooserParams.acceptTypes[0].contains("image"))
"image/*"
else "*/*"
mContext.selectedFile(types, filePathCallback)
return true
}
override fun onGeolocationPermissionsShowPrompt(
origin: String?,
callback: GeolocationPermissions.Callback
) {
mContext.runWithPermission(
Manifest.permission.ACCESS_COARSE_LOCATION,
Manifest.permission.ACCESS_FINE_LOCATION,
) {
AlertView(
"定位权限",
"当前页面需要获取定位权限",
null,
arrayOf("Confirm", "Cancel"),
null,
mContext,
AlertView.Style.Alert
) { _, position ->
when (position) {
0 -> callback.invoke(origin, true, true)
1 -> callback.invoke(origin, false, true)
}
super.onGeolocationPermissionsShowPrompt(origin, callback)
}.show()
}
}
override fun onGeolocationPermissionsHidePrompt() {
"onGeolocationPermissionsHidePrompt".loge()
super.onGeolocationPermissionsHidePrompt()
}
override fun onPermissionRequest(request: PermissionRequest) {
"onPermissionRequest".loge()
request.grant(request.resources)
}
override fun onPermissionRequestCanceled(request: PermissionRequest?) {
"onPermissionRequestCanceled".loge()
request?.loge()
super.onPermissionRequestCanceled(request)
}
}

查看文件

@ -0,0 +1,8 @@
package com.xuqm.base.web
data class XWebJsCallModel(
val callBackId: String,
val status: Int,
val errMsg: String?,
val data: Any?
)

查看文件

@ -0,0 +1,237 @@
package com.xuqm.base.web
import android.Manifest
import android.annotation.SuppressLint
import android.app.DownloadManager
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.view.View
import android.webkit.URLUtil
import android.webkit.ValueCallback
import androidx.lifecycle.ViewModelProvider
import com.bigkoo.alertview.AlertView
import com.huawei.hms.hmsscankit.ScanUtil
import com.huawei.hms.ml.scan.HmsScan
import com.luck.picture.lib.basic.PictureSelector
import com.luck.picture.lib.config.SelectMimeType
import com.luck.picture.lib.config.SelectModeConfig
import com.luck.picture.lib.entity.LocalMedia
import com.luck.picture.lib.interfaces.OnResultCallbackListener
import com.xuqm.base.R
import com.xuqm.base.common.FileHelper
import com.xuqm.base.common.GlideEngine
import com.xuqm.base.common.LogHelper
import com.xuqm.base.databinding.ActivityXWebviewBinding
import com.xuqm.base.extensions.loge
import com.xuqm.base.extensions.runWithPermission
import com.xuqm.base.extensions.showMessage
import com.xuqm.base.ui.BaseActivity
import java.io.File
class XWebViewActivity : BaseActivity<ActivityXWebviewBinding>() {
private lateinit var title: String
private lateinit var url: String
private var hasTopBar: Boolean = true
private lateinit var viewModel: XWebViewModel
override fun getLayoutId(): Int = R.layout.activity_x_webview
@SuppressLint("SetJavaScriptEnabled")
override fun initView(savedInstanceState: Bundle?) {
super.initView(savedInstanceState)
LogHelper.e("initView")
showBack(false)
intent?.apply {
hasTopBar = getBooleanExtra("hasTopBar", true)
}
if (hasTopBar) {
showBack(true)
}
viewModel = ViewModelProvider(this)[XWebViewModel::class.java]
viewModel.activity = this
viewModel.callBack = XWebCallBack(binding.XWebView)
binding.XWebView.webChromeClient = XWebChromeClient(this, viewModel)
binding.XWebView.webViewClient = XWebViewClient()
binding.XWebView.clearCache(true)
binding.XWebView.settings.javaScriptEnabled = true
binding.XWebView.settings.setGeolocationEnabled(true)
binding.XWebView.settings.domStorageEnabled = true
binding.XWebView.settings.mediaPlaybackRequiresUserGesture = false;
binding.XWebView.addJavascriptInterface(
XAndroidInterface(mContext, viewModel),
"ZHHBAndroid"
)
binding.XWebView.setDownloadListener { url, _, contentDisposition, mimeType, _ ->
downloadBySystem(url, contentDisposition, mimeType)
}
}
override fun fullscreen(): Boolean {
return false
}
override fun initData() {
super.initData()
LogHelper.e("initData")
intent?.apply {
title = getStringExtra("title") ?: ""
url = getStringExtra("url") ?: ""
}
viewModel.title.observe(this) {
// if(ToolsHelper.isNull(it)){
// baseBinding.baseToolbar.visibility=View.GONE
// }else{
// baseBinding.baseToolbar.visibility=View.VISIBLE
setTitleText(it)
// }
}
viewModel.progress.observe(this) {
binding.XProgressBar.progress = it
if (it > 90) {
binding.XProgressBar.visibility = View.GONE
} else {
binding.XProgressBar.visibility = View.VISIBLE
}
}
}
override fun lateInitView() {
super.lateInitView()
LogHelper.e("lateInitView")
LogHelper.e(url)
setTitleText(title)
binding.XWebView.loadUrl(url)
}
override fun backBtnPressed() {
if (binding.XWebView.canGoBack()) {
binding.XWebView.goBack()
} else finish()
}
override fun onBackPressed() {
if (binding.XWebView.canGoBack()) {
binding.XWebView.goBack()
} else finish()
}
private lateinit var filePathCallback: ValueCallback<Array<Uri>>
fun selectedFile(types: String, back: ValueCallback<Array<Uri>>) {
filePathCallback = back
if (types.contains("image")) {
runWithPermission(
Manifest.permission.CAMERA,
rationaleMethod = {
"无法获取相关权限".showMessage()
back.onReceiveValue(null)
},
permanentDeniedMethod = {
"请在设置界面开放对应权限".showMessage()
back.onReceiveValue(null)
},
callback = {
PictureSelector.create(this)
.openGallery(SelectMimeType.ofImage())
.setImageEngine(GlideEngine.createGlideEngine())
.setSelectionMode(SelectModeConfig.SINGLE)
.forResult(object : OnResultCallbackListener<LocalMedia> {
override fun onResult(result: ArrayList<LocalMedia>) {
val results =
result.map { FileHelper.getFileUri(File(it.realPath)) }
back.onReceiveValue(results.toTypedArray())
}
override fun onCancel() {
back.onReceiveValue(null)
}
})
})
} else {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
addCategory(Intent.CATEGORY_OPENABLE)
type = types
}
mContext.startActivityForResult(intent, 10012)
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (requestCode == 10012) {
if (resultCode == RESULT_OK) {
data?.data?.also { uri ->
filePathCallback.onReceiveValue(arrayOf(uri))
}
} else {
filePathCallback.onReceiveValue(emptyArray())
}
} else if (requestCode == 1111) {
if (resultCode == RESULT_OK) {
val obj: HmsScan? = data?.getParcelableExtra(ScanUtil.RESULT)
if (obj != null) {
viewModel.backScan(obj.originalValue)
} else {
viewModel.backScanError()
}
} else {
viewModel.backScan("Cancel")
}
}
}
private fun downloadBySystem(url: String, contentDisposition: String, mimeType: String) {
val request = DownloadManager.Request(Uri.parse(url))
val fileName: String = URLUtil.guessFileName(url, contentDisposition, mimeType)
fileName.loge()
// request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_VISIBLE)
// 设置通知栏的标题,如果不设置,默认使用文件名
// request.setTitle(BuildConfig.APP_Name);
// 设置通知栏的描述
request.setDescription("正在下载文件:${fileName}");
// 允许在计费流量下下载
request.setAllowedOverMetered(false)
// 允许该记录在下载管理界面可见
request.setVisibleInDownloadsUi(true)
// 允许漫游时下载
request.setAllowedOverRoaming(true)
// 允许下载的网路类型
request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI)
// 设置下载文件保存的路径和文件名
val file = File(FileHelper.getDownloadPath(), fileName)
request.setDestinationUri(Uri.fromFile(file))
val downloadManager = getSystemService(DOWNLOAD_SERVICE) as DownloadManager
// 添加一个下载任务
val downloadId = downloadManager.enqueue(request)
downloadId.loge()
AlertView(
"", "当前文件已经开始下载,请查看通知栏下载进度", null, arrayOf("Confirm"), null, mContext,
AlertView.Style.Alert
) { _, _ ->
}.show()
}
}

查看文件

@ -0,0 +1,19 @@
package com.xuqm.base.web
import android.webkit.WebResourceRequest
import android.webkit.WebView
import android.webkit.WebViewClient
import com.xuqm.base.common.LogHelper
class XWebViewClient : WebViewClient() {
override fun onPageFinished(view: WebView?, url: String?) {
super.onPageFinished(view, url)
LogHelper.e(url)
}
override fun shouldOverrideUrlLoading(view: WebView?, request: WebResourceRequest?): Boolean {
LogHelper.e(request?.toString())
return false
}
}

查看文件

@ -0,0 +1,22 @@
package com.xuqm.base.web
import android.content.Context
import android.content.Intent
object XWebViewHelper {
fun startWebWithTopBar(context: Context, url: String, title: String?) {
val intent: Intent = Intent(context, XWebViewActivity::class.java).apply {
title?.let { putExtra("title", title) }
putExtra("url", url)
putExtra("hasTopBar", true)
}
context.startActivity(intent)
}
fun startWebNoTopBar(context: Context, url: String) {
val intent: Intent = Intent(context, XWebViewActivity::class.java).apply {
putExtra("url", url)
putExtra("hasTopBar", false)
}
context.startActivity(intent)
}
}

查看文件

@ -0,0 +1,128 @@
package com.xuqm.base.web
import android.Manifest
import android.annotation.SuppressLint
import android.view.View
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.bigkoo.alertview.AlertView
import com.huawei.hms.hmsscankit.ScanUtil
import com.huawei.hms.ml.scan.HmsScan
import com.huawei.hms.ml.scan.HmsScanAnalyzerOptions
import com.xuqm.base.App
import com.xuqm.base.extensions.getStringForPreferences
import com.xuqm.base.extensions.log
import com.xuqm.base.extensions.runWithPermission
import com.xuqm.base.extensions.showMessage
import com.xuqm.base.viewmodel.BaseViewModel
import com.xuqm.base.common.SHARE_UESR_NAME
class XWebViewModel : BaseViewModel() {
lateinit var callBack: XWebCallBack
@SuppressLint("StaticFieldLeak")
lateinit var activity: XWebViewActivity
private val _title = MutableLiveData<String>()
val title: LiveData<String> = _title
fun setTitle(title: String) {
_title.postValue(title)
}
private lateinit var callId: String
fun scan(callBackId: String) {
callId = callBackId
App.getInstance().runOnUiThread {
activity.runWithPermission(
Manifest.permission.CAMERA,
rationaleMethod = {
"无法获取相关权限".showMessage()
callBackError(callId, "没有相关权限")
},
permanentDeniedMethod = {
"请在设置界面开放对应权限".showMessage()
callBackError(callId, "没有相关权限")
},
callback = {
ScanUtil.startScan(
activity, 1111, HmsScanAnalyzerOptions.Creator().setHmsScanTypes(
HmsScan.QRCODE_SCAN_TYPE
).create()
);
})
}
}
fun backScan(msg: String) {
callBackSuccess(callId, msg)
}
fun backScanError() {
callBackError(callId, "扫码失败")
}
private val _progress = MutableLiveData<Int>().also { it.postValue(0) }
val progress: LiveData<Int> = _progress
fun setProgress(progress: Int) {
_progress.postValue(progress)
}
fun callBackSuccess(id: String) {
if (this::callBack.isInitialized) {
callBack.success(id, null)
}
}
fun callBackSuccess(id: String, data: Any) {
if (this::callBack.isInitialized) {
callBack.success(id, data)
}
}
fun callBackError(id: String, data: String) {
if (this::callBack.isInitialized) {
callBack.error(id, data)
}
}
fun getUserInfo(callBackId: String) {
if (this::callBack.isInitialized) {
callBack.success(callBackId, activity.getStringForPreferences(SHARE_UESR_NAME))
}
}
lateinit var v: AlertView
fun setTopMenu(id: String, date: List<String>) {
"================>".log()
App.getInstance().runOnUiThread {
if (date.isNotEmpty()) {
activity.baseBinding.baseToolbar.toolbarMenu.visibility = View.VISIBLE
activity.baseBinding.baseToolbar.toolbarMenu.setOnClickListener {
v = AlertView(
"请选择需要操作的内容", "", null, date.toTypedArray(), null, activity,
AlertView.Style.ActionSheet
) { _, position ->
selected(id, position)
}
v.show()
}
} else {
activity.baseBinding.baseToolbar.toolbarMenu.visibility = View.GONE
}
}
}
private fun selected(callBackId: String, data: Int) {
v.dismissImmediately()
if (this::callBack.isInitialized) {
callBack.success(callBackId, data)
}
}
}

某些文件未显示,因为此 diff 中更改的文件太多 显示更多