fix:修复google支付回调时导致的空指针异常
fix:完善房间上麦时权限申请的前置状态判断
This commit is contained in:
@@ -1021,6 +1021,9 @@ open class BaseRoomFragment<V : IBaseRoomView?, P : BaseRoomPresenter<V>?> :
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
override fun toUpMicroPhone(micPosition: Int, currentUid: String, b: Boolean) {
|
||||
if (!lifecycle.currentState.isAtLeast(androidx.lifecycle.Lifecycle.State.CREATED)) {
|
||||
return
|
||||
}
|
||||
if (AvRoomDataManager.get().isSelfGamePlaying) {
|
||||
SingleToastUtil.showToast("遊戲中不可以換麥!")
|
||||
return
|
||||
|
@@ -13,8 +13,8 @@ import android.view.WindowManager;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.chuhai.core.component.SuperBottomSheetDialog;
|
||||
import com.google.android.material.bottomsheet.BottomSheetBehavior;
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog;
|
||||
import com.yizhuan.erban.R;
|
||||
import com.yizhuan.erban.common.util.Utils;
|
||||
import com.yizhuan.erban.ui.widget.ButtonItem;
|
||||
@@ -24,7 +24,7 @@ import java.util.List;
|
||||
/**
|
||||
* @author xiaoyu
|
||||
*/
|
||||
public class CommonPopupDialog extends BottomSheetDialog implements OnClickListener {
|
||||
public class CommonPopupDialog extends SuperBottomSheetDialog implements OnClickListener {
|
||||
private static final int BUTTON_ITEM_ID = 135798642;
|
||||
|
||||
private int mId;
|
||||
|
@@ -107,7 +107,7 @@ public class BillingManager implements PurchasesUpdatedListener {
|
||||
/*更新商品*/
|
||||
@Override
|
||||
public void onPurchasesUpdated(BillingResult billingResult, List<Purchase> purchases) {
|
||||
Log.i(TAG, "billingResult.getResponseCode()==" + billingResult.getResponseCode() + ",purchases.size()==" + purchases.size());
|
||||
Log.i(TAG, "billingResult.getResponseCode()==" + billingResult.getResponseCode());
|
||||
purchaseList.clear();
|
||||
if (billingResult.getResponseCode() == BillingResponseCode.OK) {
|
||||
for (Purchase purchase : purchases) {
|
||||
|
@@ -35,6 +35,8 @@ android {
|
||||
'src/module_luban/java',
|
||||
'src/module_easyphoto/java',
|
||||
'src/module_common/java',
|
||||
'src/module_utils/java',
|
||||
'src/module_core/java',
|
||||
|
||||
]
|
||||
|
||||
@@ -43,6 +45,8 @@ android {
|
||||
'src/module_easypermission/res',
|
||||
'src/module_easyphoto/res',
|
||||
'src/module_common/res',
|
||||
'src/module_utils/res',
|
||||
'src/module_core/res',
|
||||
|
||||
]
|
||||
|
||||
@@ -144,6 +148,9 @@ dependencies {
|
||||
api 'com.facebook.android:facebook-android-sdk:16.2.0'
|
||||
api 'com.facebook.android:facebook-login:16.2.0'
|
||||
|
||||
// 网络请求chrome数据调试
|
||||
api 'com.facebook.stetho:stetho:1.5.1'
|
||||
api 'com.facebook.stetho:stetho-okhttp3:1.5.1'
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
@@ -0,0 +1,62 @@
|
||||
import androidx.lifecycle.*
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/24 15:11
|
||||
* Desc:跟随目标生命周期销毁
|
||||
**/
|
||||
interface LifecycleCleared : LifecycleEventObserver {
|
||||
|
||||
/**
|
||||
* 是否启用
|
||||
*/
|
||||
fun isEnabledLifecycleClear(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取监听的目标生命周期
|
||||
*/
|
||||
abstract fun getTargetLifecycle(): Lifecycle?
|
||||
|
||||
/**
|
||||
* 目标生命周期已销毁:执行清除资源操作
|
||||
*/
|
||||
abstract fun onTargetCleared()
|
||||
|
||||
/**
|
||||
* 获取要执行清理的事件
|
||||
*/
|
||||
fun getClearEvent(): Lifecycle.Event? {
|
||||
return Lifecycle.Event.ON_DESTROY
|
||||
}
|
||||
|
||||
/**
|
||||
* 绑定生命周期
|
||||
*/
|
||||
fun bindLifecycleClear() {
|
||||
if (!isEnabledLifecycleClear()) {
|
||||
return
|
||||
}
|
||||
getTargetLifecycle()?.addObserver(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 取消绑定生命周期(如果实现类是自己主动销毁的,需要主动调下本方法)
|
||||
*/
|
||||
fun unBindLifecycleClear() {
|
||||
if (!isEnabledLifecycleClear()) {
|
||||
return
|
||||
}
|
||||
getTargetLifecycle()?.removeObserver(this)
|
||||
}
|
||||
|
||||
override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) {
|
||||
if (!isEnabledLifecycleClear()) {
|
||||
return
|
||||
}
|
||||
if (getClearEvent() == event) {
|
||||
unBindLifecycleClear()
|
||||
onTargetCleared()
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,54 @@
|
||||
package com.chuhai.core.component
|
||||
|
||||
import LifecycleCleared
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import com.chuhai.utils.ktx.asLifecycle
|
||||
import com.google.android.material.bottomsheet.BottomSheetDialog
|
||||
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/24 15:11
|
||||
* Desc:BottomSheetDialog
|
||||
*/
|
||||
open class SuperBottomSheetDialog : BottomSheetDialog, LifecycleCleared {
|
||||
|
||||
constructor(context: Context) : super(context) {
|
||||
init()
|
||||
}
|
||||
|
||||
constructor(context: Context, theme: Int) : super(context, theme) {
|
||||
init()
|
||||
}
|
||||
|
||||
constructor(
|
||||
context: Context,
|
||||
cancelable: Boolean,
|
||||
cancelListener: DialogInterface.OnCancelListener?
|
||||
) : super(context, cancelable, cancelListener) {
|
||||
init()
|
||||
}
|
||||
|
||||
protected open fun init() {
|
||||
|
||||
}
|
||||
|
||||
override fun getTargetLifecycle(): Lifecycle? {
|
||||
return context.asLifecycle()
|
||||
}
|
||||
|
||||
override fun onTargetCleared() {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
override fun show() {
|
||||
super.show()
|
||||
bindLifecycleClear()
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
super.dismiss()
|
||||
unBindLifecycleClear()
|
||||
}
|
||||
}
|
403
library/src/module_utils/java/com/chuhai/utils/AppUtils.java
Normal file
403
library/src/module_utils/java/com/chuhai/utils/AppUtils.java
Normal file
@@ -0,0 +1,403 @@
|
||||
package com.chuhai.utils;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.app.ActivityManager;
|
||||
import android.app.Application;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.pm.PackageInfo;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.location.LocationManager;
|
||||
import android.os.Handler;
|
||||
import android.os.Looper;
|
||||
import android.provider.Settings;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.File;
|
||||
import java.io.FileReader;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
|
||||
/**
|
||||
* <pre>
|
||||
* author:
|
||||
* ___ ___ ___ ___
|
||||
* _____ / /\ /__/\ /__/| / /\
|
||||
* / /::\ / /::\ \ \:\ | |:| / /:/
|
||||
* / /:/\:\ ___ ___ / /:/\:\ \ \:\ | |:| /__/::\
|
||||
* / /:/~/::\ /__/\ / /\ / /:/~/::\ _____\__\:\ __| |:| \__\/\:\
|
||||
* /__/:/ /:/\:| \ \:\ / /:/ /__/:/ /:/\:\ /__/::::::::\ /__/\_|:|____ \ \:\
|
||||
* \ \:\/:/~/:/ \ \:\ /:/ \ \:\/:/__\/ \ \:\~~\~~\/ \ \:\/:::::/ \__\:\
|
||||
* \ \::/ /:/ \ \:\/:/ \ \::/ \ \:\ ~~~ \ \::/~~~~ / /:/
|
||||
* \ \:\/:/ \ \::/ \ \:\ \ \:\ \ \:\ /__/:/
|
||||
* \ \::/ \__\/ \ \:\ \ \:\ \ \:\ \__\/
|
||||
* \__\/ \__\/ \__\/ \__\/
|
||||
* blog : http://blankj.com
|
||||
* time : 16/12/08
|
||||
* desc : utils about initialization
|
||||
* </pre>
|
||||
*/
|
||||
public final class AppUtils {
|
||||
|
||||
private static final ExecutorService UTIL_POOL = Executors.newFixedThreadPool(3);
|
||||
private static final Handler UTIL_HANDLER = new Handler(Looper.getMainLooper());
|
||||
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
private static Application sApplication;
|
||||
|
||||
|
||||
private AppUtils() {
|
||||
throw new UnsupportedOperationException("u can't instantiate me...");
|
||||
}
|
||||
|
||||
/**
|
||||
* Init utils.
|
||||
* <p>Init it in the class of Application.</p>
|
||||
*
|
||||
* @param context context
|
||||
*/
|
||||
public static void init(final Context context) {
|
||||
if (context == null) {
|
||||
init(getApplicationByReflect());
|
||||
return;
|
||||
}
|
||||
init((Application) context.getApplicationContext());
|
||||
}
|
||||
|
||||
/**
|
||||
* Init utils.
|
||||
* <p>Init it in the class of Application.</p>
|
||||
*
|
||||
* @param app application
|
||||
*/
|
||||
public static void init(final Application app) {
|
||||
if (sApplication == null) {
|
||||
if (app == null) {
|
||||
sApplication = getApplicationByReflect();
|
||||
} else {
|
||||
sApplication = app;
|
||||
}
|
||||
} else {
|
||||
sApplication = app;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the context of Application object.
|
||||
*
|
||||
* @return the context of Application object
|
||||
*/
|
||||
public static Application getApp() {
|
||||
if (sApplication != null) return sApplication;
|
||||
Application app = getApplicationByReflect();
|
||||
init(app);
|
||||
return app;
|
||||
}
|
||||
|
||||
|
||||
public static String getPackageName(Context context) {
|
||||
return context.getPackageName();
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取版本名
|
||||
*
|
||||
* @param noSuffix 是否去掉后缀 (如:-debug、-test)
|
||||
*/
|
||||
public static String getVersionName(boolean noSuffix) {
|
||||
PackageInfo packageInfo = getPackageInfo(getApp());
|
||||
if (packageInfo != null) {
|
||||
String versionName = packageInfo.versionName;
|
||||
if (noSuffix && versionName != null) {
|
||||
int index = versionName.indexOf("-");
|
||||
if (index >= 0) {
|
||||
return versionName.substring(0, index);
|
||||
}
|
||||
}
|
||||
return versionName;
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
//版本号
|
||||
public static int getVersionCode() {
|
||||
PackageInfo packageInfo = getPackageInfo(getApp());
|
||||
if (packageInfo != null) {
|
||||
return packageInfo.versionCode;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 比较版本
|
||||
* 1 = 大于当前版本
|
||||
* 0 = 版本一样
|
||||
* -1 = 当前版本大于更新版本
|
||||
*/
|
||||
public static int compareVersionNames(String newVersionName) {
|
||||
try {
|
||||
if (TextUtils.isEmpty(newVersionName)) {
|
||||
return -1;
|
||||
}
|
||||
int res = 0;
|
||||
String currentVersionName = getVersionName(true);
|
||||
if (currentVersionName.equals(newVersionName)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
String[] oldNumbers = currentVersionName.split("\\.");
|
||||
String[] newNumbers = newVersionName.split("\\.");
|
||||
|
||||
// To avoid IndexOutOfBounds
|
||||
int minIndex = Math.min(oldNumbers.length, newNumbers.length);
|
||||
|
||||
for (int i = 0; i < minIndex; i++) {
|
||||
int oldVersionPart = Integer.parseInt(oldNumbers[i]);
|
||||
int newVersionPart = Integer.parseInt(newNumbers[i]);
|
||||
|
||||
if (oldVersionPart < newVersionPart) {
|
||||
res = 1;
|
||||
break;
|
||||
} else if (oldVersionPart > newVersionPart) {
|
||||
res = -1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// If versions are the same so far, but they have different length...
|
||||
if (res == 0 && oldNumbers.length != newNumbers.length) {
|
||||
res = (oldNumbers.length > newNumbers.length) ? -1 : 1;
|
||||
}
|
||||
|
||||
return res;
|
||||
} catch (Exception e) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
private static PackageInfo getPackageInfo(Context context) {
|
||||
PackageInfo packageInfo;
|
||||
try {
|
||||
PackageManager pm = context.getPackageManager();
|
||||
packageInfo = pm.getPackageInfo(context.getPackageName(), PackageManager.GET_CONFIGURATIONS);
|
||||
return packageInfo;
|
||||
} catch (Exception e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
static <T> Task<T> doAsync(final Task<T> task) {
|
||||
UTIL_POOL.execute(task);
|
||||
return task;
|
||||
}
|
||||
|
||||
public static void runOnUiThread(final Runnable runnable) {
|
||||
if (Looper.myLooper() == Looper.getMainLooper()) {
|
||||
runnable.run();
|
||||
} else {
|
||||
AppUtils.UTIL_HANDLER.post(runnable);
|
||||
}
|
||||
}
|
||||
|
||||
public static void runOnUiThreadDelayed(final Runnable runnable, long delayMillis) {
|
||||
AppUtils.UTIL_HANDLER.postDelayed(runnable, delayMillis);
|
||||
}
|
||||
|
||||
static String getCurrentProcessName() {
|
||||
String name = getCurrentProcessNameByFile();
|
||||
if (!TextUtils.isEmpty(name)) return name;
|
||||
name = getCurrentProcessNameByAms();
|
||||
if (!TextUtils.isEmpty(name)) return name;
|
||||
name = getCurrentProcessNameByReflect();
|
||||
return name;
|
||||
}
|
||||
|
||||
static void fixSoftInputLeaks(final Window window) {
|
||||
InputMethodManager imm =
|
||||
(InputMethodManager) AppUtils.getApp().getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
if (imm == null) return;
|
||||
String[] leakViews = new String[]{"mLastSrvView", "mCurRootView", "mServedView", "mNextServedView"};
|
||||
for (String leakView : leakViews) {
|
||||
try {
|
||||
Field leakViewField = InputMethodManager.class.getDeclaredField(leakView);
|
||||
if (leakViewField == null) continue;
|
||||
if (!leakViewField.isAccessible()) {
|
||||
leakViewField.setAccessible(true);
|
||||
}
|
||||
Object obj = leakViewField.get(imm);
|
||||
if (!(obj instanceof View)) continue;
|
||||
View view = (View) obj;
|
||||
if (view.getRootView() == window.getDecorView().getRootView()) {
|
||||
leakViewField.set(imm, null);
|
||||
}
|
||||
} catch (Throwable ignore) {/**/}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// private method
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
private static String getCurrentProcessNameByFile() {
|
||||
try {
|
||||
File file = new File("/proc/" + android.os.Process.myPid() + "/" + "cmdline");
|
||||
BufferedReader mBufferedReader = new BufferedReader(new FileReader(file));
|
||||
String processName = mBufferedReader.readLine().trim();
|
||||
mBufferedReader.close();
|
||||
return processName;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
private static String getCurrentProcessNameByAms() {
|
||||
ActivityManager am = (ActivityManager) AppUtils.getApp().getSystemService(Context.ACTIVITY_SERVICE);
|
||||
if (am == null) return "";
|
||||
List<ActivityManager.RunningAppProcessInfo> info = am.getRunningAppProcesses();
|
||||
if (info == null || info.size() == 0) return "";
|
||||
int pid = android.os.Process.myPid();
|
||||
for (ActivityManager.RunningAppProcessInfo aInfo : info) {
|
||||
if (aInfo.pid == pid) {
|
||||
if (aInfo.processName != null) {
|
||||
return aInfo.processName;
|
||||
}
|
||||
}
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
private static String getCurrentProcessNameByReflect() {
|
||||
String processName = "";
|
||||
try {
|
||||
Application app = AppUtils.getApp();
|
||||
Field loadedApkField = app.getClass().getField("mLoadedApk");
|
||||
loadedApkField.setAccessible(true);
|
||||
Object loadedApk = loadedApkField.get(app);
|
||||
|
||||
Field activityThreadField = loadedApk.getClass().getDeclaredField("mActivityThread");
|
||||
activityThreadField.setAccessible(true);
|
||||
Object activityThread = activityThreadField.get(loadedApk);
|
||||
|
||||
Method getProcessName = activityThread.getClass().getDeclaredMethod("getProcessName");
|
||||
processName = (String) getProcessName.invoke(activityThread);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
return processName;
|
||||
}
|
||||
|
||||
private static Application getApplicationByReflect() {
|
||||
try {
|
||||
@SuppressLint("PrivateApi")
|
||||
Class<?> activityThread = Class.forName("android.app.ActivityThread");
|
||||
Object thread = activityThread.getMethod("currentActivityThread").invoke(null);
|
||||
Object app = activityThread.getMethod("getApplication").invoke(thread);
|
||||
if (app == null) {
|
||||
throw new NullPointerException("u should init first");
|
||||
}
|
||||
return (Application) app;
|
||||
} catch (NoSuchMethodException e) {
|
||||
e.printStackTrace();
|
||||
} catch (IllegalAccessException e) {
|
||||
e.printStackTrace();
|
||||
} catch (InvocationTargetException e) {
|
||||
e.printStackTrace();
|
||||
} catch (ClassNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
throw new NullPointerException("u should init first");
|
||||
}
|
||||
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
// interface
|
||||
///////////////////////////////////////////////////////////////////////////
|
||||
|
||||
public abstract static class Task<Result> implements Runnable {
|
||||
|
||||
private static final int NEW = 0;
|
||||
private static final int COMPLETING = 1;
|
||||
private static final int CANCELLED = 2;
|
||||
private static final int EXCEPTIONAL = 3;
|
||||
|
||||
private volatile int state = NEW;
|
||||
|
||||
abstract Result doInBackground();
|
||||
|
||||
private final Callback<Result> mCallback;
|
||||
|
||||
public Task(final Callback<Result> callback) {
|
||||
mCallback = callback;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
final Result t = doInBackground();
|
||||
|
||||
if (state != NEW) return;
|
||||
state = COMPLETING;
|
||||
UTIL_HANDLER.post(new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
mCallback.onCall(t);
|
||||
}
|
||||
});
|
||||
} catch (Throwable th) {
|
||||
if (state != NEW) return;
|
||||
state = EXCEPTIONAL;
|
||||
}
|
||||
}
|
||||
|
||||
public void cancel() {
|
||||
state = CANCELLED;
|
||||
}
|
||||
|
||||
public boolean isDone() {
|
||||
return state != NEW;
|
||||
}
|
||||
|
||||
public boolean isCanceled() {
|
||||
return state == CANCELLED;
|
||||
}
|
||||
}
|
||||
|
||||
public interface Callback<T> {
|
||||
void onCall(T data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否打开定位
|
||||
*/
|
||||
public static boolean getGpsStatus(Context ctx) {
|
||||
//从系统服务中获取定位管理器
|
||||
LocationManager locationManager
|
||||
= (LocationManager) ctx.getSystemService(Context.LOCATION_SERVICE);
|
||||
// 通过GPS卫星定位,定位级别可以精确到街(通过24颗卫星定位,在室外和空旷的地方定位准确、速度快)
|
||||
boolean gps = locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER);
|
||||
// 通过WLAN或移动网络(3G/2G)确定的位置(也称作AGPS,辅助GPS定位。主要用于在室内或遮盖物(建筑群或茂密的深林等)密集的地方定位)
|
||||
boolean network = locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER);
|
||||
if (gps || network) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* 打开系统定位界面
|
||||
*/
|
||||
public static void goToOpenGps(Context ctx) {
|
||||
Intent intent = new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS);
|
||||
ctx.startActivity(intent);
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package com.chuhai.utils
|
||||
|
||||
import android.os.SystemClock
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/24 15:11
|
||||
* Desc:服务器时间
|
||||
*/
|
||||
object ServiceTime {
|
||||
|
||||
// 服务器时间与系统开机时间的时差
|
||||
private var serviceTimeDiff: Long? = null
|
||||
|
||||
val time
|
||||
get() = if (serviceTimeDiff == null) System.currentTimeMillis()
|
||||
else SystemClock.elapsedRealtime() + serviceTimeDiff!!
|
||||
|
||||
/**
|
||||
* 刷新服务器时间
|
||||
*/
|
||||
fun refreshServiceTime(time: Long) {
|
||||
//serviceTimeDiff = 服务器时间 - 此刻系统启动时间
|
||||
serviceTimeDiff = time - SystemClock.elapsedRealtime()
|
||||
}
|
||||
}
|
@@ -0,0 +1,42 @@
|
||||
package com.chuhai.utils
|
||||
|
||||
import android.graphics.Outline
|
||||
import android.view.View
|
||||
import android.view.ViewOutlineProvider
|
||||
import kotlin.math.min
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/24 15:11
|
||||
* Desc:
|
||||
*/
|
||||
class ShapeViewOutlineProvider {
|
||||
|
||||
/**
|
||||
* Created by Max on 2/25/21 1:48 PM
|
||||
* Desc:圆角
|
||||
*/
|
||||
class Round(var corner: Float) : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, outline: Outline) {
|
||||
outline.setRoundRect(
|
||||
0,
|
||||
0,
|
||||
view.width,
|
||||
view.height,
|
||||
corner
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Created by Max on 2/25/21 1:48 PM
|
||||
* Desc:圆形
|
||||
*/
|
||||
class Circle : ViewOutlineProvider() {
|
||||
override fun getOutline(view: View, outline: Outline) {
|
||||
val min = min(view.width, view.height)
|
||||
val left = (view.width - min) / 2
|
||||
val top = (view.height - min) / 2
|
||||
outline.setOval(left, top, min, min)
|
||||
}
|
||||
}
|
||||
}
|
84
library/src/module_utils/java/com/chuhai/utils/UiUtils.kt
Normal file
84
library/src/module_utils/java/com/chuhai/utils/UiUtils.kt
Normal file
@@ -0,0 +1,84 @@
|
||||
package com.chuhai.utils
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.DisplayMetrics
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.WindowManager
|
||||
import androidx.core.text.TextUtilsCompat
|
||||
import androidx.core.view.ViewCompat
|
||||
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/24 15:11
|
||||
*/
|
||||
|
||||
|
||||
object UiUtils {
|
||||
fun getScreenWidth(context: Context): Int {
|
||||
val wm = context.getSystemService(Context.WINDOW_SERVICE) as? WindowManager
|
||||
val outMetrics = DisplayMetrics()
|
||||
wm?.defaultDisplay?.getMetrics(outMetrics)
|
||||
return outMetrics.widthPixels
|
||||
}
|
||||
|
||||
fun getScreenHeight(context: Context): Int {
|
||||
val wm = context.getSystemService(Context.WINDOW_SERVICE) as? WindowManager
|
||||
val outMetrics = DisplayMetrics()
|
||||
wm?.defaultDisplay?.getMetrics(outMetrics)
|
||||
return outMetrics.heightPixels
|
||||
}
|
||||
|
||||
fun getScreenRatio(context: Context): Float {
|
||||
return getScreenWidth(context) * 1.0f / getScreenHeight(context)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||
*/
|
||||
fun dip2px(dpValue: Float): Int {
|
||||
return dip2px(AppUtils.getApp(), dpValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
|
||||
*/
|
||||
fun px2dip(pxValue: Float): Float {
|
||||
return px2dip(AppUtils.getApp(), pxValue)
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 dp 的单位 转成为 px(像素)
|
||||
*/
|
||||
fun dip2px(context: Context, dpValue: Float): Int {
|
||||
return (TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dpValue, context.resources.displayMetrics) + 0.5f).toInt()
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据手机的分辨率从 px(像素) 的单位 转成为 dp
|
||||
*/
|
||||
fun px2dip(context: Context, pxValue: Float): Float {
|
||||
return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, pxValue, context.resources.displayMetrics)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否从右到左布局
|
||||
*/
|
||||
@SuppressLint("NewApi")
|
||||
fun isRtl(view: View): Boolean {
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
|
||||
View.LAYOUT_DIRECTION_RTL == view.layoutDirection
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否从右到左布局
|
||||
*/
|
||||
fun isRtl(context: Context): Boolean {
|
||||
return TextUtilsCompat.getLayoutDirectionFromLocale(context.resources.configuration.locale) == ViewCompat.LAYOUT_DIRECTION_RTL
|
||||
}
|
||||
}
|
@@ -0,0 +1,70 @@
|
||||
package com.chuhai.utils.ktx
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.ContextWrapper
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/25 15:57
|
||||
* Desc:Context相关工具
|
||||
**/
|
||||
|
||||
|
||||
/**
|
||||
* Context转换为Activity
|
||||
*/
|
||||
fun Context?.asActivity(): Activity? {
|
||||
return when {
|
||||
this is Activity -> {
|
||||
this
|
||||
}
|
||||
(this as? ContextWrapper)?.baseContext?.applicationContext != null -> {
|
||||
baseContext.asActivity()
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Context转换为Lifecycle
|
||||
*/
|
||||
fun Context?.asLifecycle(): Lifecycle? {
|
||||
if (this == null) return null
|
||||
return when (this) {
|
||||
is Lifecycle -> {
|
||||
this
|
||||
}
|
||||
is LifecycleOwner -> {
|
||||
this.lifecycle
|
||||
}
|
||||
is ContextWrapper -> {
|
||||
this.baseContext.asLifecycle()
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Context转换为LifecycleOwner
|
||||
*/
|
||||
fun Context?.asLifecycleOwner(): LifecycleOwner? {
|
||||
if (this == null) return null
|
||||
return when (this) {
|
||||
is LifecycleOwner -> {
|
||||
this
|
||||
}
|
||||
is ContextWrapper -> {
|
||||
this.baseContext.asLifecycleOwner()
|
||||
}
|
||||
else -> {
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,106 @@
|
||||
package com.chuhai.utils.ktx
|
||||
|
||||
import android.text.Editable
|
||||
import android.text.InputFilter
|
||||
import android.text.InputFilter.LengthFilter
|
||||
import android.text.Spanned
|
||||
import android.text.TextWatcher
|
||||
import android.text.method.HideReturnsTransformationMethod
|
||||
import android.text.method.PasswordTransformationMethod
|
||||
import android.widget.EditText
|
||||
|
||||
|
||||
/**
|
||||
* 设置editText输入监听
|
||||
* @param onChanged 改变事件
|
||||
* @return 是否接受此次文本的改变
|
||||
*/
|
||||
inline fun EditText.setOnInputChangedListener(
|
||||
/**
|
||||
* @param Int:当前长度
|
||||
* @return 是否接受此次文本的改变
|
||||
*/
|
||||
crossinline onChanged: (Int).() -> Boolean
|
||||
) {
|
||||
this.addTextChangedListener(object : TextWatcher {
|
||||
|
||||
var flag = false
|
||||
|
||||
override fun afterTextChanged(p0: Editable?) {
|
||||
if (flag) {
|
||||
return
|
||||
}
|
||||
if (!onChanged(p0?.length ?: 0)) {
|
||||
flag = true
|
||||
this@setOnInputChangedListener.setText(
|
||||
this@setOnInputChangedListener.getTag(
|
||||
1982329101
|
||||
) as? String
|
||||
)
|
||||
this@setOnInputChangedListener.setSelection(this@setOnInputChangedListener.length())
|
||||
flag = false
|
||||
} else {
|
||||
flag = false
|
||||
}
|
||||
}
|
||||
|
||||
override fun beforeTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
this@setOnInputChangedListener.setTag(1982329101, p0?.toString())
|
||||
}
|
||||
|
||||
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 切换密码可见度
|
||||
*/
|
||||
fun EditText.switchPasswordVisibility(visibility: Boolean) {
|
||||
transformationMethod =
|
||||
if (!visibility) HideReturnsTransformationMethod.getInstance() else PasswordTransformationMethod.getInstance()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置输入功能是否启用(不启用就相当于TextView)
|
||||
*/
|
||||
fun EditText.setInputEnabled(isEnabled: Boolean) {
|
||||
if (isEnabled) {
|
||||
isFocusable = true
|
||||
isFocusableInTouchMode = true
|
||||
isClickable = true
|
||||
} else {
|
||||
isFocusable = false
|
||||
isFocusableInTouchMode = false
|
||||
isClickable = false
|
||||
keyListener = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加输入长度限制过滤器
|
||||
*/
|
||||
fun EditText.addLengthFilter(maxLength: Int) {
|
||||
val newFilters = filters.copyOf(filters.size + 1)
|
||||
newFilters[newFilters.size - 1] = LengthFilter(maxLength)
|
||||
filters = newFilters
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 添加禁用文本过滤器
|
||||
* @param disableText 不允许输入该文本
|
||||
*/
|
||||
fun EditText.addDisableFilter(vararg disableText: CharSequence) {
|
||||
val newFilters = filters.copyOf(filters.size + 1)
|
||||
newFilters[newFilters.size - 1] = InputFilter { source, p1, p2, p3, p4, p5 ->
|
||||
disableText.forEach {
|
||||
if (source.equals(it)) {
|
||||
return@InputFilter ""
|
||||
}
|
||||
}
|
||||
return@InputFilter null
|
||||
}
|
||||
filters = newFilters
|
||||
}
|
@@ -0,0 +1,194 @@
|
||||
package com.chuhai.utils.ktx
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.res.TypedArray
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.util.TypedValue
|
||||
import androidx.annotation.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.res.ResourcesCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.chuhai.utils.AppUtils
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/24 15:11
|
||||
* 资源工具类
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 获取颜色
|
||||
*/
|
||||
fun Fragment.getColorById(@ColorRes colorResId: Int): Int {
|
||||
return ContextCompat.getColor(context!!, colorResId)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取图片
|
||||
*/
|
||||
fun Fragment.getDrawableById(@DrawableRes drawableRedId: Int): Drawable? {
|
||||
return ContextCompat.getDrawable(context!!, drawableRedId)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取颜色
|
||||
*/
|
||||
fun Activity.getColorById(@ColorRes colorResId: Int): Int {
|
||||
return ContextCompat.getColor(this, colorResId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片
|
||||
*/
|
||||
fun Activity.getDrawableById(@DrawableRes drawableRedId: Int): Drawable? {
|
||||
return ContextCompat.getDrawable(this, drawableRedId)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取颜色
|
||||
*/
|
||||
fun Context.getColorById(@ColorRes colorResId: Int): Int {
|
||||
return ContextCompat.getColor(this, colorResId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取图片
|
||||
*/
|
||||
fun Context.getDrawableById(@DrawableRes drawableRedId: Int): Drawable? {
|
||||
return ContextCompat.getDrawable(this, drawableRedId)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 获取字符串资源
|
||||
*/
|
||||
fun Any.getStringById(@StringRes stringResId: Int): String {
|
||||
return AppUtils.getApp().getString(stringResId)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取字符串资源
|
||||
*/
|
||||
fun Int.toStringRes(): String {
|
||||
return AppUtils.getApp().getString(this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源drawable
|
||||
* */
|
||||
fun Int.toDrawableRes(): Drawable? {
|
||||
return ContextCompat.getDrawable(AppUtils.getApp(), this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取资源color
|
||||
* */
|
||||
fun Int.toColorRes(): Int {
|
||||
return ContextCompat.getColor(AppUtils.getApp(), this)
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义属性-获取DrawableRes
|
||||
*/
|
||||
@DrawableRes
|
||||
fun Context.getDrawableResFromAttr(
|
||||
@AttrRes attrResId: Int,
|
||||
typedValue: TypedValue = TypedValue(),
|
||||
resolveRefs: Boolean = true
|
||||
): Int? {
|
||||
return try {
|
||||
theme.resolveAttribute(attrResId, typedValue, resolveRefs)
|
||||
return typedValue.resourceId
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义属性-获取Drawable
|
||||
*/
|
||||
fun Context.getDrawableFromAttr(@AttrRes attrId: Int): Drawable? {
|
||||
return try {
|
||||
val drawableRes = getDrawableResFromAttr(attrId) ?: return null
|
||||
ResourcesCompat.getDrawable(resources, drawableRes, null)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义属性-获取ColorRes
|
||||
*/
|
||||
@ColorRes
|
||||
fun Context.getColorResFromAttr(
|
||||
@AttrRes attrResId: Int,
|
||||
typedValue: TypedValue = TypedValue(),
|
||||
resolveRefs: Boolean = true
|
||||
): Int? {
|
||||
return try {
|
||||
theme.resolveAttribute(attrResId, typedValue, resolveRefs)
|
||||
return typedValue.resourceId
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义属性-获取Color
|
||||
*/
|
||||
@ColorRes
|
||||
fun Context.getColorFromAttr(
|
||||
@AttrRes attrResId: Int
|
||||
): Int? {
|
||||
return try {
|
||||
val colorRes = getColorFromAttr(attrResId) ?: return null
|
||||
ResourcesCompat.getColor(resources, colorRes, null)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义属性-获取LayoutRes
|
||||
*/
|
||||
@LayoutRes
|
||||
fun Context.getLayoutResFromAttr(
|
||||
@AttrRes attrResId: Int,
|
||||
typedValue: TypedValue = TypedValue(),
|
||||
resolveRefs: Boolean = true
|
||||
): Int? {
|
||||
return try {
|
||||
theme.resolveAttribute(attrResId, typedValue, resolveRefs)
|
||||
return typedValue.resourceId
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 通过自定义属性-获取Boolean
|
||||
*/
|
||||
fun Context.getBooleanResFromAttr(
|
||||
@AttrRes attrResId: Int,
|
||||
defValue: Boolean = false
|
||||
): Boolean {
|
||||
var attrs: TypedArray? = null
|
||||
try {
|
||||
attrs = obtainStyledAttributes(null, intArrayOf(attrResId))
|
||||
return attrs.getBoolean(0, defValue)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
attrs?.recycle()
|
||||
}
|
||||
return defValue
|
||||
}
|
51
library/src/module_utils/java/com/chuhai/utils/ktx/UiKtx.kt
Normal file
51
library/src/module_utils/java/com/chuhai/utils/ktx/UiKtx.kt
Normal file
@@ -0,0 +1,51 @@
|
||||
package com.chuhai.utils.ktx
|
||||
|
||||
import com.chuhai.utils.UiUtils
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
/**
|
||||
* Created by Max on 2023/10/24 15:11
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* 转换为PX值
|
||||
*/
|
||||
val Float.dp: Int get() = this.toPX()
|
||||
val Int.dp: Int get() = this.toPX()
|
||||
|
||||
/**
|
||||
* 转换为DP值
|
||||
*/
|
||||
val Float.px: Int get() = this.toDP().roundToInt()
|
||||
val Int.px: Int get() = this.toDP().roundToInt()
|
||||
|
||||
|
||||
fun Long.toDP(): Float {
|
||||
return UiUtils.px2dip(this.toFloat())
|
||||
}
|
||||
|
||||
|
||||
fun Float.toDP(): Float {
|
||||
return UiUtils.px2dip(this)
|
||||
}
|
||||
|
||||
|
||||
fun Int.toDP(): Float {
|
||||
return UiUtils.px2dip(this.toFloat())
|
||||
}
|
||||
|
||||
|
||||
fun Long.toPX(): Int {
|
||||
return UiUtils.dip2px(this.toFloat())
|
||||
}
|
||||
|
||||
|
||||
fun Float.toPX(): Int {
|
||||
return UiUtils.dip2px(this)
|
||||
}
|
||||
|
||||
|
||||
fun Int.toPX(): Int {
|
||||
return UiUtils.dip2px(this.toFloat())
|
||||
}
|
192
library/src/module_utils/java/com/chuhai/utils/ktx/ViewKtx.kt
Normal file
192
library/src/module_utils/java/com/chuhai/utils/ktx/ViewKtx.kt
Normal file
@@ -0,0 +1,192 @@
|
||||
package com.chuhai.utils.ktx
|
||||
|
||||
import android.graphics.*
|
||||
import android.os.Build
|
||||
import android.os.SystemClock
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Checkable
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.ScrollingView
|
||||
import com.chuhai.utils.ShapeViewOutlineProvider
|
||||
import com.chuhai.utils.UiUtils
|
||||
|
||||
|
||||
/**
|
||||
* 是否右-左布局
|
||||
*/
|
||||
fun View.isRtl(): Boolean {
|
||||
return UiUtils.isRtl(this)
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 展示or隐藏
|
||||
*/
|
||||
fun View.visibleOrGone(isShow: Boolean) {
|
||||
visibility = if (isShow) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示or隐藏
|
||||
*/
|
||||
inline fun View.visibleOrGone(show: View.() -> Boolean = { true }) {
|
||||
visibility = if (show(this)) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 展示or不可见
|
||||
*/
|
||||
inline fun View.visibleOrInvisible(show: View.() -> Boolean = { true }) {
|
||||
visibility = if (show(this)) {
|
||||
View.VISIBLE
|
||||
} else {
|
||||
View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击事件
|
||||
*/
|
||||
inline fun <T : View> T.singleClick(time: Long = 800, crossinline block: (T) -> Unit) {
|
||||
setOnClickListener(object : View.OnClickListener {
|
||||
private var lastClickTime: Long = 0L
|
||||
override fun onClick(v: View?) {
|
||||
val currentTimeMillis = SystemClock.elapsedRealtime()
|
||||
if (currentTimeMillis - lastClickTime > time || this is Checkable) {
|
||||
lastClickTime = currentTimeMillis
|
||||
block(this@singleClick)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 点击事件
|
||||
*/
|
||||
fun <T : View> T.singleClick(onClickListener: View.OnClickListener, time: Long = 800) {
|
||||
setOnClickListener(object : View.OnClickListener {
|
||||
private var lastClickTime: Long = 0L
|
||||
override fun onClick(v: View?) {
|
||||
val currentTimeMillis = SystemClock.elapsedRealtime()
|
||||
if (currentTimeMillis - lastClickTime > time || this is Checkable) {
|
||||
lastClickTime = currentTimeMillis
|
||||
onClickListener.onClick(v)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置View圆角矩形
|
||||
*/
|
||||
fun <T : View> T.roundCorner(corner: Int) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (outlineProvider == null || outlineProvider !is ShapeViewOutlineProvider.Round) {
|
||||
outlineProvider = ShapeViewOutlineProvider.Round(corner.toFloat())
|
||||
} else if (outlineProvider != null && outlineProvider is ShapeViewOutlineProvider.Round) {
|
||||
(outlineProvider as ShapeViewOutlineProvider.Round).corner = corner.toFloat()
|
||||
}
|
||||
clipToOutline = true
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置View为圆形
|
||||
*/
|
||||
fun <T : View> T.circle() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
if (outlineProvider == null || outlineProvider !is ShapeViewOutlineProvider.Circle) {
|
||||
outlineProvider = ShapeViewOutlineProvider.Circle()
|
||||
}
|
||||
clipToOutline = true
|
||||
}
|
||||
}
|
||||
|
||||
fun View.getBitmap(): Bitmap {
|
||||
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
|
||||
val canvas = Canvas(bitmap)
|
||||
canvas.translate(scrollX.toFloat(), scrollY.toFloat())
|
||||
draw(canvas)
|
||||
return bitmap
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置边距
|
||||
*/
|
||||
fun View?.setMargin(start: Int? = null, top: Int? = null, end: Int? = null, bottom: Int? = null) {
|
||||
(this?.layoutParams as? ViewGroup.MarginLayoutParams)?.apply {
|
||||
start?.let {
|
||||
this.marginStart = start
|
||||
}
|
||||
top?.let {
|
||||
this.topMargin = top
|
||||
}
|
||||
end?.let {
|
||||
this.marginEnd = end
|
||||
}
|
||||
bottom?.let {
|
||||
this.bottomMargin = bottom
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 设置内边距
|
||||
*/
|
||||
fun View?.setPadding2(start: Int? = null, top: Int? = null, end: Int? = null, bottom: Int? = null) {
|
||||
if (this == null) return
|
||||
this.setPadding(
|
||||
start ?: paddingStart, top ?: paddingTop, end ?: paddingEnd, bottom ?: paddingBottom
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 描边宽度
|
||||
*/
|
||||
fun TextView.strokeWidth(width: Float) {
|
||||
this.paint?.style = Paint.Style.FILL_AND_STROKE
|
||||
this.paint?.strokeWidth = width
|
||||
this.invalidate()
|
||||
}
|
||||
|
||||
/**
|
||||
* 模拟点击并取消
|
||||
*/
|
||||
fun ScrollingView.simulateClickAndCancel() {
|
||||
val view = this as? View ?: return
|
||||
val downEvent = MotionEvent.obtain(
|
||||
System.currentTimeMillis(), System.currentTimeMillis(), MotionEvent.ACTION_DOWN, (view.right - view.left) / 2f, (view.bottom - view.top) / 2f, 0
|
||||
)
|
||||
view.dispatchTouchEvent(downEvent)
|
||||
val cancelEvent = MotionEvent.obtain(
|
||||
System.currentTimeMillis(), System.currentTimeMillis(), MotionEvent.ACTION_CANCEL, (view.right - view.left) / 2f, (view.bottom - view.top) / 2f, 0
|
||||
)
|
||||
view.dispatchTouchEvent(cancelEvent)
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用灰色滤镜
|
||||
*/
|
||||
fun View.applyGrayFilter(isGray: Boolean) {
|
||||
try {
|
||||
val paint = Paint()
|
||||
val colorMatrix = ColorMatrix()
|
||||
colorMatrix.setSaturation(if (isGray) 0f else 1f)
|
||||
paint.colorFilter = ColorMatrixColorFilter(colorMatrix)
|
||||
setLayerType(View.LAYER_TYPE_HARDWARE, paint)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
|
5
library/src/module_utils/res/values/ids.xml
Normal file
5
library/src/module_utils/res/values/ids.xml
Normal file
@@ -0,0 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
|
||||
|
||||
</resources>
|
3
library/src/module_utils/res/values/strings.xml
Normal file
3
library/src/module_utils/res/values/strings.xml
Normal file
@@ -0,0 +1,3 @@
|
||||
<resources>
|
||||
|
||||
</resources>
|
Reference in New Issue
Block a user