mirror of
https://gitee.com/willfree/min-vpn-client_v2.git
synced 2026-10-04 19:42:00 +08:00
add many:解决了若干bug,成功跑起来了界面。最大的一个改变是将quick_android库由云端库改为了本地库,然后注释掉了一些报错且没有用到的代码。
This commit is contained in:
1 parent
392aec0de9
commit
f4cec991ed
190 files changed
+9646
-33
No files matched your search
+35
-23
@@ -1,6 +1,11 @@
|
|||||||
plugins {
|
apply plugin: 'com.android.application'
|
||||||
id 'com.android.application'
|
apply plugin: 'kotlin-android'
|
||||||
}
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
//plugins {
|
||||||
|
// id 'com.android.application'
|
||||||
|
// id 'kotlin-android'
|
||||||
|
// id 'kotlin-android-extensions'
|
||||||
|
//}
|
||||||
|
|
||||||
android {
|
android {
|
||||||
applicationVariants.all { variant ->
|
applicationVariants.all { variant ->
|
||||||
@@ -71,15 +76,15 @@ android {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.+'
|
// testImplementation 'junit:junit:4.+'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
|
||||||
compileOnly 'com.google.android.things:androidthings:1.0'
|
compileOnly 'com.google.android.things:androidthings:1.0'
|
||||||
|
|
||||||
// 以下为旧版本VPN客户端的依赖库(去掉kotlin)
|
// 以下为旧版本VPN客户端的依赖库
|
||||||
implementation fileTree(dir: "libs", include: ["*.jar"])
|
implementation fileTree(dir: "libs", include: ["*.jar"])
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
||||||
implementation 'androidx.core:core-ktx:1.3.1'
|
implementation 'androidx.core:core-ktx:1.3.0'
|
||||||
implementation "androidx.appcompat:appcompat:$androidx_base_version"
|
implementation "androidx.appcompat:appcompat:$androidx_base_version"
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
implementation 'androidx.constraintlayout:constraintlayout:1.1.3'
|
||||||
implementation "androidx.recyclerview:recyclerview:$androidx_base_version"
|
implementation "androidx.recyclerview:recyclerview:$androidx_base_version"
|
||||||
@@ -87,20 +92,28 @@ dependencies {
|
|||||||
|
|
||||||
implementation "org.jetbrains.anko:anko-commons:$anko_version"
|
implementation "org.jetbrains.anko:anko-commons:$anko_version"
|
||||||
|
|
||||||
// 所有的模块都依赖于 quick_android_base (必选)
|
// // 所有的模块都依赖于 quick_android_base (必选)
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_base:$quick_android_version"
|
// implementation "com.github.SunnyQjm.quickandroid:quick_android_base:$quick_android_version"
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_custom_view:$quick_android_version"
|
// implementation "com.github.SunnyQjm.quickandroid:quick_android_custom_view:$quick_android_version"
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_easy_bar:$quick_android_version"
|
// implementation "com.github.SunnyQjm.quickandroid:quick_android_easy_bar:$quick_android_version"
|
||||||
// 引入quick_android_mvp模块,来使用网络请求和MVP架构(可选)
|
// // 引入quick_android_mvp模块,来使用网络请求和MVP架构(可选)
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_mvp:$quick_android_version"
|
// implementation "com.github.SunnyQjm.quickandroid:quick_android_mvp:$quick_android_version"
|
||||||
// 引入quick_android_rx_permission模块,来接入链式调用动态请求权限的功能(可选)
|
// // 引入quick_android_rx_permission模块,来接入链式调用动态请求权限的功能(可选)
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_rx_permission:$quick_android_version"
|
// implementation "com.github.SunnyQjm.quickandroid:quick_android_rx_permission:$quick_android_version"
|
||||||
// 引入quick_android_qrcode模块,来接入扫码功能(可选)
|
// // 引入quick_android_qrcode模块,来接入扫码功能(可选)
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_qrcode:$quick_android_version"
|
//// implementation "com.github.SunnyQjm.quickandroid:quick_android_qrcode:$quick_android_version"
|
||||||
// 引入quick_android_image_picker模块,来接入图片选择,图片裁剪等功能(可选)
|
//// // 引入quick_android_image_picker模块,来接入图片选择,图片裁剪等功能(可选)
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_image_picker:$quick_android_version"
|
//// implementation "com.github.SunnyQjm.quickandroid:quick_android_image_picker:$quick_android_version"
|
||||||
// 引入文件选择功能(调用系统的文件管理器)
|
//// // 引入文件选择功能(调用系统的文件管理器)
|
||||||
implementation "com.github.SunnyQjm.quickandroid:quick_android_file_picker:$quick_android_version"
|
//// implementation "com.github.SunnyQjm.quickandroid:quick_android_file_picker:$quick_android_version"
|
||||||
|
api project(path: ':quick_android_base')
|
||||||
|
api project(path: ':quick_android_custom_view')
|
||||||
|
api project(path: ':quick_android_easy_bar')
|
||||||
|
api project(path: ':quick_android_mvp')
|
||||||
|
api project(path: ':quick_android_rx_permission')
|
||||||
|
// api project(path: ':quick_android_qrcode')
|
||||||
|
// api project(path: ':quick_android_image_picker')
|
||||||
|
// api project(path: ':quick_android_file_picker')
|
||||||
|
|
||||||
// loading button
|
// loading button
|
||||||
implementation 'br.com.simplepass:loading-button-android:2.2.0'
|
implementation 'br.com.simplepass:loading-button-android:2.2.0'
|
||||||
@@ -121,13 +134,12 @@ dependencies {
|
|||||||
implementation "com.scwang.smartrefresh:SmartRefreshLayout:$smart_refresh_layout_version"
|
implementation "com.scwang.smartrefresh:SmartRefreshLayout:$smart_refresh_layout_version"
|
||||||
implementation "com.scwang.smartrefresh:SmartRefreshHeader:$smart_refresh_layout_version"
|
implementation "com.scwang.smartrefresh:SmartRefreshHeader:$smart_refresh_layout_version"
|
||||||
|
|
||||||
|
|
||||||
// brvah
|
// brvah
|
||||||
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.49-androidx'
|
implementation 'com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.49-androidx'
|
||||||
|
|
||||||
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
implementation 'com.google.android.material:material:1.3.0-alpha01'
|
||||||
|
|
||||||
testImplementation 'junit:junit:4.13.2'
|
testImplementation 'junit:junit:4.13'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,43 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:dist="http://schemas.android.com/apk/distribution"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
package="com.pkusz.min_vpn_client">
|
package="com.pkusz.min_vpn_client">
|
||||||
|
|
||||||
<application android:label="@string/app_name">
|
<application
|
||||||
<uses-library android:name="com.google.android.things" />
|
android:name=".APP"
|
||||||
|
android:label="@string/app_name"
|
||||||
|
android:allowBackup="true"
|
||||||
|
android:icon="@drawable/logo"
|
||||||
|
android:requestLegacyExternalStorage="true"
|
||||||
|
android:roundIcon="@drawable/logo"
|
||||||
|
android:supportsRtl="true"
|
||||||
|
android:theme="@style/AppTheme"
|
||||||
|
tools:ignore="GoogleAppIndexingWarning">
|
||||||
|
<!-- <uses-library android:name="com.google.android.things" />-->
|
||||||
|
<activity android:name=".activity.welcome.WelcomeActivity">
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.MAIN"/>
|
||||||
|
<category android:name="android.intent.category.LAUNCHER"/>
|
||||||
|
</intent-filter>
|
||||||
|
</activity>
|
||||||
</application>
|
</application>
|
||||||
|
|
||||||
|
<!-- 以下为copy旧版本VPN的权限定义部分 -->
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
|
||||||
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||||
|
<uses-permission android:name="android.permission.READ_PHONE_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.READ_PRIVILEGED_PHONE_STATE"
|
||||||
|
tools:ignore="ProtectedPermissions" />
|
||||||
|
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"
|
||||||
|
tools:ignore="ScopedStorage" />
|
||||||
|
<uses-permission android:name="android.permission.CAMERA" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
<uses-permission android:name="android.permission.USE_BIOMETRIC" />
|
||||||
|
<dist:module dist:instant="true" />
|
||||||
</manifest>
|
</manifest>
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
package com.pkusz.min_vpn_client;
|
||||||
|
/*
|
||||||
|
* @Author: Wang Feng
|
||||||
|
* @Description:
|
||||||
|
* @Version: 1.0.0
|
||||||
|
* @Date: 9:27 2021/6/24
|
||||||
|
* @Copyright: MIN-Group;国家重大科技基础设施——未来网络北大实验室;深圳市信息论与未来网络重点实验室
|
||||||
|
*/
|
||||||
|
|
||||||
|
import android.app.Application;
|
||||||
|
|
||||||
|
public class APP extends Application {
|
||||||
|
@Override
|
||||||
|
public void onCreate() {
|
||||||
|
super.onCreate();
|
||||||
|
|
||||||
|
// 网络接口初始化
|
||||||
|
// ...
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -9,10 +9,14 @@ package com.pkusz.min_vpn_client.activity.base;
|
|||||||
|
|
||||||
import android.widget.Toast;
|
import android.widget.Toast;
|
||||||
import org.greenrobot.eventbus.EventBus;
|
import org.greenrobot.eventbus.EventBus;
|
||||||
import cn.qjm253.quick_android_mvp.base.activity.BaseQuickAndroidMVPActivity;
|
import org.greenrobot.eventbus.Subscribe;
|
||||||
|
import org.greenrobot.eventbus.ThreadMode;
|
||||||
|
|
||||||
public abstract class MINVpnBaseActivity<BasePresenter extends cn.qjm253.quick_android_mvp.base.mvp.BasePresenter<?, ?>>
|
import cn.qjm253.quick_android_mvp.base.activity.BaseQuickAndroidMVPActivity;
|
||||||
extends BaseQuickAndroidMVPActivity<BasePresenter>
|
import cn.qjm253.quick_android_mvp.base.mvp.BasePresenter;
|
||||||
|
|
||||||
|
public abstract class MINVpnBaseActivity<P extends BasePresenter<?, ?>>
|
||||||
|
extends BaseQuickAndroidMVPActivity<P>
|
||||||
implements MINVpnBaseView{
|
implements MINVpnBaseView{
|
||||||
|
|
||||||
public void showWarning(String msg){
|
public void showWarning(String msg){
|
||||||
@@ -37,6 +41,11 @@ public abstract class MINVpnBaseActivity<BasePresenter extends cn.qjm253.quick_a
|
|||||||
EventBus.getDefault().unregister(this);
|
EventBus.getDefault().unregister(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||||
|
public void onReceiveTest(String test){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
// **************************************************************
|
// **************************************************************
|
||||||
// cn.qjm253.quick_android_base.extensions.ContextExtension内容重写
|
// cn.qjm253.quick_android_base.extensions.ContextExtension内容重写
|
||||||
public void toast(String info) {
|
public void toast(String info) {
|
||||||
|
|||||||
@@ -14,13 +14,18 @@ import androidx.annotation.Nullable;
|
|||||||
|
|
||||||
import com.pkusz.min_vpn_client.R;
|
import com.pkusz.min_vpn_client.R;
|
||||||
import com.pkusz.min_vpn_client.activity.base.MINVpnBaseActivity;
|
import com.pkusz.min_vpn_client.activity.base.MINVpnBaseActivity;
|
||||||
|
import com.pkusz.min_vpn_client.extensions.FileUtilsExtensions;
|
||||||
|
|
||||||
import org.jetbrains.annotations.NotNull;
|
import org.jetbrains.annotations.NotNull;
|
||||||
|
|
||||||
|
import cn.qjm253.min_vpn.extensions.FileUtilsExtensionsKt;
|
||||||
|
import cn.qjm253.quick_android_base.util.FileUtils;
|
||||||
import cn.qjm253.quick_android_mvp.base.mvp.BaseView;
|
import cn.qjm253.quick_android_mvp.base.mvp.BaseView;
|
||||||
|
import io.reactivex.functions.Consumer;
|
||||||
|
import io.reactivex.plugins.RxJavaPlugins;
|
||||||
|
|
||||||
public class WelcomeActivity extends MINVpnBaseActivity<WelcomeActivityPresenter>
|
public class WelcomeActivity extends MINVpnBaseActivity<WelcomeActivityPresenter>
|
||||||
implements WelcomeActivityContract.View, BaseView {
|
implements WelcomeActivityContract.View{
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
protected void onCreate(@Nullable Bundle savedInstanceState) {
|
||||||
super.onCreate(savedInstanceState);
|
super.onCreate(savedInstanceState);
|
||||||
@@ -30,7 +35,20 @@ public class WelcomeActivity extends MINVpnBaseActivity<WelcomeActivityPresenter
|
|||||||
@Override
|
@Override
|
||||||
protected void onStart() {
|
protected void onStart() {
|
||||||
super.onStart();
|
super.onStart();
|
||||||
|
// scan.arcsoft.EncryptedUtil.FileUtils [扫描二维码部分。弃用]
|
||||||
|
FileUtilsExtensionsKt.init(FileUtils.INSTANCE,getApplicationContext());
|
||||||
|
// SafetyInfoUtil [GPS部分。弃用]
|
||||||
|
RxJavaPlugins.setErrorHandler(new Consumer<Throwable>() {
|
||||||
|
@Override
|
||||||
|
public void accept(Throwable throwable) throws Exception {
|
||||||
|
throwable.printStackTrace();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
fingerprintAuthenticate();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void fingerprintAuthenticate(){
|
||||||
|
System.out.println("指纹验证...");
|
||||||
}
|
}
|
||||||
|
|
||||||
@NotNull
|
@NotNull
|
||||||
|
|||||||
@@ -0,0 +1,26 @@
|
|||||||
|
package cn.qjm253.min_vpn.extensions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.view.View
|
||||||
|
import cn.qjm253.quick_android_base.util.DisplayUtils
|
||||||
|
|
||||||
|
fun Activity.listenKeyboardVisible(onChange: (isOpen: Boolean) -> Unit) {
|
||||||
|
val contentView = findViewById<View>(android.R.id.content)
|
||||||
|
val activityRoot = window.decorView
|
||||||
|
|
||||||
|
val rectR = Rect()
|
||||||
|
var lastOpenState = false
|
||||||
|
val visibleThreshold = DisplayUtils.dp2px(this, 100)
|
||||||
|
contentView.viewTreeObserver.addOnGlobalLayoutListener {
|
||||||
|
activityRoot.getWindowVisibleDisplayFrame(rectR)
|
||||||
|
val heightDiff = activityRoot.rootView.height - rectR.height()
|
||||||
|
|
||||||
|
// 键盘是否弹出
|
||||||
|
val isOpen = heightDiff > visibleThreshold
|
||||||
|
if (lastOpenState != isOpen) {
|
||||||
|
onChange(isOpen)
|
||||||
|
lastOpenState = isOpen
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
package cn.qjm253.min_vpn.extensions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
|
||||||
|
|
||||||
|
fun Context.easyGetColor(@ColorRes id: Int): Int {
|
||||||
|
return ContextCompat.getColor(this, id)
|
||||||
|
}
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
package cn.qjm253.min_vpn.extensions
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Environment
|
||||||
|
import cn.qjm253.quick_android_base.QuickAndroid
|
||||||
|
import cn.qjm253.quick_android_base.util.FileUtils;
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
val FileUtils.appDataPath: String
|
||||||
|
get() {
|
||||||
|
//val sdDir = File(Environment.getDataDirectory().toString() + "/" + "MIN_VPN" + "/")
|
||||||
|
if(appContext == null) {
|
||||||
|
val sdDir = File(Environment.getDataDirectory().toString() + "/" + "MIN_VPN" + "/")
|
||||||
|
if (!sdDir.exists()) {
|
||||||
|
sdDir.mkdirs()
|
||||||
|
}
|
||||||
|
return sdDir.toString()
|
||||||
|
} else {
|
||||||
|
val sdDir = File(appContext!!.getDir("MIN_VPN", Context.MODE_PRIVATE).absolutePath)
|
||||||
|
if (!sdDir.exists()) {
|
||||||
|
sdDir.mkdirs()
|
||||||
|
}
|
||||||
|
return sdDir.toString()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var appContext: Context? = null
|
||||||
|
get() {
|
||||||
|
return field
|
||||||
|
}
|
||||||
|
set(context: Context?) {
|
||||||
|
field = context
|
||||||
|
}
|
||||||
|
|
||||||
|
fun FileUtils.init(context: Context) {
|
||||||
|
appContext = context
|
||||||
|
}
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
# extensions模块说明
|
||||||
|
由于Kotlin的一些特性,例如可以直接扩展一些类的方法,故保留此部分的Kotlin代码。
|
||||||
|
(旧版本与quick_android耦合得有些严重)
|
||||||
+1
-2
@@ -1,4 +1,4 @@
|
|||||||
package com.pkusz.min_vpn_client;
|
package com.pkusz.min_vpn_client.model;
|
||||||
/*
|
/*
|
||||||
* @Author: Wang Feng
|
* @Author: Wang Feng
|
||||||
* @Description:
|
* @Description:
|
||||||
@@ -15,7 +15,6 @@ import java.net.InetSocketAddress;
|
|||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
|
|
||||||
//import cn.qjm253.min_vpn.model.request.BaseRequest;
|
|
||||||
import com.pkusz.min_vpn_client.model.request.BaseRequest;
|
import com.pkusz.min_vpn_client.model.request.BaseRequest;
|
||||||
import com.pkusz.min_vpn_client.utils.*;
|
import com.pkusz.min_vpn_client.utils.*;
|
||||||
import common.LoggerHelper;
|
import common.LoggerHelper;
|
||||||
+37
-2
@@ -1,5 +1,39 @@
|
|||||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||||
buildscript {
|
buildscript {
|
||||||
|
// ----------start: 导入quickandroid到本地----------
|
||||||
|
ext.kotlin_version = '1.3.72'
|
||||||
|
ext.okhttp_version = '4.0.0'
|
||||||
|
ext.persistent_cookie_jar_version = 'v1.0.1'
|
||||||
|
ext.retrofit_version = '2.6.0'
|
||||||
|
ext.gson_version = '2.8.6'
|
||||||
|
ext.glide_version = '4.9.0'
|
||||||
|
ext.easypermission_version = '3.0.0'
|
||||||
|
ext.bag_qrcode_zxing_version = '1.3.6'
|
||||||
|
ext.immersion_bar_version = '3.0.0-beta05'
|
||||||
|
ext.anko_version = '0.10.8'
|
||||||
|
ext.parallax_layout_version = '1.0'
|
||||||
|
ext.logger_vesion = '2.2.0'
|
||||||
|
ext.brvah_version = '2.9.46'
|
||||||
|
ext.quick_android_version = '0.1.2'
|
||||||
|
|
||||||
|
ext.rx_android_version = '2.1.1'
|
||||||
|
ext.rx_java = '2.2.10'
|
||||||
|
ext.rx_kotlin = '2.2.0'
|
||||||
|
ext.rx_image_picker_version = '2.5.2'
|
||||||
|
|
||||||
|
ext.appcompact_version = '1.1.0'
|
||||||
|
ext.constraintlayout_version = '1.1.3'
|
||||||
|
ext.junit_version = '4.13'
|
||||||
|
ext.test_runner_version = '1.2.0'
|
||||||
|
ext.espresso_core_version = '3.2.0'
|
||||||
|
ext.core_ktx_version = '1.3.0'
|
||||||
|
|
||||||
|
ext.compile_sdk_version = 30
|
||||||
|
ext.target_sdk_version = 30
|
||||||
|
ext.min_sdk_version = 19
|
||||||
|
// -------------end----------------
|
||||||
|
|
||||||
|
ext.kotlin_version = "1.3.72"
|
||||||
ext.quick_android_version = 'v0.1.9'
|
ext.quick_android_version = 'v0.1.9'
|
||||||
ext.anko_version = '0.10.8'
|
ext.anko_version = '0.10.8'
|
||||||
ext.smart_refresh_layout_version = '1.1.0'
|
ext.smart_refresh_layout_version = '1.1.0'
|
||||||
@@ -11,10 +45,11 @@ buildscript {
|
|||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
google()
|
google()
|
||||||
// jcenter()
|
// jcenter()
|
||||||
// mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath "com.android.tools.build:gradle:4.2.1"
|
classpath "com.android.tools.build:gradle:4.2.1"
|
||||||
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
|
|
||||||
// NOTE: Do not place your application dependencies here; they belong
|
// NOTE: Do not place your application dependencies here; they belong
|
||||||
// in the individual module build.gradle files
|
// in the individual module build.gradle files
|
||||||
@@ -27,7 +62,7 @@ allprojects {
|
|||||||
maven { url 'http://maven.aliyun.com/nexus/content/groups/public'}
|
maven { url 'http://maven.aliyun.com/nexus/content/groups/public'}
|
||||||
maven { url 'https://jitpack.io' }
|
maven { url 'https://jitpack.io' }
|
||||||
google()
|
google()
|
||||||
// mavenCentral()
|
mavenCentral()
|
||||||
// jcenter() // Warning: this repository is going to shut down soon
|
// jcenter() // Warning: this repository is going to shut down soon
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,3 +15,6 @@ org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
|||||||
# Android operating system, and which are packaged with your app"s APK
|
# Android operating system, and which are packaged with your app"s APK
|
||||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||||
android.useAndroidX=true
|
android.useAndroidX=true
|
||||||
|
|
||||||
|
# Automatically convert third-party libraries to use AndroidX
|
||||||
|
android.enableJetifier=true
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,161 @@
|
|||||||
|
> ## quick_android_base 模块
|
||||||
|
>
|
||||||
|
> 本模块提供了一些常用的工具库,一些常用的Kotlin扩展,以及一些基本功能的封装
|
||||||
|
|
||||||
|
- ### 初始化
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 在Application中初始化
|
||||||
|
QuickAndroid.init()
|
||||||
|
// 下面这一行输入你应用的英文名,便于为你的应用创建对应名字的应用文件夹等等
|
||||||
|
.appName("your_app_name")
|
||||||
|
// 执行下面一行可以开启应用Activity的侧滑返回支持
|
||||||
|
.enableParallaxBack(context)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- ### 侧滑返回支持说明
|
||||||
|
|
||||||
|
1. 所有需要支持侧滑返回的`Activity`都要继承 [BaseQuickAndroidActivity](https://github.com/SunnyQjm/quickandroid/blob/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/base/activity/BaseQuickAndroidActivity.kt>) ;
|
||||||
|
|
||||||
|
2. 如果继承自[BaseQuickAndroidActivity](https://github.com/SunnyQjm/quickandroid/blob/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/base/activity/BaseQuickAndroidActivity.kt>) 的`Activity`需要按业务需求开启和关闭业务需求,可以使用下面两个函数(下面两个函数定义在[BaseQuickAndroidActivity](https://github.com/SunnyQjm/quickandroid/blob/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/base/activity/BaseQuickAndroidActivity.kt>) 当中,所有集成自该类的Activity均可调用):
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 禁止侧滑返回
|
||||||
|
disableParallaxBack()
|
||||||
|
|
||||||
|
// 开启侧滑返回
|
||||||
|
enableParallaxBack()
|
||||||
|
```
|
||||||
|
|
||||||
|
3. 本侧滑返回使用的是[ParallaxBackLayout](https://github.com/anzewei/ParallaxBackLayout),如果需要自定义侧滑模式,以及触发侧滑返回的位置等等,可以参考该库提供的配置。
|
||||||
|
|
||||||
|
- ### Glide支持
|
||||||
|
|
||||||
|
1. `quick_android_base`库中已经包含了Glide依赖,引入本依赖库之后不需要再另外引入
|
||||||
|
|
||||||
|
2. 当前`quick_android_base`中Glide的版本为`4.9.0`
|
||||||
|
|
||||||
|
3. 已经定义了[MyAppGlideModule](https://github.com/SunnyQjm/quickandroid/blob/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/MyAppGlideModule.kt),不需要再定义,可以通过下面的方式使用
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
GlideApp.with(context)
|
||||||
|
.load(url)
|
||||||
|
.into(imageView)
|
||||||
|
```
|
||||||
|
|
||||||
|
- ### 动态权限请求支持
|
||||||
|
|
||||||
|
本功能基于google开源的[easypermissions](https://github.com/googlesamples/easypermissions)进行,在所有继承自[BaseQuickAndroidActivity](https://github.com/SunnyQjm/quickandroid/blob/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/base/activity/BaseQuickAndroidActivity.kt>) 的Activity或者继承自[BaseQuickAndroidFragment](https://github.com/SunnyQjm/quickandroid/blob/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/base/fragment/BaseQuickAndroidFragment.kt)的Fragment均可使用
|
||||||
|
|
||||||
|
- 动态请求权限
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
easyRequestPermissions(
|
||||||
|
arrayof(Manifest.permissions.CAMERA),
|
||||||
|
R.string.require_camera_permission_tip,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- 复写如下函数
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
/**
|
||||||
|
* 授权成功
|
||||||
|
*/
|
||||||
|
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
|
||||||
|
when(requestCode) {
|
||||||
|
0 -> { // 请求摄像头权限
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权失败
|
||||||
|
*/
|
||||||
|
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
- ### `quick_android_base`支持的其它特性
|
||||||
|
|
||||||
|
- 一些有用的工具类 [utils](https://github.com/SunnyQjm/quickandroid/tree/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/util)
|
||||||
|
|
||||||
|
- 一些有用的扩展 [extensions](https://github.com/SunnyQjm/quickandroid/tree/master/quick_android_base/src/main/java/cn/qjm253/quick_android_base/extensions)
|
||||||
|
|
||||||
|
- 便捷的页面跳转扩展(下面所有的扩展函数均可在所有Activity和Fragment中使用,只可在Kotlin项目中使用)
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
// 简单使用,跳转到一个Activity
|
||||||
|
jumpTo(MainActivity::class.java)
|
||||||
|
|
||||||
|
// 跳转到一个Activity,并处理返回结果(requestCode = 0)
|
||||||
|
jumpForResult(MainACtivity::class.java, 0)
|
||||||
|
|
||||||
|
// 跳转到一个Activity并传递参数
|
||||||
|
jumpTo(MainActivity::class.java,
|
||||||
|
IntentParam()
|
||||||
|
.add("param1", 1)
|
||||||
|
.add("param2", "value2")
|
||||||
|
.add("param3", arrayof("1", "2", "3"))
|
||||||
|
)
|
||||||
|
|
||||||
|
// 跳转到一个Activity,传递参数并处理返回结果
|
||||||
|
jumpTo(MainActivity::class.java,
|
||||||
|
0,
|
||||||
|
IntentParam()
|
||||||
|
.add("param1", 1)
|
||||||
|
.add("param2", "value2")
|
||||||
|
.add("param3", arrayof("1", "2", "3"))
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
- ### `quick_android_base`引入的所有支持库
|
||||||
|
|
||||||
|
```groovy
|
||||||
|
// gson
|
||||||
|
api "com.google.code.gson:gson:${gson_version}"
|
||||||
|
|
||||||
|
// Glide
|
||||||
|
api "com.github.bumptech.glide:glide:${glide_version}"
|
||||||
|
kapt "com.github.bumptech.glide:compiler:${glide_version}"
|
||||||
|
|
||||||
|
// Easypermission
|
||||||
|
api "pub.devrel:easypermissions:${easypermission_version}"
|
||||||
|
|
||||||
|
// RxJava
|
||||||
|
// RxAndroid
|
||||||
|
api "io.reactivex.rxjava2:rxandroid:${rx_android_version}"
|
||||||
|
api "io.reactivex.rxjava2:rxjava:${rx_java}"
|
||||||
|
// RxKotlin
|
||||||
|
api "io.reactivex.rxjava2:rxkotlin:${rx_kotlin}"
|
||||||
|
|
||||||
|
// BRVAHquick_android_base
|
||||||
|
api "com.github.CymChad:BaseRecyclerViewAdapterHelper:${brvah_version}"
|
||||||
|
|
||||||
|
// Retrofit
|
||||||
|
api "com.squareup.retrofit2:retrofit:${retrofit_version}"
|
||||||
|
api "com.squareup.retrofit2:retrofit:${retrofit_version}"
|
||||||
|
api "com.squareup.retrofit2:adapter-rxjava2:${retrofit_version}"
|
||||||
|
api "com.squareup.retrofit2:converter-gson:${retrofit_version}"
|
||||||
|
|
||||||
|
// Okhttp
|
||||||
|
api "com.squareup.okhttp3:okhttp:${okhttp_version}"
|
||||||
|
|
||||||
|
// Logger
|
||||||
|
api "com.orhanobut:logger:${logger_vesion}"
|
||||||
|
|
||||||
|
// ParallaxBackLayout
|
||||||
|
api "com.github.SunnyQjm:ParallaxBackLayout:${parallax_layout_version}"
|
||||||
|
|
||||||
|
// ImmersionBar
|
||||||
|
api "com.gyf.immersionbar:immersionbar:${immersion_bar_version}"
|
||||||
|
```
|
||||||
|
|
||||||
|
|
||||||
@@ -0,0 +1,84 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-kapt'
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion compile_sdk_version
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion min_sdk_version
|
||||||
|
targetSdkVersion target_sdk_version
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
compileOptions {
|
||||||
|
sourceCompatibility JavaVersion.VERSION_1_8
|
||||||
|
targetCompatibility JavaVersion.VERSION_1_8
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
implementation "androidx.appcompat:appcompat:${appcompact_version}"
|
||||||
|
testImplementation "junit:junit:${junit_version}"
|
||||||
|
implementation "androidx.constraintlayout:constraintlayout:${constraintlayout_version}"
|
||||||
|
androidTestImplementation "androidx.test:runner:${test_runner_version}"
|
||||||
|
androidTestImplementation "androidx.test.espresso:espresso-core:${espresso_core_version}"
|
||||||
|
|
||||||
|
// gson
|
||||||
|
api "com.google.code.gson:gson:${gson_version}"
|
||||||
|
|
||||||
|
// Glide
|
||||||
|
api "com.github.bumptech.glide:glide:${glide_version}"
|
||||||
|
kapt "com.github.bumptech.glide:compiler:${glide_version}"
|
||||||
|
|
||||||
|
|
||||||
|
// Easypermission
|
||||||
|
api "pub.devrel:easypermissions:${easypermission_version}"
|
||||||
|
|
||||||
|
// RxJava
|
||||||
|
// RxAndroid
|
||||||
|
api "io.reactivex.rxjava2:rxandroid:${rx_android_version}"
|
||||||
|
api "io.reactivex.rxjava2:rxjava:${rx_java}"
|
||||||
|
// RxKotlin
|
||||||
|
api "io.reactivex.rxjava2:rxkotlin:${rx_kotlin}"
|
||||||
|
|
||||||
|
// BRVAH
|
||||||
|
api "com.github.CymChad:BaseRecyclerViewAdapterHelper:${brvah_version}"
|
||||||
|
|
||||||
|
// Retrofit
|
||||||
|
api "com.squareup.retrofit2:retrofit:${retrofit_version}"
|
||||||
|
api "com.squareup.retrofit2:retrofit:${retrofit_version}"
|
||||||
|
api "com.squareup.retrofit2:adapter-rxjava2:${retrofit_version}"
|
||||||
|
api "com.squareup.retrofit2:converter-gson:${retrofit_version}"
|
||||||
|
|
||||||
|
// Okhttp
|
||||||
|
api "com.squareup.okhttp3:okhttp:${okhttp_version}"
|
||||||
|
|
||||||
|
// Logger
|
||||||
|
api "com.orhanobut:logger:${logger_vesion}"
|
||||||
|
|
||||||
|
// ParallaxBackLayout
|
||||||
|
api "com.github.SunnyQjm:ParallaxBackLayout:${parallax_layout_version}"
|
||||||
|
|
||||||
|
// ImmersionBar
|
||||||
|
api "com.gyf.immersionbar:immersionbar:${immersion_bar_version}"
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+177
@@ -0,0 +1,177 @@
|
|||||||
|
# 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
|
||||||
|
|
||||||
|
# AndroidX
|
||||||
|
-keep class com.google.android.material.** {*;}
|
||||||
|
-keep class androidx.** {*;}
|
||||||
|
-keep public class * extends androidx.**
|
||||||
|
-keep interface androidx.** {*;}
|
||||||
|
-dontwarn com.google.android.material.**
|
||||||
|
-dontnote com.google.android.material.**
|
||||||
|
-dontwarn androidx.**
|
||||||
|
|
||||||
|
# Glide
|
||||||
|
-keep public class * implements com.bumptech.glide.module.GlideModule
|
||||||
|
-keep public class * extends com.bumptech.glide.module.AppGlideModule
|
||||||
|
-keep public enum com.bumptech.glide.load.ImageHeaderParser$** {
|
||||||
|
**[] $VALUES;
|
||||||
|
public *;
|
||||||
|
}
|
||||||
|
|
||||||
|
# for DexGuard only
|
||||||
|
-keepresourcexmlelements manifest/application/meta-data@value=GlideModule
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##---------------Begin: proguard configuration for Gson ----------
|
||||||
|
# Gson uses generic type information stored in a class file when working with fields. Proguard
|
||||||
|
# removes such information by default, so configure it to keep all of it.
|
||||||
|
-keepattributes Signature
|
||||||
|
|
||||||
|
# For using GSON @Expose annotation
|
||||||
|
-keepattributes *Annotation*
|
||||||
|
|
||||||
|
# Gson specific classes
|
||||||
|
-dontwarn sun.misc.**
|
||||||
|
#-keep class com.google.gson.stream.** { *; }
|
||||||
|
|
||||||
|
# Application classes that will be serialized/deserialized over Gson
|
||||||
|
#-keep class com.google.gson.examples.android.model.** { <fields>; }
|
||||||
|
|
||||||
|
# Prevent proguard from stripping interface information from TypeAdapterFactory,
|
||||||
|
# JsonSerializer, JsonDeserializer instances (so they can be used in @JsonAdapter)
|
||||||
|
-keep class * implements com.google.gson.TypeAdapterFactory
|
||||||
|
-keep class * implements com.google.gson.JsonSerializer
|
||||||
|
-keep class * implements com.google.gson.JsonDeserializer
|
||||||
|
|
||||||
|
# Prevent R8 from leaving Data object members always null
|
||||||
|
-keepclassmembers,allowobfuscation class * {
|
||||||
|
@com.google.gson.annotations.SerializedName <fields>;
|
||||||
|
}
|
||||||
|
|
||||||
|
##---------------End: proguard configuration for Gson ----------
|
||||||
|
|
||||||
|
|
||||||
|
##---------------Begin: BRVAH---------------##
|
||||||
|
-keep class com.chad.library.adapter.** {
|
||||||
|
*;
|
||||||
|
}
|
||||||
|
-keep public class * extends com.chad.library.adapter.base.BaseQuickAdapter
|
||||||
|
-keep public class * extends com.chad.library.adapter.base.BaseViewHolder
|
||||||
|
-keepclassmembers class **$** extends com.chad.library.adapter.base.BaseViewHolder {
|
||||||
|
<init>(...);
|
||||||
|
}
|
||||||
|
##---------------End: BRVAH---------------##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##---------------Begin: Okhttp---------------##
|
||||||
|
-dontwarn com.squareup.okhttp3.**
|
||||||
|
-keep class com.squareup.okhttp3.** { *;}
|
||||||
|
-dontwarn okio.**
|
||||||
|
##---------------End: Okhttp---------------##
|
||||||
|
|
||||||
|
|
||||||
|
##---------------Begin: RxJava RxAndroid---------------##
|
||||||
|
-dontwarn sun.misc.**
|
||||||
|
-keepclassmembers class rx.internal.util.unsafe.*ArrayQueue*Field* {
|
||||||
|
long producerIndex;
|
||||||
|
long consumerIndex;
|
||||||
|
}
|
||||||
|
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueProducerNodeRef {
|
||||||
|
rx.internal.util.atomic.LinkedQueueNode producerNode;
|
||||||
|
}
|
||||||
|
-keepclassmembers class rx.internal.util.unsafe.BaseLinkedQueueConsumerNodeRef {
|
||||||
|
rx.internal.util.atomic.LinkedQueueNode consumerNode;
|
||||||
|
}
|
||||||
|
-dontnote rx.internal.util.PlatformDependent
|
||||||
|
##---------------End: RxJava RxAndroid---------------##
|
||||||
|
|
||||||
|
|
||||||
|
##---------------Begin: Junit---------------##
|
||||||
|
-keep class org.junit.** { *; }
|
||||||
|
-dontwarn org.junit.**
|
||||||
|
|
||||||
|
-keep class junit.** { *; }
|
||||||
|
-dontwarn junit.**
|
||||||
|
##---------------End: Junit---------------##
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
##---------------Begin: Retrofit---------------##
|
||||||
|
|
||||||
|
# Retrofit does reflection on generic parameters. InnerClasses is required to use Signature and
|
||||||
|
# EnclosingMethod is required to use InnerClasses.
|
||||||
|
-keepattributes Signature, InnerClasses, EnclosingMethod
|
||||||
|
|
||||||
|
# Retrofit does reflection on method and parameter annotations.
|
||||||
|
-keepattributes RuntimeVisibleAnnotations, RuntimeVisibleParameterAnnotations
|
||||||
|
|
||||||
|
# Retain service method parameters when optimizing.
|
||||||
|
-keepclassmembers,allowshrinking,allowobfuscation interface * {
|
||||||
|
@retrofit2.http.* <methods>;
|
||||||
|
}
|
||||||
|
|
||||||
|
# Ignore annotation used for build tooling.
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
|
||||||
|
|
||||||
|
# Ignore JSR 305 annotations for embedding nullability information.
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
|
||||||
|
-dontwarn kotlin.Unit
|
||||||
|
|
||||||
|
# Top-level functions that can only be used by Kotlin.
|
||||||
|
-dontwarn retrofit2.KotlinExtensions
|
||||||
|
|
||||||
|
# With R8 full mode, it sees no subtypes of Retrofit interfaces since they are created with a Proxy
|
||||||
|
# and replaces all potential values with null. Explicitly keeping the interfaces prevents this.
|
||||||
|
-if interface * { @retrofit2.http.* <methods>; }
|
||||||
|
-keep,allowobfuscation interface <1>
|
||||||
|
|
||||||
|
##---------------End: Retrofit---------------##
|
||||||
|
|
||||||
|
|
||||||
|
##---------------Begin: Okhttp---------------##
|
||||||
|
|
||||||
|
# JSR 305 annotations are for embedding nullability information.
|
||||||
|
-dontwarn javax.annotation.**
|
||||||
|
|
||||||
|
# A resource is loaded with a relative path so the package of this class must be preserved.
|
||||||
|
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
|
||||||
|
|
||||||
|
# Animal Sniffer compileOnly dependency to ensure APIs are compatible with older versions of Java.
|
||||||
|
-dontwarn org.codehaus.mojo.animal_sniffer.*
|
||||||
|
|
||||||
|
# OkHttp platform used only on JVM and when Conscrypt dependency is available.
|
||||||
|
-dontwarn okhttp3.internal.platform.ConscryptPlatform
|
||||||
|
|
||||||
|
##---------------Begin: End---------------##
|
||||||
|
|
||||||
|
##---------------Begin: ParallaxBackLayout---------------##
|
||||||
|
-keep public enum com.github.anzewei.parallaxbacklayout.ParallaxBack$** {
|
||||||
|
**[] $VALUES;
|
||||||
|
public *;
|
||||||
|
}
|
||||||
|
##---------------End: ParallaxBackLayout---------------##
|
||||||
|
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package cn.qjm253.quick_android_base;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
assertEquals("cn.qjm253.quick_android_base.test", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cn.qjm253.quick_android_base">
|
||||||
|
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE"/>
|
||||||
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
<uses-permission android:name="android.permission.CAMERA"/>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</manifest>
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
//package cn.qjm253.quick_android_base.util
|
||||||
|
//
|
||||||
|
//import android.content.Context
|
||||||
|
//import android.net.ConnectivityManager
|
||||||
|
//import android.net.NetworkInfo
|
||||||
|
//
|
||||||
|
//
|
||||||
|
///**
|
||||||
|
// * 网络工具类
|
||||||
|
// * Created by sunny on 18-2-23.
|
||||||
|
// */
|
||||||
|
//
|
||||||
|
//class NetworkUtil(context: Context){
|
||||||
|
//
|
||||||
|
// val manager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 检测网络是否连接
|
||||||
|
// * @return
|
||||||
|
// */
|
||||||
|
// private fun checkNetworkState(): Boolean {
|
||||||
|
// var flag = false
|
||||||
|
// //得到网络连接信息
|
||||||
|
// //去进行判断网络是否连接
|
||||||
|
// if (manager.activeNetworkInfo != null) {
|
||||||
|
// flag = manager.activeNetworkInfo.isAvailable
|
||||||
|
// }
|
||||||
|
// return flag
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 判断是否连了wifi
|
||||||
|
// */
|
||||||
|
// fun isWifiConnect(): Boolean{
|
||||||
|
// val wifiState = manager.getNetworkInfo(ConnectivityManager.TYPE_WIFI).state
|
||||||
|
// return wifiState == NetworkInfo.State.CONNECTED
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 获取当前网络的类型
|
||||||
|
// */
|
||||||
|
// fun getConnectedType(context: Context?): Int {
|
||||||
|
// if (context != null) {
|
||||||
|
// val mConnectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
// val mNetworkInfo = mConnectivityManager.activeNetworkInfo
|
||||||
|
// if (mNetworkInfo != null && mNetworkInfo.isAvailable) {
|
||||||
|
// return mNetworkInfo.type
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return -1
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * 是否是移动数据连接
|
||||||
|
// */
|
||||||
|
// fun isMobileConnected(context: Context?): Boolean {
|
||||||
|
// if (context != null) {
|
||||||
|
// val mConnectivityManager = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
// val mMobileNetworkInfo = mConnectivityManager.getNetworkInfo(ConnectivityManager.TYPE_MOBILE)
|
||||||
|
// if (mMobileNetworkInfo != null) {
|
||||||
|
// return mMobileNetworkInfo.isAvailable
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// return false
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
package cn.qjm253.quick_android_base
|
||||||
|
|
||||||
|
import com.bumptech.glide.annotation.GlideModule
|
||||||
|
import com.bumptech.glide.module.AppGlideModule
|
||||||
|
|
||||||
|
@GlideModule
|
||||||
|
class MyAppGlideModule : AppGlideModule()
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
package cn.qjm253.quick_android_base
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import android.content.Context
|
||||||
|
import com.github.anzewei.parallaxbacklayout.ParallaxHelper
|
||||||
|
import com.orhanobut.logger.AndroidLogAdapter
|
||||||
|
import com.orhanobut.logger.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
object QuickAndroid {
|
||||||
|
var APP_NAME = "QuickAndroid"
|
||||||
|
var enableParallaxBack = false
|
||||||
|
|
||||||
|
fun appName(appName: String = "quick_android"): QuickAndroid {
|
||||||
|
APP_NAME = appName
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun init(): QuickAndroid {
|
||||||
|
Logger.addLogAdapter(AndroidLogAdapter())
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启侧滑
|
||||||
|
* 如需修改侧滑触发的模式和位置,参看BaseQuickAndroidActivity
|
||||||
|
* @see cn.qjm253.quick_android_mvp.base.activity.BaseQuickAndroidActivity
|
||||||
|
*/
|
||||||
|
fun enableParallaxBack(context: Application): QuickAndroid {
|
||||||
|
/**
|
||||||
|
* 初始化侧滑操作
|
||||||
|
* 需要在Application当中初始化
|
||||||
|
*/
|
||||||
|
context.registerActivityLifecycleCallbacks(ParallaxHelper.getInstance())
|
||||||
|
enableParallaxBack = true
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
}
|
||||||
+110
@@ -0,0 +1,110 @@
|
|||||||
|
package cn.qjm253.quick_android_base.base.activity
|
||||||
|
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.appcompat.app.AppCompatActivity
|
||||||
|
import cn.qjm253.quick_android_base.QuickAndroid
|
||||||
|
import cn.qjm253.quick_android_base.base.fragment.BaseQuickAndroidFragment
|
||||||
|
import com.github.anzewei.parallaxbacklayout.ParallaxBack
|
||||||
|
import com.github.anzewei.parallaxbacklayout.ParallaxHelper
|
||||||
|
import com.github.anzewei.parallaxbacklayout.widget.ParallaxBackLayout
|
||||||
|
import pub.devrel.easypermissions.EasyPermissions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
@ParallaxBack
|
||||||
|
abstract class BaseQuickAndroidActivity : AppCompatActivity(),
|
||||||
|
EasyPermissions.PermissionCallbacks, BaseQuickAndroidFragment.OnFragmentInteractionListener {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val EP_CAMERA = 1
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否需要支持侧滑返回
|
||||||
|
*/
|
||||||
|
open fun isNeedScrollBack() = true
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 侧滑是否响应全屏
|
||||||
|
* true => 全屏滑动都会触发侧滑
|
||||||
|
* false => 边缘滑动才会触发侧滑
|
||||||
|
*/
|
||||||
|
open fun isParallaxBackLayoutFullScreen() = false
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
if (QuickAndroid.enableParallaxBack) { // 手动开启侧滑才执行侧滑相关代码
|
||||||
|
ParallaxHelper.getParallaxBackLayout(this, true)?.let { layout ->
|
||||||
|
// 设置侧滑的响应模式
|
||||||
|
if (isParallaxBackLayoutFullScreen()) {
|
||||||
|
layout.setEdgeMode(ParallaxBackLayout.EDGE_MODE_FULL)
|
||||||
|
} else {
|
||||||
|
layout.setEdgeMode(ParallaxBackLayout.EDGE_MODE_DEFAULT)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 设置是否允许触发侧滑返回
|
||||||
|
if (isNeedScrollBack()) {
|
||||||
|
layout.setEnableGesture(true)
|
||||||
|
} else {
|
||||||
|
layout.setEnableGesture(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 禁止侧滑返回
|
||||||
|
*/
|
||||||
|
fun disableParallaxBack() {
|
||||||
|
if(QuickAndroid.enableParallaxBack) {
|
||||||
|
ParallaxHelper.getParallaxBackLayout(this)
|
||||||
|
?.setEnableGesture(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 开启侧滑返回
|
||||||
|
*/
|
||||||
|
fun enableParallaxBack() {
|
||||||
|
if(QuickAndroid.enableParallaxBack){
|
||||||
|
ParallaxHelper.getParallaxBackLayout(this)
|
||||||
|
?.setEnableGesture(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权成功
|
||||||
|
*/
|
||||||
|
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权失败
|
||||||
|
*/
|
||||||
|
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open fun easyRequestPermissions(permissions: Array<String>, @StringRes tip: Int, requestCode: Int) {
|
||||||
|
if (EasyPermissions.hasPermissions(this, *permissions)) {
|
||||||
|
onPermissionsGranted(requestCode, permissions.toMutableList())
|
||||||
|
} else {
|
||||||
|
EasyPermissions.requestPermissions(this, getString(tip), requestCode, *permissions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFragmentInteraction(uri: Uri?) {
|
||||||
|
}
|
||||||
|
}
|
||||||
+191
@@ -0,0 +1,191 @@
|
|||||||
|
package cn.qjm253.quick_android_base.base.fragment
|
||||||
|
|
||||||
|
import android.Manifest
|
||||||
|
import android.content.Context
|
||||||
|
import android.net.Uri
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.view.LayoutInflater
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import cn.qjm253.quick_android_base.R
|
||||||
|
import cn.qjm253.quick_android_base.base.activity.BaseQuickAndroidActivity
|
||||||
|
import cn.qjm253.quick_android_base.params.IntentParam
|
||||||
|
import pub.devrel.easypermissions.EasyPermissions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 支持在第一次创建视图时初始化,且不会重复添加导致重叠
|
||||||
|
*/
|
||||||
|
abstract class BaseQuickAndroidFragment : Fragment(), EasyPermissions.PermissionCallbacks {
|
||||||
|
companion object {
|
||||||
|
private const val STATE_SAVE_IS_HIDDEN = "STATE_SAVE_IS_HIDDEN"
|
||||||
|
|
||||||
|
fun <T: Fragment> newInstance_(clazz: Class<T>, intentParam: IntentParam?): T {
|
||||||
|
val args = Bundle()
|
||||||
|
intentParam?.applyBundle(args)
|
||||||
|
val fragment = clazz.newInstance()
|
||||||
|
fragment.arguments = args
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected var mView: View? = null
|
||||||
|
protected var mListener: OnFragmentInteractionListener? = null
|
||||||
|
|
||||||
|
// 当前视图是否构建完成
|
||||||
|
private var prepare: Boolean = false
|
||||||
|
// 是否是第一次加载数据
|
||||||
|
private var first: Boolean = true
|
||||||
|
|
||||||
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
|
super.onCreate(savedInstanceState)
|
||||||
|
|
||||||
|
//当页面重启时,Fragment会被保存回复,而此时再加载Fragment便会导致重叠
|
||||||
|
if (savedInstanceState != null) {
|
||||||
|
val isHidden = savedInstanceState.getBoolean(STATE_SAVE_IS_HIDDEN)
|
||||||
|
val ft = fragmentManager?.beginTransaction()
|
||||||
|
if (isHidden)
|
||||||
|
ft?.hide(this)
|
||||||
|
else
|
||||||
|
ft?.show(this)
|
||||||
|
ft?.commit()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onSaveInstanceState(outState: Bundle) {
|
||||||
|
super.onSaveInstanceState(outState)
|
||||||
|
outState.putBoolean(STATE_SAVE_IS_HIDDEN, isHidden)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||||
|
if(mView == null) {
|
||||||
|
mView = inflater.inflate(getRes(), container, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 如果之前被添加过,则先移除
|
||||||
|
if(mView?.parent != null) {
|
||||||
|
(mView?.parent as ViewGroup).removeView(mView)
|
||||||
|
}
|
||||||
|
return mView
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 当视图构建完成时会调用这个回调
|
||||||
|
*/
|
||||||
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
if(!prepare) {
|
||||||
|
initView()
|
||||||
|
prepare = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 下面的函数由系统调用
|
||||||
|
* 在Fragment可见时加载数据
|
||||||
|
*
|
||||||
|
* @param isVisibleToUser
|
||||||
|
*/
|
||||||
|
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
|
||||||
|
super.setUserVisibleHint(isVisibleToUser)
|
||||||
|
if(isVisibleToUser) {
|
||||||
|
onVisible()
|
||||||
|
} else {
|
||||||
|
onInvisible()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
protected open fun onInvisible() {}
|
||||||
|
|
||||||
|
protected open fun onVisible() {
|
||||||
|
//加载数据
|
||||||
|
if (prepare && first) {
|
||||||
|
first = false
|
||||||
|
initialLoadData()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onAttach(context: Context) {
|
||||||
|
super.onAttach(context)
|
||||||
|
if (context is OnFragmentInteractionListener) {
|
||||||
|
mListener = context
|
||||||
|
} else {
|
||||||
|
throw RuntimeException("$context must implement OnFragmentInteractionListener")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fragment页面的布局文件
|
||||||
|
*/
|
||||||
|
abstract fun getRes(): Int
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 视图初始化操作
|
||||||
|
*/
|
||||||
|
abstract fun initView()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 页面第一次加载时加载数据
|
||||||
|
*/
|
||||||
|
abstract fun initialLoadData()
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This interface must be implemented by activities that contain this
|
||||||
|
* fragment to allow an interaction in this fragment to be communicated
|
||||||
|
* to the activity and potentially other fragments contained in that
|
||||||
|
* activity.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* See the Android Training lesson [Communicating with Other Fragments](http://developer.android.com/training/basics/fragments/communicating.html) for more information.
|
||||||
|
*/
|
||||||
|
interface OnFragmentInteractionListener {
|
||||||
|
// TODO: Update argument type and name
|
||||||
|
fun onFragmentInteraction(uri: Uri?)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||||
|
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权成功
|
||||||
|
*/
|
||||||
|
override fun onPermissionsGranted(requestCode: Int, perms: MutableList<String>) {
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 授权失败
|
||||||
|
*/
|
||||||
|
override fun onPermissionsDenied(requestCode: Int, perms: MutableList<String>) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open fun requestPermissions(permissions: Array<String>, @StringRes tip: Int, requestCode: Int) {
|
||||||
|
activity?.let {
|
||||||
|
if(EasyPermissions.hasPermissions(it, *permissions)) {
|
||||||
|
onPermissionsGranted(requestCode, permissions.toMutableList())
|
||||||
|
} else {
|
||||||
|
EasyPermissions.requestPermissions(this, getString(tip), requestCode, *permissions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
open fun requestCamera(@StringRes tip: Int = R.string.camera_permission_require) {
|
||||||
|
requestPermissions(arrayOf(Manifest.permission.CAMERA), tip, BaseQuickAndroidActivity.EP_CAMERA)
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.os.Vibrator
|
||||||
|
import androidx.annotation.IdRes
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentActivity
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:52
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Activity触发震动的扩展
|
||||||
|
* @param duration 震动持续时间
|
||||||
|
* @param amplitude 震动的强度,限制在 1~255 之间
|
||||||
|
*/
|
||||||
|
fun Activity.vibrator(duration: Long = 200, amplitude: Int = 100) {
|
||||||
|
val vibrator = getSystemService(Context.VIBRATOR_SERVICE) as Vibrator
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
vibrator.vibrate(VibrationEffect.createOneShot(duration, amplitude))
|
||||||
|
} else {
|
||||||
|
vibrator.vibrate(duration)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun <T: Fragment> FragmentActivity.addFragmentToActivity(@IdRes fragmentLayoutId: Int, fragmentGenerator: () -> T): T {
|
||||||
|
var fragment = this.supportFragmentManager.findFragmentById(fragmentLayoutId) as? T
|
||||||
|
if(fragment == null) {
|
||||||
|
fragment = fragmentGenerator()
|
||||||
|
val transaction = supportFragmentManager.beginTransaction()
|
||||||
|
transaction.replace(fragmentLayoutId, fragment)
|
||||||
|
transaction.commit()
|
||||||
|
}
|
||||||
|
return fragment
|
||||||
|
}
|
||||||
+12
@@ -0,0 +1,12 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import cn.qjm253.quick_android_base.util.GsonUtil
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Any.toJson(): String {
|
||||||
|
return GsonUtil.bean2Json(this)
|
||||||
|
}
|
||||||
+149
@@ -0,0 +1,149 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.app.DatePickerDialog
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.annotation.StyleRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import android.widget.Toast
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import cn.qjm253.quick_android_base.params.IntentParam
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Context.toast(info: String, duration: Int = Toast.LENGTH_SHORT) {
|
||||||
|
Toast.makeText(this, info, duration)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.toast(@StringRes stringRes: Int, duration: Int = Toast.LENGTH_SHORT) {
|
||||||
|
Toast.makeText(this, stringRes, duration)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Context.jumpTo(cls: Class<*>, vararg flags: Int) {
|
||||||
|
val intent = Intent(this, cls)
|
||||||
|
flags.forEach {
|
||||||
|
intent.addFlags(it)
|
||||||
|
}
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Context.jumpTo(cls: Class<*>, intentParam: IntentParam? = null, vararg flags: Int) {
|
||||||
|
val intent = Intent(this, cls)
|
||||||
|
flags.forEach {
|
||||||
|
intent.addFlags(it)
|
||||||
|
}
|
||||||
|
intentParam?.applyParam(intent)
|
||||||
|
startActivity(intent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity.jumpForResult(cls: Class<*>, requestCode: Int = 0, intentParam: IntentParam? = null) {
|
||||||
|
val intent = Intent(this, cls)
|
||||||
|
intentParam?.applyParam(intent)
|
||||||
|
startActivityForResult(intent, requestCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Fragment.jumpForResult(cls: Class<*>, requestCode: Int, intentParam: IntentParam? = null) {
|
||||||
|
val intent = Intent(activity, cls)
|
||||||
|
intentParam?.applyParam(intent)
|
||||||
|
startActivityForResult(intent, requestCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////
|
||||||
|
///////// 下面是软键盘相关的扩展
|
||||||
|
////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 切换状态,打开则关闭,关闭则打开
|
||||||
|
*/
|
||||||
|
fun Context.changeSoftKeyboard() {
|
||||||
|
val im = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
|
||||||
|
if (!im.isActive)
|
||||||
|
return
|
||||||
|
im.toggleSoftInput(0, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 隐藏软键盘
|
||||||
|
*/
|
||||||
|
fun Context.hideSoftKeyboard(view: View) {
|
||||||
|
val im = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
if (!im.isActive)
|
||||||
|
return
|
||||||
|
//隐藏软键盘 //
|
||||||
|
im.hideSoftInputFromWindow(view.windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.showSoftKeyboard(view: View) {
|
||||||
|
val im = this.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
if (im.isActive)
|
||||||
|
return
|
||||||
|
im.showSoftInputFromInputMethod(view.windowToken, 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Context.showSelectDateDialog(
|
||||||
|
@StyleRes style: Int = android.R.style.Theme_DeviceDefault_Dialog,
|
||||||
|
listener: (View, Int, Int, Int) -> Unit =
|
||||||
|
{ view, year, month, dayOfMonth -> }
|
||||||
|
) {
|
||||||
|
val c = Calendar.getInstance()
|
||||||
|
DatePickerDialog(
|
||||||
|
this, style, listener, c.get(Calendar.YEAR),
|
||||||
|
c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.showSelectDateDialog(
|
||||||
|
@StyleRes style: Int = android.R.style.Theme_DeviceDefault_Dialog,
|
||||||
|
listener: (Long) -> Unit =
|
||||||
|
{ time -> }
|
||||||
|
) {
|
||||||
|
val c = Calendar.getInstance()
|
||||||
|
DatePickerDialog(
|
||||||
|
this, style, { view, year, month, dayOfMonth ->
|
||||||
|
c.timeInMillis = 0
|
||||||
|
c.set(Calendar.YEAR, year)
|
||||||
|
c.set(Calendar.MONTH, month)
|
||||||
|
c.set(Calendar.DAY_OF_MONTH, dayOfMonth)
|
||||||
|
listener(c.timeInMillis)
|
||||||
|
}, c.get(Calendar.YEAR),
|
||||||
|
c.get(Calendar.MONTH), c.get(Calendar.DAY_OF_MONTH)
|
||||||
|
)
|
||||||
|
.show()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun Context.checkMyPermission(permission: String, granted: () -> Unit = {}, deny: () -> Unit = {}) {
|
||||||
|
if (ContextCompat.checkSelfPermission(this, permission) == PackageManager.PERMISSION_GRANTED) {
|
||||||
|
granted()
|
||||||
|
} else {
|
||||||
|
deny()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 不够位数的在前面补0,保留num的长度位数字
|
||||||
|
* @param code
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
private fun autoGenericCode(code: String, num: Int): String {
|
||||||
|
return String.format("%0" + num + "d", Integer.parseInt(code))
|
||||||
|
}
|
||||||
|
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import java.lang.StringBuilder
|
||||||
|
import java.math.RoundingMode
|
||||||
|
import java.text.DecimalFormat
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun Double.toFixed(num: Int): String {
|
||||||
|
val sb = StringBuilder("0.")
|
||||||
|
for(i in 1..num){
|
||||||
|
sb.append("0")
|
||||||
|
}
|
||||||
|
val df = DecimalFormat(sb.toString())
|
||||||
|
df.roundingMode = RoundingMode.HALF_UP;
|
||||||
|
return df.format(this)
|
||||||
|
}
|
||||||
+44
@@ -0,0 +1,44 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import androidx.fragment.app.Fragment
|
||||||
|
import androidx.fragment.app.FragmentManager
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-8 下午3:42
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun <T: Fragment> FragmentManager.getLazySingleton(tag: String, clazz: Class<T>): Lazy<T> {
|
||||||
|
val fragmentManager = this
|
||||||
|
return object : Lazy<T> {
|
||||||
|
private var t: T? = null
|
||||||
|
|
||||||
|
private fun _getFragment(): T {
|
||||||
|
if(t == null) {
|
||||||
|
t = getFragment(tag, clazz, fragmentManager)
|
||||||
|
}
|
||||||
|
return t!!
|
||||||
|
}
|
||||||
|
|
||||||
|
override val value: T
|
||||||
|
get() = _getFragment()
|
||||||
|
|
||||||
|
override fun isInitialized(): Boolean {
|
||||||
|
return t != null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun <T: Fragment> getFragment(tag: String, clazz: Class<T>, fragmentManager: FragmentManager): T {
|
||||||
|
var fragment: T? = fragmentManager.findFragmentByTag(tag) as? T
|
||||||
|
if (fragment == null) {
|
||||||
|
fragment = clazz.newInstance()
|
||||||
|
fragmentManager
|
||||||
|
.beginTransaction()
|
||||||
|
.add(fragment, tag)
|
||||||
|
.commitNow()
|
||||||
|
}
|
||||||
|
return fragment!!
|
||||||
|
}
|
||||||
|
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.media.MediaMetadataRetriever
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import android.widget.ImageView
|
||||||
|
import cn.qjm253.quick_android_base.GlideApp
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.VideoDecoder.FRAME_OPTION
|
||||||
|
import com.bumptech.glide.request.RequestOptions
|
||||||
|
import com.bumptech.glide.load.engine.bitmap_recycle.BitmapPool
|
||||||
|
import com.bumptech.glide.load.resource.bitmap.BitmapTransformation
|
||||||
|
import java.security.MessageDigest
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
fun ImageView.load(url: String) {
|
||||||
|
GlideApp.with(this)
|
||||||
|
.load(url)
|
||||||
|
.into(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ImageView.loadCircle(url: String) {
|
||||||
|
GlideApp.with(this)
|
||||||
|
.load(url)
|
||||||
|
.circleCrop()
|
||||||
|
.into(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun ImageView.loadCircle(@DrawableRes res: Int) {
|
||||||
|
GlideApp.with(this)
|
||||||
|
.load(res)
|
||||||
|
.circleCrop()
|
||||||
|
.into(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("CheckResult")
|
||||||
|
fun ImageView.loadVideoFrame(url: String, frameTimeMicros: Long) {
|
||||||
|
val requestOptions = RequestOptions.frameOf(frameTimeMicros)
|
||||||
|
requestOptions.set(FRAME_OPTION, MediaMetadataRetriever.OPTION_CLOSEST);
|
||||||
|
requestOptions.transform(object : BitmapTransformation() {
|
||||||
|
override fun transform(pool: BitmapPool, toTransform: Bitmap, outWidth: Int, outHeight: Int): Bitmap {
|
||||||
|
return toTransform
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun updateDiskCacheKey(messageDigest: MessageDigest) {
|
||||||
|
try {
|
||||||
|
messageDigest.update((context.packageName + "RotateTransform").toByteArray(charset("utf-8")))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
GlideApp.with(this)
|
||||||
|
.load(url)
|
||||||
|
.apply(requestOptions)
|
||||||
|
.into(this)
|
||||||
|
}
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import com.orhanobut.logger.Logger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun String.e(vararg args: Any) {
|
||||||
|
Logger.e(this, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Throwable.e(vararg args: Any) {
|
||||||
|
Logger.e(this, this.message ?: "", args)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun String.i(vararg args: Any) {
|
||||||
|
Logger.i(this, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.d(vararg args: Any) {
|
||||||
|
Logger.d(this, args)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.json(vararg args: Any) {
|
||||||
|
Logger.json(this)
|
||||||
|
}
|
||||||
+48
@@ -0,0 +1,48 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
fun Long.toYMD(): String {
|
||||||
|
val instance = Calendar.getInstance()
|
||||||
|
instance.timeInMillis = this
|
||||||
|
return "${instance.get(Calendar.YEAR)}" +
|
||||||
|
"-${instance.get(Calendar.MONTH) + 1}" +
|
||||||
|
"-${instance.get(Calendar.DAY_OF_MONTH)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Long.toYMD_HM(): String {
|
||||||
|
val instance = Calendar.getInstance()
|
||||||
|
instance.timeInMillis = this
|
||||||
|
return "${instance.get(Calendar.YEAR)}" +
|
||||||
|
"-${instance.get(Calendar.MONTH) + 1}" +
|
||||||
|
"-${instance.get(Calendar.DAY_OF_MONTH)}" +
|
||||||
|
" ${instance.get(Calendar.HOUR_OF_DAY)}" +
|
||||||
|
":${instance.get(Calendar.MINUTE)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Long.toYMD_HMS(): String {
|
||||||
|
val instance = Calendar.getInstance()
|
||||||
|
instance.timeInMillis = this
|
||||||
|
return "${instance.get(Calendar.YEAR)}" +
|
||||||
|
"-${instance.get(Calendar.MONTH) + 1}" +
|
||||||
|
"-${instance.get(Calendar.DAY_OF_MONTH)}" +
|
||||||
|
" ${instance.get(Calendar.HOUR_OF_DAY)}" +
|
||||||
|
":${instance.get(Calendar.MINUTE)}" +
|
||||||
|
":${instance.get(Calendar.SECOND)}"
|
||||||
|
}
|
||||||
|
|
||||||
|
fun String.toTimeStamp(): Long {
|
||||||
|
val arr = this.split('-')
|
||||||
|
val instance = Calendar.getInstance()
|
||||||
|
instance.timeInMillis = 0
|
||||||
|
instance.set(Calendar.YEAR, arr[0].toInt())
|
||||||
|
instance.set(Calendar.MONTH, arr[1].toInt() - 1)
|
||||||
|
instance.set(Calendar.DAY_OF_MONTH, arr[2].toInt())
|
||||||
|
return instance.timeInMillis
|
||||||
|
}
|
||||||
+13
@@ -0,0 +1,13 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sunny on 17-12-29.
|
||||||
|
*/
|
||||||
|
|
||||||
|
fun easySin(angle: Float, radius:Int = 1): Double {
|
||||||
|
return Math.sin(Math.PI * angle / 180) * radius
|
||||||
|
}
|
||||||
|
|
||||||
|
fun easyCos(angle: Float, radius: Int = 1): Double {
|
||||||
|
return Math.cos(Math.PI * angle / 180) * radius
|
||||||
|
}
|
||||||
+20
@@ -0,0 +1,20 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* Created by sunny on 18-1-2.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 该函数用来判断一个触摸事件是否发生在某个具体View的范围内
|
||||||
|
*/
|
||||||
|
fun MotionEvent.isInView(view: View): Boolean {
|
||||||
|
val location = IntArray(2)
|
||||||
|
view.getLocationOnScreen(location)
|
||||||
|
val x = location[0]
|
||||||
|
val y = location[1]
|
||||||
|
return !(this.x < x || this.x > x + view.width || this.y < y || this.y > y + view.height)
|
||||||
|
}
|
||||||
+39
@@ -0,0 +1,39 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import kotlin.properties.ReadWriteProperty
|
||||||
|
import kotlin.reflect.KProperty
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sunny on 17-12-28.
|
||||||
|
*/
|
||||||
|
|
||||||
|
object MyDelegates{
|
||||||
|
fun <T> notNullAndOnlyInitFirstTime(): ReadWriteProperty<Any?, T> = NotNullAndOnlyInitFirstTime()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 本代理类代理的属性,不可为null,可延迟初始化,但是只会被初始化一次,第一次初始化之后的set操作都将被忽略
|
||||||
|
*/
|
||||||
|
private class NotNullAndOnlyInitFirstTime<T> : ReadWriteProperty<Any?, T> {
|
||||||
|
private var value: T? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get代理
|
||||||
|
* @param thisRef 被代理的对象
|
||||||
|
* @param property 保存了被代理对象的名字等
|
||||||
|
*/
|
||||||
|
override fun getValue(thisRef: Any?, property: KProperty<*>): T =
|
||||||
|
value ?: throw IllegalStateException("${property.name} is not initialized!")
|
||||||
|
|
||||||
|
/**
|
||||||
|
* set代理
|
||||||
|
* @param thisRef 被代理的对象
|
||||||
|
* @param property 保存了被代理对象的名字等
|
||||||
|
* @param value 元数据
|
||||||
|
*/
|
||||||
|
override fun setValue(thisRef: Any?, property: KProperty<*>, value: T) {
|
||||||
|
if(this.value == null)
|
||||||
|
this.value = value
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.Html
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import cn.qjm253.quick_android_base.util.SpannableUtil
|
||||||
|
|
||||||
|
@SuppressLint("ResourceType")
|
||||||
|
fun TextView.setStyleText(text: String = this.text.toString(), @StringRes textRes: Int = -1,
|
||||||
|
isBold: Boolean = false, isItalic: Boolean = false, @ColorInt color: Int = -1,
|
||||||
|
@ColorRes colorRes: Int = -1) {
|
||||||
|
val t = if (textRes > 0)
|
||||||
|
context.getString(textRes)
|
||||||
|
else
|
||||||
|
text
|
||||||
|
var result: CharSequence = t
|
||||||
|
if (isBold)
|
||||||
|
result = SpannableUtil.bold(result)
|
||||||
|
if (isItalic)
|
||||||
|
result = SpannableUtil.italic(result)
|
||||||
|
if (color > 0 || colorRes > 0)
|
||||||
|
result = SpannableUtil.color(if (colorRes > 0) resources.getColor(colorRes) else color, result)
|
||||||
|
this.text = result
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun TextView.showHtml(html: String) {
|
||||||
|
this.text = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
|
||||||
|
Html.fromHtml(html, Html.FROM_HTML_MODE_COMPACT)
|
||||||
|
} else {
|
||||||
|
Html.fromHtml(html)
|
||||||
|
}
|
||||||
|
}
|
||||||
+123
@@ -0,0 +1,123 @@
|
|||||||
|
package cn.qjm253.quick_android_base.extensions
|
||||||
|
|
||||||
|
import android.animation.Animator
|
||||||
|
import android.animation.AnimatorSet
|
||||||
|
import android.animation.ObjectAnimator
|
||||||
|
import android.animation.ValueAnimator
|
||||||
|
import android.view.View
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sunny on 17-12-29.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*************************************************************************
|
||||||
|
* ObjectAnimator生成
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
fun View.scaleXAnimation(vararg values: Float): ObjectAnimator {
|
||||||
|
return ObjectAnimator.ofFloat(this, "scaleX", *values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.scaleYAnimation(vararg values: Float): ObjectAnimator {
|
||||||
|
return ObjectAnimator.ofFloat(this, "scaleY", *values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.rotationAnimation(vararg values: Float): ObjectAnimator {
|
||||||
|
return ObjectAnimator.ofFloat(this, "rotation", *values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.transXAnimation(vararg values: Float): ObjectAnimator {
|
||||||
|
return ObjectAnimator.ofFloat(this, "translationX", *values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.transYAnimation(vararg values: Float): ObjectAnimator {
|
||||||
|
return ObjectAnimator.ofFloat(this, "translationY", *values)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.alphaAnimation(vararg values: Float): ObjectAnimator {
|
||||||
|
return ObjectAnimator.ofFloat(this, "alpha", *values)
|
||||||
|
}
|
||||||
|
/*************************************************************************
|
||||||
|
* 属性动画
|
||||||
|
**************************************************************************/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* XY轴同比例缩放
|
||||||
|
*/
|
||||||
|
fun View.scaleXY(vararg values: Float, duration: Long = 500): View {
|
||||||
|
val animatorSet = AnimatorSet()
|
||||||
|
animatorSet.setDuration(duration)
|
||||||
|
.play(this.scaleXAnimation(*values))
|
||||||
|
.with(this.scaleYAnimation(*values))
|
||||||
|
animatorSet.start()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 旋转
|
||||||
|
*/
|
||||||
|
fun View.rotate(vararg values: Float, duration: Long = 500, repeatCount: Int = 0,
|
||||||
|
repeatMode: Int = ValueAnimator.RESTART): ObjectAnimator {
|
||||||
|
val animation = this.rotationAnimation(*values)
|
||||||
|
with(animation
|
||||||
|
.setDuration(duration)){
|
||||||
|
this.repeatCount = repeatCount
|
||||||
|
this.repeatMode = repeatMode
|
||||||
|
start()
|
||||||
|
}
|
||||||
|
return animation
|
||||||
|
// return this
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平移
|
||||||
|
*/
|
||||||
|
fun View.transXY(transX: FloatArray = floatArrayOf(0f, 0f), transY: FloatArray = floatArrayOf(0f, 0f)
|
||||||
|
, duration: Long = 500): View {
|
||||||
|
val animatorSet = AnimatorSet()
|
||||||
|
animatorSet.setDuration(duration)
|
||||||
|
.play(this.transXAnimation(*transX))
|
||||||
|
.with(this.transYAnimation(*transY))
|
||||||
|
animatorSet
|
||||||
|
.start()
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun View.doAnim(floats: FloatArray = floatArrayOf(0f, 0f), callback: (value: Float) -> Unit = {}) {
|
||||||
|
val objAnim = ObjectAnimator.ofFloat(*floats)
|
||||||
|
objAnim.addUpdateListener {
|
||||||
|
val value = it.animatedValue as Float
|
||||||
|
callback(value)
|
||||||
|
}
|
||||||
|
objAnim.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多个属性动画并行运行
|
||||||
|
*/
|
||||||
|
fun View.animer(vararg animations: ObjectAnimator, duration: Long, listener: Animator.AnimatorListener? = null) {
|
||||||
|
if (animations.isEmpty())
|
||||||
|
return
|
||||||
|
val animatorSet = AnimatorSet()
|
||||||
|
val builder = animatorSet.setDuration(duration)
|
||||||
|
.play(animations[0])
|
||||||
|
animations.filterIndexed { index, _ -> index != 0 }
|
||||||
|
.forEach { builder.with(it) }
|
||||||
|
if (listener != null)
|
||||||
|
animatorSet.addListener(listener)
|
||||||
|
animatorSet.start()
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 平移旋转渐变组合动画
|
||||||
|
*/
|
||||||
|
fun View.transRotateAlpha(transX: FloatArray = floatArrayOf(0f, 0f), transY: FloatArray = floatArrayOf(0f, 0f),
|
||||||
|
rotation: FloatArray = floatArrayOf(this.rotation, this.rotation),
|
||||||
|
alpha: FloatArray = floatArrayOf(this.alpha, this.alpha), duration: Long = 500,
|
||||||
|
listener: Animator.AnimatorListener? = null) {
|
||||||
|
this.animer(transXAnimation(*transX), transYAnimation(*transY), rotationAnimation(*rotation),
|
||||||
|
alphaAnimation(*alpha), duration = duration, listener = listener)
|
||||||
|
}
|
||||||
@@ -0,0 +1,16 @@
|
|||||||
|
package cn.qjm253.quick_android_base.model
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 上午9:25
|
||||||
|
*/
|
||||||
|
|
||||||
|
data class SystemInfo(
|
||||||
|
val brand: String = "",
|
||||||
|
val model: String = "",
|
||||||
|
val hardware: String = "",
|
||||||
|
val sdkInt: Int = 22,
|
||||||
|
val release: String = "",
|
||||||
|
val statusBarHeight: Int = 0,
|
||||||
|
val systemLanguage: String = ""
|
||||||
|
)
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
package cn.qjm253.quick_android_base.params
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.os.Parcelable
|
||||||
|
import java.io.Serializable
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sunny on 18-1-28.
|
||||||
|
*/
|
||||||
|
class IntentParam(val keys: MutableList<String> = mutableListOf(),
|
||||||
|
val values: MutableList<Any> = mutableListOf()) {
|
||||||
|
|
||||||
|
fun add(key: String, value: Any?): IntentParam {
|
||||||
|
if(value == null)
|
||||||
|
return this
|
||||||
|
keys.add(key)
|
||||||
|
values.add(value)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyParam(intent: Intent){
|
||||||
|
for(i in 0 until keys.size){
|
||||||
|
when(values[i]){
|
||||||
|
is Boolean -> intent.putExtra(keys[i], values[i] as Boolean)
|
||||||
|
is Byte -> intent.putExtra(keys[i], values[i] as Byte)
|
||||||
|
is Char -> intent.putExtra(keys[i], values[i] as Char)
|
||||||
|
is Short -> intent.putExtra(keys[i], values[i] as Short)
|
||||||
|
is Int -> intent.putExtra(keys[i], values[i] as Int)
|
||||||
|
is Long -> intent.putExtra(keys[i], values[i] as Long)
|
||||||
|
is Float -> intent.putExtra(keys[i], values[i] as Float)
|
||||||
|
is Double -> intent.putExtra(keys[i], values[i] as Double)
|
||||||
|
is String -> intent.putExtra(keys[i], values[i] as String)
|
||||||
|
is CharSequence -> intent.putExtra(keys[i], values[i] as CharSequence)
|
||||||
|
is Parcelable -> intent.putExtra(keys[i], values[i] as Parcelable)
|
||||||
|
is BooleanArray -> intent.putExtra(keys[i], values[i] as BooleanArray)
|
||||||
|
is ByteArray -> intent.putExtra(keys[i], values[i] as ByteArray)
|
||||||
|
is CharArray -> intent.putExtra(keys[i], values[i] as CharArray)
|
||||||
|
is ShortArray -> intent.putExtra(keys[i], values[i] as ShortArray)
|
||||||
|
is IntArray -> intent.putExtra(keys[i], values[i] as IntArray)
|
||||||
|
is LongArray -> intent.putExtra(keys[i], values[i] as LongArray)
|
||||||
|
is FloatArray -> intent.putExtra(keys[i], values[i] as FloatArray)
|
||||||
|
is DoubleArray -> intent.putExtra(keys[i], values[i] as DoubleArray)
|
||||||
|
is Serializable -> intent.putExtra(keys[i], values[i] as Serializable)
|
||||||
|
is Array<*> -> intent.putExtra(keys[i], values[i] as Array<out String>)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyBundle(bundle: Bundle) {
|
||||||
|
for(i in 0 until keys.size){
|
||||||
|
when(values[i]){
|
||||||
|
is Boolean -> bundle.putBoolean(keys[i], values[i] as Boolean)
|
||||||
|
is Byte -> bundle.putByte(keys[i], values[i] as Byte)
|
||||||
|
is Char -> bundle.putChar(keys[i], values[i] as Char)
|
||||||
|
is Short -> bundle.putShort(keys[i], values[i] as Short)
|
||||||
|
is Int -> bundle.putInt(keys[i], values[i] as Int)
|
||||||
|
is Long -> bundle.putLong(keys[i], values[i] as Long)
|
||||||
|
is Float -> bundle.putFloat(keys[i], values[i] as Float)
|
||||||
|
is Double -> bundle.putDouble(keys[i], values[i] as Double)
|
||||||
|
is String -> bundle.putString(keys[i], values[i] as String)
|
||||||
|
is CharSequence -> bundle.putCharSequence(keys[i], values[i] as CharSequence)
|
||||||
|
is Parcelable -> bundle.putParcelable(keys[i], values[i] as Parcelable)
|
||||||
|
is BooleanArray -> bundle.putBooleanArray(keys[i], values[i] as BooleanArray)
|
||||||
|
is ByteArray -> bundle.putByteArray(keys[i], values[i] as ByteArray)
|
||||||
|
is CharArray -> bundle.putCharArray(keys[i], values[i] as CharArray)
|
||||||
|
is ShortArray -> bundle.putShortArray(keys[i], values[i] as ShortArray)
|
||||||
|
is IntArray -> bundle.putIntArray(keys[i], values[i] as IntArray)
|
||||||
|
is LongArray -> bundle.putLongArray(keys[i], values[i] as LongArray)
|
||||||
|
is FloatArray -> bundle.putFloatArray(keys[i], values[i] as FloatArray)
|
||||||
|
is DoubleArray -> bundle.putDoubleArray(keys[i], values[i] as DoubleArray)
|
||||||
|
is Serializable -> bundle.putSerializable(keys[i], values[i] as Serializable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+128
@@ -0,0 +1,128 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util
|
||||||
|
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 账户相关属性验证工具
|
||||||
|
*/
|
||||||
|
object AccountValidatorUtil {
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证用户名
|
||||||
|
*/
|
||||||
|
val REGEX_USERNAME = "^[a-zA-Z]\\w{5,20}$"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证密码
|
||||||
|
*/
|
||||||
|
val REGEX_PASSWORD = "^[a-zA-Z0-9]{6,20}$"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证手机号
|
||||||
|
*/
|
||||||
|
val REGEX_MOBILE = "^((16[0-9])|(19[0-9])(17[0-9])|(14[0-9])|(13[0-9])|(15[^4,\\D])|(18[0-9]))\\d{8}$"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证邮箱
|
||||||
|
*/
|
||||||
|
val REGEX_EMAIL = "^([a-z0-9A-Z]+[-|\\.]?)+[a-z0-9A-Z]@([a-z0-9A-Z]+(-[a-z0-9A-Z]+)?\\.)+[a-zA-Z]{2,}$"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证汉字
|
||||||
|
*/
|
||||||
|
val REGEX_CHINESE = "^[\u4e00-\u9fa5],{0,}$"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证身份证
|
||||||
|
*/
|
||||||
|
val REGEX_ID_CARD = "(^\\d{18}$)|(^\\d{15}$)"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证URL
|
||||||
|
*/
|
||||||
|
val REGEX_URL = "http(s)?://([\\w-]+\\.)+[\\w-]+(/[\\w- ./?%&=]*)?"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 正则表达式:验证IP地址
|
||||||
|
*/
|
||||||
|
val REGEX_IP_ADDR = "(25[0-5]|2[0-4]\\d|[0-1]\\d{2}|[1-9]?\\d)"
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验用户名
|
||||||
|
*
|
||||||
|
* @param username
|
||||||
|
* @return 校验通过返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun isUsername(username: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_USERNAME, username)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验密码
|
||||||
|
*
|
||||||
|
* @param password
|
||||||
|
* @return 校验通过返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun isPassword(password: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_PASSWORD, password)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验手机号
|
||||||
|
*
|
||||||
|
* @param mobile
|
||||||
|
* @return 校验通过返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun isMobile(mobile: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_MOBILE, mobile)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验邮箱
|
||||||
|
*
|
||||||
|
* @param email
|
||||||
|
* @return 校验通过返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun isEmail(email: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_EMAIL, email)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验汉字
|
||||||
|
*
|
||||||
|
* @param chinese
|
||||||
|
* @return 校验通过返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun isChinese(chinese: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_CHINESE, chinese)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验身份证
|
||||||
|
*
|
||||||
|
* @param idCard
|
||||||
|
* @return 校验通过返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun isIDCard(idCard: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_ID_CARD, idCard)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验URL
|
||||||
|
*
|
||||||
|
* @param url
|
||||||
|
* @return 校验通过返回true,否则返回false
|
||||||
|
*/
|
||||||
|
fun isUrl(url: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_URL, url)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 校验IP地址
|
||||||
|
*
|
||||||
|
* @param ipAddr
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun isIPAddr(ipAddr: String): Boolean {
|
||||||
|
return Pattern.matches(REGEX_IP_ADDR, ipAddr)
|
||||||
|
}
|
||||||
|
}
|
||||||
+366
@@ -0,0 +1,366 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util;
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
|
import android.content.ContentUris;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.DocumentsContract;
|
||||||
|
import android.provider.MediaStore;
|
||||||
|
|
||||||
|
import androidx.loader.content.CursorLoader;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class ContentUriUtil {
|
||||||
|
/**
|
||||||
|
* // * Get a file path from a Uri. This will get the the path for Storage Access
|
||||||
|
* // * Framework Documents, as well as the _data field for the MediaStore and
|
||||||
|
* // * other file-based ContentProviders.
|
||||||
|
* // *
|
||||||
|
* // * @param context The context.
|
||||||
|
* // * @param uri The Uri to query.
|
||||||
|
* // * @author paulburke
|
||||||
|
* //
|
||||||
|
*/
|
||||||
|
// public static String getPath(final Context context, final Uri uri) {
|
||||||
|
//
|
||||||
|
// final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
//
|
||||||
|
// // DocumentProvider
|
||||||
|
// if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
// // ExternalStorageProvider
|
||||||
|
// if (isExternalStorageDocument(uri)) {
|
||||||
|
// final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
// final String[] split = docId.split(":");
|
||||||
|
// final String type = split[0];
|
||||||
|
//
|
||||||
|
// if ("primary".equalsIgnoreCase(type)) {
|
||||||
|
// return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// // TODO handle non-primary volumes
|
||||||
|
// }
|
||||||
|
// // DownloadsProvider
|
||||||
|
// else if (isDownloadsDocument(uri)) {
|
||||||
|
// final String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
// if (id.startsWith("raw:")) {
|
||||||
|
// return uri.getPath();
|
||||||
|
// }
|
||||||
|
// final Uri contentUri = ContentUris.withAppendedId(
|
||||||
|
// Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||||
|
// return getDataColumn(context, contentUri, null, null);
|
||||||
|
// }
|
||||||
|
// // MediaProvider
|
||||||
|
// else if (isMediaDocument(uri)) {
|
||||||
|
// final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
// final String[] split = docId.split(":");
|
||||||
|
// final String type = split[0];
|
||||||
|
//
|
||||||
|
// Uri contentUri = null;
|
||||||
|
// if ("image".equals(type)) {
|
||||||
|
// contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
// } else if ("video".equals(type)) {
|
||||||
|
// contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
// } else if ("audio".equals(type)) {
|
||||||
|
// contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// final String selection = "_id=?";
|
||||||
|
// final String[] selectionArgs = new String[]{
|
||||||
|
// split[1]
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// // MediaStore (and general)
|
||||||
|
// else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
//
|
||||||
|
// // Return the remote address
|
||||||
|
// if (isGooglePhotosUri(uri))
|
||||||
|
// return uri.getLastPathSegment();
|
||||||
|
//
|
||||||
|
// return getDataColumn(context, uri, null, null);
|
||||||
|
// }
|
||||||
|
// // File
|
||||||
|
// else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
// return uri.getPath();
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * *
|
||||||
|
// * Get the value of the data column for this Uri. This is useful for
|
||||||
|
// * MediaStore Uris, and other file-based ContentProviders.
|
||||||
|
// *
|
||||||
|
// * @param context The context.
|
||||||
|
// * @param uri The Uri to query.
|
||||||
|
// * @param selection (Optional) Filter used in the query.
|
||||||
|
// * @param selectionArgs (Optional) Selection arguments used in the query.
|
||||||
|
// * @return The value of the _data column, which is typically a file path.
|
||||||
|
// */
|
||||||
|
// public static String getDataColumn(Context context, Uri uri, String selection,
|
||||||
|
// String[] selectionArgs) {
|
||||||
|
//
|
||||||
|
// Cursor cursor = null;
|
||||||
|
// final String column = "_data";
|
||||||
|
// final String[] projection = {
|
||||||
|
// column
|
||||||
|
// };
|
||||||
|
//
|
||||||
|
// try {
|
||||||
|
// cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs,
|
||||||
|
// null);
|
||||||
|
// if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
// final int index = cursor.getColumnIndexOrThrow(column);
|
||||||
|
// return cursor.getString(index);
|
||||||
|
// }
|
||||||
|
// } finally {
|
||||||
|
// if (cursor != null)
|
||||||
|
// cursor.close();
|
||||||
|
// }
|
||||||
|
// return null;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * @param uri The Uri to check.
|
||||||
|
// * @return Whether the Uri authority is ExternalStorageProvider.
|
||||||
|
// */
|
||||||
|
// public static boolean isExternalStorageDocument(Uri uri) {
|
||||||
|
// return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * @param uri The Uri to check.
|
||||||
|
// * @return Whether the Uri authority is DownloadsProvider.
|
||||||
|
// */
|
||||||
|
// public static boolean isDownloadsDocument(Uri uri) {
|
||||||
|
// return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * @param uri The Uri to check.
|
||||||
|
// * @return Whether the Uri authority is MediaProvider.
|
||||||
|
// */
|
||||||
|
// public static boolean isMediaDocument(Uri uri) {
|
||||||
|
// return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// /**
|
||||||
|
// * @param uri The Uri to check.
|
||||||
|
// * @return Whether the Uri authority is Google Photos.
|
||||||
|
// */
|
||||||
|
// public static boolean isGooglePhotosUri(Uri uri) {
|
||||||
|
// return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||||
|
// }
|
||||||
|
|
||||||
|
private static String failReason;
|
||||||
|
|
||||||
|
static String errorReason() {
|
||||||
|
return failReason;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String getPath(final Context context, final Uri uri) {
|
||||||
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) {
|
||||||
|
return getRealPathFromURI_BelowAPI19(context, uri);
|
||||||
|
}
|
||||||
|
return getRealPathFromURI_API19(context, uri);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressLint("NewApi")
|
||||||
|
public static String getRealPathFromURI_API19(final Context context, final Uri uri) {
|
||||||
|
|
||||||
|
final boolean isKitKat = Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT;
|
||||||
|
|
||||||
|
if (isKitKat && DocumentsContract.isDocumentUri(context, uri)) {
|
||||||
|
|
||||||
|
if (isExternalStorageDocument(uri)) {
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
if ("primary".equalsIgnoreCase(type)) {
|
||||||
|
if (split.length > 1) {
|
||||||
|
return Environment.getExternalStorageDirectory() + "/" + split[1];
|
||||||
|
} else {
|
||||||
|
return Environment.getExternalStorageDirectory() + "/";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return "storage" + "/" + docId.replace(":", "/");
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (isRawDownloadsDocument(uri)) {
|
||||||
|
String fileName = getFilePath(context, uri);
|
||||||
|
String subFolderName = getSubFolders(uri);
|
||||||
|
|
||||||
|
if (fileName != null) {
|
||||||
|
return Environment.getExternalStorageDirectory().toString() + "/Download/" + subFolderName + fileName;
|
||||||
|
}
|
||||||
|
String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
|
||||||
|
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||||
|
return getDataColumn(context, contentUri, null, null);
|
||||||
|
} else if (isDownloadsDocument(uri)) {
|
||||||
|
|
||||||
|
System.out.println("downloadsDocument");
|
||||||
|
String fileName = getFilePath(context, uri);
|
||||||
|
|
||||||
|
if (fileName != null) {
|
||||||
|
return Environment.getExternalStorageDirectory().toString() + "/Download/" + fileName;
|
||||||
|
}
|
||||||
|
String id = DocumentsContract.getDocumentId(uri);
|
||||||
|
if (id.startsWith("raw:")) {
|
||||||
|
id = id.replaceFirst("raw:", "");
|
||||||
|
File file = new File(id);
|
||||||
|
if (file.exists())
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
if (id.startsWith("raw%3A%2F")) {
|
||||||
|
id = id.replaceFirst("raw%3A%2F", "");
|
||||||
|
File file = new File(id);
|
||||||
|
if (file.exists())
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
final Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
|
||||||
|
return getDataColumn(context, contentUri, null, null);
|
||||||
|
} else if (isMediaDocument(uri)) {
|
||||||
|
final String docId = DocumentsContract.getDocumentId(uri);
|
||||||
|
final String[] split = docId.split(":");
|
||||||
|
final String type = split[0];
|
||||||
|
|
||||||
|
Uri contentUri = null;
|
||||||
|
if ("image".equals(type)) {
|
||||||
|
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("video".equals(type)) {
|
||||||
|
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
} else if ("audio".equals(type)) {
|
||||||
|
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
|
||||||
|
}
|
||||||
|
|
||||||
|
final String selection = "_id=?";
|
||||||
|
final String[] selectionArgs = new String[]{
|
||||||
|
split[1]
|
||||||
|
};
|
||||||
|
|
||||||
|
return getDataColumn(context, contentUri, selection, selectionArgs);
|
||||||
|
}
|
||||||
|
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
if (isGooglePhotosUri(uri)) {
|
||||||
|
return uri.getLastPathSegment();
|
||||||
|
}
|
||||||
|
if (getDataColumn(context, uri, null, null) == null) {
|
||||||
|
failReason = "dataReturnedNull";
|
||||||
|
}
|
||||||
|
return getDataColumn(context, uri, null, null);
|
||||||
|
} else if ("file".equalsIgnoreCase(uri.getScheme())) {
|
||||||
|
return uri.getPath();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String getSubFolders(Uri uri) {
|
||||||
|
String replaceChars = String.valueOf(uri).replace("%2F", "/").replace("%20", " ").replace("%3A", ":");
|
||||||
|
String[] bits = replaceChars.split("/");
|
||||||
|
String sub5 = bits[bits.length - 2];
|
||||||
|
String sub4 = bits[bits.length - 3];
|
||||||
|
String sub3 = bits[bits.length - 4];
|
||||||
|
String sub2 = bits[bits.length - 5];
|
||||||
|
String sub1 = bits[bits.length - 6];
|
||||||
|
if (sub1.equals("Download")) {
|
||||||
|
return sub2 + "/" + sub3 + "/" + sub4 + "/" + sub5 + "/";
|
||||||
|
} else if (sub2.equals("Download")) {
|
||||||
|
return sub3 + "/" + sub4 + "/" + sub5 + "/";
|
||||||
|
} else if (sub3.equals("Download")) {
|
||||||
|
return sub4 + "/" + sub5 + "/";
|
||||||
|
} else if (sub4.equals("Download")) {
|
||||||
|
return sub5 + "/";
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getRealPathFromURI_BelowAPI19(Context context, Uri contentUri) {
|
||||||
|
String[] proj = {MediaStore.Video.Media.DATA};
|
||||||
|
CursorLoader loader = new CursorLoader(context, contentUri, proj, null, null, null);
|
||||||
|
Cursor cursor = loader.loadInBackground();
|
||||||
|
assert cursor != null;
|
||||||
|
int column_index = cursor.getColumnIndexOrThrow(MediaStore.Video.Media.DATA);
|
||||||
|
cursor.moveToFirst();
|
||||||
|
String result = cursor.getString(column_index);
|
||||||
|
cursor.close();
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("TryFinallyCanBeTryWithResources")
|
||||||
|
private static String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
final String column = "_data";
|
||||||
|
final String[] projection = {column};
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
final int index = cursor.getColumnIndexOrThrow(column);
|
||||||
|
return cursor.getString(index);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
failReason = e.getMessage();
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@SuppressWarnings("TryFinallyCanBeTryWithResources")
|
||||||
|
private static String getFilePath(Context context, Uri uri) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
final String[] projection = {MediaStore.Files.FileColumns.DISPLAY_NAME};
|
||||||
|
try {
|
||||||
|
cursor = context.getContentResolver().query(uri, projection, null, null,
|
||||||
|
null);
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
final int index = cursor.getColumnIndexOrThrow(MediaStore.Files.FileColumns.DISPLAY_NAME);
|
||||||
|
return cursor.getString(index);
|
||||||
|
}
|
||||||
|
} catch (Exception e) {
|
||||||
|
failReason = e.getMessage();
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isExternalStorageDocument(Uri uri) {
|
||||||
|
return "com.android.externalstorage.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isDownloadsDocument(Uri uri) {
|
||||||
|
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isRawDownloadsDocument(Uri uri) {
|
||||||
|
String uriToString = String.valueOf(uri);
|
||||||
|
return uriToString.contains("com.android.providers.downloads.documents/document/raw");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isMediaDocument(Uri uri) {
|
||||||
|
return "com.android.providers.media.documents".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static boolean isGooglePhotosUri(Uri uri) {
|
||||||
|
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util;
|
||||||
|
|
||||||
|
import android.app.Activity;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Rect;
|
||||||
|
import android.util.TypedValue;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by liufengkai on 15/11/8.
|
||||||
|
*/
|
||||||
|
public class DisplayUtils {
|
||||||
|
/**
|
||||||
|
* px to dip 尺寸不变
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param pxValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int px2dip(Context context, float pxValue) {
|
||||||
|
final float scale = context.getResources().getDisplayMetrics().density;
|
||||||
|
return (int) (pxValue / scale + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dip to px 尺寸不变
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param dipValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int dip2px(Context context, float dipValue) {
|
||||||
|
final float scale = context.getResources().getDisplayMetrics().density;
|
||||||
|
return (int) (dipValue * scale + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dp to px
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param dp
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int dp2px(Context context, int dp) {
|
||||||
|
return (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_DIP,
|
||||||
|
dp,
|
||||||
|
context.getResources().getDisplayMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* px to sp 文字不变
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param pxValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int px2sp(Context context, float pxValue) {
|
||||||
|
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
|
||||||
|
return (int) (pxValue / fontScale + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sp to px 文字不变
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param spValue
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int sp2px(Context context, float spValue) {
|
||||||
|
final float fontScale = context.getResources().getDisplayMetrics().scaledDensity;
|
||||||
|
return (int) (spValue * fontScale + 0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* sp to px
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param px
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static int sp2px(Context context, int px) {
|
||||||
|
return (int) TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_SP,
|
||||||
|
px,
|
||||||
|
context.getResources().getDisplayMetrics());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int[] getScreenParams(Context context) {
|
||||||
|
|
||||||
|
WindowManager manager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
|
||||||
|
|
||||||
|
int width = manager.getDefaultDisplay().getWidth();
|
||||||
|
int height = manager.getDefaultDisplay().getHeight();
|
||||||
|
|
||||||
|
return new int[]{width, height};
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int getStatusBarHeight(Activity context) {
|
||||||
|
Rect frame = new Rect();
|
||||||
|
context.getWindow().getDecorView().getWindowVisibleDisplayFrame(frame);
|
||||||
|
return frame.top;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,118 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util
|
||||||
|
|
||||||
|
import android.os.Environment
|
||||||
|
import cn.qjm253.quick_android_base.QuickAndroid
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by tanshunwang on 2016/9/21 0021.
|
||||||
|
*/
|
||||||
|
object FileUtils {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得应用的根目录
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
//判断sd卡是否存在
|
||||||
|
//获取根目录
|
||||||
|
val appPath: String
|
||||||
|
get() {
|
||||||
|
val sdCardExist = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
|
||||||
|
val sdDir = if (sdCardExist) {
|
||||||
|
File(Environment.getExternalStorageDirectory().path + "/" + QuickAndroid.APP_NAME + "/")
|
||||||
|
} else {
|
||||||
|
File(Environment.getDataDirectory().toString() + "/" + QuickAndroid.APP_NAME + "/")
|
||||||
|
}
|
||||||
|
if (!sdDir.exists()) {
|
||||||
|
sdDir.mkdirs()
|
||||||
|
}
|
||||||
|
return sdDir.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
val cachePath: String
|
||||||
|
get() {
|
||||||
|
val targetPath = "$appPath/Cache/"
|
||||||
|
val file = File(targetPath)
|
||||||
|
if (!file.exists())
|
||||||
|
file.mkdirs()
|
||||||
|
return targetPath
|
||||||
|
}
|
||||||
|
|
||||||
|
val recordPath: String
|
||||||
|
get() {
|
||||||
|
val targetPath = "$appPath/Record/"
|
||||||
|
val file = File(targetPath)
|
||||||
|
if (!file.exists())
|
||||||
|
file.mkdirs()
|
||||||
|
return targetPath
|
||||||
|
}
|
||||||
|
|
||||||
|
val picturePath: String
|
||||||
|
get() {
|
||||||
|
val targetPath = "$appPath/Picture/"
|
||||||
|
val file = File(targetPath)
|
||||||
|
if (!file.exists())
|
||||||
|
file.mkdirs()
|
||||||
|
return targetPath
|
||||||
|
}
|
||||||
|
|
||||||
|
val downloadPath: String
|
||||||
|
get() {
|
||||||
|
val targetPath = "$appPath/Download/"
|
||||||
|
val file = File(targetPath)
|
||||||
|
if (!file.exists())
|
||||||
|
file.mkdirs()
|
||||||
|
return targetPath
|
||||||
|
}
|
||||||
|
|
||||||
|
val wifiDirectPath: String
|
||||||
|
get() {
|
||||||
|
val path = "$appPath/WifiDirectFile/"
|
||||||
|
val file = File(path)
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.mkdirs()
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获得SDCard的根目录
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
//判断sd卡是否存在
|
||||||
|
val sdPath: String?
|
||||||
|
get() {
|
||||||
|
val sdCardExist = Environment.getExternalStorageState() == Environment.MEDIA_MOUNTED
|
||||||
|
return if (sdCardExist) {
|
||||||
|
Environment.getExternalStorageDirectory().path
|
||||||
|
} else {
|
||||||
|
null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
val path: String
|
||||||
|
get() {
|
||||||
|
val path = "$appPath/header/"
|
||||||
|
val file = File(path)
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.mkdirs()
|
||||||
|
println("54555")
|
||||||
|
}
|
||||||
|
return path
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generateRecordFile(fileName: String): File {
|
||||||
|
return File(recordPath + File.separator + fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun generatePictureFile(fileName: String): File {
|
||||||
|
return File(picturePath + File.separator + fileName)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util
|
||||||
|
|
||||||
|
import java.util.concurrent.atomic.AtomicInteger
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-14 下午8:16
|
||||||
|
*/
|
||||||
|
|
||||||
|
object GeneratedId {
|
||||||
|
private val sNextGeneratedId = AtomicInteger(20000)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a value suitable for use in setId
|
||||||
|
* This value will not collide with ID values generated at build time by aapt for R.id.
|
||||||
|
*
|
||||||
|
* @return a generated ID value
|
||||||
|
*/
|
||||||
|
fun generateViewId(): Int {
|
||||||
|
while (true) {
|
||||||
|
val result = sNextGeneratedId.get()
|
||||||
|
// aapt-generated IDs have the high byte nonzero; clamp to the range under that.
|
||||||
|
var newValue = result + 1
|
||||||
|
if (newValue > 0x00FFFFFF) newValue = 1 // Roll over to 1, not 0.
|
||||||
|
if (sNextGeneratedId.compareAndSet(result, newValue)) {
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util
|
||||||
|
|
||||||
|
import com.google.gson.Gson
|
||||||
|
import com.google.gson.JsonSyntaxException
|
||||||
|
import com.google.gson.reflect.TypeToken
|
||||||
|
|
||||||
|
import java.util.ArrayList
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sunny on 2017/4/19 0019.
|
||||||
|
*/
|
||||||
|
object GsonUtil {
|
||||||
|
val instance: Gson = Gson()
|
||||||
|
|
||||||
|
|
||||||
|
fun <T> json2Bean(json: String, beanClass: Class<T>): T? {
|
||||||
|
var bean: T? = null
|
||||||
|
try {
|
||||||
|
bean = instance.fromJson(json, beanClass)
|
||||||
|
} catch (e: JsonSyntaxException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return bean
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> json2Bean(json: String): T? {
|
||||||
|
var bean: T? = null
|
||||||
|
val type = object : TypeToken<ArrayList<T>>() {
|
||||||
|
|
||||||
|
}.type
|
||||||
|
try {
|
||||||
|
bean = instance.fromJson<T>(json, type)
|
||||||
|
} catch (e: JsonSyntaxException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return bean
|
||||||
|
}
|
||||||
|
|
||||||
|
// public static ArrayList<Tag> json2TagList(String json) {
|
||||||
|
// Type type = new TypeToken<ArrayList<Tag>>(){}.getType();
|
||||||
|
// ArrayList<Tag> bean = new ArrayList<>();
|
||||||
|
// try {
|
||||||
|
// bean = getInstance().fromJson(json, type);
|
||||||
|
// } catch (JsonSyntaxException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// return bean;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static ArrayList<Record> json2RecordList(String json) {
|
||||||
|
// Type type = new TypeToken<ArrayList<Record>>(){}.getType();
|
||||||
|
// ArrayList<Record> bean = new ArrayList<>();
|
||||||
|
// try {
|
||||||
|
// bean = getInstance().fromJson(json, type);
|
||||||
|
// } catch (JsonSyntaxException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// return bean;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// public static ArrayList<Image> json2ImageList(String json) {
|
||||||
|
// Type type = new TypeToken<ArrayList<Image>>(){}.getType();
|
||||||
|
// ArrayList<Image> bean = new ArrayList<>();
|
||||||
|
// try {
|
||||||
|
// bean = getInstance().fromJson(json, type);
|
||||||
|
// } catch (JsonSyntaxException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// return bean;
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
fun bean2Json(`object`: Any): String {
|
||||||
|
return instance.toJson(`object`)
|
||||||
|
}
|
||||||
|
}
|
||||||
+41
@@ -0,0 +1,41 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util
|
||||||
|
|
||||||
|
import io.reactivex.ObservableTransformer
|
||||||
|
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||||
|
import io.reactivex.schedulers.Schedulers
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理Rx线程转换
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-7 上午8:43
|
||||||
|
*/
|
||||||
|
object RxSchedulersHelper {
|
||||||
|
|
||||||
|
fun <T> io_main(): ObservableTransformer<T, T> {
|
||||||
|
return ObservableTransformer { upstream ->
|
||||||
|
upstream
|
||||||
|
.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> io_io(): ObservableTransformer<T, T>{
|
||||||
|
return ObservableTransformer { upstream ->
|
||||||
|
upstream.subscribeOn(Schedulers.io())
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fun <T> main_io(): ObservableTransformer<T, T> {
|
||||||
|
return ObservableTransformer { upstream ->
|
||||||
|
upstream.subscribeOn(AndroidSchedulers.mainThread())
|
||||||
|
.observeOn(Schedulers.io())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun <T> comppute_main(): ObservableTransformer<T, T> {
|
||||||
|
return ObservableTransformer { upstream ->
|
||||||
|
upstream.subscribeOn(Schedulers.computation())
|
||||||
|
.observeOn(AndroidSchedulers.mainThread())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,303 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.SharedPreferences;
|
||||||
|
|
||||||
|
import org.json.JSONArray;
|
||||||
|
import org.json.JSONException;
|
||||||
|
import org.json.JSONObject;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 简化Sp存储的工具类
|
||||||
|
*
|
||||||
|
* @author liufengkai
|
||||||
|
*/
|
||||||
|
public class SpUtils {
|
||||||
|
|
||||||
|
public SpUtils() {
|
||||||
|
/* cannot be instantiated */
|
||||||
|
throw new UnsupportedOperationException("cannot be instantiated");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sp文件名
|
||||||
|
*/
|
||||||
|
public static final String FILE_NAME = "share_data";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存数据的方法,添加具体数据类型
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param key
|
||||||
|
* @param object
|
||||||
|
*/
|
||||||
|
public static void put(Context context, String key, Object object) {
|
||||||
|
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
|
||||||
|
// 判断数据类型
|
||||||
|
if (object instanceof String) {
|
||||||
|
editor.putString(key, (String) object);
|
||||||
|
} else if (object instanceof Integer) {
|
||||||
|
editor.putInt(key, (Integer) object);
|
||||||
|
} else if (object instanceof Boolean) {
|
||||||
|
editor.putBoolean(key, (Boolean) object);
|
||||||
|
} else if (object instanceof Float) {
|
||||||
|
editor.putFloat(key, (Float) object);
|
||||||
|
} else if (object instanceof Long) {
|
||||||
|
editor.putLong(key, (Long) object);
|
||||||
|
} else {
|
||||||
|
editor.putString(key, object.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void put(Context context, String key, String value) {
|
||||||
|
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putString(key, value);
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void put(Context context, String key, int value) {
|
||||||
|
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putInt(key, value);
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void put(Context context, String key, float value) {
|
||||||
|
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putFloat(key, value);
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void put(Context context, String key, long value) {
|
||||||
|
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.putLong(key, value);
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String get(Context context, String key, String defaultValue) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
return sp.getString(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static int get(Context context, String key, int defaultValue) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
return sp.getInt(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static float get(Context context, String key, float defaultValue) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
return sp.getFloat(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static long get(Context context, String key, long defaultValue) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
return sp.getLong(key, defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static boolean get(Context context, String key, boolean judge) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
return sp.getBoolean(key, judge);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取保存数据的方法,添加具体数据类型
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param key
|
||||||
|
* @param defaultObject
|
||||||
|
* @return object
|
||||||
|
*/
|
||||||
|
public static Object get(Context context, String key, Object defaultObject) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
|
||||||
|
if (defaultObject instanceof String) {
|
||||||
|
return sp.getString(key, (String) defaultObject);
|
||||||
|
} else if (defaultObject instanceof Integer) {
|
||||||
|
return sp.getInt(key, (Integer) defaultObject);
|
||||||
|
} else if (defaultObject instanceof Boolean) {
|
||||||
|
return sp.getBoolean(key, (Boolean) defaultObject);
|
||||||
|
} else if (defaultObject instanceof Float) {
|
||||||
|
return sp.getFloat(key, (Float) defaultObject);
|
||||||
|
} else if (defaultObject instanceof Long) {
|
||||||
|
return sp.getLong(key, (Long) defaultObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 存入哈希表
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param map
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
public static void putMap(Context context, Map<?, ?> map, String key) {
|
||||||
|
JSONArray jsonArray = new JSONArray();
|
||||||
|
Set set = map.entrySet();
|
||||||
|
Iterator iterator = set.iterator();
|
||||||
|
for (int i = 0; i < map.size(); i++) {
|
||||||
|
Map.Entry mapEntry = (Map.Entry) iterator.next();
|
||||||
|
try {
|
||||||
|
JSONObject object = new JSONObject();
|
||||||
|
object.put("key", mapEntry.getKey());
|
||||||
|
object.put("value", mapEntry.getValue());
|
||||||
|
jsonArray.put(object);
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
put(context, key, jsonArray.toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 读取哈希表
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param key
|
||||||
|
* @return map
|
||||||
|
*/
|
||||||
|
public static Map getMap(Context context, String key) {
|
||||||
|
Map map = new HashMap();
|
||||||
|
try {
|
||||||
|
JSONArray jsonArray = new JSONArray((String) get(context, key, ""));
|
||||||
|
for (int i = 0; i < jsonArray.length(); i++) {
|
||||||
|
try {
|
||||||
|
JSONObject object = jsonArray.getJSONObject(i);
|
||||||
|
map.put(object.getString("key"), object.getString("value"));
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (JSONException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return map;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 移除某个key值已经对应的值
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param key
|
||||||
|
*/
|
||||||
|
public static void remove(Context context, String key) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.remove(key);
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 清除所有数据
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
public static void clear(Context context) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
SharedPreferences.Editor editor = sp.edit();
|
||||||
|
editor.clear();
|
||||||
|
SharedPreferencesCompat.apply(editor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询某个key是否存在
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @param key
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean contains(Context context, String key) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
return sp.contains(key);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 返回所有的键值对
|
||||||
|
*
|
||||||
|
* @param context
|
||||||
|
* @return map
|
||||||
|
*/
|
||||||
|
public static Map<String, ?> getAll(Context context) {
|
||||||
|
SharedPreferences sp = context.getSharedPreferences(FILE_NAME,
|
||||||
|
Context.MODE_PRIVATE);
|
||||||
|
return sp.getAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解决SharedPreferencesCompat.apply方法的兼容类
|
||||||
|
*
|
||||||
|
* @author liufengkai
|
||||||
|
*/
|
||||||
|
private static class SharedPreferencesCompat {
|
||||||
|
|
||||||
|
private static final Method sApplyMethod = findApplyMethod();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 反射查找apply的方法
|
||||||
|
*
|
||||||
|
* @return method
|
||||||
|
*/
|
||||||
|
@SuppressWarnings({"unchecked", "rawtypes"})
|
||||||
|
private static Method findApplyMethod() {
|
||||||
|
try {
|
||||||
|
Class clz = SharedPreferences.Editor.class;
|
||||||
|
return clz.getMethod("apply");
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果找到则使用apply执行,否则使用commit
|
||||||
|
*
|
||||||
|
* @param editor
|
||||||
|
*/
|
||||||
|
public static void apply(SharedPreferences.Editor editor) {
|
||||||
|
try {
|
||||||
|
if (sApplyMethod != null) {
|
||||||
|
sApplyMethod.invoke(editor);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
} catch (IllegalArgumentException | IllegalAccessException | InvocationTargetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
editor.commit();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,85 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util
|
||||||
|
|
||||||
|
import android.graphics.Typeface
|
||||||
|
import android.text.Spannable
|
||||||
|
import android.text.SpannableStringBuilder
|
||||||
|
import android.text.Spanned
|
||||||
|
import android.text.style.ForegroundColorSpan
|
||||||
|
import android.text.style.StyleSpan
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* SpannableText的工具类,可以设置文本的显示样式
|
||||||
|
* Created by sunny on 18-1-27.
|
||||||
|
*/
|
||||||
|
|
||||||
|
object SpannableUtil {
|
||||||
|
/**
|
||||||
|
* Returns a CharSequence that concatenates the specified array of CharSequence
|
||||||
|
* objects and then applies a list of zero or more tags to the entire range.
|
||||||
|
*
|
||||||
|
* @param content an array of character sequences to apply a style to
|
||||||
|
* @param tags the styled span objects to apply to the content
|
||||||
|
* such as android.text.style.StyleSpan
|
||||||
|
*/
|
||||||
|
private fun apply(content: Array<out CharSequence>, vararg tags: Any): CharSequence {
|
||||||
|
val text = SpannableStringBuilder()
|
||||||
|
openTags(text, tags)
|
||||||
|
for (item in content) {
|
||||||
|
text.append(item)
|
||||||
|
}
|
||||||
|
closeTags(text, tags)
|
||||||
|
return text
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates over an array of tags and applies them to the beginning of the specified
|
||||||
|
* Spannable object so that future text appended to the text will have the styling
|
||||||
|
* applied to it. Do not call this method directly.
|
||||||
|
*/
|
||||||
|
private fun openTags(text: Spannable, tags: Array<out Any>) {
|
||||||
|
for (tag in tags) {
|
||||||
|
text.setSpan(tag, 0, 0, Spannable.SPAN_MARK_MARK)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* "Closes" the specified tags on a Spannable by updating the spans to be
|
||||||
|
* endpoint-exclusive so that future text appended to the end will not take
|
||||||
|
* on the same styling. Do not call this method directly.
|
||||||
|
*/
|
||||||
|
private fun closeTags(text: Spannable, tags: Array<out Any>) {
|
||||||
|
val len = text.length
|
||||||
|
for (tag in tags) {
|
||||||
|
if (len > 0) {
|
||||||
|
text.setSpan(tag, 0, len, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||||
|
} else {
|
||||||
|
text.removeSpan(tag)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CharSequence that applies boldface to the concatenation
|
||||||
|
* of the specified CharSequence objects.
|
||||||
|
*/
|
||||||
|
fun bold(vararg content: CharSequence): CharSequence {
|
||||||
|
return apply(content, StyleSpan(Typeface.BOLD))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CharSequence that applies italics to the concatenation
|
||||||
|
* of the specified CharSequence objects.
|
||||||
|
*/
|
||||||
|
fun italic(vararg content: CharSequence): CharSequence {
|
||||||
|
return apply(content, StyleSpan(Typeface.ITALIC))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a CharSequence that applies a foreground color to the
|
||||||
|
* concatenation of the specified CharSequence objects.
|
||||||
|
*/
|
||||||
|
fun color(color: Int, vararg content: CharSequence): CharSequence {
|
||||||
|
return apply(content, ForegroundColorSpan(color))
|
||||||
|
}
|
||||||
|
}
|
||||||
+88
@@ -0,0 +1,88 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import cn.qjm253.quick_android_base.model.SystemInfo
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统信息的工具类
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 上午9:02
|
||||||
|
*/
|
||||||
|
|
||||||
|
object SystemInfoHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统状态栏的高度
|
||||||
|
*/
|
||||||
|
fun getStatusBarHeight(context: Context): Int {
|
||||||
|
val resourceId = context.resources.getIdentifier("status_bar_height", "dimen", "android")
|
||||||
|
return context.resources.getDimensionPixelSize(resourceId)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取手机型号
|
||||||
|
*/
|
||||||
|
fun getSystemModel(): String {
|
||||||
|
return android.os.Build.MODEL ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取手机厂商
|
||||||
|
*/
|
||||||
|
fun getDeviceBrand(): String {
|
||||||
|
return android.os.Build.BRAND ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取手机的版本号
|
||||||
|
*/
|
||||||
|
fun getSystemVersion(): String {
|
||||||
|
return android.os.Build.VERSION.RELEASE
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前手机的语言
|
||||||
|
*/
|
||||||
|
fun getSystemLanguage(): String {
|
||||||
|
return Locale.getDefault().language ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前系统上的语言列表
|
||||||
|
*/
|
||||||
|
fun getSystemLanguageLists(): Array<out Locale> {
|
||||||
|
return Locale.getAvailableLocales() ?: arrayOf()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取系统硬件信息
|
||||||
|
*/
|
||||||
|
fun getSystemHardware(): String {
|
||||||
|
return android.os.Build.HARDWARE ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取当前系统的SDK版本
|
||||||
|
*/
|
||||||
|
fun getSdkInt(): Int {
|
||||||
|
return android.os.Build.VERSION.SDK_INT
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构建系统信息
|
||||||
|
*/
|
||||||
|
fun buildSystemInfo(context: Context): SystemInfo {
|
||||||
|
return SystemInfo(
|
||||||
|
getDeviceBrand(),
|
||||||
|
getSystemModel(),
|
||||||
|
getSystemHardware(),
|
||||||
|
getSdkInt(),
|
||||||
|
getSystemVersion(),
|
||||||
|
getStatusBarHeight(context),
|
||||||
|
getSystemLanguage()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
+162
@@ -0,0 +1,162 @@
|
|||||||
|
package cn.qjm253.quick_android_base.util.notification
|
||||||
|
|
||||||
|
import android.app.Notification
|
||||||
|
import android.app.NotificationChannel
|
||||||
|
import android.app.NotificationManager
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.os.Build
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import cn.qjm253.quick_android_base.QuickAndroid
|
||||||
|
import cn.qjm253.quick_android_base.R
|
||||||
|
import cn.qjm253.quick_android_base.params.IntentParam
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 通知显示工具类
|
||||||
|
* Created by sunny on 18-1-30.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class MyNotificationUtil private constructor(val context: Context, channelId: String = QuickAndroid.APP_NAME) {
|
||||||
|
|
||||||
|
private var notification: Notification? = null
|
||||||
|
private var notificationManager: NotificationManager =
|
||||||
|
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
private var builder = NotificationCompat.Builder(context, channelId)
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private var default_notifyId = 0
|
||||||
|
fun newInstance(context: Context): MyNotificationUtil {
|
||||||
|
return MyNotificationUtil(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun generateNotifyId(): Int {
|
||||||
|
default_notifyId = (default_notifyId + 1) % 100
|
||||||
|
return default_notifyId
|
||||||
|
}
|
||||||
|
|
||||||
|
fun createChannelForOreo(context: Context, channelId: String, channelName: String) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
val channel = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
NotificationChannel(channelId, channelName, NotificationManager.IMPORTANCE_LOW)
|
||||||
|
} else {
|
||||||
|
TODO("VERSION.SDK_INT < O")
|
||||||
|
}
|
||||||
|
channel.enableLights(true)
|
||||||
|
channel.setShowBadge(true)
|
||||||
|
channel.lightColor = Color.GREEN
|
||||||
|
channel.lockscreenVisibility = Notification.VISIBILITY_PUBLIC
|
||||||
|
|
||||||
|
notificationManager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun notify(notification: Notification, notificationId: Int){
|
||||||
|
notificationManager.notify(notificationId, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun cancel(notificationId: Int){
|
||||||
|
notificationManager.cancel(notificationId)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun initProgress(progress: Int, progressMax: Int = 100, indeterminate: Boolean = true,
|
||||||
|
title: String = "", content: String = "", ticker: String = ""): MyNotificationUtil {
|
||||||
|
initNotification(progress = progress, progressMax = progressMax, indeterminate = indeterminate,
|
||||||
|
title = title, content = content, ticker = ticker, isProgress = true, isSound = false,
|
||||||
|
isVibrate = false, autoCancel = false)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun initNotification(@DrawableRes smallIcon: Int = R.drawable.logo,
|
||||||
|
@DrawableRes bigIcon: Int = R.drawable.logo, title: String = "",
|
||||||
|
content: String = "", ticker: String = "", time: Long = System.currentTimeMillis(),
|
||||||
|
/**
|
||||||
|
* 1.将AutoCancel设为true后,当你点击通知栏的notification后,它会自动被取消消失,
|
||||||
|
* 不设置的话点击消息后也不清除,但可以滑动删除
|
||||||
|
* 2.将Ongoing设为true 那么notification将不能滑动删除
|
||||||
|
*/
|
||||||
|
autoCancel: Boolean = true, onGoing: Boolean = false,
|
||||||
|
/**
|
||||||
|
* 从Android4.1开始,可以通过以下方法,设置notification的优先级,
|
||||||
|
* 优先级越高的,通知排的越靠前,优先级低的,不会在手机最顶部的状态栏显示图标
|
||||||
|
*/
|
||||||
|
priority: Int = NotificationCompat.PRIORITY_DEFAULT,
|
||||||
|
/**
|
||||||
|
* Notification.DEFAULT_ALL:铃声、闪光、震动均系统默认。
|
||||||
|
* Notification.DEFAULT_SOUND:系统默认铃声。
|
||||||
|
* Notification.DEFAULT_VIBRATE:系统默认震动。
|
||||||
|
* Notification.DEFAULT_LIGHTS:系统默认闪光。
|
||||||
|
* notifyBuilder.setDefaults(Notification.DEFAULT_ALL);
|
||||||
|
*/
|
||||||
|
isSound: Boolean = true, isVibrate: Boolean = true, isLight: Boolean = true,
|
||||||
|
/**
|
||||||
|
* 下面是进度相关设置
|
||||||
|
*/
|
||||||
|
progress: Int = 0, progressMax: Int = 100, indeterminate: Boolean = true,
|
||||||
|
/**
|
||||||
|
* 是否显示进度条
|
||||||
|
*/
|
||||||
|
isProgress: Boolean = false,
|
||||||
|
/**
|
||||||
|
* 点击跳转相关设置
|
||||||
|
* 注意事项: setContentIntent一定要在build之前调用,不然无效
|
||||||
|
*/
|
||||||
|
requestCode: Int = 0, target: Class<*>? = null,
|
||||||
|
pendingIntentFlag: Int = PendingIntent.FLAG_UPDATE_CURRENT,
|
||||||
|
pendingIntentParam: IntentParam? = null,
|
||||||
|
|
||||||
|
// 适配Android8.0+ 通知必须配置渠道,要不然无法在通知栏显示
|
||||||
|
channelId: String = QuickAndroid.APP_NAME,
|
||||||
|
channelName: String = QuickAndroid.APP_NAME
|
||||||
|
)
|
||||||
|
: MyNotificationUtil {
|
||||||
|
// 适配Android8.0+ 通知必须配置渠道,要不然无法在通知栏显示
|
||||||
|
createChannelForOreo(context, channelId, channelName)
|
||||||
|
|
||||||
|
builder.setAutoCancel(autoCancel)
|
||||||
|
builder.setSmallIcon(smallIcon)
|
||||||
|
val bitmap = BitmapFactory.decodeResource(context.resources, bigIcon)
|
||||||
|
builder.setLargeIcon(bitmap)
|
||||||
|
builder.setContentText(content)
|
||||||
|
builder.setContentTitle(title)
|
||||||
|
builder.setTicker(ticker)
|
||||||
|
builder.setWhen(time)
|
||||||
|
builder.priority = priority
|
||||||
|
builder.setOngoing(onGoing)
|
||||||
|
var defaults = 0
|
||||||
|
if (isSound) {
|
||||||
|
defaults = defaults or Notification.DEFAULT_SOUND
|
||||||
|
}
|
||||||
|
if (isVibrate) {
|
||||||
|
defaults = defaults or Notification.DEFAULT_VIBRATE
|
||||||
|
}
|
||||||
|
if (isLight) {
|
||||||
|
defaults = defaults or Notification.DEFAULT_LIGHTS
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isProgress)
|
||||||
|
builder.setProgress(progressMax, progress, indeterminate)
|
||||||
|
if (target != null) {
|
||||||
|
val intent = Intent(context, target)
|
||||||
|
pendingIntentParam?.applyParam(intent)
|
||||||
|
builder.setContentIntent(PendingIntent.getActivity(context, requestCode,
|
||||||
|
intent, pendingIntentFlag))
|
||||||
|
}
|
||||||
|
builder.setDefaults(defaults)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show(notificationId: Int = generateNotifyId()): Int {
|
||||||
|
notification = builder.build()
|
||||||
|
notificationManager.notify(notificationId, notification)
|
||||||
|
return notificationId
|
||||||
|
}
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -0,0 +1,3 @@
|
|||||||
|
<resources>
|
||||||
|
<string name="camera_permission_require">本功能需要请求摄像头权限</string>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
package cn.qjm253.quick_android_base;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
|
||||||
|
|
||||||
|
android {
|
||||||
|
compileSdkVersion compile_sdk_version
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion min_sdk_version
|
||||||
|
targetSdkVersion target_sdk_version
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
|
||||||
|
implementation "androidx.appcompat:appcompat:${appcompact_version}"
|
||||||
|
testImplementation "junit:junit:${junit_version}"
|
||||||
|
androidTestImplementation "androidx.test:runner:${test_runner_version}"
|
||||||
|
androidTestImplementation "androidx.test.espresso:espresso-core:${espresso_core_version}"
|
||||||
|
|
||||||
|
implementation project(path: ':quick_android_base')
|
||||||
|
|
||||||
|
}
|
||||||
+21
@@ -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
|
||||||
+26
@@ -0,0 +1,26 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import androidx.test.InstrumentationRegistry;
|
||||||
|
import androidx.test.runner.AndroidJUnit4;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4.class)
|
||||||
|
public class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
public void useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
Context appContext = InstrumentationRegistry.getTargetContext();
|
||||||
|
|
||||||
|
assertEquals("cn.qjm253.quick_android_custom_view.test", appContext.getPackageName());
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cn.qjm253.quick_android_custom_view"/>
|
||||||
+102
@@ -0,0 +1,102 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.custom_drawable_size_view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.appcompat.widget.AppCompatButton
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个Button的封装组件,支持自定义四个方向的drawable的大小
|
||||||
|
*
|
||||||
|
* 1. 使用 'left_drawable', 'right_drawable', 'top_drawable', 'bottom_drawable' 属性来设置四个方向的drawable资源
|
||||||
|
*
|
||||||
|
* 2. 使用 'drawable_width', 'drawable_height' 统一设置四个drawable的大小
|
||||||
|
*
|
||||||
|
* 3. 使用 'left_drawable_width', 'left_drawable_height', 'right_drawable_width', 'right_drawable_height',
|
||||||
|
* 'top_drawable_width', 'top_drawable_height', 'bottom_drawable_width', 'bottom_drawable_height'
|
||||||
|
* 来定制指定方向drawable的大小
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 下午9:09
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
open class DrawableButton @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.buttonStyle
|
||||||
|
) : AppCompatButton(context, attrs, defStyleAttr) {
|
||||||
|
companion object {
|
||||||
|
const val DEFAULT_SIZE = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableButton)
|
||||||
|
|
||||||
|
val width = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_drawable_width,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
val height = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_drawable_height,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
|
||||||
|
val sizeWrap = SizeWrap()
|
||||||
|
|
||||||
|
val letfD = ta.getDrawable(R.styleable.DrawableButton_left_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_left_drawable_width,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_left_drawable_height,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val rightD = ta.getDrawable(R.styleable.DrawableButton_right_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_right_drawable_width,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_right_drawable_height,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val topD = ta.getDrawable(R.styleable.DrawableButton_top_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_top_drawable_width,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_top_drawable_height,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val bottomD = ta.getDrawable(R.styleable.DrawableButton_bottom_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_bottom_drawable_width,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.DrawableButton_bottom_drawable_height,
|
||||||
|
DEFAULT_SIZE
|
||||||
|
)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setCompoundDrawables(letfD, topD, rightD, bottomD)
|
||||||
|
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
+75
@@ -0,0 +1,75 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.custom_drawable_size_view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.appcompat.widget.AppCompatEditText
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个EditText的封装组件,支持自定义四个方向的drawable的大小
|
||||||
|
*
|
||||||
|
* 1. 使用 'left_drawable', 'right_drawable', 'top_drawable', 'bottom_drawable' 属性来设置四个方向的drawable资源
|
||||||
|
*
|
||||||
|
* 2. 使用 'drawable_width', 'drawable_height' 统一设置四个drawable的大小
|
||||||
|
*
|
||||||
|
* 3. 使用 'left_drawable_width', 'left_drawable_height', 'right_drawable_width', 'right_drawable_height',
|
||||||
|
* 'top_drawable_width', 'top_drawable_height', 'bottom_drawable_width', 'bottom_drawable_height'
|
||||||
|
* 来定制指定方向drawable的大小
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 下午9:04
|
||||||
|
*/
|
||||||
|
|
||||||
|
open class DrawableEditText @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle
|
||||||
|
) : AppCompatEditText(context, attrs, defStyleAttr) {
|
||||||
|
companion object {
|
||||||
|
const val DEFAULT_SIZE = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableEditText)
|
||||||
|
|
||||||
|
val width = ta.getDimensionPixelOffset(R.styleable.DrawableEditText_drawable_width, DEFAULT_SIZE)
|
||||||
|
val height = ta.getDimensionPixelOffset(R.styleable.DrawableEditText_drawable_height, DEFAULT_SIZE)
|
||||||
|
|
||||||
|
val sizeWrap = SizeWrap()
|
||||||
|
|
||||||
|
val letfD = ta.getDrawable(R.styleable.DrawableEditText_left_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(R.styleable.DrawableEditText_left_drawable_width, DEFAULT_SIZE)
|
||||||
|
val lheight =
|
||||||
|
ta.getDimensionPixelOffset(R.styleable.DrawableEditText_left_drawable_height, DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val rightD = ta.getDrawable(R.styleable.DrawableEditText_right_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(R.styleable.DrawableEditText_right_drawable_width, DEFAULT_SIZE)
|
||||||
|
val lheight =
|
||||||
|
ta.getDimensionPixelOffset(R.styleable.DrawableEditText_right_drawable_height, DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val topD = ta.getDrawable(R.styleable.DrawableEditText_top_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(R.styleable.DrawableEditText_top_drawable_width, DEFAULT_SIZE)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(R.styleable.DrawableEditText_top_drawable_height, DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val bottomD = ta.getDrawable(R.styleable.DrawableEditText_bottom_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth =
|
||||||
|
ta.getDimensionPixelOffset(R.styleable.DrawableEditText_bottom_drawable_width, DEFAULT_SIZE)
|
||||||
|
val lheight =
|
||||||
|
ta.getDimensionPixelOffset(R.styleable.DrawableEditText_bottom_drawable_height, DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setCompoundDrawables(letfD, topD, rightD, bottomD)
|
||||||
|
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
+76
@@ -0,0 +1,76 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.custom_drawable_size_view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.appcompat.widget.AppCompatTextView
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个TextView的封装组件,支持自定义四个方向的drawable的大小
|
||||||
|
*
|
||||||
|
* 1. 使用 'left_drawable', 'right_drawable', 'top_drawable', 'bottom_drawable' 属性来设置四个方向的drawable资源
|
||||||
|
*
|
||||||
|
* 2. 使用 'drawable_width', 'drawable_height' 统一设置四个drawable的大小
|
||||||
|
*
|
||||||
|
* 3. 使用 'left_drawable_width', 'left_drawable_height', 'right_drawable_width', 'right_drawable_height',
|
||||||
|
* 'top_drawable_width', 'top_drawable_height', 'bottom_drawable_width', 'bottom_drawable_height'
|
||||||
|
* 来定制指定方向drawable的大小
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 下午8:48
|
||||||
|
*/
|
||||||
|
|
||||||
|
open class DrawableTextView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = android.R.attr.textViewStyle
|
||||||
|
) : AppCompatTextView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val DRAWABLE_DEFAULT_SIZE = 20
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
val ta = context.obtainStyledAttributes(attrs, R.styleable.DrawableTextView)
|
||||||
|
|
||||||
|
val width = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_drawable_width, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
val height = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_drawable_height, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
|
||||||
|
val sizeWrap = SizeWrap()
|
||||||
|
|
||||||
|
val letfD = ta.getDrawable(R.styleable.DrawableTextView_left_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_left_drawable_width, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_left_drawable_height, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val rightD = ta.getDrawable(R.styleable.DrawableTextView_right_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_right_drawable_width, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_right_drawable_height, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val topD = ta.getDrawable(R.styleable.DrawableTextView_top_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_top_drawable_width, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_top_drawable_height, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
val bottomD = ta.getDrawable(R.styleable.DrawableTextView_bottom_drawable)
|
||||||
|
?.apply {
|
||||||
|
val lwidth = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_bottom_drawable_width, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
val lheight = ta.getDimensionPixelOffset(R.styleable.DrawableTextView_bottom_drawable_height, DRAWABLE_DEFAULT_SIZE)
|
||||||
|
sizeWrap.checkSize(width, height, lwidth, lheight)
|
||||||
|
setBounds(0, 0, sizeWrap.width, sizeWrap.height)
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setCompoundDrawables(letfD, topD, rightD, bottomD)
|
||||||
|
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.custom_drawable_size_view
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个处理drawable自定义大小设置的工具类
|
||||||
|
*
|
||||||
|
* 1. 四个方向的drawable都有一个默认大小 DRAWABLE_DEFAULT_SIZE;
|
||||||
|
*
|
||||||
|
* 2. 使用 'drawable_width' 和 'drawable_height' 来设置全局大小,则四个方向的drawable均被设置;
|
||||||
|
*
|
||||||
|
* 3. 如果使用 'left_drawable_width' 和 'left_drawable_height' 这种单独设置某个方向的drawable会覆盖默认大小和全局大小
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 下午9:00
|
||||||
|
*/
|
||||||
|
class SizeWrap(var width: Int = -1, var height: Int = -1) {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 检查用户是否自定义了drawable的size,按上述规则进行判断
|
||||||
|
*/
|
||||||
|
fun checkSize(globalWidth: Int, globalHeight: Int, localWidth: Int, localHeight: Int) {
|
||||||
|
width = DrawableTextView.DRAWABLE_DEFAULT_SIZE
|
||||||
|
height = DrawableTextView.DRAWABLE_DEFAULT_SIZE
|
||||||
|
|
||||||
|
// 对某一个具体方向图标大小的设置会覆盖对全局图标大小的设置
|
||||||
|
if (localHeight > 0 && localWidth > 0) {
|
||||||
|
width = localWidth
|
||||||
|
height = localHeight
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (localHeight == DrawableTextView.DRAWABLE_DEFAULT_SIZE && localWidth == DrawableTextView.DRAWABLE_DEFAULT_SIZE
|
||||||
|
&& globalHeight > 0 && globalWidth > 0) {
|
||||||
|
width = globalWidth
|
||||||
|
height = globalHeight
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+74
@@ -0,0 +1,74 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.other
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.text.Editable
|
||||||
|
import android.text.TextWatcher
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import cn.qjm253.quick_android_custom_view.tin_view.TinEditText
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个可以显示清空按钮的EditText组件,right_drawable一般设置成有清空含义的图标
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-14 下午8:01
|
||||||
|
*/
|
||||||
|
|
||||||
|
class ClearAbleEditText @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle
|
||||||
|
) : TinEditText(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
|
||||||
|
init {
|
||||||
|
changeDrawables(right = null)
|
||||||
|
addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun afterTextChanged(s: Editable?) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
|
||||||
|
if (s?.length ?: 0 > 0) {
|
||||||
|
changeDrawables(right = mDrawables[2])
|
||||||
|
} else {
|
||||||
|
changeDrawables(right = null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onFocusChange(v: View?, hasFocus: Boolean) {
|
||||||
|
super.onFocusChange(v, hasFocus)
|
||||||
|
if (!hasFocus || text?.isEmpty() == true)
|
||||||
|
changeDrawables(right = null)
|
||||||
|
else
|
||||||
|
changeDrawables(right = mDrawables[2])
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTouchEvent(event: MotionEvent?): Boolean {
|
||||||
|
if (event?.action == MotionEvent.ACTION_UP) {
|
||||||
|
//getTotalPaddingRight()图标左边缘至控件右边缘的距离
|
||||||
|
//getWidth() - getTotalPaddingRight()表示从最左边到图标左边缘的位置
|
||||||
|
//getWidth() - getPaddingRight()表示最左边到图标右边缘的位置
|
||||||
|
val touchable = event.x > width - totalPaddingRight && event.x < width - paddingRight
|
||||||
|
|
||||||
|
if (touchable) {
|
||||||
|
this.setText("")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.onTouchEvent(event)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun changeDrawables(
|
||||||
|
left: Drawable? = mDrawables[0],
|
||||||
|
top: Drawable? = mDrawables[1],
|
||||||
|
right: Drawable? = mDrawables[2],
|
||||||
|
bottom: Drawable? = mDrawables[3]
|
||||||
|
) {
|
||||||
|
setCompoundDrawables(left, top, right, bottom)
|
||||||
|
}
|
||||||
|
}
|
||||||
+37
@@ -0,0 +1,37 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.other
|
||||||
|
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-14 下午8:13
|
||||||
|
*/
|
||||||
|
interface LineMenu {
|
||||||
|
fun getLeft_icon(): ImageView?
|
||||||
|
|
||||||
|
fun getRight_icon(): ImageView?
|
||||||
|
|
||||||
|
fun getTv_title(): TextView?
|
||||||
|
|
||||||
|
fun getTv_value(): TextView?
|
||||||
|
|
||||||
|
fun setLeftIcon(@DrawableRes res: Int)
|
||||||
|
|
||||||
|
fun setRightIcon(@DrawableRes res: Int)
|
||||||
|
|
||||||
|
|
||||||
|
fun setRightIconVisible(visible: Int)
|
||||||
|
|
||||||
|
fun setLeftIconVisible(visible: Int)
|
||||||
|
|
||||||
|
fun setTitleIconVisible(visible: Int)
|
||||||
|
|
||||||
|
fun setValueIconVisible(visible: Int)
|
||||||
|
|
||||||
|
fun setTitle(title: String)
|
||||||
|
|
||||||
|
fun setValue(value: String)
|
||||||
|
}
|
||||||
+271
@@ -0,0 +1,271 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.other
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import cn.qjm253.quick_android_base.util.GeneratedId
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 菜单(可以设置图标)
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-14 下午8:14
|
||||||
|
*/
|
||||||
|
|
||||||
|
class LineMenuItem @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0) : RelativeLayout(context, attrs, defStyleAttr), LineMenu {
|
||||||
|
|
||||||
|
private var myContext: Context? = null
|
||||||
|
private var menuTitle: String? = null
|
||||||
|
private var menuValue: String? = null
|
||||||
|
private var menuTitleSize: Float = 0.toFloat()
|
||||||
|
@ColorInt
|
||||||
|
private var menuTitleColor: Int = 0
|
||||||
|
private var menuValueSize: Float = 0.toFloat()
|
||||||
|
@ColorInt
|
||||||
|
private var menuValueColor: Int = 0
|
||||||
|
@DrawableRes
|
||||||
|
private var leftResource: Int = 0
|
||||||
|
private var rightResource: Int = 0
|
||||||
|
|
||||||
|
private var isRightIconVisible: Boolean = false
|
||||||
|
private var isLeftIconVisible: Boolean = false
|
||||||
|
|
||||||
|
private var leftIconSize: Int = 0
|
||||||
|
private var rightIconSize: Int = 0
|
||||||
|
|
||||||
|
private var leftMargin: Int = 0
|
||||||
|
private var rightMargin: Int = 0
|
||||||
|
private var leftIconTextSpan: Int = 0
|
||||||
|
|
||||||
|
private var leftIcon: ImageView? = null
|
||||||
|
private var rightIcon: ImageView? = null
|
||||||
|
private var tvTitle: TextView? = null
|
||||||
|
private var tvValue: TextView? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
this.myContext = context
|
||||||
|
getAttr(context, attrs, defStyleAttr)
|
||||||
|
initView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAttr(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
|
||||||
|
val ta = context.obtainStyledAttributes(attrs, R.styleable.LineMenuItem)
|
||||||
|
menuTitle = ta.getString(R.styleable.LineMenuItem_menu_title)
|
||||||
|
menuValue = ta.getString(R.styleable.LineMenuItem_menu_value)
|
||||||
|
menuTitleSize = px2sp(context,
|
||||||
|
ta.getDimensionPixelSize(R.styleable.LineMenuItem_menu_title_size,
|
||||||
|
sp2px(context, DEFAULT_TEXT_SIZE.toFloat())).toFloat()).toFloat()
|
||||||
|
menuTitleColor = ta.getColor(R.styleable.LineMenuItem_menu_title_color,
|
||||||
|
resources.getColor(DEFAULT_TITLE_COLOR))
|
||||||
|
menuValueSize = px2sp(context,
|
||||||
|
ta.getDimension(R.styleable.LineMenuItem_menu_value_size,
|
||||||
|
sp2px(context, DEFAULT_TEXT_SIZE.toFloat()).toFloat())).toFloat()
|
||||||
|
menuValueColor = ta.getColor(R.styleable.LineMenuItem_menu_value_color,
|
||||||
|
resources.getColor(DEFAULT_TITLE_COLOR))
|
||||||
|
leftResource = ta.getResourceId(R.styleable.LineMenuItem_left_resource, DEFAULT_LEFT_RESOURCE)
|
||||||
|
rightResource = ta.getResourceId(R.styleable.LineMenuItem_right_resource, DEFAULT_RIGHT_RESOURCE)
|
||||||
|
leftIconSize = ta.getDimensionPixelSize(R.styleable.LineMenuItem_left_icon_size,
|
||||||
|
dip2px(context, DEFAULT_LEFT_ICON_SIZE.toFloat()))
|
||||||
|
rightIconSize = ta.getDimensionPixelSize(R.styleable.LineMenuItem_right_icon_size,
|
||||||
|
dip2px(context, DEFAULT_RIGHT_ICON_SIZE.toFloat()))
|
||||||
|
isRightIconVisible = ta
|
||||||
|
.getBoolean(R.styleable.LineMenuItem_is_right_icon_visible, true)
|
||||||
|
isLeftIconVisible = ta
|
||||||
|
.getBoolean(R.styleable.LineMenuItem_is_left_icon_visible, false)
|
||||||
|
leftMargin = ta.getDimensionPixelSize(R.styleable.LineMenuItem_left_margin, dip2px(context, DEFAULT_MARGIN))
|
||||||
|
rightMargin = ta.getDimensionPixelOffset(R.styleable.LineMenuItem_right_margin, dip2px(context, DEFAULT_MARGIN))
|
||||||
|
leftIconTextSpan = ta.getDimensionPixelOffset(R.styleable.LineMenuItem_left_icon_text_span,
|
||||||
|
dip2px(context, DEFAULT_MARGIN))
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
leftIcon = ImageView(myContext)
|
||||||
|
leftIcon!!.setImageResource(leftResource)
|
||||||
|
leftIcon!!.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||||
|
leftIcon!!.setBackgroundResource(R.color.transparent)
|
||||||
|
val left_icon_param = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
left_icon_param.addRule(CENTER_VERTICAL)
|
||||||
|
left_icon_param.width = leftIconSize
|
||||||
|
left_icon_param.height = leftIconSize
|
||||||
|
left_icon_param.leftMargin = leftMargin
|
||||||
|
leftIcon!!.layoutParams = left_icon_param
|
||||||
|
addView(leftIcon, left_icon_param)
|
||||||
|
if (!isLeftIconVisible) {
|
||||||
|
leftIcon!!.visibility = View.GONE
|
||||||
|
}
|
||||||
|
val left_icon_id = GeneratedId.generateViewId()
|
||||||
|
leftIcon!!.id = left_icon_id
|
||||||
|
|
||||||
|
tvTitle = TextView(myContext)
|
||||||
|
tvTitle!!.text = menuTitle
|
||||||
|
tvTitle!!.setTextColor(menuTitleColor)
|
||||||
|
tvTitle!!.textSize = menuTitleSize
|
||||||
|
tvTitle!!.setBackgroundResource(R.color.transparent)
|
||||||
|
val title_param = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
title_param.addRule(CENTER_VERTICAL)
|
||||||
|
title_param.leftMargin = leftIconTextSpan
|
||||||
|
title_param.addRule(RIGHT_OF, left_icon_id)
|
||||||
|
tvTitle!!.layoutParams = title_param
|
||||||
|
addView(tvTitle, title_param)
|
||||||
|
|
||||||
|
|
||||||
|
rightIcon = ImageView(myContext)
|
||||||
|
rightIcon!!.setImageResource(rightResource)
|
||||||
|
rightIcon!!.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||||
|
rightIcon!!.setBackgroundResource(R.color.transparent)
|
||||||
|
val right_icon_param = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
right_icon_param.addRule(CENTER_VERTICAL)
|
||||||
|
right_icon_param.addRule(ALIGN_PARENT_RIGHT)
|
||||||
|
right_icon_param.width = rightIconSize
|
||||||
|
right_icon_param.height = rightIconSize
|
||||||
|
right_icon_param.rightMargin = rightMargin
|
||||||
|
rightIcon!!.layoutParams = right_icon_param
|
||||||
|
addView(rightIcon, right_icon_param)
|
||||||
|
val right_icon_id = GeneratedId.generateViewId()
|
||||||
|
rightIcon!!.id = right_icon_id
|
||||||
|
|
||||||
|
tvValue = TextView(myContext)
|
||||||
|
tvValue!!.text = menuValue
|
||||||
|
tvValue!!.setTextColor(menuValueColor)
|
||||||
|
tvValue!!.textSize = menuValueSize
|
||||||
|
tvValue!!.setBackgroundResource(R.color.transparent)
|
||||||
|
val tv_value_param = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||||
|
tv_value_param.addRule(CENTER_VERTICAL)
|
||||||
|
if (isRightIconVisible)
|
||||||
|
tv_value_param.addRule(LEFT_OF, right_icon_id)
|
||||||
|
else
|
||||||
|
tv_value_param.addRule(ALIGN_PARENT_RIGHT)
|
||||||
|
tv_value_param.rightMargin = dip2px(myContext!!, DEFAULT_MARGIN)
|
||||||
|
tvValue!!.layoutParams = tv_value_param
|
||||||
|
addView(tvValue, tv_value_param)
|
||||||
|
|
||||||
|
if (isRightIconVisible)
|
||||||
|
setRightIconVisible(View.VISIBLE)
|
||||||
|
else
|
||||||
|
setRightIconVisible(View.INVISIBLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getLeft_icon(): ImageView? {
|
||||||
|
return leftIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getRight_icon(): ImageView? {
|
||||||
|
return rightIcon
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTv_title(): TextView? {
|
||||||
|
return tvTitle
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun getTv_value(): TextView? {
|
||||||
|
return tvValue
|
||||||
|
}
|
||||||
|
|
||||||
|
fun value(): String {
|
||||||
|
return getTv_value()?.text?.toString() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
fun title(): String {
|
||||||
|
return getTv_title()?.text?.toString() ?: ""
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLeftIcon(@DrawableRes res: Int) {
|
||||||
|
this.post {
|
||||||
|
leftResource = res
|
||||||
|
leftIcon!!.setImageResource(leftResource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setRightIcon(@DrawableRes res: Int) {
|
||||||
|
this.post {
|
||||||
|
rightResource = res
|
||||||
|
rightIcon!!.setImageResource(rightResource)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setRightIconVisible(visible: Int) {
|
||||||
|
if (rightIcon != null)
|
||||||
|
rightIcon!!.visibility = visible
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setLeftIconVisible(visible: Int) {
|
||||||
|
if (leftIcon != null)
|
||||||
|
leftIcon!!.visibility = visible
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTitleIconVisible(visible: Int) {
|
||||||
|
if (tvTitle != null)
|
||||||
|
tvTitle!!.visibility = visible
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValueIconVisible(visible: Int) {
|
||||||
|
if (tvValue != null)
|
||||||
|
tvValue!!.visibility = visible
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setTitle(title: String) {
|
||||||
|
if (tvTitle != null)
|
||||||
|
tvTitle!!.text = title
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun setValue(value: String) {
|
||||||
|
if (tvValue != null)
|
||||||
|
tvValue!!.text = value
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val DEFAULT_TEXT_SIZE = 14
|
||||||
|
private val DEFAULT_MARGIN = 13f
|
||||||
|
|
||||||
|
private val DEFAULT_TITLE_COLOR = R.color.text_gray
|
||||||
|
private val DEFAULT_LEFT_RESOURCE = R.drawable.menu
|
||||||
|
private val DEFAULT_RIGHT_RESOURCE = R.drawable.icon_arrow
|
||||||
|
private val DEFAULT_LEFT_ICON_SIZE = 24
|
||||||
|
private val DEFAULT_RIGHT_ICON_SIZE = 24
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||||
|
*/
|
||||||
|
fun dip2px(context: Context, dpValue: Float): Int {
|
||||||
|
val scale = context.resources.displayMetrics.density
|
||||||
|
return (dpValue * scale + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将px值转换为sp值,保证文字大小不变
|
||||||
|
*
|
||||||
|
* @param pxValue (DisplayMetrics类中属性scaledDensity)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun px2sp(context: Context, pxValue: Float): Int {
|
||||||
|
val fontScale = context.resources.displayMetrics.scaledDensity
|
||||||
|
return (pxValue / fontScale + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将sp值转换为px值,保证文字大小不变
|
||||||
|
*
|
||||||
|
* @param spValue (DisplayMetrics类中属性scaledDensity)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun sp2px(context: Context, spValue: Float): Int {
|
||||||
|
val fontScale = context.resources.displayMetrics.scaledDensity
|
||||||
|
return (spValue * fontScale + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
+154
@@ -0,0 +1,154 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.other
|
||||||
|
|
||||||
|
import android.widget.CompoundButton
|
||||||
|
import android.content.Context
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import android.os.Build
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.Switch
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-14 下午8:30
|
||||||
|
*/
|
||||||
|
class LineMenuSwitch @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||||
|
private var menuTitle: String? = null
|
||||||
|
@ColorInt
|
||||||
|
private var menuTitleColor: Int = 0
|
||||||
|
private var menuTitleSize: Float = 0.toFloat()
|
||||||
|
|
||||||
|
@DrawableRes
|
||||||
|
private var switchThumb: Int = 0
|
||||||
|
@DrawableRes
|
||||||
|
private var switchTrack: Int = 0
|
||||||
|
|
||||||
|
private var leftMargin: Int = 0
|
||||||
|
private var rightMargin: Int = 0
|
||||||
|
|
||||||
|
private var tv_title: TextView? = null
|
||||||
|
private var aSwitch: Switch? = null
|
||||||
|
private var checked = false
|
||||||
|
private var onCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
getAttr(context, attrs, defStyleAttr)
|
||||||
|
initView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initView() {
|
||||||
|
tv_title = TextView(context)
|
||||||
|
tv_title!!.text = menuTitle
|
||||||
|
tv_title!!.setTextColor(menuTitleColor)
|
||||||
|
tv_title!!.textSize = menuTitleSize
|
||||||
|
tv_title!!.setBackgroundResource(R.color.transparent)
|
||||||
|
val title_param = LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
title_param.addRule(CENTER_VERTICAL)
|
||||||
|
title_param.leftMargin = leftMargin
|
||||||
|
tv_title!!.layoutParams = title_param
|
||||||
|
addView(tv_title, title_param)
|
||||||
|
|
||||||
|
aSwitch = Switch(context)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
|
||||||
|
aSwitch!!.setThumbResource(switchThumb)
|
||||||
|
aSwitch!!.setTrackResource(switchTrack)
|
||||||
|
}
|
||||||
|
val switch_param = LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
switch_param.addRule(ALIGN_PARENT_END)
|
||||||
|
}
|
||||||
|
switch_param.addRule(ALIGN_PARENT_RIGHT)
|
||||||
|
switch_param.addRule(CENTER_VERTICAL)
|
||||||
|
switch_param.rightMargin = rightMargin
|
||||||
|
addView(aSwitch, switch_param)
|
||||||
|
aSwitch!!.isChecked = checked
|
||||||
|
aSwitch!!.setOnCheckedChangeListener(onCheckedChangeListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getAttr(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
|
||||||
|
val ta = context.obtainStyledAttributes(attrs, R.styleable.LineMenuSwitch)
|
||||||
|
menuTitle = ta.getString(R.styleable.LineMenuSwitch_menu_title)
|
||||||
|
menuTitleSize = ta.getFloat(R.styleable.LineMenuSwitch_menu_title_size, DEFAULT_TEXT_SIZE.toFloat())
|
||||||
|
menuTitleColor = ta.getColor(
|
||||||
|
R.styleable.LineMenuSwitch_menu_title_color,
|
||||||
|
resources.getColor(DEFAULT_TITLE_COLOR)
|
||||||
|
)
|
||||||
|
leftMargin = ta.getDimensionPixelSize(
|
||||||
|
R.styleable.LineMenuSwitch_left_margin,
|
||||||
|
dip2px(context, DEFAULT_MARGIN)
|
||||||
|
)
|
||||||
|
rightMargin = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.LineMenuSwitch_right_margin,
|
||||||
|
dip2px(context, DEFAULT_MARGIN)
|
||||||
|
)
|
||||||
|
switchThumb = ta.getResourceId(R.styleable.LineMenuSwitch_switch_thumb, R.drawable.switch_thumb)
|
||||||
|
switchTrack = ta.getResourceId(R.styleable.LineMenuSwitch_switch_track, R.drawable.switch_track)
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setChecked(checked: Boolean) {
|
||||||
|
this.checked = checked
|
||||||
|
if (aSwitch != null)
|
||||||
|
aSwitch!!.isChecked = checked
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnCheckedChangeListener(onCheckedChangeListener: CompoundButton.OnCheckedChangeListener) {
|
||||||
|
this.onCheckedChangeListener = onCheckedChangeListener
|
||||||
|
if (aSwitch != null)
|
||||||
|
aSwitch!!.setOnCheckedChangeListener(onCheckedChangeListener)
|
||||||
|
}
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
|
||||||
|
private val DEFAULT_TEXT_SIZE = 14
|
||||||
|
private val DEFAULT_MARGIN = 13f
|
||||||
|
|
||||||
|
private val DEFAULT_TITLE_COLOR = R.color.text_gray
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||||
|
*/
|
||||||
|
fun dip2px(context: Context, dpValue: Float): Int {
|
||||||
|
val scale = context.resources.displayMetrics.density
|
||||||
|
return (dpValue * scale + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将px值转换为sp值,保证文字大小不变
|
||||||
|
*
|
||||||
|
* @param pxValue (DisplayMetrics类中属性scaledDensity)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun px2sp(context: Context, pxValue: Float): Int {
|
||||||
|
val fontScale = context.resources.displayMetrics.scaledDensity
|
||||||
|
return (pxValue / fontScale + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将sp值转换为px值,保证文字大小不变
|
||||||
|
*
|
||||||
|
* @param spValue (DisplayMetrics类中属性scaledDensity)
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
fun sp2px(context: Context, spValue: Float): Int {
|
||||||
|
val fontScale = context.resources.displayMetrics.scaledDensity
|
||||||
|
return (spValue * fontScale + 0.5f).toInt()
|
||||||
|
}
|
||||||
|
}
|
||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.other
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.viewpager.widget.ViewPager
|
||||||
|
import android.view.MotionEvent
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-14 下午7:52
|
||||||
|
*/
|
||||||
|
|
||||||
|
class NoneScrollViewPager(context: Context, attrs: AttributeSet? = null) : ViewPager(context, attrs) {
|
||||||
|
private var isScroll = false
|
||||||
|
init {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否拦截
|
||||||
|
* 拦截:会走到自己的onTouchEvent方法里面来
|
||||||
|
* 不拦截:事件传递给子孩子
|
||||||
|
*/
|
||||||
|
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
||||||
|
// return false;//可行,不拦截事件,
|
||||||
|
// return true;//不行,孩子无法处理事件
|
||||||
|
//return super.onInterceptTouchEvent(ev);//不行,会有细微移动
|
||||||
|
return if (isScroll) {
|
||||||
|
super.onInterceptTouchEvent(ev)
|
||||||
|
} else {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 是否消费事件
|
||||||
|
* 消费:事件就结束
|
||||||
|
* 不消费:往父控件传
|
||||||
|
*/
|
||||||
|
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
||||||
|
//return false;// 可行,不消费,传给父控件
|
||||||
|
//return true;// 可行,消费,拦截事件
|
||||||
|
//super.onTouchEvent(ev); //不行,
|
||||||
|
//虽然onInterceptTouchEvent中拦截了,
|
||||||
|
//但是如果viewpage里面子控件不是viewgroup,还是会调用这个方法.
|
||||||
|
return if (isScroll) {
|
||||||
|
super.onTouchEvent(ev)
|
||||||
|
} else {
|
||||||
|
true// 可行,消费,拦截事件
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setScroll(scroll: Boolean) {
|
||||||
|
isScroll = scroll
|
||||||
|
}
|
||||||
|
}
|
||||||
+112
@@ -0,0 +1,112 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.tin_view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
import cn.qjm253.quick_android_custom_view.custom_drawable_size_view.DrawableButton
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个 DrawableButton 的封装组件,在DrawableButton的基础上支持设置drawable的tinColor
|
||||||
|
*
|
||||||
|
* @see DrawableButton
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 下午10:33
|
||||||
|
*/
|
||||||
|
class TinButton @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.buttonStyle
|
||||||
|
): DrawableButton(context, attrs, defStyleAttr) {
|
||||||
|
companion object {
|
||||||
|
const val NONE = 0x0000
|
||||||
|
const val LEFT = 0x0001
|
||||||
|
const val TOP = 0x0002
|
||||||
|
const val RIGHT = 0x0004
|
||||||
|
const val BOTTOM = 0x0008
|
||||||
|
const val ALL = 0x000f
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var tints: Int = TinTextView.NONE
|
||||||
|
private lateinit var mDrawables: Array<Drawable?>
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!isInEditMode) {
|
||||||
|
mDrawables = compoundDrawables
|
||||||
|
loadFromAttribute(context, attrs, defStyleAttr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从布局文件中加载属性配置
|
||||||
|
*/
|
||||||
|
private fun loadFromAttribute(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
|
||||||
|
attrs?.let {
|
||||||
|
val ta = context.obtainStyledAttributes(it, R.styleable.TinButton)
|
||||||
|
tints = ta.getInt(R.styleable.TinButton_tints, ALL)
|
||||||
|
ta.getColorStateList(R.styleable.TinButton_tinColor)?.let { tinColorStateList ->
|
||||||
|
updateDrawablesTinColor(tinColorStateList)
|
||||||
|
}
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置哪些方向的drawable需要设置tinColor
|
||||||
|
*
|
||||||
|
* @see TinButton.NONE -> 四个方向都不着色
|
||||||
|
* @see TinButton.LEFT -> 左边drawable着色
|
||||||
|
* @see TinButton.RIGHT -> 右边drawable着色
|
||||||
|
* @see TinButton.TOP -> 顶部drawable着色
|
||||||
|
* @see TinButton.BOTTOM -> 底部drawable着色
|
||||||
|
* @see TinButton.ALL -> 四个方向都着色
|
||||||
|
*
|
||||||
|
* 如果需要单独设置两个方向可以 => TinButton.LEFT or TinButton.RIGHT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun updateTints(tints: Int) {
|
||||||
|
if (tints < 0 || tints > 8)
|
||||||
|
return
|
||||||
|
this.tints = tints
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
private fun updateDrawablesTinColor(colorStateList: ColorStateList) {
|
||||||
|
//设置icon
|
||||||
|
if (mDrawables[0] != null && (tints and TinTextView.LEFT) == TinTextView.LEFT) {
|
||||||
|
mDrawables[0] = DrawableCompat.wrap(mDrawables[0]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[0]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[1] != null && (tints and TinTextView.TOP) == TinTextView.TOP) {
|
||||||
|
mDrawables[1] = DrawableCompat.wrap(mDrawables[1]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[1]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[2] != null && (tints and TinTextView.RIGHT) == TinTextView.RIGHT) {
|
||||||
|
mDrawables[2] = DrawableCompat.wrap(mDrawables[2]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[2]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[3] != null && (tints and TinTextView.BOTTOM) == TinTextView.BOTTOM) {
|
||||||
|
mDrawables[3] = DrawableCompat.wrap(mDrawables[3]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[3]!!, colorStateList)
|
||||||
|
}
|
||||||
|
setCompoundDrawables(mDrawables[0], mDrawables[1], mDrawables[2], mDrawables[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
fun updateDrawablesTinColor(@ColorRes color: Int) {
|
||||||
|
ContextCompat.getColorStateList(context, color)?.let {
|
||||||
|
updateDrawablesTinColor(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+146
@@ -0,0 +1,146 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.tin_view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
import cn.qjm253.quick_android_custom_view.custom_drawable_size_view.DrawableEditText
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个 DrawableEditText 的封装组件,在 DrawableEditText 的基础上支持设置drawable的tinColor
|
||||||
|
*
|
||||||
|
* @see DrawableEditText
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 下午10:15
|
||||||
|
*/
|
||||||
|
|
||||||
|
open class TinEditText @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.editTextStyle
|
||||||
|
) : DrawableEditText(context, attrs, defStyleAttr), View.OnFocusChangeListener {
|
||||||
|
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
const val NONE = 0x0000
|
||||||
|
const val LEFT = 0x0001
|
||||||
|
const val TOP = 0x0002
|
||||||
|
const val RIGHT = 0x0004
|
||||||
|
const val BOTTOM = 0x0008
|
||||||
|
const val ALL = 0x000f
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var tints: Int = NONE
|
||||||
|
|
||||||
|
private var tinColor: ColorStateList? = null
|
||||||
|
private var focusedTinColor: ColorStateList? = null
|
||||||
|
|
||||||
|
protected lateinit var mDrawables: Array<Drawable?>
|
||||||
|
|
||||||
|
init {
|
||||||
|
|
||||||
|
if (!isInEditMode) {
|
||||||
|
mDrawables = compoundDrawables
|
||||||
|
loadFromAttribute(context, attrs, defStyleAttr)
|
||||||
|
this.onFocusChangeListener = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从布局文件中加载属性配置
|
||||||
|
*/
|
||||||
|
private fun loadFromAttribute(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
|
||||||
|
attrs?.let {
|
||||||
|
val ta = context.obtainStyledAttributes(it, R.styleable.TinEditText)
|
||||||
|
tints = ta.getInt(R.styleable.TinEditText_tints, ALL)
|
||||||
|
tinColor = ta.getColorStateList(R.styleable.TinEditText_tinColor)
|
||||||
|
focusedTinColor = ta.getColorStateList(R.styleable.TinEditText_focusedTinColor)
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
// 根绝当前是否聚焦,修改tinColor
|
||||||
|
onFocusChange(this, isFocused)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置哪些方向的drawable需要设置tinColor
|
||||||
|
*
|
||||||
|
* @see TinEditText.NONE -> 四个方向都不着色
|
||||||
|
* @see TinEditText.LEFT -> 左边drawable着色
|
||||||
|
* @see TinEditText.RIGHT -> 右边drawable着色
|
||||||
|
* @see TinEditText.TOP -> 顶部drawable着色
|
||||||
|
* @see TinEditText.BOTTOM -> 底部drawable着色
|
||||||
|
* @see TinEditText.ALL -> 四个方向都着色
|
||||||
|
*
|
||||||
|
* 如果需要单独设置两个方向可以 => TinEditText.LEFT or TinEditText.RIGHT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun updateTints(tints: Int) {
|
||||||
|
if (tints < 0 || tints > 8)
|
||||||
|
return
|
||||||
|
this.tints = tints
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
private fun updateDrawablesTinColor(colorStateList: ColorStateList?) {
|
||||||
|
colorStateList?.let {
|
||||||
|
//设置icon
|
||||||
|
if (mDrawables[0] != null && (tints and LEFT) == LEFT) {
|
||||||
|
mDrawables[0] = DrawableCompat.wrap(mDrawables[0]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[0]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[1] != null && (tints and TinTextView.TOP) == TinTextView.TOP) {
|
||||||
|
mDrawables[1] = DrawableCompat.wrap(mDrawables[1]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[1]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[2] != null && (tints and RIGHT) == RIGHT) {
|
||||||
|
mDrawables[2] = DrawableCompat.wrap(mDrawables[2]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[2]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[3] != null && (tints and BOTTOM) == BOTTOM) {
|
||||||
|
mDrawables[3] = DrawableCompat.wrap(mDrawables[3]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[3]!!, colorStateList)
|
||||||
|
}
|
||||||
|
setCompoundDrawables(mDrawables[0], mDrawables[1], mDrawables[2], mDrawables[3])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
fun updateDrawablesTinColor(@ColorRes color: Int) {
|
||||||
|
this.tinColor = ContextCompat.getColorStateList(context, color)
|
||||||
|
onFocusChange(this, isFocused)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 更新聚焦状态下 drawable 的颜色
|
||||||
|
*/
|
||||||
|
fun updateDrawablesFocusedTinColor(@ColorRes color: Int) {
|
||||||
|
this.focusedTinColor = ContextCompat.getColorStateList(context, color)
|
||||||
|
onFocusChange(this, isFocused)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 处理EditText焦点变化的时候修改 drawable 的 tinColor
|
||||||
|
*/
|
||||||
|
override fun onFocusChange(v: View?, hasFocus: Boolean) {
|
||||||
|
if (hasFocus) {
|
||||||
|
updateDrawablesTinColor(focusedTinColor)
|
||||||
|
} else {
|
||||||
|
updateDrawablesTinColor(tinColor)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+65
@@ -0,0 +1,65 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.tin_view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.appcompat.widget.AppCompatImageView
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个 DrawableButton 的封装组件,在AppCompatImageView的基础上支持设置drawable的tinColor
|
||||||
|
*
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-11 下午4:46
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TinImageView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
|
) : AppCompatImageView(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
|
private var mDrawable: Drawable? = drawable
|
||||||
|
|
||||||
|
init {
|
||||||
|
loadFromAttribute(context, attrs, defStyleAttr)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从布局文件中加载属性配置
|
||||||
|
*/
|
||||||
|
private fun loadFromAttribute(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
|
||||||
|
attrs?.let {
|
||||||
|
val ta = context.obtainStyledAttributes(it, R.styleable.TinImageView)
|
||||||
|
ta.getColorStateList(R.styleable.TinImageView_tinColor)?.let { tinColorStateList ->
|
||||||
|
updateDrawablesTinColor(tinColorStateList)
|
||||||
|
}
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
fun updateDrawablesTinColor(@ColorRes color: Int) {
|
||||||
|
updateDrawablesTinColor(ContextCompat.getColorStateList(context, color))
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
private fun updateDrawablesTinColor(colorStateList: ColorStateList?) {
|
||||||
|
if (mDrawable == null) {
|
||||||
|
mDrawable = drawable
|
||||||
|
}
|
||||||
|
colorStateList?.let {
|
||||||
|
mDrawable?.let { _mDrawable ->
|
||||||
|
mDrawable = DrawableCompat.wrap(_mDrawable.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawable!!, colorStateList)
|
||||||
|
setImageDrawable(mDrawable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+114
@@ -0,0 +1,114 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view.tin_view
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.res.ColorStateList
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.core.graphics.drawable.DrawableCompat
|
||||||
|
import cn.qjm253.quick_android_custom_view.R
|
||||||
|
import cn.qjm253.quick_android_custom_view.custom_drawable_size_view.DrawableTextView
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 这是一个 DrawableTextView 的封装组件,在DrawableTextView的基础上支持设置drawable的tinColor
|
||||||
|
*
|
||||||
|
* @see DrawableTextView
|
||||||
|
* @author SunnyQjm
|
||||||
|
* @date 19-7-10 下午9:12
|
||||||
|
*/
|
||||||
|
|
||||||
|
class TinTextView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = android.R.attr.textViewStyle
|
||||||
|
) : DrawableTextView(context, attrs, defStyleAttr) {
|
||||||
|
companion object {
|
||||||
|
const val NONE = 0x0000
|
||||||
|
const val LEFT = 0x0001
|
||||||
|
const val TOP = 0x0002
|
||||||
|
const val RIGHT = 0x0004
|
||||||
|
const val BOTTOM = 0x0008
|
||||||
|
const val ALL = 0x000f
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private var tints: Int = NONE
|
||||||
|
private lateinit var mDrawables: Array<Drawable?>
|
||||||
|
|
||||||
|
init {
|
||||||
|
if (!isInEditMode) {
|
||||||
|
mDrawables = compoundDrawables
|
||||||
|
loadFromAttribute(context, attrs, defStyleAttr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 从布局文件中加载属性配置
|
||||||
|
*/
|
||||||
|
private fun loadFromAttribute(context: Context, attrs: AttributeSet?, defStyleAttr: Int) {
|
||||||
|
attrs?.let {
|
||||||
|
val ta = context.obtainStyledAttributes(it, R.styleable.TinTextView)
|
||||||
|
tints = ta.getInt(R.styleable.TinTextView_tints, ALL)
|
||||||
|
ta.getColorStateList(R.styleable.TinTextView_tinColor)?.let { tinColorStateList ->
|
||||||
|
updateDrawablesTinColor(tinColorStateList)
|
||||||
|
}
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置哪些方向的drawable需要设置tinColor
|
||||||
|
*
|
||||||
|
* @see TinTextView.NONE -> 四个方向都不着色
|
||||||
|
* @see TinTextView.LEFT -> 左边drawable着色
|
||||||
|
* @see TinTextView.RIGHT -> 右边drawable着色
|
||||||
|
* @see TinTextView.TOP -> 顶部drawable着色
|
||||||
|
* @see TinTextView.BOTTOM -> 底部drawable着色
|
||||||
|
* @see TinTextView.ALL -> 四个方向都着色
|
||||||
|
*
|
||||||
|
* 如果需要单独设置两个方向可以 => TinTextView.LEFT or TinTextView.RIGHT
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
fun updateTints(tints: Int) {
|
||||||
|
if (tints < 0 || tints > 8)
|
||||||
|
return
|
||||||
|
this.tints = tints
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
private fun updateDrawablesTinColor(colorStateList: ColorStateList) {
|
||||||
|
//设置icon
|
||||||
|
if (mDrawables[0] != null && (tints and LEFT) == LEFT) {
|
||||||
|
mDrawables[0] = DrawableCompat.wrap(mDrawables[0]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[0]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[1] != null && (tints and TOP) == TOP) {
|
||||||
|
mDrawables[1] = DrawableCompat.wrap(mDrawables[1]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[1]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[2] != null && (tints and RIGHT) == RIGHT) {
|
||||||
|
mDrawables[2] = DrawableCompat.wrap(mDrawables[2]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[2]!!, colorStateList)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mDrawables[3] != null && (tints and BOTTOM) == BOTTOM) {
|
||||||
|
mDrawables[3] = DrawableCompat.wrap(mDrawables[3]!!.mutate())
|
||||||
|
DrawableCompat.setTintList(mDrawables[3]!!, colorStateList)
|
||||||
|
}
|
||||||
|
setCompoundDrawables(mDrawables[0], mDrawables[1], mDrawables[2], mDrawables[3])
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 设置tinColor的颜色
|
||||||
|
*/
|
||||||
|
fun updateDrawablesTinColor(@ColorRes color: Int) {
|
||||||
|
ContextCompat.getColorStateList(context, color)?.let {
|
||||||
|
updateDrawablesTinColor(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<size android:width="@dimen/switch_height"
|
||||||
|
android:height="@dimen/switch_height"/>
|
||||||
|
<corners android:radius="@dimen/half_of_switch_height"/>
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/white"
|
||||||
|
android:endColor="@color/white"/>
|
||||||
|
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/colorPrimary"/>
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<size android:height="@dimen/switch_height"
|
||||||
|
android:width="@dimen/switch_width"/>
|
||||||
|
<corners android:radius="15dp"/>
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/colorPrimary"
|
||||||
|
android:endColor="@color/colorPrimary"/>
|
||||||
|
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<size android:width="@dimen/switch_height"
|
||||||
|
android:height="@dimen/switch_height"/>
|
||||||
|
<corners android:radius="@dimen/half_of_switch_height"/>
|
||||||
|
<gradient
|
||||||
|
android:startColor="@color/white"
|
||||||
|
android:endColor="@color/white"/>
|
||||||
|
|
||||||
|
<stroke
|
||||||
|
android:width="1dp"
|
||||||
|
android:color="@color/grey"/>
|
||||||
|
</shape>
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:shape="rectangle">
|
||||||
|
<size
|
||||||
|
android:width="@dimen/switch_width"
|
||||||
|
android:height="@dimen/switch_height"/>
|
||||||
|
<corners android:radius="15dp"/>
|
||||||
|
<gradient
|
||||||
|
android:endColor="@color/grey"
|
||||||
|
android:startColor="@color/grey"/>
|
||||||
|
</shape>
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 992 B |
Binary file not shown.
|
After Width: | Height: | Size: 1.9 KiB |
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_checked="true" android:drawable="@drawable/blue_thumb"/>
|
||||||
|
<item android:drawable="@drawable/gray_thumb"/>
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<item android:state_checked="true" android:drawable="@drawable/blue_track"/>
|
||||||
|
<item android:drawable="@drawable/gray_track"/>
|
||||||
|
</selector>
|
||||||
@@ -0,0 +1,89 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<attr name="left_drawable" format="reference"/>
|
||||||
|
<attr name="right_drawable" format="reference"/>
|
||||||
|
<attr name="top_drawable" format="reference"/>
|
||||||
|
<attr name="bottom_drawable" format="reference"/>
|
||||||
|
|
||||||
|
<attr name="drawable_width" format="dimension"/>
|
||||||
|
<attr name="drawable_height" format="dimension"/>
|
||||||
|
|
||||||
|
<attr name="left_drawable_width" format="dimension"/>
|
||||||
|
<attr name="left_drawable_height" format="dimension"/>
|
||||||
|
|
||||||
|
<attr name="right_drawable_width" format="dimension"/>
|
||||||
|
<attr name="right_drawable_height" format="dimension"/>
|
||||||
|
|
||||||
|
<attr name="top_drawable_width" format="dimension"/>
|
||||||
|
<attr name="top_drawable_height" format="dimension"/>
|
||||||
|
|
||||||
|
<attr name="bottom_drawable_width" format="dimension"/>
|
||||||
|
<attr name="bottom_drawable_height" format="dimension"/>
|
||||||
|
|
||||||
|
<declare-styleable name="DrawableEditText">
|
||||||
|
<attr name="left_drawable" />
|
||||||
|
<attr name="right_drawable" />
|
||||||
|
<attr name="top_drawable" />
|
||||||
|
<attr name="bottom_drawable" />
|
||||||
|
|
||||||
|
<attr name="drawable_width" />
|
||||||
|
<attr name="drawable_height" />
|
||||||
|
|
||||||
|
<attr name="left_drawable_width" />
|
||||||
|
<attr name="left_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="right_drawable_width" />
|
||||||
|
<attr name="right_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="top_drawable_width" />
|
||||||
|
<attr name="top_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="bottom_drawable_width" />
|
||||||
|
<attr name="bottom_drawable_height" />
|
||||||
|
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="DrawableTextView">
|
||||||
|
<attr name="left_drawable" />
|
||||||
|
<attr name="right_drawable" />
|
||||||
|
<attr name="top_drawable" />
|
||||||
|
<attr name="bottom_drawable" />
|
||||||
|
|
||||||
|
<attr name="drawable_width" />
|
||||||
|
<attr name="drawable_height" />
|
||||||
|
|
||||||
|
<attr name="left_drawable_width" />
|
||||||
|
<attr name="left_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="right_drawable_width" />
|
||||||
|
<attr name="right_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="top_drawable_width" />
|
||||||
|
<attr name="top_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="bottom_drawable_width" />
|
||||||
|
<attr name="bottom_drawable_height" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="DrawableButton">
|
||||||
|
<attr name="left_drawable" />
|
||||||
|
<attr name="right_drawable" />
|
||||||
|
<attr name="top_drawable" />
|
||||||
|
<attr name="bottom_drawable" />
|
||||||
|
|
||||||
|
<attr name="drawable_width" />
|
||||||
|
<attr name="drawable_height" />
|
||||||
|
|
||||||
|
<attr name="left_drawable_width" />
|
||||||
|
<attr name="left_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="right_drawable_width" />
|
||||||
|
<attr name="right_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="top_drawable_width" />
|
||||||
|
<attr name="top_drawable_height" />
|
||||||
|
|
||||||
|
<attr name="bottom_drawable_width" />
|
||||||
|
<attr name="bottom_drawable_height" />
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<attr name="menu_title" format="string|reference" />
|
||||||
|
<attr name="menu_title_size" format="dimension" />
|
||||||
|
<attr name="menu_title_color" format="color" />
|
||||||
|
<attr name="right_margin" format="dimension" />
|
||||||
|
<attr name="left_margin" format="dimension" />
|
||||||
|
|
||||||
|
<attr name="menu_value" format="string|reference" />
|
||||||
|
<attr name="menu_value_size" format="dimension" />
|
||||||
|
<attr name="menu_value_color" format="color" />
|
||||||
|
<attr name="left_resource" format="reference" />
|
||||||
|
<attr name="right_resource" format="reference" />
|
||||||
|
<attr name="left_icon_size" format="dimension" />
|
||||||
|
<attr name="right_icon_size" format="dimension" />
|
||||||
|
<attr name="is_right_icon_visible" format="boolean" />
|
||||||
|
<attr name="is_left_icon_visible" format="boolean" />
|
||||||
|
<attr name="left_icon_text_span" format="dimension" />
|
||||||
|
|
||||||
|
<declare-styleable name="LineMenuItem">
|
||||||
|
<attr name="menu_title" />
|
||||||
|
<attr name="menu_title_size" />
|
||||||
|
<attr name="menu_title_color" />
|
||||||
|
<attr name="right_margin" />
|
||||||
|
<attr name="left_margin" />
|
||||||
|
<attr name="menu_value" />
|
||||||
|
<attr name="menu_value_size" />
|
||||||
|
<attr name="menu_value_color" />
|
||||||
|
<attr name="left_resource" />
|
||||||
|
<attr name="right_resource" />
|
||||||
|
<attr name="left_icon_size" />
|
||||||
|
<attr name="right_icon_size" />
|
||||||
|
<attr name="is_right_icon_visible" />
|
||||||
|
<attr name="is_left_icon_visible" />
|
||||||
|
<attr name="left_icon_text_span" />
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="ExpandableLineMenuItem">
|
||||||
|
<attr name="menu_height" format="dimension"/>
|
||||||
|
<attr name="menu_width" format="dimension"/>
|
||||||
|
<attr name="open" format="boolean"/>
|
||||||
|
<attr name="openedAble" format="boolean"/>
|
||||||
|
<attr name="menu_bg" format="color|reference"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="LineMenuSwitch">
|
||||||
|
<attr name="menu_title" />
|
||||||
|
<attr name="menu_title_size" />
|
||||||
|
<attr name="menu_title_color" />
|
||||||
|
<attr name="right_margin" />
|
||||||
|
<attr name="left_margin" />
|
||||||
|
<attr name="switch_track" format="reference" />
|
||||||
|
<attr name="switch_thumb" format="reference" />
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="doyin_black">#161923</color>
|
||||||
|
<color name="light_grey">#eee</color>
|
||||||
|
<color name="grey">#bbb</color>
|
||||||
|
<color name="white">#fff</color>
|
||||||
|
<color name="transparent">#00ffffff</color>
|
||||||
|
<color name="text_gray">#333333</color>
|
||||||
|
<color name="black">#000</color>
|
||||||
|
<color name="red">#f00</color>
|
||||||
|
|
||||||
|
<color name="line_gray">#dddddd</color>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<dimen name="switch_height">26dp</dimen>
|
||||||
|
<dimen name="half_of_switch_height">13dp</dimen>
|
||||||
|
<dimen name="switch_width">35dp</dimen>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<resources>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,33 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
|
||||||
|
<attr name="tints" format="enum">
|
||||||
|
<enum name="NONE" value="0x0000"/>
|
||||||
|
<enum name="LEFT" value="0x0001"/>
|
||||||
|
<enum name="TOP" value="0x0002"/>
|
||||||
|
<enum name="RIGHT" value="0x0004"/>
|
||||||
|
<enum name="BOTTOM" value="0x0008"/>
|
||||||
|
<enum name="ALL" value="0x000f"/>
|
||||||
|
</attr>
|
||||||
|
|
||||||
|
<attr name="tinColor" format="color"/>
|
||||||
|
<declare-styleable name="TinEditText">
|
||||||
|
<attr name="tints"/>
|
||||||
|
<attr name="tinColor"/>
|
||||||
|
<attr name="focusedTinColor" format="color"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="TinTextView">
|
||||||
|
<attr name="tints"/>
|
||||||
|
<attr name="tinColor"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="TinButton">
|
||||||
|
<attr name="tints"/>
|
||||||
|
<attr name="tinColor"/>
|
||||||
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="TinImageView">
|
||||||
|
<attr name="tinColor"/>
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package cn.qjm253.quick_android_custom_view;
|
||||||
|
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
|
||||||
|
*/
|
||||||
|
public class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
public void addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
apply plugin: 'com.android.library'
|
||||||
|
apply plugin: 'kotlin-android'
|
||||||
|
apply plugin: 'kotlin-android-extensions'
|
||||||
|
android {
|
||||||
|
compileSdkVersion compile_sdk_version
|
||||||
|
|
||||||
|
defaultConfig {
|
||||||
|
minSdkVersion min_sdk_version
|
||||||
|
targetSdkVersion target_sdk_version
|
||||||
|
versionCode 1
|
||||||
|
versionName "1.0"
|
||||||
|
|
||||||
|
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
|
||||||
|
consumerProguardFiles 'consumer-rules.pro'
|
||||||
|
}
|
||||||
|
|
||||||
|
buildTypes {
|
||||||
|
release {
|
||||||
|
minifyEnabled false
|
||||||
|
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation fileTree(dir: 'libs', include: ['*.jar'])
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||||
|
implementation "androidx.appcompat:appcompat:${appcompact_version}"
|
||||||
|
implementation "androidx.core:core-ktx:${core_ktx_version}"
|
||||||
|
testImplementation "junit:junit:${junit_version}"
|
||||||
|
|
||||||
|
implementation project(path: ':quick_android_base')
|
||||||
|
implementation project(path: ':quick_android_custom_view')
|
||||||
|
|
||||||
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
|
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||||
|
}
|
||||||
Whitespace-only changes.
+21
@@ -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
|
||||||
+24
@@ -0,0 +1,24 @@
|
|||||||
|
package cn.qjm253.quick_android_easy_bar
|
||||||
|
|
||||||
|
import androidx.test.platform.app.InstrumentationRegistry
|
||||||
|
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
import org.junit.runner.RunWith
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Instrumented test, which will execute on an Android device.
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
@RunWith(AndroidJUnit4::class)
|
||||||
|
class ExampleInstrumentedTest {
|
||||||
|
@Test
|
||||||
|
fun useAppContext() {
|
||||||
|
// Context of the app under test.
|
||||||
|
val appContext = InstrumentationRegistry.getInstrumentation().targetContext
|
||||||
|
assertEquals("cn.qjm253.quick_android_easy_bar.test", appContext.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
package="cn.qjm253.quick_android_easy_bar" />
|
||||||
@@ -0,0 +1,354 @@
|
|||||||
|
package cn.qjm253.quick_android_easy_bar
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.os.Build
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.util.TypedValue
|
||||||
|
import android.view.View
|
||||||
|
import android.view.ViewGroup
|
||||||
|
import android.widget.ImageView
|
||||||
|
import android.widget.RelativeLayout
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.Dimension
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
import cn.qjm253.quick_android_base.util.DisplayUtils
|
||||||
|
import cn.qjm253.quick_android_custom_view.tin_view.TinImageView
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Sunny on 2017/8/17 0017.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EasyBar @JvmOverloads constructor(
|
||||||
|
context: Context,
|
||||||
|
attrs: AttributeSet? = null,
|
||||||
|
defStyleAttr: Int = 0
|
||||||
|
) : RelativeLayout(context, attrs, defStyleAttr) {
|
||||||
|
private var title: String? = null
|
||||||
|
@Dimension
|
||||||
|
private var titleSize: Int = 0
|
||||||
|
@ColorInt
|
||||||
|
private var titleColor: Int = 0
|
||||||
|
@DrawableRes
|
||||||
|
private var leftIcon: Int = 0
|
||||||
|
@DrawableRes
|
||||||
|
private var rightIcon: Int = 0
|
||||||
|
@Dimension
|
||||||
|
private var iconSize: Int = 0
|
||||||
|
@Dimension
|
||||||
|
private var iconMargin: Int = 0
|
||||||
|
|
||||||
|
private var leftText: String = ""
|
||||||
|
private var rightText: String = ""
|
||||||
|
private var displayMode: Mode = Mode.TEXT
|
||||||
|
|
||||||
|
|
||||||
|
private lateinit var imgLeft: TinImageView
|
||||||
|
private lateinit var imgRight: TinImageView
|
||||||
|
private lateinit var tvTitle: TextView
|
||||||
|
private lateinit var tvLeftText: TextView
|
||||||
|
private lateinit var tvRightText: TextView
|
||||||
|
private var mListener: OnEasyBarClickListener? = null
|
||||||
|
|
||||||
|
init {
|
||||||
|
initAttr(context, attrs)
|
||||||
|
initView(context)
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressLint("ObsoleteSdkInt")
|
||||||
|
private fun initView(context: Context) {
|
||||||
|
|
||||||
|
//left icon
|
||||||
|
imgLeft = TinImageView(context)
|
||||||
|
imgLeft.setImageResource(leftIcon)
|
||||||
|
imgLeft.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||||
|
val imgLeftParam = RelativeLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
imgLeftParam.addRule(RelativeLayout.CENTER_VERTICAL)
|
||||||
|
imgLeftParam.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
||||||
|
imgLeftParam.width = iconSize
|
||||||
|
imgLeftParam.height = iconSize
|
||||||
|
imgLeft.layoutParams = imgLeftParam
|
||||||
|
imgLeft.setPadding(iconMargin, iconMargin, iconMargin, iconMargin)
|
||||||
|
addView(imgLeft, imgLeftParam)
|
||||||
|
|
||||||
|
imgLeft.setOnClickListener { v ->
|
||||||
|
mListener?.onLeftIconClick(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
//center title
|
||||||
|
tvTitle = TextView(context)
|
||||||
|
tvTitle.ellipsize = TextUtils.TruncateAt.END
|
||||||
|
tvTitle.setSingleLine()
|
||||||
|
tvTitle.text = title
|
||||||
|
tvTitle.textSize = titleSize.toFloat()
|
||||||
|
tvTitle.setTextColor(titleColor)
|
||||||
|
val tvTitleParam = RelativeLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
val titleMargin = iconSize
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||||
|
tvTitleParam.marginStart = titleMargin
|
||||||
|
tvTitleParam.marginEnd = titleMargin
|
||||||
|
} else {
|
||||||
|
tvTitleParam.leftMargin = titleMargin
|
||||||
|
tvTitleParam.rightMargin = titleMargin
|
||||||
|
}
|
||||||
|
tvTitleParam.addRule(RelativeLayout.CENTER_IN_PARENT)
|
||||||
|
tvTitle.layoutParams = tvTitleParam
|
||||||
|
addView(tvTitle, tvTitleParam)
|
||||||
|
|
||||||
|
//left text
|
||||||
|
tvLeftText = TextView(context)
|
||||||
|
tvLeftText.textSize = titleSize.toFloat()
|
||||||
|
tvLeftText.setTextColor(titleColor)
|
||||||
|
val tvLeftTextParam = RelativeLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
tvLeftTextParam.addRule(RelativeLayout.ALIGN_PARENT_LEFT)
|
||||||
|
tvLeftTextParam.addRule(RelativeLayout.CENTER_VERTICAL)
|
||||||
|
tvLeftTextParam.leftMargin = iconMargin
|
||||||
|
tvLeftText.layoutParams = tvLeftTextParam
|
||||||
|
addView(tvLeftText)
|
||||||
|
tvLeftText.text = leftText
|
||||||
|
tvLeftText.visibility = View.INVISIBLE
|
||||||
|
tvLeftText.setOnClickListener { v ->
|
||||||
|
mListener?.onLeftIconClick(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//right text
|
||||||
|
tvRightText = TextView(context)
|
||||||
|
tvRightText.textSize = titleSize.toFloat()
|
||||||
|
tvRightText.setTextColor(titleColor)
|
||||||
|
val tvRightTextParam = RelativeLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
tvRightTextParam.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
|
||||||
|
tvRightTextParam.addRule(RelativeLayout.CENTER_VERTICAL)
|
||||||
|
tvRightTextParam.rightMargin = iconMargin
|
||||||
|
tvRightText.layoutParams = tvRightTextParam
|
||||||
|
addView(tvRightText)
|
||||||
|
tvRightText.text = rightText
|
||||||
|
tvRightText.visibility = View.INVISIBLE
|
||||||
|
tvRightText.setOnClickListener { v ->
|
||||||
|
mListener?.onRightIconClick(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
//right icon
|
||||||
|
imgRight = TinImageView(context)
|
||||||
|
imgRight.setImageResource(rightIcon)
|
||||||
|
imgRight.scaleType = ImageView.ScaleType.FIT_CENTER
|
||||||
|
val imgRightParam = RelativeLayout.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT
|
||||||
|
)
|
||||||
|
imgRightParam.addRule(RelativeLayout.CENTER_VERTICAL)
|
||||||
|
imgRightParam.addRule(RelativeLayout.ALIGN_PARENT_RIGHT)
|
||||||
|
imgRightParam.width = iconSize
|
||||||
|
imgRightParam.height = iconSize
|
||||||
|
imgRight.layoutParams = imgRightParam
|
||||||
|
imgRight.setPadding(iconMargin, iconMargin, iconMargin, iconMargin)
|
||||||
|
addView(imgRight, imgRightParam)
|
||||||
|
|
||||||
|
imgRight.visibility = View.INVISIBLE
|
||||||
|
imgRight.setOnClickListener { v ->
|
||||||
|
mListener?.onRightIconClick(v)
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisplayMode(displayMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initAttr(context: Context, attrs: AttributeSet?) {
|
||||||
|
val ta = context.obtainStyledAttributes(attrs, R.styleable.EasyBar)
|
||||||
|
title = ta.getString(R.styleable.EasyBar_title)
|
||||||
|
titleSize = DisplayUtils.px2sp(
|
||||||
|
context,
|
||||||
|
ta.getDimensionPixelSize(
|
||||||
|
R.styleable.EasyBar_title_size,
|
||||||
|
TypedValue.applyDimension(
|
||||||
|
TypedValue.COMPLEX_UNIT_SP,
|
||||||
|
14f,
|
||||||
|
resources.displayMetrics
|
||||||
|
).toInt()
|
||||||
|
).toFloat()
|
||||||
|
)
|
||||||
|
titleColor =
|
||||||
|
ta.getColor(R.styleable.EasyBar_title_color, resources.getColor(R.color.text_gray))
|
||||||
|
leftIcon = ta.getResourceId(R.styleable.EasyBar_left_icon, R.drawable.back)
|
||||||
|
// rightIcon = ta.getResourceId(R.styleable.EasyBar_right_icon, R.drawable.back)
|
||||||
|
iconSize = ta.getDimensionPixelSize(
|
||||||
|
R.styleable.EasyBar_icon_size,
|
||||||
|
DisplayUtils.dip2px(context, 24f)
|
||||||
|
)
|
||||||
|
iconMargin = ta.getDimensionPixelOffset(
|
||||||
|
R.styleable.EasyBar_icon_margin,
|
||||||
|
DisplayUtils.dip2px(context, 12f)
|
||||||
|
)
|
||||||
|
leftText = ta.getString(R.styleable.EasyBar_left_text).toString()
|
||||||
|
rightText = ta.getString(R.styleable.EasyBar_right_text).toString()
|
||||||
|
val mode = ta.getInt(R.styleable.EasyBar_displayMode, 0)
|
||||||
|
displayMode = when (mode) {
|
||||||
|
0 -> Mode.ICON
|
||||||
|
1 -> Mode.TEXT
|
||||||
|
else -> Mode.ICON
|
||||||
|
}
|
||||||
|
ta.recycle()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitle(title: String) {
|
||||||
|
this.title = title
|
||||||
|
tvTitle.text = title
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setTitle(@StringRes titleRes: Int) {
|
||||||
|
this.title = context.getString(titleRes)
|
||||||
|
tvTitle.setText(titleRes)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLeftText(text: String) {
|
||||||
|
this.leftText = text
|
||||||
|
tvLeftText.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setRightText(text: String) {
|
||||||
|
this.rightText = text
|
||||||
|
tvRightText.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setOnEasyBarClickListener(listener: OnEasyBarClickListener) {
|
||||||
|
this.mListener = listener
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
fun setRightIcon(drawable: Drawable) {
|
||||||
|
imgRight.setImageDrawable(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setRightIcon(bitmap: Bitmap) {
|
||||||
|
imgRight.setImageBitmap(bitmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setRightIcon(@DrawableRes res: Int) {
|
||||||
|
imgRight.setImageResource(res)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRightIcon(): TinImageView? {
|
||||||
|
return imgRight
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getRightText(): String? {
|
||||||
|
return tvRightText.text.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLeftIcon(drawable: Drawable) {
|
||||||
|
imgLeft.setImageDrawable(drawable)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLeftIcon(bitmap: Bitmap) {
|
||||||
|
imgLeft.setImageBitmap(bitmap)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setLeftIcon(@DrawableRes res: Int) {
|
||||||
|
imgLeft.setImageResource(res)
|
||||||
|
println("drawable: ${imgLeft.drawable}")
|
||||||
|
println("drawable state: ${imgLeft.drawableState}")
|
||||||
|
println("drawing time: ${imgLeft.drawingTime}")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getLeftIcon(): TinImageView? {
|
||||||
|
return imgLeft
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OnEasyBarClickListener {
|
||||||
|
/**
|
||||||
|
* 如果是图标模式,则响应左图标点击,否则响应左文字点击
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
*/
|
||||||
|
fun onLeftIconClick(view: View)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 如果是图标模式,则响应右图标点击,否则响应右文字点击
|
||||||
|
*
|
||||||
|
* @param view
|
||||||
|
*/
|
||||||
|
fun onRightIconClick(view: View)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
enum class Mode {
|
||||||
|
ICON, TEXT, ICON_TEXT, TEXT_ICON, ICON_, TEXT_, _ICON, _TEXT, NONE
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setDisplayMode(mode: Mode?) {
|
||||||
|
when (mode) {
|
||||||
|
Mode.ICON -> {
|
||||||
|
tvLeftText.visibility = View.INVISIBLE
|
||||||
|
tvRightText.visibility = View.INVISIBLE
|
||||||
|
imgLeft.visibility = View.VISIBLE
|
||||||
|
imgRight.visibility = View.VISIBLE
|
||||||
|
|
||||||
|
}
|
||||||
|
Mode.TEXT -> {
|
||||||
|
tvLeftText.visibility = View.VISIBLE
|
||||||
|
tvRightText.visibility = View.VISIBLE
|
||||||
|
imgLeft.visibility = View.INVISIBLE
|
||||||
|
imgRight.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
Mode.ICON_TEXT -> {
|
||||||
|
tvLeftText.visibility = View.INVISIBLE
|
||||||
|
tvRightText.visibility = View.VISIBLE
|
||||||
|
imgLeft.visibility = View.VISIBLE
|
||||||
|
imgRight.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
Mode.TEXT_ICON -> {
|
||||||
|
tvLeftText.visibility = View.VISIBLE
|
||||||
|
tvRightText.visibility = View.INVISIBLE
|
||||||
|
imgLeft.visibility = View.INVISIBLE
|
||||||
|
imgRight.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
Mode.TEXT_ -> {
|
||||||
|
tvLeftText.visibility = View.VISIBLE
|
||||||
|
tvRightText.visibility = View.INVISIBLE
|
||||||
|
imgLeft.visibility = View.INVISIBLE
|
||||||
|
imgRight.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
Mode.ICON_ -> {
|
||||||
|
tvLeftText.visibility = View.INVISIBLE
|
||||||
|
tvRightText.visibility = View.INVISIBLE
|
||||||
|
imgLeft.visibility = View.VISIBLE
|
||||||
|
imgRight.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
Mode._ICON -> {
|
||||||
|
tvLeftText.visibility = View.INVISIBLE
|
||||||
|
tvRightText.visibility = View.INVISIBLE
|
||||||
|
imgLeft.visibility = View.INVISIBLE
|
||||||
|
imgRight.visibility = View.VISIBLE
|
||||||
|
}
|
||||||
|
Mode._TEXT -> {
|
||||||
|
tvLeftText.visibility = View.INVISIBLE
|
||||||
|
tvRightText.visibility = View.VISIBLE
|
||||||
|
imgLeft.visibility = View.INVISIBLE
|
||||||
|
imgRight.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
Mode.NONE -> {
|
||||||
|
tvLeftText.visibility = View.INVISIBLE
|
||||||
|
tvRightText.visibility = View.INVISIBLE
|
||||||
|
imgLeft.visibility = View.INVISIBLE
|
||||||
|
imgRight.visibility = View.INVISIBLE
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
+89
@@ -0,0 +1,89 @@
|
|||||||
|
package cn.qjm253.quick_android_easy_bar
|
||||||
|
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.view.View
|
||||||
|
import androidx.annotation.ColorInt
|
||||||
|
import androidx.annotation.ColorRes
|
||||||
|
import androidx.annotation.DrawableRes
|
||||||
|
import androidx.annotation.StringRes
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by sunny on 18-1-25.
|
||||||
|
*/
|
||||||
|
|
||||||
|
class EasyBarParams(
|
||||||
|
val mode: EasyBar.Mode = EasyBar.Mode.ICON_,
|
||||||
|
val title: String = "",
|
||||||
|
val leftCallback: (v: View) -> Unit = {},
|
||||||
|
val rightCallback: (v: View) -> Unit = {
|
||||||
|
println("empty right")
|
||||||
|
}, @DrawableRes val leftRes: Int = R.drawable.back,
|
||||||
|
@DrawableRes val rightRes: Int = 0,
|
||||||
|
val leftText: String = "",
|
||||||
|
val rightText: String = "", @StringRes val titleRes: Int = -1,
|
||||||
|
val isCoverCallback: Boolean = true,
|
||||||
|
@ColorRes
|
||||||
|
val tinColor: Int = -1
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
|
fun EasyBar.init(easyBarParams: EasyBarParams) {
|
||||||
|
init(
|
||||||
|
easyBarParams.mode,
|
||||||
|
easyBarParams.title,
|
||||||
|
easyBarParams.leftCallback,
|
||||||
|
easyBarParams.rightCallback,
|
||||||
|
easyBarParams.leftRes,
|
||||||
|
easyBarParams.rightRes,
|
||||||
|
easyBarParams.leftText,
|
||||||
|
easyBarParams.rightText,
|
||||||
|
easyBarParams.titleRes,
|
||||||
|
easyBarParams.isCoverCallback,
|
||||||
|
easyBarParams.tinColor
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 标题栏初始化
|
||||||
|
*/
|
||||||
|
@SuppressLint("ResourceType")
|
||||||
|
fun EasyBar.init(
|
||||||
|
mode: EasyBar.Mode = EasyBar.Mode.ICON_,
|
||||||
|
title: String = "",
|
||||||
|
leftCallback: (v: View) -> Unit = {},
|
||||||
|
rightCallback: (v: View) -> Unit = {
|
||||||
|
println("empty right")
|
||||||
|
}, @DrawableRes leftRes: Int = R.drawable.back,
|
||||||
|
@DrawableRes rightRes: Int = 0,
|
||||||
|
leftText: String = "",
|
||||||
|
rightText: String = "", @StringRes titleRes: Int = -1,
|
||||||
|
isCoverCallback: Boolean = true,
|
||||||
|
@ColorRes tinColor: Int = -1
|
||||||
|
|
||||||
|
) {
|
||||||
|
this.setDisplayMode(mode)
|
||||||
|
if (titleRes > 0)
|
||||||
|
this.setTitle(titleRes)
|
||||||
|
else
|
||||||
|
this.setTitle(title)
|
||||||
|
this.setRightIcon(rightRes)
|
||||||
|
this.setLeftText(leftText)
|
||||||
|
this.setRightText(rightText)
|
||||||
|
this.setLeftIcon(leftRes)
|
||||||
|
if (tinColor > 0) {
|
||||||
|
this.getLeftIcon()?.updateDrawablesTinColor(tinColor)
|
||||||
|
this.getRightIcon()?.updateDrawablesTinColor(tinColor)
|
||||||
|
}
|
||||||
|
if (isCoverCallback)
|
||||||
|
this.setOnEasyBarClickListener(object : EasyBar.OnEasyBarClickListener {
|
||||||
|
override fun onLeftIconClick(view: View) {
|
||||||
|
leftCallback(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onRightIconClick(view: View) {
|
||||||
|
rightCallback.invoke(view)
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
Binary file not shown.
|
After Width: | Height: | Size: 752 B |
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<color name="text_gray">#333333</color>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<resources>
|
||||||
|
<declare-styleable name="EasyBar">
|
||||||
|
<attr name="title" format="string" />
|
||||||
|
<attr name="title_size" format="dimension" />
|
||||||
|
<attr name="title_color" format="color" />
|
||||||
|
<attr name="left_icon" format="reference" />
|
||||||
|
<attr name="right_icon" format="reference" />
|
||||||
|
<attr name="icon_size" format="dimension" />
|
||||||
|
<attr name="icon_margin" format="dimension" />
|
||||||
|
<attr name="left_text" format="string" />
|
||||||
|
<attr name="right_text" format="string" />
|
||||||
|
<attr name="displayMode">
|
||||||
|
<enum name="ICON" value="0" />
|
||||||
|
<enum name="TEXT" value="1" />
|
||||||
|
</attr>
|
||||||
|
</declare-styleable>
|
||||||
|
</resources>
|
||||||
@@ -0,0 +1,2 @@
|
|||||||
|
<resources>
|
||||||
|
</resources>
|
||||||
+17
@@ -0,0 +1,17 @@
|
|||||||
|
package cn.qjm253.quick_android_easy_bar
|
||||||
|
|
||||||
|
import org.junit.Test
|
||||||
|
|
||||||
|
import org.junit.Assert.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Example local unit test, which will execute on the development machine (host).
|
||||||
|
*
|
||||||
|
* See [testing documentation](http://d.android.com/tools/testing).
|
||||||
|
*/
|
||||||
|
class ExampleUnitTest {
|
||||||
|
@Test
|
||||||
|
fun addition_isCorrect() {
|
||||||
|
assertEquals(4, 2 + 2)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
/build
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
> ## quick_android_file_picker 模块说明文档
|
||||||
|
>
|
||||||
|
> 本模块简单封装了打开系统的文件管理器选择文件的功能,并返回文件的原始Uri以及经过处理后文件的绝对路径。
|
||||||
|
>
|
||||||
|
> 通过RxJava用简单的链式调用即可获得选择文件的结果
|
||||||
|
|
||||||
|
- 返回数据结构
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
data class FilePickerResult (
|
||||||
|
val path: String, // 文件的绝对路径
|
||||||
|
val uri: Uri // 文件的Uri
|
||||||
|
)
|
||||||
|
```
|
||||||
|
|
||||||
|
- ### 使用示例
|
||||||
|
|
||||||
|
- 本接口需要读取文件的权限 => `READ_EXTERNAL_STORAGE`,权限的获取可以参考本库的另一个模块 [quick_android_rx_permission模块文档](https://github.com/SunnyQjm/quickandroid/tree/master/quick_android_rx_permission)
|
||||||
|
|
||||||
|
- 包含权限请求的使用示例:
|
||||||
|
|
||||||
|
ps: `checkMyPermission` 为 [quick_android_base模块文档](https://github.com/SunnyQjm/quickandroid/tree/master/quick_android_base) 模块提供的用于检查权限是否存在的 `Context` 类的扩展方法
|
||||||
|
|
||||||
|
```kotlin
|
||||||
|
QuickAndroidFilePicker
|
||||||
|
.create(this)
|
||||||
|
.request(
|
||||||
|
arrayOf(
|
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe {
|
||||||
|
if (it.granted) {
|
||||||
|
checkMyPermission(Manifest.permission.READ_EXTERNAL_STORAGE, {
|
||||||
|
QuickAndroid
|
||||||
|
.createFilePicker(this)
|
||||||
|
.pickerFile()
|
||||||
|
.subscribe { fpr ->
|
||||||
|
toast(fpr.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
//// 使用 QuickAndroid 扩展的用法
|
||||||
|
//////////////////////////////////////////////////////////////
|
||||||
|
QuickAndroid
|
||||||
|
.createRxPermission(this)
|
||||||
|
.request(
|
||||||
|
arrayOf(
|
||||||
|
Manifest.permission.READ_EXTERNAL_STORAGE
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.subscribe {
|
||||||
|
if (it.granted) {
|
||||||
|
checkMyPermission(Manifest.permission.READ_EXTERNAL_STORAGE, {
|
||||||
|
QuickAndroid
|
||||||
|
.createFilePicker(this)
|
||||||
|
.pickerFile()
|
||||||
|
.subscribe { fpr ->
|
||||||
|
toast(fpr.path)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
Loaded 100 of 190 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user