parent
							
								
									367f2cb703
								
							
						
					
					
						commit
						77682bbd1d
					
				| @ -1,7 +1,6 @@ | ||||
| package com.android.base.kotlin | ||||
| package com.android.base.adapter.recycler | ||||
| 
 | ||||
| import android.view.View | ||||
| import com.android.base.adapter.recycler.ViewHolder | ||||
| import kotlinx.android.extensions.CacheImplementation | ||||
| import kotlinx.android.extensions.ContainerOptions | ||||
| import kotlinx.android.extensions.LayoutContainer | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.android.base.kotlin | ||||
| package com.android.base.app.aac | ||||
| 
 | ||||
| import androidx.lifecycle.MutableLiveData | ||||
| 
 | ||||
| @ -1,15 +0,0 @@ | ||||
| package com.android.base.kotlin | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.graphics.drawable.Drawable | ||||
| import androidx.annotation.ColorRes | ||||
| import androidx.annotation.DrawableRes | ||||
| import androidx.core.content.ContextCompat | ||||
| 
 | ||||
| fun Context.colorFromId(@ColorRes id: Int): Int { | ||||
|     return ContextCompat.getColor(this, id) | ||||
| } | ||||
| 
 | ||||
| fun Context.drawableFromId(@DrawableRes id: Int): Drawable? { | ||||
|     return ContextCompat.getDrawable(this, id) | ||||
| } | ||||
| @ -1,18 +0,0 @@ | ||||
| package com.android.base.kotlin | ||||
| 
 | ||||
| import android.graphics.drawable.Drawable | ||||
| import androidx.annotation.ColorRes | ||||
| import androidx.annotation.DrawableRes | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.fragment.app.Fragment | ||||
| 
 | ||||
| 
 | ||||
| fun Fragment.colorFromId(@ColorRes colorRes: Int): Int { | ||||
|     val safeContext = context ?: return 0 | ||||
|     return ContextCompat.getColor(safeContext, colorRes) | ||||
| } | ||||
| 
 | ||||
| fun Fragment.drawableFromId(@DrawableRes colorRes: Int): Drawable? { | ||||
|     val safeContext = context ?: return null | ||||
|     return ContextCompat.getDrawable(safeContext, colorRes) | ||||
| } | ||||
| @ -1,58 +0,0 @@ | ||||
| package com.android.base.kotlin | ||||
| 
 | ||||
| /** | ||||
|  * 参考: | ||||
|  * - [Boolean扩展](https://blog.kotliner.cn/2017/06/19/interesting-booleanext/) | ||||
|  */ | ||||
| sealed class Ext<out T> constructor(val boolean: Boolean) | ||||
| 
 | ||||
| /** 如果该对象不是null,则执行action */ | ||||
| fun <T, E> T?.ifNonNull(action: T.() -> E): Ext<E> { | ||||
|     if (this != null) { | ||||
|         return WithData(action()) | ||||
|     } | ||||
|     return Otherwise | ||||
| } | ||||
| 
 | ||||
| /** 如果该对象是null,则执行action */ | ||||
| fun <T, E> T?.ifNull(action: () -> E) { | ||||
|     if (this == null) { | ||||
|         action() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline fun <T> Boolean.yes(block: () -> T): Ext<T> = when { | ||||
|     this -> { | ||||
|         WithData(block()) | ||||
|     } | ||||
|     else -> Otherwise | ||||
| } | ||||
| 
 | ||||
| inline fun <T> Boolean.no(block: () -> T) = when { | ||||
|     this -> Otherwise | ||||
|     else -> { | ||||
|         WithData(block()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| object Otherwise : Ext<Nothing>(true) | ||||
| 
 | ||||
| class WithData<out T>(val data: T) : Ext<T>(false) | ||||
| 
 | ||||
| /**除此以外*/ | ||||
| inline infix fun <T> Ext<T>.otherwise(block: () -> T): T { | ||||
|     return when (this) { | ||||
|         is Otherwise -> block() | ||||
|         is WithData<T> -> this.data | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline operator fun <T> Boolean.invoke(block: () -> T) = yes(block) | ||||
| 
 | ||||
| fun Any?.javaClassName(): String { | ||||
|     return if (this == null) { | ||||
|         "" | ||||
|     } else { | ||||
|         this::class.java.name | ||||
|     } | ||||
| } | ||||
| @ -1,21 +0,0 @@ | ||||
| package com.android.base.kotlin | ||||
| 
 | ||||
| import android.content.res.TypedArray | ||||
| 
 | ||||
| inline fun <T : TypedArray?, R> T.use(block: (T) -> R): R { | ||||
|     var recycled = false | ||||
|     try { | ||||
|         return block(this) | ||||
|     } catch (e: Exception) { | ||||
|         recycled = true | ||||
|         try { | ||||
|             this?.recycle() | ||||
|         } catch (exception: Exception) { | ||||
|         } | ||||
|         throw e | ||||
|     } finally { | ||||
|         if (!recycled) { | ||||
|             this?.recycle() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -1,8 +0,0 @@ | ||||
| package com.android.base.kotlin | ||||
| 
 | ||||
| import com.android.base.utils.android.UnitConverter | ||||
| 
 | ||||
| fun dip(value: Int): Int = UnitConverter.dpToPx(value) | ||||
| fun dip(value: Float): Float = UnitConverter.dpToPx(value) | ||||
| fun sp(value: Int): Int = UnitConverter.spToPx(value) | ||||
| fun sp(value: Float): Float = UnitConverter.spToPx(value) | ||||
| @ -1,172 +0,0 @@ | ||||
| package com.android.base.kotlin | ||||
| 
 | ||||
| import android.graphics.drawable.Drawable | ||||
| import android.view.View | ||||
| import android.view.View.FOCUS_DOWN | ||||
| import android.view.View.FOCUS_UP | ||||
| import android.view.ViewGroup | ||||
| import android.view.ViewTreeObserver | ||||
| import android.widget.ScrollView | ||||
| import androidx.annotation.ColorRes | ||||
| import androidx.annotation.DrawableRes | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.core.view.ViewCompat | ||||
| import androidx.core.widget.NestedScrollView | ||||
| import com.android.base.rx.subscribeIgnoreError | ||||
| import com.android.base.utils.android.ViewUtils | ||||
| import com.android.base.utils.android.compat.AndroidVersion.atLeast | ||||
| import com.jakewharton.rxbinding3.view.clicks | ||||
| import io.reactivex.Observable | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| fun View.visibleOrGone(visible: Boolean) { | ||||
|     if (visible) { | ||||
|         this.visibility = View.VISIBLE | ||||
|     } else { | ||||
|         this.visibility = View.GONE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.visibleOrInvisible(visible: Boolean) { | ||||
|     if (visible) { | ||||
|         this.visibility = View.VISIBLE | ||||
|     } else { | ||||
|         this.visibility = View.INVISIBLE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.visible() { | ||||
|     this.visibility = View.VISIBLE | ||||
| } | ||||
| 
 | ||||
| fun View.invisible() { | ||||
|     this.visibility = View.INVISIBLE | ||||
| } | ||||
| 
 | ||||
| fun View.gone() { | ||||
|     this.visibility = View.GONE | ||||
| } | ||||
| 
 | ||||
| fun View.isVisible() = this.visibility == View.VISIBLE | ||||
| fun View.isInvisible() = this.visibility == View.INVISIBLE | ||||
| fun View.isGone() = this.visibility == View.GONE | ||||
| 
 | ||||
| fun View.realContext() = ViewUtils.getRealContext(this) | ||||
| 
 | ||||
| inline fun View.doOnLayoutAvailable(crossinline block: () -> Unit) { | ||||
|     //isLaidOut 方法作用:如果 view 已经通过至少一个布局,则返回true,因为它最后一次附加到窗口或从窗口分离。 | ||||
|     ViewCompat.isLaidOut(this).yes { | ||||
|         block() | ||||
|     }.otherwise { | ||||
|         addOnLayoutChangeListener(object : View.OnLayoutChangeListener { | ||||
|             override fun onLayoutChange(v: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) { | ||||
|                 removeOnLayoutChangeListener(this) | ||||
|                 block() | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline fun <T : View> T.onGlobalLayoutOnce(crossinline action: T.() -> Unit) { | ||||
|     val t: T = this | ||||
|     t.viewTreeObserver | ||||
|             .addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { | ||||
|                 override fun onGlobalLayout() { | ||||
|                     action.invoke(t) | ||||
|                     if (atLeast(16)) { | ||||
|                         viewTreeObserver.removeOnGlobalLayoutListener(this) | ||||
|                     } else { | ||||
|                         @Suppress("DEPRECATION") | ||||
|                         viewTreeObserver.removeGlobalOnLayoutListener(this) | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingAll(padding: Int) { | ||||
|     this.setPadding(padding, padding, padding, padding) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingLeft(padding: Int) { | ||||
|     this.setPadding(padding, paddingTop, paddingRight, paddingBottom) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddRight(padding: Int) { | ||||
|     this.setPadding(paddingLeft, paddingTop, padding, paddingBottom) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingTop(padding: Int) { | ||||
|     this.setPadding(paddingLeft, padding, paddingRight, paddingBottom) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingBottom(padding: Int) { | ||||
|     this.setPadding(paddingLeft, paddingTop, paddingRight, padding) | ||||
| } | ||||
| 
 | ||||
| fun View.colorFromId(@ColorRes colorRes: Int): Int { | ||||
|     val safeContext = context ?: return 0 | ||||
|     return ContextCompat.getColor(safeContext, colorRes) | ||||
| } | ||||
| 
 | ||||
| fun View.drawableFromId(@DrawableRes colorRes: Int): Drawable? { | ||||
|     val safeContext = context ?: return null | ||||
|     return ContextCompat.getDrawable(safeContext, colorRes) | ||||
| } | ||||
| 
 | ||||
| fun View.setWidth(width: Int) { | ||||
|     val params = layoutParams ?: ViewGroup.LayoutParams(0, 0) | ||||
|     params.width = width | ||||
|     layoutParams = params | ||||
| } | ||||
| 
 | ||||
| fun View.setHeight(height: Int) { | ||||
|     val params = layoutParams ?: ViewGroup.LayoutParams(0, 0) | ||||
|     params.height = height | ||||
|     layoutParams = params | ||||
| } | ||||
| 
 | ||||
| fun View.setSize(width: Int, height: Int) { | ||||
|     val params = layoutParams ?: ViewGroup.LayoutParams(0, 0) | ||||
|     params.width = width | ||||
|     params.height = height | ||||
|     layoutParams = params | ||||
| } | ||||
| 
 | ||||
| fun View.onDebouncedClick(onClick: (View) -> Unit) { | ||||
|     onClickObservable(500) | ||||
|             .subscribeIgnoreError { onClick(this) } | ||||
| } | ||||
| 
 | ||||
| fun View.onDebouncedClick(milliseconds: Long, onClick: (View) -> Unit) { | ||||
|     onClickObservable(milliseconds) | ||||
|             .subscribeIgnoreError { onClick(this) } | ||||
| } | ||||
| 
 | ||||
| fun View.onClickObservable(): Observable<Unit> { | ||||
|     return onClickObservable(500) | ||||
| } | ||||
| 
 | ||||
| fun View.onClickObservable(milliseconds: Long): Observable<Unit> { | ||||
|     return clicks() | ||||
|             .throttleFirst(milliseconds, TimeUnit.MILLISECONDS) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
| } | ||||
| 
 | ||||
| inline val ViewGroup.views get() = (0 until childCount).map { getChildAt(it) } | ||||
| 
 | ||||
| fun ScrollView.scrollToBottom() { | ||||
|     fullScroll(FOCUS_DOWN) | ||||
| } | ||||
| 
 | ||||
| fun ScrollView.scrollToTop() { | ||||
|     fullScroll(FOCUS_UP) | ||||
| } | ||||
| 
 | ||||
| fun NestedScrollView.scrollToBottom() { | ||||
|     fullScroll(FOCUS_DOWN) | ||||
| } | ||||
| 
 | ||||
| fun NestedScrollView.scrollToTop() { | ||||
|     fullScroll(FOCUS_UP) | ||||
| } | ||||
| @ -1,83 +0,0 @@ | ||||
| package com.android.base.utils.android; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.TypedArray; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.net.Uri; | ||||
| 
 | ||||
| import com.android.base.utils.BaseUtils; | ||||
| 
 | ||||
| import androidx.annotation.ArrayRes; | ||||
| import androidx.annotation.AttrRes; | ||||
| import androidx.annotation.NonNull; | ||||
| import androidx.annotation.StringRes; | ||||
| 
 | ||||
| 
 | ||||
| public class ResourceUtils { | ||||
| 
 | ||||
|     private ResourceUtils() { | ||||
|         throw new UnsupportedOperationException("no need instantiation"); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * @param name       资源的名称,如 ic_launcher 或者 com.example.android/drawable/ic_launcher(这是,下面两个参数可以省略) | ||||
|      * @param defType    资源的类型,如 drawable | ||||
|      * @param defPackage 包名 | ||||
|      * @return 资源id | ||||
|      */ | ||||
|     public static int getResource(String name, String defType, String defPackage) { | ||||
|         return BaseUtils.getResources().getIdentifier(name, defType, defPackage); | ||||
|     } | ||||
| 
 | ||||
|     public static CharSequence getText(@StringRes int id) { | ||||
|         return BaseUtils.getResources().getText(id); | ||||
|     } | ||||
| 
 | ||||
|     public static String getString(@StringRes int id) { | ||||
|         return BaseUtils.getResources().getString(id); | ||||
|     } | ||||
| 
 | ||||
|     public static String getString(@StringRes int id, Object... formatArgs) { | ||||
|         return BaseUtils.getResources().getString(id, formatArgs); | ||||
|     } | ||||
| 
 | ||||
|     public static String[] getStringArray(@ArrayRes int id) { | ||||
|         return BaseUtils.getResources().getStringArray(id); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static int[] getIntArray(@ArrayRes int id) { | ||||
|         return BaseUtils.getResources().getIntArray(id); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static Uri createUriByResource(int id) { | ||||
|         return Uri.parse("android.resource://" + BaseUtils.getAppContext().getPackageName() + "/" + id); | ||||
|     } | ||||
| 
 | ||||
|     public static Uri createUriByAssets(String path) { | ||||
|         return Uri.parse("file:///android_asset/" + path); | ||||
|     } | ||||
| 
 | ||||
|     public static int getStyledColor(@NonNull Context context, @AttrRes int attr) { | ||||
|         TypedArray a = context.obtainStyledAttributes(null, new int[]{attr}); | ||||
|         try { | ||||
|             return a.getColor(0, 0x000000); | ||||
|         } finally { | ||||
|             a.recycle(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static Drawable getStyledDrawable(@NonNull Context context, @AttrRes int attr) { | ||||
|         TypedArray a = context.obtainStyledAttributes(null, new int[]{attr}); | ||||
|         try { | ||||
|             return a.getDrawable(0); | ||||
|         } finally { | ||||
|             a.recycle(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static int getDimensPixelSize(int dimenId) { | ||||
|         return BaseUtils.getResources().getDimensionPixelSize(dimenId); | ||||
|     } | ||||
| } | ||||
| @ -1,76 +0,0 @@ | ||||
| package com.android.base.utils.android; | ||||
| 
 | ||||
| import android.content.res.ColorStateList; | ||||
| import android.graphics.Bitmap; | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.Paint; | ||||
| import android.graphics.PorterDuff; | ||||
| import android.graphics.PorterDuffColorFilter; | ||||
| import android.graphics.drawable.Drawable; | ||||
| 
 | ||||
| import androidx.core.graphics.drawable.DrawableCompat; | ||||
| 
 | ||||
| /** | ||||
|  * usage: | ||||
|  * <pre> | ||||
|  *     {@code | ||||
|  * | ||||
|  *              int[][] states = new int[][]{ | ||||
|  *                  new int[]{android.R.attr.state_selected}, // pressed
 | ||||
|  *                  new int[]{-android.R.attr.state_selected}  // unpressed
 | ||||
|  *             }; | ||||
|  * | ||||
|  *              int[] colors = new int[]{ | ||||
|  *                  Color.BLACK, | ||||
|  *                  Color.GRAY | ||||
|  *              }; | ||||
|  * | ||||
|  *              ColorStateList colorStateList = new ColorStateList(states, colors); | ||||
|  *              Drawable aaa = ContextCompat.getDrawable(getContext(), R.drawable.aaa); | ||||
|  *              Drawable bbb = ContextCompat.getDrawable(getContext(), R.drawable.bbb); | ||||
|  *              Drawable aaaTint = TintUtils.tint(aaa, colorStateList); | ||||
|  *              Drawable bbbTint = TintUtils.tint(bbb, colorStateList); | ||||
|  *              aaaTv.setCompoundDrawablesWithIntrinsicBounds(null, aaaTint, null, null); | ||||
|  *              bbbTv.setCompoundDrawablesWithIntrinsicBounds(null, bbbTint, null, null); | ||||
|  * </pre> | ||||
|  */ | ||||
| @SuppressWarnings("unused,WeakerAccess") | ||||
| public class TintUtils { | ||||
| 
 | ||||
|     private TintUtils() { | ||||
|     } | ||||
| 
 | ||||
|     public static Drawable tint(Drawable originDrawable, int color) { | ||||
|         return tint(originDrawable, ColorStateList.valueOf(color)); | ||||
|     } | ||||
| 
 | ||||
|     public static Drawable tint(Drawable originDrawable, int color, PorterDuff.Mode tintMode) { | ||||
|         return tint(originDrawable, ColorStateList.valueOf(color), tintMode); | ||||
|     } | ||||
| 
 | ||||
|     public static Drawable tint(Drawable originDrawable, ColorStateList colorStateList) { | ||||
|         return tint(originDrawable, colorStateList, null); | ||||
|     } | ||||
| 
 | ||||
|     public static Drawable tint(Drawable originDrawable, ColorStateList colorStateList, PorterDuff.Mode tintMode) { | ||||
|         Drawable tintDrawable = DrawableCompat.wrap(originDrawable); | ||||
|         if (tintMode != null) { | ||||
|             DrawableCompat.setTintMode(tintDrawable, tintMode); | ||||
|         } | ||||
|         DrawableCompat.setTintList(tintDrawable, colorStateList); | ||||
|         return tintDrawable; | ||||
|     } | ||||
| 
 | ||||
|     public static Bitmap tintBitmap(Bitmap inBitmap, int tintColor) { | ||||
|         if (inBitmap == null) { | ||||
|             return null; | ||||
|         } | ||||
|         Bitmap outBitmap = Bitmap.createBitmap(inBitmap.getWidth(), inBitmap.getHeight(), inBitmap.getConfig()); | ||||
|         Canvas canvas = new Canvas(outBitmap); | ||||
|         Paint paint = new Paint(Paint.ANTI_ALIAS_FLAG); | ||||
|         paint.setColorFilter(new PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN)); | ||||
|         canvas.drawBitmap(inBitmap, 0, 0, paint); | ||||
|         return outBitmap; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,72 +0,0 @@ | ||||
| package com.android.base.utils.android; | ||||
| 
 | ||||
| 
 | ||||
| import android.util.DisplayMetrics; | ||||
| import android.util.TypedValue; | ||||
| 
 | ||||
| import com.android.base.utils.BaseUtils; | ||||
| 
 | ||||
| public class UnitConverter { | ||||
| 
 | ||||
|     private UnitConverter() { | ||||
|         throw new UnsupportedOperationException("no need instantiation"); | ||||
|     } | ||||
| 
 | ||||
|     public static float dpToPx(float dp) { | ||||
|         return dp * BaseUtils.getDisplayMetrics().density; | ||||
|     } | ||||
| 
 | ||||
|     public static int dpToPx(int dp) { | ||||
|         return (int) (dp * BaseUtils.getDisplayMetrics().density + 0.5f); | ||||
|     } | ||||
| 
 | ||||
|     public static float pxToDp(float px) { | ||||
|         return px / BaseUtils.getDisplayMetrics().density; | ||||
|     } | ||||
| 
 | ||||
|     public static int pxToDp(int px) { | ||||
|         return (int) (px / BaseUtils.getDisplayMetrics().density + 0.5f); | ||||
|     } | ||||
| 
 | ||||
|     public static float spToPx(float sp) { | ||||
|         return sp * BaseUtils.getDisplayMetrics().scaledDensity; | ||||
|     } | ||||
| 
 | ||||
|     public static int spToPx(int sp) { | ||||
|         return (int) (sp * BaseUtils.getDisplayMetrics().scaledDensity + 0.5f); | ||||
|     } | ||||
| 
 | ||||
|     public static float pxToSp(float px) { | ||||
|         return px / BaseUtils.getDisplayMetrics().scaledDensity; | ||||
|     } | ||||
| 
 | ||||
|     public static int pxToSp(int px) { | ||||
|         return (int) (px / BaseUtils.getDisplayMetrics().scaledDensity + 0.5f); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 各种单位转换,该方法存在于{@link TypedValue} 中 | ||||
|      * | ||||
|      * @param unit  单位 | ||||
|      * @param value 值 | ||||
|      * @return 转换结果 | ||||
|      */ | ||||
|     public static float applyDimension(int unit, float value) { | ||||
|         DisplayMetrics metrics = BaseUtils.getDisplayMetrics(); | ||||
|         switch (unit) { | ||||
|             case TypedValue.COMPLEX_UNIT_PX: | ||||
|                 return value; | ||||
|             case TypedValue.COMPLEX_UNIT_DIP: | ||||
|                 return value * metrics.density; | ||||
|             case TypedValue.COMPLEX_UNIT_SP: | ||||
|                 return value * metrics.scaledDensity; | ||||
|             case TypedValue.COMPLEX_UNIT_PT: | ||||
|                 return value * metrics.xdpi * (1.0f / 72); | ||||
|             case TypedValue.COMPLEX_UNIT_IN: | ||||
|                 return value * metrics.xdpi; | ||||
|             case TypedValue.COMPLEX_UNIT_MM: | ||||
|                 return value * metrics.xdpi * (1.0f / 25.4f); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| } | ||||
| @ -1,208 +0,0 @@ | ||||
| package com.android.base.utils.android; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.graphics.Bitmap; | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.Picture; | ||||
| import android.graphics.drawable.Drawable; | ||||
| import android.os.Build; | ||||
| import android.view.View; | ||||
| import android.view.ViewGroup; | ||||
| import android.webkit.WebView; | ||||
| import android.widget.TextView; | ||||
| 
 | ||||
| import androidx.annotation.IdRes; | ||||
| import androidx.annotation.Nullable; | ||||
| import androidx.fragment.app.FragmentActivity; | ||||
| 
 | ||||
| public class ViewUtils { | ||||
| 
 | ||||
|     private ViewUtils() { | ||||
|     } | ||||
| 
 | ||||
|     private static final int ACTION_VISIBLE = 0x01; | ||||
|     private static final int ACTION_GONE = 0x02; | ||||
|     private static final int ACTION_INVISIBLE = 0x03; | ||||
|     private static final int ACTION_DISABLE = 0x04; | ||||
|     private static final int ACTION_ENABLE = 0x05; | ||||
| 
 | ||||
|     public static boolean measureWithMaxSize(View view) { | ||||
|         ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); | ||||
|         if (layoutParams == null || (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT)) { | ||||
|             return false; | ||||
|         } | ||||
|         int size = 1 << 30 - 1;//即后30位
 | ||||
|         int measureSpec = View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.AT_MOST); | ||||
|         view.measure(measureSpec, measureSpec); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean measureWithScreenSize(View view) { | ||||
|         ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); | ||||
|         if (layoutParams == null || (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT)) { | ||||
|             return false; | ||||
|         } | ||||
|         view.measure( | ||||
|                 View.MeasureSpec.makeMeasureSpec(WindowUtils.getScreenWidth(), View.MeasureSpec.AT_MOST), | ||||
|                 View.MeasureSpec.makeMeasureSpec(WindowUtils.getScreenHeight(), View.MeasureSpec.AT_MOST)); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean measureWithSize(View view, int width, int height) { | ||||
|         ViewGroup.LayoutParams layoutParams = view.getLayoutParams(); | ||||
|         if (layoutParams == null || (layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT)) { | ||||
|             return false; | ||||
|         } | ||||
|         view.measure( | ||||
|                 View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST), | ||||
|                 View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static void disable(View view1, View view2) { | ||||
|         view1.setEnabled(false); | ||||
|         view2.setEnabled(false); | ||||
|     } | ||||
| 
 | ||||
|     public static void disable(View view1, View view2, View view3) { | ||||
|         view1.setEnabled(false); | ||||
|         view2.setEnabled(false); | ||||
|         view3.setEnabled(false); | ||||
|     } | ||||
| 
 | ||||
|     public static void disable(View view1, View view2, View view3, View... views) { | ||||
|         view1.setEnabled(false); | ||||
|         view2.setEnabled(false); | ||||
|         view3.setEnabled(false); | ||||
|         doAction(ACTION_DISABLE, views); | ||||
|     } | ||||
| 
 | ||||
|     public static void enable(View view1, View view2) { | ||||
|         view1.setEnabled(true); | ||||
|         view2.setEnabled(true); | ||||
|     } | ||||
| 
 | ||||
|     public static void enable(View view1, View view2, View view3) { | ||||
|         view1.setEnabled(true); | ||||
|         view2.setEnabled(true); | ||||
|         view3.setEnabled(true); | ||||
|     } | ||||
| 
 | ||||
|     public static void enable(View view1, View view2, View view3, View... views) { | ||||
|         view1.setEnabled(true); | ||||
|         view2.setEnabled(true); | ||||
|         view3.setEnabled(true); | ||||
|         doAction(ACTION_ENABLE, views); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     public static void gone(View view1, View view2) { | ||||
|         view1.setVisibility(View.GONE); | ||||
|         view2.setVisibility(View.GONE); | ||||
|     } | ||||
| 
 | ||||
|     public static void gone(View view1, View view2, View view3) { | ||||
|         view1.setVisibility(View.GONE); | ||||
|         view2.setVisibility(View.GONE); | ||||
|         view3.setVisibility(View.GONE); | ||||
|     } | ||||
| 
 | ||||
|     public static void gone(View view1, View view2, View view3, View... views) { | ||||
|         view1.setVisibility(View.GONE); | ||||
|         view2.setVisibility(View.GONE); | ||||
|         view3.setVisibility(View.GONE); | ||||
|         doAction(ACTION_GONE, views); | ||||
|     } | ||||
| 
 | ||||
|     public static void visible(View view1, View view2) { | ||||
|         view1.setVisibility(View.VISIBLE); | ||||
|         view2.setVisibility(View.VISIBLE); | ||||
|     } | ||||
| 
 | ||||
|     public static void visible(View view1, View view2, View view3) { | ||||
|         view1.setVisibility(View.VISIBLE); | ||||
|         view2.setVisibility(View.VISIBLE); | ||||
|         view3.setVisibility(View.VISIBLE); | ||||
|     } | ||||
| 
 | ||||
|     public static void visible(View view1, View view2, View view3, View... views) { | ||||
|         view1.setVisibility(View.VISIBLE); | ||||
|         view2.setVisibility(View.VISIBLE); | ||||
|         view3.setVisibility(View.VISIBLE); | ||||
|         doAction(ACTION_VISIBLE, views); | ||||
|     } | ||||
| 
 | ||||
|     public static void invisible(View view1, View view2) { | ||||
|         view1.setVisibility(View.INVISIBLE); | ||||
|         view2.setVisibility(View.INVISIBLE); | ||||
|     } | ||||
| 
 | ||||
|     public static void invisible(View view1, View view2, View view3) { | ||||
|         view1.setVisibility(View.INVISIBLE); | ||||
|         view2.setVisibility(View.INVISIBLE); | ||||
|         view3.setVisibility(View.INVISIBLE); | ||||
|     } | ||||
| 
 | ||||
|     public static void invisible(View view1, View view2, View view3, View... views) { | ||||
|         view1.setVisibility(View.INVISIBLE); | ||||
|         view2.setVisibility(View.INVISIBLE); | ||||
|         view3.setVisibility(View.INVISIBLE); | ||||
|         doAction(ACTION_INVISIBLE, views); | ||||
|     } | ||||
| 
 | ||||
|     private static void doAction(int action, View... views) { | ||||
|         for (View view : views) { | ||||
|             if (action == ACTION_GONE) { | ||||
|                 view.setVisibility(View.GONE); | ||||
|             } else if (action == ACTION_INVISIBLE) { | ||||
|                 view.setVisibility(View.INVISIBLE); | ||||
|             } else if (action == ACTION_VISIBLE) { | ||||
|                 view.setVisibility(View.VISIBLE); | ||||
|             } else if (action == ACTION_ENABLE) { | ||||
|                 view.setEnabled(true); | ||||
|             } else if (action == ACTION_DISABLE) { | ||||
|                 view.setEnabled(false); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void setBackgroundDrawable(View view, Drawable drawable) { | ||||
|         if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { | ||||
|             view.setBackground(drawable); | ||||
|         } else { | ||||
|             view.setBackgroundDrawable(drawable); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static <V extends View> V find(View view, @IdRes int viewId) { | ||||
|         @SuppressWarnings("unchecked") | ||||
|         V v = (V) view.findViewById(viewId); | ||||
|         return v; | ||||
|     } | ||||
| 
 | ||||
|     public static Bitmap captureBitmapFromWebView(WebView webView) { | ||||
|         Picture snapShot = webView.capturePicture(); | ||||
|         Bitmap bmp = Bitmap.createBitmap(snapShot.getWidth(), snapShot.getHeight(), Bitmap.Config.ARGB_8888); | ||||
|         Canvas canvas = new Canvas(bmp); | ||||
|         snapShot.draw(canvas); | ||||
|         return bmp; | ||||
|     } | ||||
| 
 | ||||
|     public static void clearTextDrawable(TextView textView) { | ||||
|         textView.setCompoundDrawables(null, null, null, null); | ||||
|     } | ||||
| 
 | ||||
|     @Nullable | ||||
|     public static FragmentActivity getRealContext(View view) { | ||||
|         Context context = view.getContext(); | ||||
|         while (context instanceof android.content.ContextWrapper) { | ||||
|             if (context instanceof FragmentActivity) { | ||||
|                 return (FragmentActivity) context; | ||||
|             } | ||||
|             context = ((android.content.ContextWrapper) context).getBaseContext(); | ||||
|         } | ||||
|         return null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,73 +0,0 @@ | ||||
| package com.android.base.utils.android.anim; | ||||
| 
 | ||||
| import android.app.Activity; | ||||
| import android.app.ActivityOptions; | ||||
| import android.content.Intent; | ||||
| import android.transition.Explode; | ||||
| import android.transition.Fade; | ||||
| import android.transition.Slide; | ||||
| import android.view.View; | ||||
| import android.view.Window; | ||||
| 
 | ||||
| import com.android.base.utils.android.compat.AndroidVersion; | ||||
| 
 | ||||
| import androidx.annotation.AnimRes; | ||||
| import androidx.appcompat.app.AppCompatActivity; | ||||
| import androidx.core.app.ActivityCompat; | ||||
| import androidx.core.app.ActivityOptionsCompat; | ||||
| 
 | ||||
| public class ActivityAnimUtils { | ||||
| 
 | ||||
|     private ActivityAnimUtils() { | ||||
|         throw new UnsupportedOperationException("no need instantiation"); | ||||
|     } | ||||
| 
 | ||||
|     public static void startActivity(Activity activity, Intent intent, View share) { | ||||
|         if (AndroidVersion.atLeast(21)) { | ||||
|             ActivityOptions options = ActivityOptions.makeSceneTransitionAnimation(activity, share, share.getTransitionName()); | ||||
|             activity.startActivity(intent, options.toBundle()); | ||||
|         } else {//小于5.0,使用makeScaleUpAnimation
 | ||||
|             ActivityOptionsCompat options = ActivityOptionsCompat.makeScaleUpAnimation(share, share.getWidth() / 2, share.getHeight() / 2, 0, 0); | ||||
|             ActivityCompat.startActivity(activity, intent, options.toBundle()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
|     // Enter And Exit Transition
 | ||||
|     ///////////////////////////////////////////////////////////////////////////
 | ||||
| 
 | ||||
|     public static final int TYPE_EXPLODE = 1; | ||||
|     public static final int TYPE_SLIDE = 2; | ||||
|     public static final int TYPE_FADE = 3; | ||||
| 
 | ||||
|     /** | ||||
|      * @param activity context | ||||
|      * @param type     {@link #TYPE_EXPLODE},{@link #TYPE_SLIDE},{@link #TYPE_FADE} | ||||
|      */ | ||||
|     private static void setTransition(Activity activity, int type) { | ||||
|         if (AndroidVersion.atLeast(21)) { | ||||
|             Window window = activity.getWindow(); | ||||
|             window.requestFeature(Window.FEATURE_CONTENT_TRANSITIONS); | ||||
|             switch (type) { | ||||
|                 case TYPE_EXPLODE: | ||||
|                     window.setEnterTransition(new Explode()); | ||||
|                     window.setExitTransition(new Explode()); | ||||
|                     break; | ||||
|                 case TYPE_SLIDE: | ||||
|                     window.setEnterTransition(new Slide()); | ||||
|                     window.setExitTransition(new Slide()); | ||||
|                     break; | ||||
|                 case TYPE_FADE: | ||||
|                     window.setEnterTransition(new Fade()); | ||||
|                     window.setExitTransition(new Fade()); | ||||
|                     break; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void finishWithAnimation(AppCompatActivity activity, @AnimRes int enterAnim, @AnimRes int exitAnim) { | ||||
|         activity.supportFinishAfterTransition(); | ||||
|         activity.overridePendingTransition(enterAnim, exitAnim); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,43 +0,0 @@ | ||||
| package com.android.base.utils.android.text; | ||||
| 
 | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.Paint; | ||||
| import android.graphics.RectF; | ||||
| import android.text.style.ReplacementSpan; | ||||
| 
 | ||||
| import androidx.annotation.NonNull; | ||||
| 
 | ||||
| /** | ||||
|  * 圆角背景Span | ||||
|  */ | ||||
| public class RoundedBackgroundSpan extends ReplacementSpan { | ||||
| 
 | ||||
|     private int mPadding; | ||||
|     private int mBackgroundColor; | ||||
|     private int mTextColor; | ||||
|     private int mCorner; | ||||
| 
 | ||||
|     public RoundedBackgroundSpan(int backgroundColor, int textColor, int padding, int corner) { | ||||
|         super(); | ||||
|         mBackgroundColor = backgroundColor; | ||||
|         mTextColor = textColor; | ||||
|         mCorner = corner; | ||||
|         mPadding = padding; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public int getSize(@NonNull Paint paint, CharSequence text, int start, int end, Paint.FontMetricsInt fm) { | ||||
|         return (int) (mPadding + paint.measureText(text.subSequence(start, end).toString()) + mPadding); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void draw(@NonNull Canvas canvas, CharSequence text, int start, int end, float x, int top, int y, int bottom, @NonNull Paint paint) { | ||||
|         float width = paint.measureText(text.subSequence(start, end).toString()); | ||||
|         RectF rect = new RectF(x, top, x + width + (2 * mPadding), bottom); | ||||
|         paint.setColor(mBackgroundColor); | ||||
|         canvas.drawRoundRect(rect, mCorner, mCorner, paint); | ||||
|         paint.setColor(mTextColor); | ||||
|         canvas.drawText(text, start, end, x + mPadding, y, paint); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,39 @@ | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.app.Activity | ||||
| import android.app.ActivityOptions | ||||
| import android.content.Intent | ||||
| import android.transition.Transition | ||||
| import android.view.View | ||||
| import android.view.Window | ||||
| import androidx.annotation.AnimRes | ||||
| import androidx.appcompat.app.AppCompatActivity | ||||
| import androidx.core.app.ActivityCompat | ||||
| import androidx.core.app.ActivityOptionsCompat | ||||
| import com.android.base.utils.android.compat.AndroidVersion | ||||
| 
 | ||||
| 
 | ||||
| fun Activity.startActivityWithTransition(intent: Intent, share: View) { | ||||
|     if (AndroidVersion.atLeast(21)) { | ||||
|         val options = ActivityOptions.makeSceneTransitionAnimation(this, share, share.transitionName) | ||||
|         startActivity(intent, options.toBundle()) | ||||
|     } else {//小于5.0,使用makeScaleUpAnimation | ||||
|         val options = ActivityOptionsCompat.makeScaleUpAnimation(share, share.width / 2, share.height / 2, 0, 0) | ||||
|         ActivityCompat.startActivity(this, intent, options.toBundle()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun Activity.setTransition(type: Transition) { | ||||
|     if (AndroidVersion.atLeast(21)) { | ||||
|         with(window) { | ||||
|             requestFeature(Window.FEATURE_CONTENT_TRANSITIONS) | ||||
|             enterTransition = type | ||||
|             exitTransition = type | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun AppCompatActivity.finishWithAnimation(@AnimRes enterAnim: Int, @AnimRes exitAnim: Int) { | ||||
|     supportFinishAfterTransition() | ||||
|     overridePendingTransition(enterAnim, exitAnim) | ||||
| } | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.android.base.kotlin | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.app.Dialog | ||||
| 
 | ||||
| @ -1,4 +1,4 @@ | ||||
| package com.android.base.kotlin | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.view.Menu | ||||
| import android.view.MenuItem | ||||
| @ -0,0 +1,121 @@ | ||||
| @file:JvmName("Resources") | ||||
| 
 | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.content.res.TypedArray | ||||
| import android.graphics.drawable.Drawable | ||||
| import android.net.Uri | ||||
| import android.view.View | ||||
| import androidx.annotation.* | ||||
| 
 | ||||
| import androidx.core.content.ContextCompat | ||||
| import androidx.fragment.app.Fragment | ||||
| import com.android.base.utils.BaseUtils | ||||
| 
 | ||||
| inline fun <T : TypedArray?, R> T.use(block: (T) -> R): R { | ||||
|     var recycled = false | ||||
|     try { | ||||
|         return block(this) | ||||
|     } catch (e: Exception) { | ||||
|         recycled = true | ||||
|         try { | ||||
|             this?.recycle() | ||||
|         } catch (exception: Exception) { | ||||
|         } | ||||
|         throw e | ||||
|     } finally { | ||||
|         if (!recycled) { | ||||
|             this?.recycle() | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun Fragment.colorFromId(@ColorRes colorRes: Int): Int { | ||||
|     val safeContext = context ?: return 0 | ||||
|     return ContextCompat.getColor(safeContext, colorRes) | ||||
| } | ||||
| 
 | ||||
| fun Fragment.drawableFromId(@DrawableRes colorRes: Int): Drawable? { | ||||
|     val safeContext = context ?: return null | ||||
|     return ContextCompat.getDrawable(safeContext, colorRes) | ||||
| } | ||||
| 
 | ||||
| fun View.colorFromId(@ColorRes colorRes: Int): Int { | ||||
|     val safeContext = context ?: return 0 | ||||
|     return ContextCompat.getColor(safeContext, colorRes) | ||||
| } | ||||
| 
 | ||||
| fun View.drawableFromId(@DrawableRes colorRes: Int): Drawable? { | ||||
|     val safeContext = context ?: return null | ||||
|     return ContextCompat.getDrawable(safeContext, colorRes) | ||||
| } | ||||
| 
 | ||||
| fun Context.colorFromId(@ColorRes id: Int): Int { | ||||
|     return ContextCompat.getColor(this, id) | ||||
| } | ||||
| 
 | ||||
| fun Context.drawableFromId(@DrawableRes id: Int): Drawable? { | ||||
|     return ContextCompat.getDrawable(this, id) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * - [name] 资源的名称,如 ic_launcher 或者 com.example.android/drawable/ic_launcher(这是,下面两个参数可以省略) | ||||
|  * - [defType] 资源的类型,如 drawable | ||||
|  * - [defPackage] 包名 | ||||
|  * | ||||
|  * 返回资源 id | ||||
|  */ | ||||
| fun getResource(name: String, defType: String, defPackage: String): Int { | ||||
|     return BaseUtils.getResources().getIdentifier(name, defType, defPackage) | ||||
| } | ||||
| 
 | ||||
| fun getText(@StringRes id: Int): CharSequence { | ||||
|     return BaseUtils.getResources().getText(id) | ||||
| } | ||||
| 
 | ||||
| fun getString(@StringRes id: Int): String { | ||||
|     return BaseUtils.getResources().getString(id) | ||||
| } | ||||
| 
 | ||||
| fun getString(@StringRes id: Int, vararg formatArgs: Any): String { | ||||
|     return BaseUtils.getResources().getString(id, *formatArgs) | ||||
| } | ||||
| 
 | ||||
| fun getStringArray(@ArrayRes id: Int): Array<String> { | ||||
|     return BaseUtils.getResources().getStringArray(id) | ||||
| } | ||||
| 
 | ||||
| fun getIntArray(@ArrayRes id: Int): IntArray { | ||||
|     return BaseUtils.getResources().getIntArray(id) | ||||
| } | ||||
| 
 | ||||
| fun createUriByResource(id: Int): Uri { | ||||
|     return Uri.parse("android.resource://" + BaseUtils.getAppContext().packageName + "/" + id) | ||||
| } | ||||
| 
 | ||||
| fun createUriByAssets(path: String): Uri { | ||||
|     return Uri.parse("file:///android_asset/$path") | ||||
| } | ||||
| 
 | ||||
| fun getStyledColor(context: Context, @AttrRes attr: Int): Int { | ||||
|     val a = context.obtainStyledAttributes(null, intArrayOf(attr)) | ||||
|     try { | ||||
|         return a.getColor(0, 0x000000) | ||||
|     } finally { | ||||
|         a.recycle() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun getStyledDrawable(context: Context, @AttrRes attr: Int): Drawable? { | ||||
|     val a = context.obtainStyledAttributes(null, intArrayOf(attr)) | ||||
|     try { | ||||
|         return a.getDrawable(0) | ||||
|     } finally { | ||||
|         a.recycle() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun getDimensionPixelSize(dimenId: Int): Int { | ||||
|     return BaseUtils.getResources().getDimensionPixelSize(dimenId) | ||||
| } | ||||
| @ -0,0 +1,26 @@ | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.graphics.Canvas | ||||
| import android.graphics.Paint | ||||
| import android.graphics.RectF | ||||
| import android.text.style.ReplacementSpan | ||||
| 
 | ||||
| /** | ||||
|  * 圆角背景Span | ||||
|  */ | ||||
| class RoundedBackgroundSpan(private val backgroundColor: Int, private val textColor: Int, private val padding: Int, private val corner: Int) : ReplacementSpan() { | ||||
| 
 | ||||
|     override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fm: Paint.FontMetricsInt?): Int { | ||||
|         return (padding.toFloat() + paint.measureText(text.subSequence(start, end).toString()) + padding.toFloat()).toInt() | ||||
|     } | ||||
| 
 | ||||
|     override fun draw(canvas: Canvas, text: CharSequence, start: Int, end: Int, x: Float, top: Int, y: Int, bottom: Int, paint: Paint) { | ||||
|         val width = paint.measureText(text.subSequence(start, end).toString()) | ||||
|         val rect = RectF(x, top.toFloat(), x + width + (2 * padding).toFloat(), bottom.toFloat()) | ||||
|         paint.color = backgroundColor | ||||
|         canvas.drawRoundRect(rect, corner.toFloat(), corner.toFloat(), paint) | ||||
|         paint.color = textColor | ||||
|         canvas.drawText(text, start, end, x + padding, y.toFloat(), paint) | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,22 @@ | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.view.View | ||||
| import android.widget.ScrollView | ||||
| import androidx.core.widget.NestedScrollView | ||||
| 
 | ||||
| 
 | ||||
| fun ScrollView.scrollToBottom() { | ||||
|     fullScroll(View.FOCUS_DOWN) | ||||
| } | ||||
| 
 | ||||
| fun ScrollView.scrollToTop() { | ||||
|     fullScroll(View.FOCUS_UP) | ||||
| } | ||||
| 
 | ||||
| fun NestedScrollView.scrollToBottom() { | ||||
|     fullScroll(View.FOCUS_DOWN) | ||||
| } | ||||
| 
 | ||||
| fun NestedScrollView.scrollToTop() { | ||||
|     fullScroll(View.FOCUS_UP) | ||||
| } | ||||
| @ -0,0 +1,61 @@ | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.util.TypedValue | ||||
| import com.android.base.utils.BaseUtils | ||||
| 
 | ||||
| fun dip(value: Int): Int = dpToPx(value) | ||||
| fun dip(value: Float): Float = dpToPx(value) | ||||
| fun sp(value: Int): Int = spToPx(value) | ||||
| fun sp(value: Float): Float = spToPx(value) | ||||
| 
 | ||||
| fun dpToPx(dp: Float): Float { | ||||
|     return dp * BaseUtils.getDisplayMetrics().density | ||||
| } | ||||
| 
 | ||||
| fun dpToPx(dp: Int): Int { | ||||
|     return (dp * BaseUtils.getDisplayMetrics().density + 0.5f).toInt() | ||||
| } | ||||
| 
 | ||||
| fun pxToDp(px: Float): Float { | ||||
|     return px / BaseUtils.getDisplayMetrics().density | ||||
| } | ||||
| 
 | ||||
| fun pxToDp(px: Int): Int { | ||||
|     return (px / BaseUtils.getDisplayMetrics().density + 0.5f).toInt() | ||||
| } | ||||
| 
 | ||||
| fun spToPx(sp: Float): Float { | ||||
|     return sp * BaseUtils.getDisplayMetrics().scaledDensity | ||||
| } | ||||
| 
 | ||||
| fun spToPx(sp: Int): Int { | ||||
|     return (sp * BaseUtils.getDisplayMetrics().scaledDensity + 0.5f).toInt() | ||||
| } | ||||
| 
 | ||||
| fun pxToSp(px: Float): Float { | ||||
|     return px / BaseUtils.getDisplayMetrics().scaledDensity | ||||
| } | ||||
| 
 | ||||
| fun pxToSp(px: Int): Int { | ||||
|     return (px / BaseUtils.getDisplayMetrics().scaledDensity + 0.5f).toInt() | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 各种单位转换,该方法存在于[TypedValue] 中 | ||||
|  * | ||||
|  * @param unit  单位 | ||||
|  * @param value 值 | ||||
|  * @return 转换结果 | ||||
|  */ | ||||
| fun applyDimension(unit: Int, value: Float): Float { | ||||
|     val metrics = BaseUtils.getDisplayMetrics() | ||||
|     when (unit) { | ||||
|         TypedValue.COMPLEX_UNIT_PX -> return value | ||||
|         TypedValue.COMPLEX_UNIT_DIP -> return value * metrics.density | ||||
|         TypedValue.COMPLEX_UNIT_SP -> return value * metrics.scaledDensity | ||||
|         TypedValue.COMPLEX_UNIT_PT -> return value * metrics.xdpi * (1.0F / 72) | ||||
|         TypedValue.COMPLEX_UNIT_IN -> return value * metrics.xdpi | ||||
|         TypedValue.COMPLEX_UNIT_MM -> return value * metrics.xdpi * (1.0F / 25.4F) | ||||
|     } | ||||
|     return 0f | ||||
| } | ||||
| @ -0,0 +1,54 @@ | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.content.res.ColorStateList | ||||
| import android.graphics.* | ||||
| import android.graphics.drawable.Drawable | ||||
| import androidx.core.graphics.drawable.DrawableCompat | ||||
| 
 | ||||
| 
 | ||||
| fun tintBitmap(inBitmap: Bitmap, tintColor: Int): Bitmap { | ||||
|     val outBitmap = Bitmap.createBitmap(inBitmap.width, inBitmap.height, inBitmap.config) | ||||
|     val canvas = Canvas(outBitmap) | ||||
|     val paint = Paint(Paint.ANTI_ALIAS_FLAG) | ||||
|     paint.colorFilter = PorterDuffColorFilter(tintColor, PorterDuff.Mode.SRC_IN) | ||||
|     canvas.drawBitmap(inBitmap, 0f, 0f, paint) | ||||
|     return outBitmap | ||||
| } | ||||
| 
 | ||||
| fun tint(originDrawable: Drawable, color: Int): Drawable { | ||||
|     return tint(originDrawable, ColorStateList.valueOf(color)) | ||||
| } | ||||
| 
 | ||||
| fun tint(originDrawable: Drawable, color: Int, tintMode: PorterDuff.Mode? = null): Drawable { | ||||
|     return tint(originDrawable, ColorStateList.valueOf(color), tintMode) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * usage: | ||||
|  * ``` | ||||
|  *  int[][] states = new int[][]{ | ||||
|  *      new int[]{android.R.attr.state_selected}, // pressed | ||||
|  *      new int[]{-android.R.attr.state_selected}  // unpressed | ||||
|  *  }; | ||||
|  *  int[] colors = new int[]{ | ||||
|  *      Color.BLACK, | ||||
|  *      Color.GRAY | ||||
|  *  }; | ||||
|  *  ColorStateList colorStateList = new ColorStateList(states, colors); | ||||
|  *  Drawable aaa = ContextCompat.getDrawable(getContext(), R.drawable.aaa); | ||||
|  *  Drawable bbb = ContextCompat.getDrawable(getContext(), R.drawable.bbb); | ||||
|  *  Drawable aaaTint = TintUtils.tint(aaa, colorStateList); | ||||
|  *  Drawable bbbTint = TintUtils.tint(bbb, colorStateList); | ||||
|  *  aaaTv.setCompoundDrawablesWithIntrinsicBounds(null, aaaTint, null, null); | ||||
|  *  bbbTv.setCompoundDrawablesWithIntrinsicBounds(null, bbbTint, null, null); | ||||
|  * ``` | ||||
|  */ | ||||
| @JvmOverloads | ||||
| fun tint(originDrawable: Drawable, colorStateList: ColorStateList, tintMode: PorterDuff.Mode? = null): Drawable { | ||||
|     val tintDrawable = DrawableCompat.wrap(originDrawable) | ||||
|     if (tintMode != null) { | ||||
|         DrawableCompat.setTintMode(tintDrawable, tintMode) | ||||
|     } | ||||
|     DrawableCompat.setTintList(tintDrawable, colorStateList) | ||||
|     return tintDrawable | ||||
| } | ||||
| @ -0,0 +1,397 @@ | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.annotation.SuppressLint | ||||
| import android.graphics.drawable.Drawable | ||||
| import android.os.Build | ||||
| import android.view.MotionEvent | ||||
| import android.view.View | ||||
| import android.view.ViewGroup | ||||
| import android.view.ViewTreeObserver | ||||
| import androidx.annotation.IdRes | ||||
| import androidx.core.view.ViewCompat | ||||
| import androidx.fragment.app.FragmentActivity | ||||
| import com.android.base.rx.subscribeIgnoreError | ||||
| import com.android.base.utils.android.WindowUtils | ||||
| import com.android.base.utils.android.compat.AndroidVersion.atLeast | ||||
| import com.android.base.utils.common.otherwise | ||||
| import com.android.base.utils.common.yes | ||||
| import com.jakewharton.rxbinding3.view.clicks | ||||
| import io.reactivex.Observable | ||||
| import io.reactivex.android.schedulers.AndroidSchedulers | ||||
| import java.util.concurrent.TimeUnit | ||||
| 
 | ||||
| fun View.visibleOrGone(visible: Boolean) { | ||||
|     if (visible) { | ||||
|         this.visibility = View.VISIBLE | ||||
|     } else { | ||||
|         this.visibility = View.GONE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.visibleOrInvisible(visible: Boolean) { | ||||
|     if (visible) { | ||||
|         this.visibility = View.VISIBLE | ||||
|     } else { | ||||
|         this.visibility = View.INVISIBLE | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.visible() { | ||||
|     this.visibility = View.VISIBLE | ||||
| } | ||||
| 
 | ||||
| fun View.invisible() { | ||||
|     this.visibility = View.INVISIBLE | ||||
| } | ||||
| 
 | ||||
| fun View.gone() { | ||||
|     this.visibility = View.GONE | ||||
| } | ||||
| 
 | ||||
| fun View.isVisible() = this.visibility == View.VISIBLE | ||||
| fun View.isInvisible() = this.visibility == View.INVISIBLE | ||||
| fun View.isGone() = this.visibility == View.GONE | ||||
| 
 | ||||
| val View.realContext: FragmentActivity? | ||||
|     get() { | ||||
|         var context = context | ||||
|         while (context is android.content.ContextWrapper) { | ||||
|             if (context is FragmentActivity) { | ||||
|                 return context | ||||
|             } | ||||
|             context = context.baseContext | ||||
|         } | ||||
|         return null | ||||
|     } | ||||
| 
 | ||||
| fun View.setClickFeedback(pressAlpha: Float = 0.5F) { | ||||
|     this.setOnTouchListener { v, event -> | ||||
|         when (event.action) { | ||||
|             MotionEvent.ACTION_DOWN -> { | ||||
|                 v.alpha = pressAlpha | ||||
|             } | ||||
|             MotionEvent.ACTION_UP -> { | ||||
|                 v.alpha = 1F | ||||
|             } | ||||
|             MotionEvent.ACTION_CANCEL -> { | ||||
|                 v.alpha = 1F | ||||
|             } | ||||
|         } | ||||
|         false | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline fun View.doOnLayoutAvailable(crossinline block: () -> Unit) { | ||||
|     //isLaidOut 方法作用:如果 view 已经通过至少一个布局,则返回true,因为它最后一次附加到窗口或从窗口分离。 | ||||
|     ViewCompat.isLaidOut(this).yes { | ||||
|         block() | ||||
|     }.otherwise { | ||||
|         addOnLayoutChangeListener(object : View.OnLayoutChangeListener { | ||||
|             override fun onLayoutChange(v: View?, left: Int, top: Int, right: Int, bottom: Int, oldLeft: Int, oldTop: Int, oldRight: Int, oldBottom: Int) { | ||||
|                 removeOnLayoutChangeListener(this) | ||||
|                 block() | ||||
|             } | ||||
|         }) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline fun <T : View> T.onGlobalLayoutOnce(crossinline action: T.() -> Unit) { | ||||
|     val t: T = this | ||||
|     t.viewTreeObserver | ||||
|             .addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { | ||||
|                 override fun onGlobalLayout() { | ||||
|                     action.invoke(t) | ||||
|                     if (atLeast(16)) { | ||||
|                         viewTreeObserver.removeOnGlobalLayoutListener(this) | ||||
|                     } else { | ||||
|                         @Suppress("DEPRECATION") | ||||
|                         viewTreeObserver.removeGlobalOnLayoutListener(this) | ||||
|                     } | ||||
|                 } | ||||
|             }) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingAll(padding: Int) { | ||||
|     this.setPadding(padding, padding, padding, padding) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingLeft(padding: Int) { | ||||
|     this.setPadding(padding, paddingTop, paddingRight, paddingBottom) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddRight(padding: Int) { | ||||
|     this.setPadding(paddingLeft, paddingTop, padding, paddingBottom) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingTop(padding: Int) { | ||||
|     this.setPadding(paddingLeft, padding, paddingRight, paddingBottom) | ||||
| } | ||||
| 
 | ||||
| fun View.setPaddingBottom(padding: Int) { | ||||
|     this.setPadding(paddingLeft, paddingTop, paddingRight, padding) | ||||
| } | ||||
| 
 | ||||
| fun newLayoutParams(width: Int = ViewGroup.MarginLayoutParams.WRAP_CONTENT, height: Int = ViewGroup.MarginLayoutParams.WRAP_CONTENT): ViewGroup.LayoutParams { | ||||
|     return ViewGroup.LayoutParams(width, height) | ||||
| } | ||||
| 
 | ||||
| fun newMarginLayoutParams(width: Int = ViewGroup.MarginLayoutParams.WRAP_CONTENT, height: Int = ViewGroup.MarginLayoutParams.WRAP_CONTENT): ViewGroup.MarginLayoutParams { | ||||
|     return ViewGroup.MarginLayoutParams(width, height) | ||||
| } | ||||
| 
 | ||||
| fun View.setTopMargin(topMargin: Int) { | ||||
|     val params: ViewGroup.LayoutParams? = layoutParams | ||||
|     if (params is ViewGroup.MarginLayoutParams) { | ||||
|         params.topMargin = topMargin | ||||
|     } else { | ||||
|         layoutParams = newMarginLayoutParams().apply { | ||||
|             this.topMargin = topMargin | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.setBottomMargin(bottomMargin: Int) { | ||||
|     val params: ViewGroup.LayoutParams? = layoutParams | ||||
|     if (params is ViewGroup.MarginLayoutParams) { | ||||
|         params.bottomMargin = bottomMargin | ||||
|     } else { | ||||
|         layoutParams = newMarginLayoutParams().apply { | ||||
|             this.bottomMargin = bottomMargin | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.setLeftMargin(leftMargin: Int) { | ||||
|     val params: ViewGroup.LayoutParams? = layoutParams | ||||
|     if (params is ViewGroup.MarginLayoutParams) { | ||||
|         params.leftMargin = leftMargin | ||||
|     } else { | ||||
|         layoutParams = newMarginLayoutParams().apply { | ||||
|             this.leftMargin = leftMargin | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.setRightMargin(rightMargin: Int) { | ||||
|     val params: ViewGroup.LayoutParams? = layoutParams | ||||
|     if (params is ViewGroup.MarginLayoutParams) { | ||||
|         params.rightMargin = rightMargin | ||||
|     } else { | ||||
|         layoutParams = newMarginLayoutParams().apply { | ||||
|             this.rightMargin = rightMargin | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.setRightMargins(leftMargin: Int, topMargin: Int, rightMargin: Int, bottomMargin: Int) { | ||||
|     val params: ViewGroup.LayoutParams? = layoutParams | ||||
|     if (params is ViewGroup.MarginLayoutParams) { | ||||
|         params.rightMargin = rightMargin | ||||
|         params.leftMargin = leftMargin | ||||
|         params.bottomMargin = bottomMargin | ||||
|         params.topMargin = topMargin | ||||
|     } else { | ||||
|         layoutParams = newMarginLayoutParams().apply { | ||||
|             this.rightMargin = rightMargin | ||||
|             this.leftMargin = leftMargin | ||||
|             this.bottomMargin = bottomMargin | ||||
|             this.topMargin = topMargin | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun View.setWidth(width: Int) { | ||||
|     val params = layoutParams ?: ViewGroup.LayoutParams(0, 0) | ||||
|     params.width = width | ||||
|     layoutParams = params | ||||
| } | ||||
| 
 | ||||
| fun View.setHeight(height: Int) { | ||||
|     val params = layoutParams ?: ViewGroup.LayoutParams(0, 0) | ||||
|     params.height = height | ||||
|     layoutParams = params | ||||
| } | ||||
| 
 | ||||
| fun View.setSize(width: Int, height: Int) { | ||||
|     val params = layoutParams ?: ViewGroup.LayoutParams(0, 0) | ||||
|     params.width = width | ||||
|     params.height = height | ||||
|     layoutParams = params | ||||
| } | ||||
| 
 | ||||
| fun View.onDebouncedClick(onClick: (View) -> Unit) { | ||||
|     onClickObservable(500) | ||||
|             .subscribeIgnoreError { onClick(this) } | ||||
| } | ||||
| 
 | ||||
| fun View.onDebouncedClick(milliseconds: Long, onClick: (View) -> Unit) { | ||||
|     onClickObservable(milliseconds) | ||||
|             .subscribeIgnoreError { onClick(this) } | ||||
| } | ||||
| 
 | ||||
| fun View.onClickObservable(): Observable<Unit> { | ||||
|     return onClickObservable(500) | ||||
| } | ||||
| 
 | ||||
| fun View.onClickObservable(milliseconds: Long): Observable<Unit> { | ||||
|     return clicks() | ||||
|             .throttleFirst(milliseconds, TimeUnit.MILLISECONDS) | ||||
|             .observeOn(AndroidSchedulers.mainThread()) | ||||
| } | ||||
| 
 | ||||
| inline val ViewGroup.views get() = (0 until childCount).map { getChildAt(it) } | ||||
| 
 | ||||
| fun View.measureSelf(): Boolean { | ||||
|     val layoutParams = layoutParams | ||||
|     if (layoutParams == null || layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT) { | ||||
|         return false | ||||
|     } | ||||
|     val size = 1 shl 30 - 1//即后30位 | ||||
|     val measureSpec = View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.AT_MOST) | ||||
|     measure(measureSpec, measureSpec) | ||||
|     return true | ||||
| } | ||||
| 
 | ||||
| fun View.measureSelfWithScreenSize(): Boolean { | ||||
|     val layoutParams = layoutParams | ||||
|     if (layoutParams == null || layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT) { | ||||
|         return false | ||||
|     } | ||||
|     measure( | ||||
|             View.MeasureSpec.makeMeasureSpec(WindowUtils.getScreenWidth(), View.MeasureSpec.AT_MOST), | ||||
|             View.MeasureSpec.makeMeasureSpec(WindowUtils.getScreenHeight(), View.MeasureSpec.AT_MOST)) | ||||
|     return true | ||||
| } | ||||
| 
 | ||||
| fun View.measureSelf(width: Int, height: Int): Boolean { | ||||
|     val layoutParams = layoutParams | ||||
|     if (layoutParams == null || layoutParams.width == ViewGroup.LayoutParams.MATCH_PARENT && layoutParams.height == ViewGroup.LayoutParams.MATCH_PARENT) { | ||||
|         return false | ||||
|     } | ||||
|     measure( | ||||
|             View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.AT_MOST), | ||||
|             View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.AT_MOST)) | ||||
|     return true | ||||
| } | ||||
| 
 | ||||
| @SuppressLint("ObsoleteSdkInt") | ||||
| fun View.setBgDrawable(drawable: Drawable) { | ||||
|     if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) { | ||||
|         background = drawable | ||||
|     } else { | ||||
|         setBackgroundDrawable(drawable) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @Suppress("UNCHECKED_CAST") | ||||
| fun <V : View> View.find(@IdRes viewId: Int): V { | ||||
|     return findViewById<View>(viewId) as V | ||||
| } | ||||
| 
 | ||||
| private const val ACTION_VISIBLE = 0x01 | ||||
| private const val ACTION_GONE = 0x02 | ||||
| private const val ACTION_INVISIBLE = 0x03 | ||||
| private const val ACTION_DISABLE = 0x04 | ||||
| private const val ACTION_ENABLE = 0x05 | ||||
| 
 | ||||
| fun disable(view1: View, view2: View) { | ||||
|     view1.isEnabled = false | ||||
|     view2.isEnabled = false | ||||
| } | ||||
| 
 | ||||
| fun disable(view1: View, view2: View, view3: View) { | ||||
|     view1.isEnabled = false | ||||
|     view2.isEnabled = false | ||||
|     view3.isEnabled = false | ||||
| } | ||||
| 
 | ||||
| fun disable(view1: View, view2: View, view3: View, vararg views: View) { | ||||
|     view1.isEnabled = false | ||||
|     view2.isEnabled = false | ||||
|     view3.isEnabled = false | ||||
|     doAction(ACTION_DISABLE, *views) | ||||
| } | ||||
| 
 | ||||
| fun enable(view1: View, view2: View) { | ||||
|     view1.isEnabled = true | ||||
|     view2.isEnabled = true | ||||
| } | ||||
| 
 | ||||
| fun enable(view1: View, view2: View, view3: View) { | ||||
|     view1.isEnabled = true | ||||
|     view2.isEnabled = true | ||||
|     view3.isEnabled = true | ||||
| } | ||||
| 
 | ||||
| fun enable(view1: View, view2: View, view3: View, vararg views: View) { | ||||
|     view1.isEnabled = true | ||||
|     view2.isEnabled = true | ||||
|     view3.isEnabled = true | ||||
|     doAction(ACTION_ENABLE, *views) | ||||
| } | ||||
| 
 | ||||
| fun gone(view1: View, view2: View) { | ||||
|     view1.visibility = View.GONE | ||||
|     view2.visibility = View.GONE | ||||
| } | ||||
| 
 | ||||
| fun gone(view1: View, view2: View, view3: View) { | ||||
|     view1.visibility = View.GONE | ||||
|     view2.visibility = View.GONE | ||||
|     view3.visibility = View.GONE | ||||
| } | ||||
| 
 | ||||
| fun gone(view1: View, view2: View, view3: View, vararg views: View) { | ||||
|     view1.visibility = View.GONE | ||||
|     view2.visibility = View.GONE | ||||
|     view3.visibility = View.GONE | ||||
|     doAction(ACTION_GONE, *views) | ||||
| } | ||||
| 
 | ||||
| fun visible(view1: View, view2: View) { | ||||
|     view1.visibility = View.VISIBLE | ||||
|     view2.visibility = View.VISIBLE | ||||
| } | ||||
| 
 | ||||
| fun visible(view1: View, view2: View, view3: View) { | ||||
|     view1.visibility = View.VISIBLE | ||||
|     view2.visibility = View.VISIBLE | ||||
|     view3.visibility = View.VISIBLE | ||||
| } | ||||
| 
 | ||||
| fun visible(view1: View, view2: View, view3: View, vararg views: View) { | ||||
|     view1.visibility = View.VISIBLE | ||||
|     view2.visibility = View.VISIBLE | ||||
|     view3.visibility = View.VISIBLE | ||||
|     doAction(ACTION_VISIBLE, *views) | ||||
| } | ||||
| 
 | ||||
| fun invisible(view1: View, view2: View) { | ||||
|     view1.visibility = View.INVISIBLE | ||||
|     view2.visibility = View.INVISIBLE | ||||
| } | ||||
| 
 | ||||
| fun invisible(view1: View, view2: View, view3: View) { | ||||
|     view1.visibility = View.INVISIBLE | ||||
|     view2.visibility = View.INVISIBLE | ||||
|     view3.visibility = View.INVISIBLE | ||||
| } | ||||
| 
 | ||||
| fun invisible(view1: View, view2: View, view3: View, vararg views: View) { | ||||
|     view1.visibility = View.INVISIBLE | ||||
|     view2.visibility = View.INVISIBLE | ||||
|     view3.visibility = View.INVISIBLE | ||||
|     doAction(ACTION_INVISIBLE, *views) | ||||
| } | ||||
| 
 | ||||
| private fun doAction(action: Int, vararg views: View) { | ||||
|     for (view in views) { | ||||
|         when (action) { | ||||
|             ACTION_GONE -> view.visibility = View.GONE | ||||
|             ACTION_INVISIBLE -> view.visibility = View.INVISIBLE | ||||
|             ACTION_VISIBLE -> view.visibility = View.VISIBLE | ||||
|             ACTION_ENABLE -> view.isEnabled = true | ||||
|             ACTION_DISABLE -> view.isEnabled = false | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,14 @@ | ||||
| package com.android.base.utils.android.views | ||||
| 
 | ||||
| import android.graphics.Bitmap | ||||
| import android.graphics.Canvas | ||||
| import android.webkit.WebView | ||||
| 
 | ||||
| 
 | ||||
| fun captureBitmapFromWebView(webView: WebView): Bitmap { | ||||
|     val snapShot = webView.capturePicture() | ||||
|     val bmp = Bitmap.createBitmap(snapShot.width, snapShot.height, Bitmap.Config.ARGB_8888) | ||||
|     val canvas = Canvas(bmp) | ||||
|     snapShot.draw(canvas) | ||||
|     return bmp | ||||
| } | ||||
| @ -1,59 +0,0 @@ | ||||
| package com.android.base.utils.common; | ||||
| 
 | ||||
| import java.util.Collection; | ||||
| import java.util.Map; | ||||
| 
 | ||||
| /** | ||||
|  * 对象检查工具 | ||||
|  */ | ||||
| public class Checker { | ||||
| 
 | ||||
|     private Checker() { | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isEmpty(Collection<?> data) { | ||||
|         return data == null || data.size() == 0; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean notEmpty(Collection<?> data) { | ||||
|         return !isEmpty(data); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isNull(Object o) { | ||||
|         return o == null; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isEmpty(Map<?, ?> map) { | ||||
|         return map == null || map.size() == 0; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean notEmpty(Map<?, ?> map) { | ||||
|         return !isEmpty(map); | ||||
|     } | ||||
| 
 | ||||
|     public static <T> boolean isEmpty(T[] t) { | ||||
|         return t == null || t.length == 0; | ||||
|     } | ||||
| 
 | ||||
|     public static <T> boolean notEmpty(T[] t) { | ||||
|         return !isEmpty(t); | ||||
|     } | ||||
| 
 | ||||
|     public static <T> T requireNonNull(T obj) { | ||||
|         if (obj == null) | ||||
|             throw new NullPointerException(); | ||||
|         return obj; | ||||
|     } | ||||
| 
 | ||||
|     public static <T> T requireNonNull(T obj, String message) { | ||||
|         if (obj == null) | ||||
|             throw new NullPointerException(message); | ||||
|         return obj; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean nonNull(Object obj) { | ||||
|         return obj != null; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,49 @@ | ||||
| @file:JvmName("Checker") | ||||
| 
 | ||||
| package com.android.base.utils.common | ||||
| 
 | ||||
| fun isEmpty(data: Collection<*>?): Boolean { | ||||
|     return data == null || data.isEmpty() | ||||
| } | ||||
| 
 | ||||
| fun notEmpty(data: Collection<*>?): Boolean { | ||||
|     return !isEmpty(data) | ||||
| } | ||||
| 
 | ||||
| fun isNull(o: Any?): Boolean { | ||||
|     return o == null | ||||
| } | ||||
| 
 | ||||
| fun isEmpty(map: Map<*, *>?): Boolean { | ||||
|     return map == null || map.isEmpty() | ||||
| } | ||||
| 
 | ||||
| fun notEmpty(map: Map<*, *>): Boolean { | ||||
|     return !isEmpty(map) | ||||
| } | ||||
| 
 | ||||
| fun <T> isEmpty(t: Array<T>?): Boolean { | ||||
|     return t == null || t.isEmpty() | ||||
| } | ||||
| 
 | ||||
| fun <T> notEmpty(t: Array<T>): Boolean { | ||||
|     return !isEmpty(t) | ||||
| } | ||||
| 
 | ||||
| fun <T> requireNonNull(obj: T?): T { | ||||
|     if (obj == null) { | ||||
|         throw NullPointerException("requireNonNull failed") | ||||
|     } | ||||
|     return obj | ||||
| } | ||||
| 
 | ||||
| fun <T> requireNonNull(obj: T?, message: String): T { | ||||
|     if (obj == null) { | ||||
|         throw NullPointerException("requireNonNull failed: %s".format(message)) | ||||
|     } | ||||
|     return obj | ||||
| } | ||||
| 
 | ||||
| fun nonNull(obj: Any?): Boolean { | ||||
|     return obj != null | ||||
| } | ||||
| @ -1,62 +0,0 @@ | ||||
| package com.android.base.utils.common; | ||||
| 
 | ||||
| import java.io.Closeable; | ||||
| import java.io.IOException; | ||||
| 
 | ||||
| 
 | ||||
| public class CloseUtils { | ||||
| 
 | ||||
|     private CloseUtils() { | ||||
|         throw new UnsupportedOperationException("u can't instantiate me..."); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 安静关闭IO | ||||
|      */ | ||||
|     public static void closeIO(Closeable closeable) { | ||||
|         if (closeable != null) { | ||||
|             try { | ||||
|                 closeable.close(); | ||||
|             } catch (IOException e) { | ||||
|                 e.printStackTrace(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 关闭IO | ||||
|      */ | ||||
|     public static void closeIOQuietly(Closeable closeable) { | ||||
|         if (closeable != null) { | ||||
|             try { | ||||
|                 closeable.close(); | ||||
|             } catch (IOException ignored) { | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 关闭IO | ||||
|      * | ||||
|      * @param closeables closeable | ||||
|      */ | ||||
|     public static void closeIO(Closeable... closeables) { | ||||
|         if (closeables == null) return; | ||||
|         for (Closeable closeable : closeables) { | ||||
|             closeIO(closeable); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 安静关闭IO | ||||
|      * | ||||
|      * @param closeables closeable | ||||
|      */ | ||||
|     public static void closeIOQuietly(Closeable... closeables) { | ||||
|         if (closeables == null) return; | ||||
|         for (Closeable closeable : closeables) { | ||||
|             closeIOQuietly(closeable); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,23 +0,0 @@ | ||||
| package com.android.base.utils.common; | ||||
| 
 | ||||
| import java.util.ArrayList; | ||||
| import java.util.List; | ||||
| 
 | ||||
| 
 | ||||
| public class CollectionUtils { | ||||
| 
 | ||||
|     private CollectionUtils() { | ||||
|         throw new UnsupportedOperationException(); | ||||
|     } | ||||
| 
 | ||||
|     public static <T> ArrayList<T> toArrayList(List<T> list) { | ||||
|         if (list == null) { | ||||
|             return new ArrayList<>(0); | ||||
|         } | ||||
|         if (list instanceof ArrayList) { | ||||
|             return (ArrayList<T>) list; | ||||
|         } | ||||
|         return new ArrayList<>(list); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -1,9 +1,15 @@ | ||||
| package com.android.base.kotlin | ||||
| package com.android.base.utils.common | ||||
| 
 | ||||
| import com.android.base.utils.common.CollectionUtils | ||||
| fun <E> List<E>?.toArrayList(copy: Boolean = false): ArrayList<E> { | ||||
| 
 | ||||
|     if (this == null) { | ||||
|         return ArrayList(0) | ||||
|     } | ||||
| 
 | ||||
|     return if (!copy && this is java.util.ArrayList<E>) { | ||||
|         this | ||||
|     } else ArrayList(this) | ||||
| 
 | ||||
| fun <E> List<E>?.toArrayList(): ArrayList<E> { | ||||
|     return CollectionUtils.toArrayList(this) | ||||
| } | ||||
| 
 | ||||
| fun <E> MutableList<E>.removeWhich(filter: (E) -> Boolean): Boolean { | ||||
| @ -1,90 +0,0 @@ | ||||
| package com.android.base.utils.common; | ||||
| 
 | ||||
| import android.util.Log; | ||||
| 
 | ||||
| import java.io.File; | ||||
| 
 | ||||
| /** | ||||
|  * @author Ztiany | ||||
|  * Email: 1169654504@qq.com | ||||
|  * Date : 2016-11-30 16:29 | ||||
|  */ | ||||
| public class FileUtils { | ||||
| 
 | ||||
|     private static final String TAG = FileUtils.class.getSimpleName(); | ||||
| 
 | ||||
|     private FileUtils() { | ||||
|         throw new UnsupportedOperationException("no need instantiation"); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean makeFilePath(File file) { | ||||
|         if (file == null) { | ||||
|             return false; | ||||
|         } | ||||
|         File parent = file.getParentFile(); | ||||
|         return parent.exists() || makeDir(parent); | ||||
|     } | ||||
| 
 | ||||
|     private static boolean makeDir(File file) { | ||||
|         return file != null && (file.exists() || file.mkdirs()); | ||||
|     } | ||||
| 
 | ||||
|     public static long sizeOfFile(File file) { | ||||
|         if (file == null || !file.exists()) { | ||||
|             return 0; | ||||
|         } | ||||
|         if (file.isDirectory()) { | ||||
|             long length = 0; | ||||
|             for (File subFile : file.listFiles()) { | ||||
|                 length += sizeOfFile(subFile); | ||||
|             } | ||||
|             return length; | ||||
|         } else { | ||||
|             return file.length(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static void deleteFile(File file) { | ||||
|         if (file.isDirectory()) { | ||||
|             for (File subFile : file.listFiles()) { | ||||
|                 deleteFile(subFile); | ||||
|             } | ||||
|         } else { | ||||
|             boolean delete = file.delete(); | ||||
|             Log.d(TAG, "delete:" + delete); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isFileExists(File file) { | ||||
|         return file != null && file.isFile() && file.exists(); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 获取全路径中的文件拓展名 | ||||
|      * | ||||
|      * @param file 文件 | ||||
|      * @return 文件拓展名 | ||||
|      */ | ||||
|     public static String getFileExtension(File file) { | ||||
|         if (file == null) return null; | ||||
|         return getFileExtension(file.getPath()); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 获取全路径中的文件拓展名 | ||||
|      * | ||||
|      * @param filePath 文件路径 | ||||
|      * @return 文件拓展名 | ||||
|      */ | ||||
|     public static String getFileExtension(String filePath) { | ||||
|         if (StringChecker.isEmpty(filePath)) { | ||||
|             return filePath; | ||||
|         } | ||||
|         int lastPoi = filePath.lastIndexOf('.'); | ||||
|         int lastSep = filePath.lastIndexOf(File.separator); | ||||
|         if (lastPoi == -1 || lastSep >= lastPoi) { | ||||
|             return ""; | ||||
|         } | ||||
|         return filePath.substring(lastPoi + 1); | ||||
|     } | ||||
| } | ||||
| @ -0,0 +1,68 @@ | ||||
| @file:JvmName("Files") | ||||
| 
 | ||||
| package com.android.base.utils.common | ||||
| 
 | ||||
| import com.blankj.utilcode.util.FileUtils | ||||
| import timber.log.Timber | ||||
| import java.io.File | ||||
| 
 | ||||
| fun File?.makeFilePath(): Boolean { | ||||
|     if (this == null) { | ||||
|         return false | ||||
|     } | ||||
|     val parent = this.parentFile | ||||
|     return parent.exists() || makeDir(parent) | ||||
| } | ||||
| 
 | ||||
| private fun makeDir(file: File?): Boolean { | ||||
|     return file != null && (file.exists() || file.mkdirs()) | ||||
| } | ||||
| 
 | ||||
| fun File?.sizeOf(): Long { | ||||
|     if (this == null || !this.exists()) { | ||||
|         return 0 | ||||
|     } | ||||
|     return if (this.isDirectory) { | ||||
|         var length: Long = 0 | ||||
|         for (subFile in this.listFiles()) { | ||||
|             length += subFile.sizeOf() | ||||
|         } | ||||
|         length | ||||
|     } else { | ||||
|         this.length() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun File.deleteSelf() { | ||||
|     if (this.isDirectory) { | ||||
|         for (subFile in this.listFiles()) { | ||||
|             subFile.deleteSelf() | ||||
|         } | ||||
|     } else { | ||||
|         val delete = this.delete() | ||||
|         Timber.d("deleteFile: $delete") | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| fun File?.isFileExists(): Boolean { | ||||
|     return this != null && this.isFile && this.exists() | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 获取全路径中的文件拓展名 | ||||
|  * | ||||
|  * @return 文件拓展名 | ||||
|  */ | ||||
| fun File?.getFileExtension(): String? { | ||||
|     return if (this == null) null else getFileExtension(this.path) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 获取全路径中的文件拓展名 | ||||
|  * | ||||
|  * @param filePath 文件路径 | ||||
|  * @return 文件拓展名 | ||||
|  */ | ||||
| fun getFileExtension(filePath: String): String { | ||||
|     return FileUtils.getFileExtension(filePath) | ||||
| } | ||||
| @ -0,0 +1,117 @@ | ||||
| @file:JvmName("Lang") | ||||
| 
 | ||||
| package com.android.base.utils.common | ||||
| 
 | ||||
| import java.io.Closeable | ||||
| import java.io.IOException | ||||
| 
 | ||||
| /** 参考: [Boolean扩展](https://blog.kotliner.cn/2017/06/19/interesting-booleanext/) */ | ||||
| sealed class Ext<out T> constructor(val boolean: Boolean) | ||||
| 
 | ||||
| /** 如果该对象不是null,则执行action */ | ||||
| fun <T, E> T?.ifNonNull(action: T.() -> E): Ext<E> { | ||||
|     if (this != null) { | ||||
|         return WithData(action()) | ||||
|     } | ||||
|     return Otherwise | ||||
| } | ||||
| 
 | ||||
| /** 如果该对象是null,则执行action */ | ||||
| fun <T, E> T?.ifNull(action: () -> E) { | ||||
|     if (this == null) { | ||||
|         action() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline fun <T> Boolean.yes(block: () -> T): Ext<T> = when { | ||||
|     this -> { | ||||
|         WithData(block()) | ||||
|     } | ||||
|     else -> Otherwise | ||||
| } | ||||
| 
 | ||||
| inline fun <T> Boolean.no(block: () -> T) = when { | ||||
|     this -> Otherwise | ||||
|     else -> { | ||||
|         WithData(block()) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| object Otherwise : Ext<Nothing>(true) | ||||
| 
 | ||||
| class WithData<out T>(val data: T) : Ext<T>(false) | ||||
| 
 | ||||
| /**除此以外*/ | ||||
| inline infix fun <T> Ext<T>.otherwise(block: () -> T): T { | ||||
|     return when (this) { | ||||
|         is Otherwise -> block() | ||||
|         is WithData<T> -> this.data | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline operator fun <T> Boolean.invoke(block: () -> T) = yes(block) | ||||
| 
 | ||||
| fun Any?.javaClassName(): String { | ||||
|     return if (this == null) { | ||||
|         "" | ||||
|     } else { | ||||
|         this::class.java.name | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| inline fun ignoreCrash(code: () -> Unit) { | ||||
|     try { | ||||
|         code() | ||||
|     } catch (e: Exception) { | ||||
|         e.printStackTrace() | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 安静关闭IO | ||||
|  */ | ||||
| fun closeIO(closeable: Closeable?) { | ||||
|     if (closeable != null) { | ||||
|         try { | ||||
|             closeable.close() | ||||
|         } catch (e: IOException) { | ||||
|             e.printStackTrace() | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 关闭IO | ||||
|  */ | ||||
| fun closeIOQuietly(closeable: Closeable?) { | ||||
|     if (closeable != null) { | ||||
|         try { | ||||
|             closeable.close() | ||||
|         } catch (ignored: IOException) { | ||||
|         } | ||||
| 
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 关闭IO | ||||
|  * | ||||
|  * @param closeables closeable | ||||
|  */ | ||||
| fun closeIO(vararg closeables: Closeable?) { | ||||
|     for (closeable in closeables) { | ||||
|         closeIO(closeable) | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 安静关闭IO | ||||
|  * | ||||
|  * @param closeables closeable | ||||
|  */ | ||||
| fun closeIOQuietly(vararg closeables: Closeable?) { | ||||
|     for (closeable in closeables) { | ||||
|         closeIOQuietly(closeable) | ||||
|     } | ||||
| } | ||||
| @ -1,126 +0,0 @@ | ||||
| package com.android.base.utils.common; | ||||
| 
 | ||||
| import java.util.regex.Pattern; | ||||
| 
 | ||||
| public class StringChecker { | ||||
| 
 | ||||
|     private StringChecker() { | ||||
|         throw new UnsupportedOperationException("no need instantiation"); | ||||
|     } | ||||
| 
 | ||||
|     private static final String CHINA_PHONE_REG = "^1\\d{10}$"; | ||||
|     private static final String ID_CARD_REG = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}"; | ||||
|     private static final String EMAIL_REG = "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*"; | ||||
|     private static final String DIGIT_REG = "\\-?[1-9]\\d+\""; | ||||
|     private static final String URL_REG = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?"; | ||||
|     private static final String CHINESE_REGEX = "^[\u4E00-\u9FA5]+$"; | ||||
|     private static final String DECIMALS_REG = "\\-?[1-9]\\d+(\\.\\d+)?"; | ||||
|     private static final String LETTERS_REG = ".*[a-zA-Z]++.*"; | ||||
|     private static final String DIGITAL_REG = ".*[0-9]++.*"; | ||||
|     private static final String DIGITAL_LETTER_ONLY_REG = "^[A-Za-z0-9]+$"; | ||||
| 
 | ||||
|     /** | ||||
|      * 验证中国的手机号 | ||||
|      * | ||||
|      * @return 验证成功返回 true,验证失败返回 false | ||||
|      */ | ||||
|     public static boolean isChinaPhoneNumber(String mobile) { | ||||
|         return !isEmpty(mobile) && Pattern.matches(CHINA_PHONE_REG, mobile); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean containsLetter(String text) { | ||||
|         return !isEmpty(text) && Pattern.matches(LETTERS_REG, text); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean containsDigital(String text) { | ||||
|         return !isEmpty(text) && Pattern.matches(DIGITAL_REG, text); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean containsDigtalLetterOnly(String text) { | ||||
|         return !isEmpty(text) && Pattern.matches(DIGITAL_LETTER_ONLY_REG, text); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 验证身份证号码 | ||||
|      * | ||||
|      * @param idCard 居民身份证号码15位或18位,最后一位可能是数字或字母 | ||||
|      * @return 验证成功返回true,验证失败返回false | ||||
|      */ | ||||
|     public static boolean isIdCard(String idCard) { | ||||
|         return !isEmpty(idCard) && Pattern.matches(ID_CARD_REG, idCard); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 验证Email | ||||
|      * | ||||
|      * @param email email地址,格式:zhangsan@sina.com,zhangsan@xxx.com.cn,xxx代表邮件服务商 | ||||
|      * @return 验证成功返回true,验证失败返回false | ||||
|      */ | ||||
|     public static boolean isEmail(String email) { | ||||
|         return !isEmpty(email) && Pattern.matches(EMAIL_REG, email); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 验证整数(正整数和负整数) | ||||
|      * | ||||
|      * @param digit 一位或多位0-9之间的整数 | ||||
|      * @return 验证成功返回true,验证失败返回false | ||||
|      */ | ||||
|     public static boolean isDigit(String digit) { | ||||
|         return !isEmpty(digit) && Pattern.matches(DIGIT_REG, digit); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 验证整数和浮点数(正负整数和正负浮点数) | ||||
|      * | ||||
|      * @param decimals 一位或多位0-9之间的浮点数,如:1.23,233.30 | ||||
|      * @return 验证成功返回true,验证失败返回false | ||||
|      */ | ||||
|     public static boolean isDecimals(String decimals) { | ||||
|         return !isEmpty(decimals) && Pattern.matches(DECIMALS_REG, decimals); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 验证中文 | ||||
|      * | ||||
|      * @param chinese 中文字符 | ||||
|      * @return 验证成功返回true,验证失败返回false | ||||
|      */ | ||||
|     public static boolean isChinese(String chinese) { | ||||
|         return !isEmpty(chinese) && Pattern.matches(CHINESE_REGEX, chinese); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 验证URL地址 | ||||
|      * | ||||
|      * @param url url | ||||
|      * @return 验证成功返回true,验证失败返回false | ||||
|      */ | ||||
|     public static boolean isURL(String url) { | ||||
|         return !isEmpty(url) && Pattern.matches(URL_REG, url); | ||||
|     } | ||||
| 
 | ||||
|     /** | ||||
|      * 获取字符串的字符个数 | ||||
|      */ | ||||
|     public static int getCharLength(String string) { | ||||
|         if (isEmpty(string)) { | ||||
|             return 0; | ||||
|         } | ||||
|         return string.trim().toCharArray().length; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isCharLength(String string, int length) { | ||||
|         return null != string && ((isEmpty(string) && length == 0) || (string.trim().toCharArray().length == length)); | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isLengthIn(String string, int min, int max) { | ||||
|         int length = string == null ? 0 : string.length(); | ||||
|         return length <= max && length >= min; | ||||
|     } | ||||
| 
 | ||||
|     public static boolean isEmpty(CharSequence str) { | ||||
|         return str == null || str.toString().trim().length() == 0; | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,120 @@ | ||||
| @file:JvmName("Strings") | ||||
| 
 | ||||
| package com.android.base.utils.common | ||||
| 
 | ||||
| import java.util.regex.Pattern | ||||
| 
 | ||||
| 
 | ||||
| private const val CHINA_PHONE_REG = "^1\\d{10}$" | ||||
| private const val ID_CARD_REG = "[1-9]\\d{13,16}[a-zA-Z0-9]{1}" | ||||
| private const val EMAIL_REG = "\\w+([-+.]\\w+)*@\\w+([-.]\\w+)*\\.\\w+([-.]\\w+)*" | ||||
| private const val DIGIT_REG = "\\-?[1-9]\\d+\"" | ||||
| private const val URL_REG = "(https?://(w{3}\\.)?)?\\w+\\.\\w+(\\.[a-zA-Z]+)*(:\\d{1,5})?(/\\w*)*(\\??(.+=.*)?(&.+=.*)?)?" | ||||
| private const val CHINESE_REGEX = "^[\u4E00-\u9FA5]+$" | ||||
| private const val DECIMALS_REG = "\\-?[1-9]\\d+(\\.\\d+)?" | ||||
| private const val LETTERS_REG = ".*[a-zA-Z]++.*" | ||||
| private const val DIGITAL_REG = ".*[0-9]++.*" | ||||
| private const val DIGITAL_LETTER_ONLY_REG = "^[A-Za-z0-9]+$" | ||||
| 
 | ||||
| /** | ||||
|  * 验证中国的手机号 | ||||
|  * | ||||
|  * @return 验证成功返回 true,验证失败返回 false | ||||
|  */ | ||||
| fun isChinaPhoneNumber(mobile: String): Boolean { | ||||
|     return !isEmpty(mobile) && Pattern.matches(CHINA_PHONE_REG, mobile) | ||||
| } | ||||
| 
 | ||||
| fun containsLetter(text: String): Boolean { | ||||
|     return !isEmpty(text) && Pattern.matches(LETTERS_REG, text) | ||||
| } | ||||
| 
 | ||||
| fun containsDigital(text: String): Boolean { | ||||
|     return !isEmpty(text) && Pattern.matches(DIGITAL_REG, text) | ||||
| } | ||||
| 
 | ||||
| fun containsDigtalLetterOnly(text: String): Boolean { | ||||
|     return !isEmpty(text) && Pattern.matches(DIGITAL_LETTER_ONLY_REG, text) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 验证身份证号码 | ||||
|  * | ||||
|  * @param idCard 居民身份证号码15位或18位,最后一位可能是数字或字母 | ||||
|  * @return 验证成功返回true,验证失败返回false | ||||
|  */ | ||||
| fun isIdCard(idCard: String): Boolean { | ||||
|     return !isEmpty(idCard) && Pattern.matches(ID_CARD_REG, idCard) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 验证Email | ||||
|  * | ||||
|  * @param email email地址,格式:zhangsan@sina.com,zhangsan@xxx.com.cn,xxx代表邮件服务商 | ||||
|  * @return 验证成功返回true,验证失败返回false | ||||
|  */ | ||||
| fun isEmail(email: String): Boolean { | ||||
|     return !isEmpty(email) && Pattern.matches(EMAIL_REG, email) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 验证整数(正整数和负整数) | ||||
|  * | ||||
|  * @param digit 一位或多位0-9之间的整数 | ||||
|  * @return 验证成功返回true,验证失败返回false | ||||
|  */ | ||||
| fun isDigit(digit: String): Boolean { | ||||
|     return !isEmpty(digit) && Pattern.matches(DIGIT_REG, digit) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 验证整数和浮点数(正负整数和正负浮点数) | ||||
|  * | ||||
|  * @param decimals 一位或多位0-9之间的浮点数,如:1.23,233.30 | ||||
|  * @return 验证成功返回true,验证失败返回false | ||||
|  */ | ||||
| fun isDecimals(decimals: String): Boolean { | ||||
|     return !isEmpty(decimals) && Pattern.matches(DECIMALS_REG, decimals) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 验证中文 | ||||
|  * | ||||
|  * @param chinese 中文字符 | ||||
|  * @return 验证成功返回true,验证失败返回false | ||||
|  */ | ||||
| fun isChinese(chinese: String): Boolean { | ||||
|     return !isEmpty(chinese) && Pattern.matches(CHINESE_REGEX, chinese) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 验证URL地址 | ||||
|  * | ||||
|  * @param url url | ||||
|  * @return 验证成功返回true,验证失败返回false | ||||
|  */ | ||||
| fun isURL(url: String): Boolean { | ||||
|     return !isEmpty(url) && Pattern.matches(URL_REG, url) | ||||
| } | ||||
| 
 | ||||
| /** | ||||
|  * 获取字符串的字符个数 | ||||
|  */ | ||||
| fun getCharLength(string: String): Int { | ||||
|     return if (isEmpty(string)) { | ||||
|         0 | ||||
|     } else string.trim { it <= ' ' }.toCharArray().size | ||||
| } | ||||
| 
 | ||||
| fun isCharLength(string: String?, length: Int): Boolean { | ||||
|     return null != string && (isEmpty(string) && length == 0 || string.trim { it <= ' ' }.toCharArray().size == length) | ||||
| } | ||||
| 
 | ||||
| fun isLengthIn(string: String?, min: Int, max: Int): Boolean { | ||||
|     val length = string?.length ?: 0 | ||||
|     return length in min..max | ||||
| } | ||||
| 
 | ||||
| fun isEmpty(str: CharSequence?): Boolean { | ||||
|     return str == null || str.toString().trim { it <= ' ' }.isEmpty() | ||||
| } | ||||
| @ -1,111 +0,0 @@ | ||||
| package com.android.base.widget.viewpager; | ||||
| 
 | ||||
| import android.content.Context; | ||||
| import android.content.res.TypedArray; | ||||
| import android.graphics.Canvas; | ||||
| import android.graphics.Color; | ||||
| import android.graphics.Paint; | ||||
| import android.util.AttributeSet; | ||||
| import android.view.View; | ||||
| 
 | ||||
| import com.android.base.R; | ||||
| import com.android.base.utils.android.UnitConverter; | ||||
| 
 | ||||
| 
 | ||||
| public class PageNumberView extends View implements IPagerNumberView { | ||||
| 
 | ||||
|     private static final String DIVISION = "/"; | ||||
| 
 | ||||
|     private int mPageSize; | ||||
|     private int mPosition; | ||||
| 
 | ||||
|     private int mCenterX; | ||||
| 
 | ||||
|     private Paint mPaint; | ||||
|     private int mTextHeight; | ||||
|     private int mTextBaseLine; | ||||
| 
 | ||||
|     public PageNumberView(Context context) { | ||||
|         this(context, null); | ||||
|     } | ||||
| 
 | ||||
|     public PageNumberView(Context context, AttributeSet attrs) { | ||||
|         this(context, attrs, 0); | ||||
|     } | ||||
| 
 | ||||
|     public PageNumberView(Context context, AttributeSet attrs, int defStyleAttr) { | ||||
|         super(context, attrs, defStyleAttr); | ||||
| 
 | ||||
|         TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.PageNumberView); | ||||
| 
 | ||||
|         /*是否可以缩放*/ | ||||
|         int color = typedArray.getColor(R.styleable.PageNumberView_pnv_text_color, Color.BLACK); | ||||
|         /*用于支持5.0的transition动画*/ | ||||
|         int size = typedArray.getDimensionPixelSize(R.styleable.PageNumberView_pnv_text_size, UnitConverter.spToPx(14)); | ||||
| 
 | ||||
|         typedArray.recycle(); | ||||
| 
 | ||||
|         mPaint = new Paint(Paint.ANTI_ALIAS_FLAG); | ||||
|         mPaint.setTextSize(size); | ||||
|         mPaint.setColor(color); | ||||
| 
 | ||||
|         Paint.FontMetricsInt fontMetricsInt = mPaint.getFontMetricsInt(); | ||||
|         mTextHeight = fontMetricsInt.bottom - fontMetricsInt.top; | ||||
|         mTextBaseLine = mTextHeight - fontMetricsInt.bottom; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { | ||||
|         String text = String.valueOf(mPageSize); | ||||
|         float textWidth = mPaint.measureText(text); | ||||
|         float divisionWidth = mPaint.measureText(DIVISION); | ||||
|         setMeasuredDimension((int) (textWidth * 2 + divisionWidth), mTextHeight); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onDraw(Canvas canvas) { | ||||
|         super.onDraw(canvas); | ||||
|         float divisionHalfSize = mPaint.measureText(DIVISION) / 2; | ||||
|         float left = mCenterX - divisionHalfSize; | ||||
|         float right = mCenterX + divisionHalfSize; | ||||
|         String positionText = String.valueOf(mPosition); | ||||
|         String sizeText = String.valueOf(mPageSize); | ||||
|         canvas.drawText(DIVISION, left, mTextBaseLine, mPaint); | ||||
|         canvas.drawText(positionText, left - mPaint.measureText(positionText), mTextBaseLine, mPaint); | ||||
|         canvas.drawText(sizeText, right, mTextBaseLine, mPaint); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     protected void onSizeChanged(int w, int h, int oldw, int oldh) { | ||||
|         super.onSizeChanged(w, h, oldw, oldh); | ||||
|         mCenterX = w / 2; | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setPageScrolled(int position, @SuppressWarnings("UnusedParameters") float positionOffset) { | ||||
|         mPosition = position; | ||||
|         if (mPageSize > 1) { | ||||
|             if (mPosition == 0) { | ||||
|                 mPosition = mPageSize; | ||||
|             } else if (mPosition == mPageSize + 1) { | ||||
|                 mPosition = 1; | ||||
|             } | ||||
|         } else { | ||||
|             mPosition = mPageSize; | ||||
|         } | ||||
|         invalidate(); | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setViewPager(ZViewPager viewPager) { | ||||
|         //no op
 | ||||
|     } | ||||
| 
 | ||||
|     @Override | ||||
|     public void setPageSize(int pageSize) { | ||||
|         mPageSize = pageSize; | ||||
|         mPosition = pageSize;//只有一個的時候
 | ||||
|         requestLayout(); | ||||
|     } | ||||
| 
 | ||||
| } | ||||
| @ -0,0 +1,100 @@ | ||||
| package com.android.base.widget.viewpager | ||||
| 
 | ||||
| import android.content.Context | ||||
| import android.graphics.Canvas | ||||
| import android.graphics.Color | ||||
| import android.graphics.Paint | ||||
| import android.util.AttributeSet | ||||
| import android.view.View | ||||
| import com.android.base.R | ||||
| import com.android.base.utils.android.views.dip | ||||
| 
 | ||||
| 
 | ||||
| class PageNumberView @JvmOverloads constructor( | ||||
|         context: Context, | ||||
|         attrs: AttributeSet? = null, | ||||
|         defStyleAttr: Int = 0 | ||||
| ) : View(context, attrs, defStyleAttr), IPagerNumberView { | ||||
| 
 | ||||
|     private var mPageSize: Int = 0 | ||||
|     private var mPosition: Int = 0 | ||||
| 
 | ||||
|     private var mCenterX: Int = 0 | ||||
| 
 | ||||
|     private val mPaint: Paint | ||||
|     private val mTextHeight: Int | ||||
|     private val mTextBaseLine: Int | ||||
| 
 | ||||
|     init { | ||||
| 
 | ||||
|         val typedArray = context.obtainStyledAttributes(attrs, R.styleable.PageNumberView) | ||||
| 
 | ||||
|         /*是否可以缩放*/ | ||||
|         val color = typedArray.getColor(R.styleable.PageNumberView_pnv_text_color, Color.BLACK) | ||||
|         /*用于支持5.0的transition动画*/ | ||||
|         val size = typedArray.getDimensionPixelSize(R.styleable.PageNumberView_pnv_text_size, dip(14)) | ||||
| 
 | ||||
|         typedArray.recycle() | ||||
| 
 | ||||
|         mPaint = Paint(Paint.ANTI_ALIAS_FLAG) | ||||
|         mPaint.textSize = size.toFloat() | ||||
|         mPaint.color = color | ||||
| 
 | ||||
|         val fontMetricsInt = mPaint.fontMetricsInt | ||||
|         mTextHeight = fontMetricsInt.bottom - fontMetricsInt.top | ||||
|         mTextBaseLine = mTextHeight - fontMetricsInt.bottom | ||||
|     } | ||||
| 
 | ||||
|     override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { | ||||
|         val text = mPageSize.toString() | ||||
|         val textWidth = mPaint.measureText(text) | ||||
|         val divisionWidth = mPaint.measureText(DIVISION) | ||||
|         setMeasuredDimension((textWidth * 2 + divisionWidth).toInt(), mTextHeight) | ||||
|     } | ||||
| 
 | ||||
|     override fun onDraw(canvas: Canvas) { | ||||
|         super.onDraw(canvas) | ||||
|         val divisionHalfSize = mPaint.measureText(DIVISION) / 2 | ||||
|         val left = mCenterX - divisionHalfSize | ||||
|         val right = mCenterX + divisionHalfSize | ||||
|         val positionText = mPosition.toString() | ||||
|         val sizeText = mPageSize.toString() | ||||
|         canvas.drawText(DIVISION, left, mTextBaseLine.toFloat(), mPaint) | ||||
|         canvas.drawText(positionText, left - mPaint.measureText(positionText), mTextBaseLine.toFloat(), mPaint) | ||||
|         canvas.drawText(sizeText, right, mTextBaseLine.toFloat(), mPaint) | ||||
|     } | ||||
| 
 | ||||
|     override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { | ||||
|         super.onSizeChanged(w, h, oldw, oldh) | ||||
|         mCenterX = w / 2 | ||||
|     } | ||||
| 
 | ||||
|     override fun setPageScrolled(position: Int, positionOffset: Float) { | ||||
|         mPosition = position | ||||
|         if (mPageSize > 1) { | ||||
|             if (mPosition == 0) { | ||||
|                 mPosition = mPageSize | ||||
|             } else if (mPosition == mPageSize + 1) { | ||||
|                 mPosition = 1 | ||||
|             } | ||||
|         } else { | ||||
|             mPosition = mPageSize | ||||
|         } | ||||
|         invalidate() | ||||
|     } | ||||
| 
 | ||||
|     override fun setViewPager(viewPager: ZViewPager) { | ||||
|         //no op | ||||
|     } | ||||
| 
 | ||||
|     override fun setPageSize(pageSize: Int) { | ||||
|         mPageSize = pageSize | ||||
|         mPosition = pageSize//只有一個的時候 | ||||
|         requestLayout() | ||||
|     } | ||||
| 
 | ||||
|     companion object { | ||||
|         private const val DIVISION = "/" | ||||
|     } | ||||
| 
 | ||||
| } | ||||
					Loading…
					
					
				
		Reference in new issue