{
+ ProtoAdapter_Transform() {
+ super(FieldEncoding.LENGTH_DELIMITED, Transform.class);
+ }
+
+ @Override
+ public int encodedSize(Transform value) {
+ return (value.a != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(1, value.a) : 0)
+ + (value.b != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(2, value.b) : 0)
+ + (value.c != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(3, value.c) : 0)
+ + (value.d != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(4, value.d) : 0)
+ + (value.tx != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(5, value.tx) : 0)
+ + (value.ty != null ? ProtoAdapter.FLOAT.encodedSizeWithTag(6, value.ty) : 0)
+ + value.unknownFields().size();
+ }
+
+ @Override
+ public void encode(ProtoWriter writer, Transform value) throws IOException {
+ if (value.a != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 1, value.a);
+ if (value.b != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 2, value.b);
+ if (value.c != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 3, value.c);
+ if (value.d != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 4, value.d);
+ if (value.tx != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 5, value.tx);
+ if (value.ty != null) ProtoAdapter.FLOAT.encodeWithTag(writer, 6, value.ty);
+ writer.writeBytes(value.unknownFields());
+ }
+
+ @Override
+ public Transform decode(ProtoReader reader) throws IOException {
+ Builder builder = new Builder();
+ long token = reader.beginMessage();
+ for (int tag; (tag = reader.nextTag()) != -1;) {
+ switch (tag) {
+ case 1: builder.a(ProtoAdapter.FLOAT.decode(reader)); break;
+ case 2: builder.b(ProtoAdapter.FLOAT.decode(reader)); break;
+ case 3: builder.c(ProtoAdapter.FLOAT.decode(reader)); break;
+ case 4: builder.d(ProtoAdapter.FLOAT.decode(reader)); break;
+ case 5: builder.tx(ProtoAdapter.FLOAT.decode(reader)); break;
+ case 6: builder.ty(ProtoAdapter.FLOAT.decode(reader)); break;
+ default: {
+ FieldEncoding fieldEncoding = reader.peekFieldEncoding();
+ Object value = fieldEncoding.rawProtoAdapter().decode(reader);
+ builder.addUnknownField(tag, fieldEncoding, value);
+ }
+ }
+ }
+ reader.endMessage(token);
+ return builder.build();
+ }
+
+ @Override
+ public Transform redact(Transform value) {
+ Builder builder = value.newBuilder();
+ builder.clearUnknownFields();
+ return builder.build();
+ }
+ }
+}
diff --git a/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/Pools.kt b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/Pools.kt
new file mode 100644
index 000000000..7382ab84b
--- /dev/null
+++ b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/Pools.kt
@@ -0,0 +1,102 @@
+package com.opensource.svgaplayer.utils
+
+/**
+ * Helper class for creating pools of objects. An example use looks like this:
+ *
+ * public class MyPooledClass {
+ *
+ * private static final SynchronizedPool sPool =
+ * new SynchronizedPool(10);
+ *
+ * public static MyPooledClass obtain() {
+ * MyPooledClass instance = sPool.acquire();
+ * return (instance != null) ? instance : new MyPooledClass();
+ * }
+ *
+ * public void recycle() {
+ * // Clear state if needed.
+ * sPool.release(this);
+ * }
+ *
+ * . . .
+ * }
+ *
+ *
+ */
+class Pools private constructor() {
+
+ /**
+ * Interface for managing a pool of objects.
+ *
+ * @param The pooled type.
+ */
+ interface Pool {
+ /**
+ * @return An instance from the pool if such, null otherwise.
+ */
+ fun acquire(): T?
+
+ /**
+ * Release an instance to the pool.
+ *
+ * @param instance The instance to release.
+ * @return Whether the instance was put in the pool.
+ *
+ * @throws IllegalStateException If the instance is already in the pool.
+ */
+ fun release(instance: T): Boolean
+ }
+
+ /**
+ * Simple (non-synchronized) pool of objects.
+ *
+ * @param maxPoolSize The max pool size.
+ *
+ * @throws IllegalArgumentException If the max pool size is less than zero.
+ *
+ * @param The pooled type.
+ */
+ open class SimplePool(maxPoolSize: Int) : Pool {
+ private val mPool: Array
+ private var mPoolSize = 0
+
+ init {
+ require(maxPoolSize > 0) { "The max pool size must be > 0" }
+ mPool = arrayOfNulls(maxPoolSize)
+ }
+
+ @Suppress("UNCHECKED_CAST")
+ override fun acquire(): T? {
+ if (mPoolSize > 0) {
+ val lastPooledIndex = mPoolSize - 1
+ val instance = mPool[lastPooledIndex] as T?
+ mPool[lastPooledIndex] = null
+ mPoolSize--
+ return instance
+ }
+ return null
+ }
+
+ override fun release(instance: T): Boolean {
+ check(!isInPool(instance)) { "Already in the pool!" }
+ if (mPoolSize < mPool.size) {
+ mPool[mPoolSize] = instance
+ mPoolSize++
+ return true
+ }
+ return false
+ }
+
+ private fun isInPool(instance: T): Boolean {
+ for (i in 0 until mPoolSize) {
+ if (mPool[i] === instance) {
+ return true
+ }
+ }
+ return false
+ }
+
+ }
+
+
+}
\ No newline at end of file
diff --git a/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/SVGAScaleInfo.kt b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/SVGAScaleInfo.kt
new file mode 100644
index 000000000..792abc266
--- /dev/null
+++ b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/SVGAScaleInfo.kt
@@ -0,0 +1,146 @@
+package com.opensource.svgaplayer.utils
+
+import android.widget.ImageView
+
+/**
+ * Created by ubt on 2018/1/19.
+ */
+class SVGAScaleInfo {
+
+ var tranFx : Float = 0.0f
+ var tranFy : Float = 0.0f
+ var scaleFx : Float = 1.0f
+ var scaleFy : Float = 1.0f
+ var ratio = 1.0f
+ var ratioX = false
+
+ private fun resetVar(){
+ tranFx = 0.0f
+ tranFy = 0.0f
+ scaleFx = 1.0f
+ scaleFy = 1.0f
+ ratio = 1.0f
+ ratioX = false
+ }
+
+ fun performScaleType(canvasWidth : Float, canvasHeight: Float, videoWidth : Float, videoHeight : Float, scaleType: ImageView.ScaleType) {
+ if (canvasWidth == 0.0f || canvasHeight == 0.0f || videoWidth == 0.0f || videoHeight == 0.0f) {
+ return
+ }
+
+ resetVar()
+ val canW_vidW_f = (canvasWidth - videoWidth) / 2.0f
+ val canH_vidH_f = (canvasHeight - videoHeight) / 2.0f
+
+ val videoRatio = videoWidth / videoHeight
+ val canvasRatio = canvasWidth / canvasHeight
+
+ val canH_d_vidH = canvasHeight / videoHeight
+ val canW_d_vidW = canvasWidth / videoWidth
+
+ when (scaleType) {
+ ImageView.ScaleType.CENTER -> {
+ tranFx = canW_vidW_f
+ tranFy = canH_vidH_f
+ }
+ ImageView.ScaleType.CENTER_CROP -> {
+ if (videoRatio > canvasRatio) {
+ ratio = canH_d_vidH
+ ratioX = false
+ scaleFx = canH_d_vidH
+ scaleFy = canH_d_vidH
+ tranFx = (canvasWidth - videoWidth * (canH_d_vidH)) / 2.0f
+ }
+ else {
+ ratio = canW_d_vidW
+ ratioX = true
+ scaleFx = canW_d_vidW
+ scaleFy = canW_d_vidW
+ tranFy = (canvasHeight - videoHeight * (canW_d_vidW)) / 2.0f
+ }
+ }
+ ImageView.ScaleType.CENTER_INSIDE -> {
+ if (videoWidth < canvasWidth && videoHeight < canvasHeight) {
+ tranFx = canW_vidW_f
+ tranFy = canH_vidH_f
+ }
+ else {
+ if (videoRatio > canvasRatio) {
+ ratio = canW_d_vidW
+ ratioX = true
+ scaleFx = canW_d_vidW
+ scaleFy = canW_d_vidW
+ tranFy = (canvasHeight - videoHeight * (canW_d_vidW)) / 2.0f
+
+ }
+ else {
+ ratio = canH_d_vidH
+ ratioX = false
+ scaleFx = canH_d_vidH
+ scaleFy = canH_d_vidH
+ tranFx = (canvasWidth - videoWidth * (canH_d_vidH)) / 2.0f
+ }
+ }
+ }
+ ImageView.ScaleType.FIT_CENTER -> {
+ if (videoRatio > canvasRatio) {
+ ratio = canW_d_vidW
+ ratioX = true
+ scaleFx = canW_d_vidW
+ scaleFy = canW_d_vidW
+ tranFy = (canvasHeight - videoHeight * (canW_d_vidW)) / 2.0f
+ }
+ else {
+ ratio = canH_d_vidH
+ ratioX = false
+ scaleFx = canH_d_vidH
+ scaleFy = canH_d_vidH
+ tranFx = (canvasWidth - videoWidth * (canH_d_vidH)) / 2.0f
+ }
+ }
+ ImageView.ScaleType.FIT_START -> {
+ if (videoRatio > canvasRatio) {
+ ratio = canW_d_vidW
+ ratioX = true
+ scaleFx = canW_d_vidW
+ scaleFy = canW_d_vidW
+ }
+ else {
+ ratio = canH_d_vidH
+ ratioX = false
+ scaleFx = canH_d_vidH
+ scaleFy = canH_d_vidH
+ }
+ }
+ ImageView.ScaleType.FIT_END -> {
+ if (videoRatio > canvasRatio) {
+ ratio = canW_d_vidW
+ ratioX = true
+ scaleFx = canW_d_vidW
+ scaleFy = canW_d_vidW
+ tranFy= canvasHeight - videoHeight * (canW_d_vidW)
+ }
+ else {
+ ratio = canH_d_vidH
+ ratioX = false
+ scaleFx = canH_d_vidH
+ scaleFy = canH_d_vidH
+ tranFx = canvasWidth - videoWidth * (canH_d_vidH)
+ }
+ }
+ ImageView.ScaleType.FIT_XY -> {
+ ratio = Math.max(canW_d_vidW, canH_d_vidH)
+ ratioX = canW_d_vidW > canH_d_vidH
+ scaleFx = canW_d_vidW
+ scaleFy = canH_d_vidH
+ }
+ else -> {
+ ratio = canW_d_vidW
+ ratioX = true
+ scaleFx = canW_d_vidW
+ scaleFy = canW_d_vidW
+ }
+ }
+ }
+
+}
diff --git a/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/SVGAStructs.kt b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/SVGAStructs.kt
new file mode 100644
index 000000000..f87b30db0
--- /dev/null
+++ b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/SVGAStructs.kt
@@ -0,0 +1,11 @@
+package com.opensource.svgaplayer.utils
+
+/**
+ * Created by cuiminghui on 2017/3/29.
+ */
+
+class SVGAPoint(val x: Float, val y: Float, val value: Float)
+
+class SVGARect(val x: Double, val y: Double, val width: Double, val height: Double)
+
+class SVGARange(val location: Int, val length: Int)
\ No newline at end of file
diff --git a/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/DefaultLogCat.kt b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/DefaultLogCat.kt
new file mode 100644
index 000000000..33200b0e1
--- /dev/null
+++ b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/DefaultLogCat.kt
@@ -0,0 +1,28 @@
+package com.opensource.svgaplayer.utils.log
+
+import android.util.Log
+
+/**
+ * 内部默认 ILogger 接口实现
+ */
+class DefaultLogCat : ILogger {
+ override fun verbose(tag: String, msg: String) {
+ Log.v(tag, msg)
+ }
+
+ override fun info(tag: String, msg: String) {
+ Log.i(tag, msg)
+ }
+
+ override fun debug(tag: String, msg: String) {
+ Log.d(tag, msg)
+ }
+
+ override fun warn(tag: String, msg: String) {
+ Log.w(tag, msg)
+ }
+
+ override fun error(tag: String, msg: String?, error: Throwable?) {
+ Log.e(tag, msg, error)
+ }
+}
\ No newline at end of file
diff --git a/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/ILogger.kt b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/ILogger.kt
new file mode 100644
index 000000000..ad935104c
--- /dev/null
+++ b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/ILogger.kt
@@ -0,0 +1,12 @@
+package com.opensource.svgaplayer.utils.log
+
+/**
+ * log 外部接管接口
+ **/
+interface ILogger {
+ fun verbose(tag: String, msg: String)
+ fun info(tag: String, msg: String)
+ fun debug(tag: String, msg: String)
+ fun warn(tag: String, msg: String)
+ fun error(tag: String, msg: String?, error: Throwable?)
+}
\ No newline at end of file
diff --git a/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/LogUtils.kt b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/LogUtils.kt
new file mode 100644
index 000000000..60c67f9c2
--- /dev/null
+++ b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/LogUtils.kt
@@ -0,0 +1,57 @@
+package com.opensource.svgaplayer.utils.log
+
+/**
+ * 日志输出
+ */
+internal object LogUtils {
+ private const val TAG = "SVGALog"
+
+ fun verbose(tag: String = TAG, msg: String) {
+ if (!SVGALogger.isLogEnabled()) {
+ return
+ }
+ SVGALogger.getSVGALogger()?.verbose(tag, msg)
+ }
+
+ fun info(tag: String = TAG, msg: String) {
+ if (!SVGALogger.isLogEnabled()) {
+ return
+ }
+ SVGALogger.getSVGALogger()?.info(tag, msg)
+ }
+
+ fun debug(tag: String = TAG, msg: String) {
+ if (!SVGALogger.isLogEnabled()) {
+ return
+ }
+ SVGALogger.getSVGALogger()?.debug(tag, msg)
+ }
+
+ fun warn(tag: String = TAG, msg: String) {
+ if (!SVGALogger.isLogEnabled()) {
+ return
+ }
+ SVGALogger.getSVGALogger()?.warn(tag, msg)
+ }
+
+ fun error(tag: String = TAG, msg: String) {
+ if (!SVGALogger.isLogEnabled()) {
+ return
+ }
+ SVGALogger.getSVGALogger()?.error(tag, msg, null)
+ }
+
+ fun error(tag: String, error: Throwable) {
+ if (!SVGALogger.isLogEnabled()) {
+ return
+ }
+ SVGALogger.getSVGALogger()?.error(tag, error.message, error)
+ }
+
+ fun error(tag: String = TAG, msg: String, error: Throwable) {
+ if (!SVGALogger.isLogEnabled()) {
+ return
+ }
+ SVGALogger.getSVGALogger()?.error(tag, msg, error)
+ }
+}
\ No newline at end of file
diff --git a/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/SVGALogger.kt b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/SVGALogger.kt
new file mode 100644
index 000000000..5767c6385
--- /dev/null
+++ b/SVGAlibrary/src/main/java/com/opensource/svgaplayer/utils/log/SVGALogger.kt
@@ -0,0 +1,40 @@
+package com.opensource.svgaplayer.utils.log
+
+/**
+ * SVGA logger 配置管理
+ **/
+object SVGALogger {
+
+ private var mLogger: ILogger? = DefaultLogCat()
+ private var isLogEnabled = false
+
+ /**
+ * log 接管注入
+ */
+ fun injectSVGALoggerImp(logImp: ILogger): SVGALogger {
+ mLogger = logImp
+ return this
+ }
+
+ /**
+ * 设置是否开启 log
+ */
+ fun setLogEnabled(isEnabled: Boolean): SVGALogger {
+ isLogEnabled = isEnabled
+ return this
+ }
+
+ /**
+ * 获取当前 ILogger 实现类
+ */
+ fun getSVGALogger(): ILogger? {
+ return mLogger
+ }
+
+ /**
+ * 是否开启 log
+ */
+ fun isLogEnabled(): Boolean {
+ return isLogEnabled
+ }
+}
\ No newline at end of file
diff --git a/SVGAlibrary/src/main/res/values/attrs.xml b/SVGAlibrary/src/main/res/values/attrs.xml
new file mode 100644
index 000000000..471c34453
--- /dev/null
+++ b/SVGAlibrary/src/main/res/values/attrs.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/SVGAlibrary/src/main/res/values/strings.xml b/SVGAlibrary/src/main/res/values/strings.xml
new file mode 100644
index 000000000..475f55349
--- /dev/null
+++ b/SVGAlibrary/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ SVGAPlayer
+
diff --git a/TabLayout/.gitignore b/TabLayout/.gitignore
new file mode 100644
index 000000000..796b96d1c
--- /dev/null
+++ b/TabLayout/.gitignore
@@ -0,0 +1 @@
+/build
diff --git a/TabLayout/build.gradle b/TabLayout/build.gradle
new file mode 100644
index 000000000..7838d7412
--- /dev/null
+++ b/TabLayout/build.gradle
@@ -0,0 +1,30 @@
+apply plugin: 'com.android.library'
+apply plugin: 'kotlin-android'
+
+android {
+ compileSdkVersion rootProject.ext.android.compileSdkVersion
+ buildToolsVersion rootProject.ext.android.buildToolsVersion
+
+ defaultConfig {
+ minSdkVersion rootProject.ext.android.minSdkVersion
+ targetSdkVersion rootProject.ext.android.targetSdkVersion
+ versionCode rootProject.ext.android.versionCode
+ versionName rootProject.ext.android.versionName
+ manifestPlaceholders = rootProject.ext.manifestPlaceholders
+
+ consumerProguardFiles 'consumer-rules.pro'
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+}
+
+dependencies {
+ implementation 'androidx.core:core-ktx:1.7.0'
+}
+
+//apply from: "$gradleHost/master/publish.gradle"
\ No newline at end of file
diff --git a/TabLayout/consumer-rules.pro b/TabLayout/consumer-rules.pro
new file mode 100644
index 000000000..e69de29bb
diff --git a/TabLayout/proguard-rules.pro b/TabLayout/proguard-rules.pro
new file mode 100644
index 000000000..f1b424510
--- /dev/null
+++ b/TabLayout/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
diff --git a/TabLayout/src/main/AndroidManifest.xml b/TabLayout/src/main/AndroidManifest.xml
new file mode 100644
index 000000000..e86e891c7
--- /dev/null
+++ b/TabLayout/src/main/AndroidManifest.xml
@@ -0,0 +1,2 @@
+
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/AbsDslDrawable.kt b/TabLayout/src/main/java/com/angcyo/tablayout/AbsDslDrawable.kt
new file mode 100644
index 000000000..d790825ad
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/AbsDslDrawable.kt
@@ -0,0 +1,200 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.content.res.ColorStateList
+import android.graphics.*
+import android.graphics.drawable.Drawable
+import android.text.TextPaint
+import android.util.AttributeSet
+import android.view.View
+import androidx.core.view.ViewCompat
+
+/**
+ * 基础自绘Drawable
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/25
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+
+abstract class AbsDslDrawable : Drawable() {
+
+ companion object {
+ /**不绘制*/
+ const val DRAW_TYPE_DRAW_NONE = 0x00
+
+ /**[android.view.View.draw]*/
+ const val DRAW_TYPE_DRAW_AFTER = 0x01
+ const val DRAW_TYPE_DRAW_BEFORE = 0x02
+
+ /**[android.view.View.onDraw]*/
+ const val DRAW_TYPE_ON_DRAW_AFTER = 0x04
+ const val DRAW_TYPE_ON_DRAW_BEFORE = 0x08
+ }
+
+ /**画笔*/
+ val textPaint: TextPaint by lazy {
+ TextPaint(Paint.ANTI_ALIAS_FLAG).apply {
+ isFilterBitmap = true
+ style = Paint.Style.FILL
+ textSize = 12 * dp
+ }
+ }
+
+ val drawRect = Rect()
+ val drawRectF = RectF()
+
+ /**需要在那个方法中触发绘制*/
+ var drawType = DRAW_TYPE_ON_DRAW_AFTER
+
+ /**xml属性读取*/
+ open fun initAttribute(
+ context: Context,
+ attributeSet: AttributeSet? = null
+ ) {
+ //val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.xxx)
+ //typedArray.recycle()
+ }
+
+ //
+
+ /**附着的[View]*/
+ val attachView: View?
+ get() = if (callback is View) callback as? View else null
+
+ val isInEditMode: Boolean
+ get() = attachView?.isInEditMode ?: false
+ val paddingLeft: Int
+ get() = attachView?.paddingLeft ?: 0
+ val paddingRight: Int
+ get() = attachView?.paddingRight ?: 0
+ val paddingTop: Int
+ get() = attachView?.paddingTop ?: 0
+ val paddingBottom: Int
+ get() = attachView?.paddingBottom ?: 0
+ val viewHeight: Int
+ get() = attachView?.measuredHeight ?: 0
+ val viewWidth: Int
+ get() = attachView?.measuredWidth ?: 0
+ val viewDrawHeight: Int
+ get() = viewHeight - paddingTop - paddingBottom
+ val viewDrawWidth: Int
+ get() = viewWidth - paddingLeft - paddingRight
+
+ val isViewRtl: Boolean
+ get() = attachView != null && ViewCompat.getLayoutDirection(attachView!!) == ViewCompat.LAYOUT_DIRECTION_RTL
+
+ //
+
+ //
+
+ /**核心方法, 绘制*/
+ override fun draw(canvas: Canvas) {
+
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ return super.getIntrinsicWidth()
+ }
+
+ override fun getMinimumWidth(): Int {
+ return super.getMinimumWidth()
+ }
+
+ override fun getIntrinsicHeight(): Int {
+ return super.getIntrinsicHeight()
+ }
+
+ override fun getMinimumHeight(): Int {
+ return super.getMinimumHeight()
+ }
+
+ override fun setAlpha(alpha: Int) {
+ if (textPaint.alpha != alpha) {
+ textPaint.alpha = alpha
+ invalidateSelf()
+ }
+ }
+
+ override fun getAlpha(): Int {
+ return textPaint.alpha
+ }
+
+ override fun setBounds(left: Int, top: Int, right: Int, bottom: Int) {
+ super.setBounds(left, top, right, bottom)
+ }
+
+ override fun setBounds(bounds: Rect) {
+ super.setBounds(bounds)
+ }
+
+ //不透明度
+ override fun getOpacity(): Int {
+ return if (alpha < 255) PixelFormat.TRANSLUCENT else PixelFormat.OPAQUE
+ }
+
+ override fun getColorFilter(): ColorFilter? {
+ return textPaint.colorFilter
+ }
+
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ textPaint.colorFilter = colorFilter
+ invalidateSelf()
+ }
+
+ override fun mutate(): Drawable {
+ return super.mutate()
+ }
+
+ override fun setDither(dither: Boolean) {
+ textPaint.isDither = dither
+ invalidateSelf()
+ }
+
+ override fun setFilterBitmap(filter: Boolean) {
+ textPaint.isFilterBitmap = filter
+ invalidateSelf()
+ }
+
+ override fun isFilterBitmap(): Boolean {
+ return textPaint.isFilterBitmap
+ }
+
+ override fun onBoundsChange(bounds: Rect) {
+ super.onBoundsChange(bounds)
+ }
+
+ override fun onLevelChange(level: Int): Boolean {
+ return super.onLevelChange(level)
+ }
+
+ override fun onStateChange(state: IntArray): Boolean {
+ return super.onStateChange(state)
+ }
+
+ override fun setTintList(tint: ColorStateList?) {
+ super.setTintList(tint)
+ }
+
+ override fun setTintMode(tintMode: PorterDuff.Mode?) {
+ super.setTintMode(tintMode)
+ }
+
+ override fun setTintBlendMode(blendMode: BlendMode?) {
+ super.setTintBlendMode(blendMode)
+ }
+
+ override fun setHotspot(x: Float, y: Float) {
+ super.setHotspot(x, y)
+ }
+
+ override fun setHotspotBounds(left: Int, top: Int, right: Int, bottom: Int) {
+ super.setHotspotBounds(left, top, right, bottom)
+ }
+
+ override fun getHotspotBounds(outRect: Rect) {
+ super.getHotspotBounds(outRect)
+ }
+
+ //
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslBadgeDrawable.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslBadgeDrawable.kt
new file mode 100644
index 000000000..1962d3d8f
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslBadgeDrawable.kt
@@ -0,0 +1,275 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.text.TextUtils
+import android.util.AttributeSet
+import android.view.Gravity
+import kotlin.math.max
+
+/**
+ * 未读数, 未读小红点, 角标绘制Drawable
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/12/13
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+open class DslBadgeDrawable : DslGradientDrawable() {
+
+ val dslGravity = DslGravity()
+
+ /**重力*/
+ var badgeGravity: Int = Gravity.CENTER //Gravity.TOP or Gravity.RIGHT
+
+ /**角标文本颜色*/
+ var badgeTextColor = Color.WHITE
+
+ /**角标的文本(默认居中绘制文本,暂不支持修改), 空字符串会绘制成小圆点
+ * null 不绘制角标
+ * "" 空字符绘制圆点
+ * 其他 正常绘制
+ * */
+ var badgeText: String? = null
+
+ /**角标的文本大小*/
+ var badgeTextSize: Float = 12 * dp
+ set(value) {
+ field = value
+ textPaint.textSize = field
+ }
+
+ /**当[badgeText]只有1个字符时, 使用圆形背景*/
+ var badgeAutoCircle: Boolean = true
+
+ /**圆点状态时的半径大小*/
+ var badgeCircleRadius = 4 * dpi
+
+ /**原点状态下, 单独配置的偏移*/
+ var badgeCircleOffsetX: Int = 0
+ var badgeCircleOffsetY: Int = 0
+
+ /**额外偏移距离, 会根据[Gravity]自动取负值*/
+ var badgeOffsetX: Int = 0
+ var badgeOffsetY: Int = 0
+
+ /**文本偏移*/
+ var badgeTextOffsetX: Int = 0
+ var badgeTextOffsetY: Int = 0
+
+ /**圆点状态时无效*/
+ var badgePaddingLeft = 0
+ var badgePaddingRight = 0
+ var badgePaddingTop = 0
+ var badgePaddingBottom = 0
+
+ /**最小的高度大小, px. 大于0生效; 圆点时属性无效*/
+ var badgeMinHeight = -2
+
+ /**最小的宽度大小, px. 大于0生效; 圆点时属性无效;
+ * -1 表示使用使用计算出来的高度值*/
+ var badgeMinWidth = -2
+
+ //计算属性
+ val textWidth: Float
+ get() = textPaint.textWidth(badgeText)
+
+ //最大的宽度
+ val maxWidth: Int
+ get() = max(
+ textWidth.toInt(),
+ originDrawable?.minimumWidth ?: 0
+ ) + badgePaddingLeft + badgePaddingRight
+
+ //最大的高度
+ val maxHeight: Int
+ get() = max(
+ textHeight.toInt(),
+ originDrawable?.minimumHeight ?: 0
+ ) + badgePaddingTop + badgePaddingBottom
+
+ val textHeight: Float
+ get() = textPaint.textHeight()
+
+ //原型状态
+ val isCircle: Boolean
+ get() = TextUtils.isEmpty(badgeText)
+
+ override fun initAttribute(context: Context, attributeSet: AttributeSet?) {
+ super.initAttribute(context, attributeSet)
+ updateOriginDrawable()
+ }
+
+ override fun draw(canvas: Canvas) {
+ //super.draw(canvas)
+
+ if (badgeText == null) {
+ return
+ }
+
+ with(dslGravity) {
+ gravity = if (isViewRtl) {
+ when (badgeGravity) {
+ Gravity.RIGHT -> {
+ Gravity.LEFT
+ }
+ Gravity.LEFT -> {
+ Gravity.RIGHT
+ }
+ else -> {
+ badgeGravity
+ }
+ }
+ } else {
+ badgeGravity
+ }
+
+ setGravityBounds(bounds)
+
+ if (isCircle) {
+ gravityOffsetX = badgeCircleOffsetX
+ gravityOffsetY = badgeCircleOffsetY
+ } else {
+ gravityOffsetX = badgeOffsetX
+ gravityOffsetY = badgeOffsetY
+ }
+
+ val textWidth = textPaint.textWidth(badgeText)
+ val textHeight = textPaint.textHeight()
+
+ val drawHeight = if (isCircle) {
+ badgeCircleRadius.toFloat()
+ } else {
+ val height = textHeight + badgePaddingTop + badgePaddingBottom
+ if (badgeMinHeight > 0) {
+ max(height, badgeMinHeight.toFloat())
+ } else {
+ height
+ }
+ }
+
+ val drawWidth = if (isCircle) {
+ badgeCircleRadius.toFloat()
+ } else {
+ val width = textWidth + badgePaddingLeft + badgePaddingRight
+ if (badgeMinWidth == -1) {
+ max(width, drawHeight)
+ } else if (badgeMinWidth > 0) {
+ max(width, badgeMinWidth.toFloat())
+ } else {
+ width
+ }
+ }
+
+ applyGravity(drawWidth, drawHeight) { centerX, centerY ->
+
+ if (isCircle) {
+ textPaint.color = gradientSolidColor
+
+ //圆心计算
+ val cx: Float
+ val cy: Float
+ if (gravity.isCenter()) {
+ cx = centerX.toFloat()
+ cy = centerY.toFloat()
+ } else {
+ cx = centerX.toFloat() + _gravityOffsetX
+ cy = centerY.toFloat() + _gravityOffsetY
+ }
+
+ //绘制圆
+ textPaint.color = gradientSolidColor
+ canvas.drawCircle(
+ cx,
+ cy,
+ badgeCircleRadius.toFloat(),
+ textPaint
+ )
+
+ //圆的描边
+ if (gradientStrokeWidth > 0 && gradientStrokeColor != Color.TRANSPARENT) {
+ val oldWidth = textPaint.strokeWidth
+ val oldStyle = textPaint.style
+
+ textPaint.color = gradientStrokeColor
+ textPaint.strokeWidth = gradientStrokeWidth.toFloat()
+ textPaint.style = Paint.Style.STROKE
+
+ canvas.drawCircle(
+ cx,
+ cy,
+ badgeCircleRadius.toFloat(),
+ textPaint
+ )
+
+ textPaint.strokeWidth = oldWidth
+ textPaint.style = oldStyle
+ }
+
+ } else {
+ textPaint.color = badgeTextColor
+
+ val textDrawX: Float = centerX - textWidth / 2
+ val textDrawY: Float = centerY + textHeight / 2
+
+ val bgLeft = _gravityLeft
+ val bgTop = _gravityTop
+
+ //绘制背景
+ if (badgeAutoCircle && badgeText?.length == 1) {
+ if (gradientSolidColor != Color.TRANSPARENT) {
+ textPaint.color = gradientSolidColor
+ canvas.drawCircle(
+ centerX.toFloat(),
+ centerY.toFloat(),
+ max(maxWidth, maxHeight).toFloat() / 2,
+ textPaint
+ )
+ }
+ } else {
+ originDrawable?.apply {
+ setBounds(
+ bgLeft, bgTop,
+ (bgLeft + drawWidth).toInt(),
+ (bgTop + drawHeight).toInt()
+ )
+ draw(canvas)
+ }
+ }
+
+ //绘制文本
+ textPaint.color = badgeTextColor
+ canvas.drawText(
+ badgeText!!,
+ textDrawX + badgeTextOffsetX,
+ textDrawY - textPaint.descent() + badgeTextOffsetY,
+ textPaint
+ )
+ }
+ }
+ }
+ }
+
+ override fun getIntrinsicWidth(): Int {
+ val width = if (isCircle) {
+ badgeCircleRadius * 2
+ } else if (badgeAutoCircle && badgeText?.length == 1) {
+ max(maxWidth, maxHeight)
+ } else {
+ maxWidth
+ }
+ return max(badgeMinWidth, width)
+ }
+
+ override fun getIntrinsicHeight(): Int {
+ val height = if (isCircle) {
+ badgeCircleRadius * 2
+ } else if (badgeAutoCircle && badgeText?.length == 1) {
+ max(maxWidth, maxHeight)
+ } else {
+ maxHeight
+ }
+ return max(badgeMinHeight, height)
+ }
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslGradientDrawable.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslGradientDrawable.kt
new file mode 100644
index 000000000..336c5b9d7
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslGradientDrawable.kt
@@ -0,0 +1,306 @@
+package com.angcyo.tablayout
+
+import android.content.res.ColorStateList
+import android.content.res.Resources
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.ColorFilter
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.os.Build
+import androidx.annotation.IntDef
+import java.util.*
+
+/**
+ * 用来构建GradientDrawable
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/27
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+open class DslGradientDrawable : AbsDslDrawable() {
+
+ /**形状*/
+ @Shape
+ var gradientShape = GradientDrawable.RECTANGLE
+
+ /**填充的颜色*/
+ var gradientSolidColor = Color.TRANSPARENT
+
+ /**边框的颜色*/
+ var gradientStrokeColor = Color.TRANSPARENT
+
+ /**边框的宽度*/
+ var gradientStrokeWidth = 0
+
+ /**蚂蚁线的宽度*/
+ var gradientDashWidth = 0f
+
+ /**蚂蚁线之间的间距*/
+ var gradientDashGap = 0f
+
+ /**
+ * 四个角, 8个设置点的圆角信息
+ * 从 左上y轴->左上x轴->右上x轴->右上y轴..., 开始设置.
+ */
+ var gradientRadii = floatArrayOf(0f, 0f, 0f, 0f, 0f, 0f, 0f, 0f)
+
+ /**颜色渐变*/
+ var gradientColors: IntArray? = null
+ var gradientColorsOffsets: FloatArray? = null
+
+ /**渐变中心点坐标*/
+ var gradientCenterX = 0.5f
+ var gradientCenterY = 0.5f
+
+ /**渐变半径, 非比例值, 是px值. [GradientDrawable.RADIAL_GRADIENT]类型才有效*/
+ var gradientRadius = 0.5f
+
+ /** 渐变方向, 默认从左到右 */
+ var gradientOrientation = GradientDrawable.Orientation.LEFT_RIGHT
+
+ /** 渐变类型 */
+ @GradientType
+ var gradientType = GradientDrawable.LINEAR_GRADIENT
+
+ /**真正绘制的[Drawable]*/
+ var originDrawable: Drawable? = null
+
+ /**宽度补偿*/
+ var gradientWidthOffset: Int = 0
+
+ /**高度补偿*/
+ var gradientHeightOffset: Int = 0
+
+ /**当前的配置, 是否能生成有效的[GradientDrawable]*/
+ open fun isValidConfig(): Boolean {
+ return gradientSolidColor != Color.TRANSPARENT ||
+ gradientStrokeColor != Color.TRANSPARENT ||
+ gradientColors != null
+ }
+
+ fun _fillRadii(array: FloatArray, radii: String?) {
+ if (radii.isNullOrEmpty()) {
+ return
+ }
+ val split = radii.split(",")
+ if (split.size != 8) {
+ throw IllegalArgumentException("radii 需要8个值.")
+ } else {
+ val dp = Resources.getSystem().displayMetrics.density
+ for (i in split.indices) {
+ array[i] = split[i].toFloat() * dp
+ }
+ }
+ }
+
+ fun fillRadii(radius: Float) {
+ Arrays.fill(gradientRadii, radius)
+ }
+
+ fun fillRadii(radius: Int) {
+ _fillRadii(gradientRadii, radius.toFloat())
+ }
+
+ fun _fillRadii(array: FloatArray, radius: Float) {
+ Arrays.fill(array, radius)
+ }
+
+ fun _fillRadii(array: FloatArray, radius: Int) {
+ _fillRadii(array, radius.toFloat())
+ }
+
+ fun _fillColor(colors: String?): IntArray? {
+ if (colors.isNullOrEmpty()) {
+ return null
+ }
+ val split = colors.split(",")
+
+ return IntArray(split.size) {
+ val str = split[it]
+ if (str.startsWith("#")) {
+ Color.parseColor(str)
+ } else {
+ str.toInt()
+ }
+ }
+ }
+
+ /**构建或者更新[originDrawable]*/
+ open fun updateOriginDrawable(): GradientDrawable? {
+ val drawable: GradientDrawable? = when (originDrawable) {
+ null -> GradientDrawable()
+ is GradientDrawable -> originDrawable as GradientDrawable
+ else -> {
+ null
+ }
+ }
+
+ drawable?.apply {
+ bounds = this@DslGradientDrawable.bounds
+
+ shape = gradientShape
+ setStroke(
+ gradientStrokeWidth,
+ gradientStrokeColor,
+ gradientDashWidth,
+ gradientDashGap
+ )
+ setColor(gradientSolidColor)
+ cornerRadii = gradientRadii
+
+ if (gradientColors != null) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+ setGradientCenter(
+ this@DslGradientDrawable.gradientCenterX,
+ this@DslGradientDrawable.gradientCenterY
+ )
+ }
+ gradientRadius = this@DslGradientDrawable.gradientRadius
+ gradientType = this@DslGradientDrawable.gradientType
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ orientation = gradientOrientation
+ }
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ setColors(gradientColors, gradientColorsOffsets)
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
+ colors = gradientColors
+ }
+ }
+
+ originDrawable = this
+ invalidateSelf()
+ }
+
+ return drawable
+ }
+
+ open fun configDrawable(config: DslGradientDrawable.() -> Unit): DslGradientDrawable {
+ this.config()
+ updateOriginDrawable()
+ return this
+ }
+
+ override fun draw(canvas: Canvas) {
+ super.draw(canvas)
+ originDrawable?.apply {
+ setBounds(
+ this@DslGradientDrawable.bounds.left - gradientWidthOffset / 2,
+ this@DslGradientDrawable.bounds.top - gradientHeightOffset / 2,
+ this@DslGradientDrawable.bounds.right + gradientWidthOffset / 2,
+ this@DslGradientDrawable.bounds.bottom + gradientHeightOffset / 2
+ )
+ draw(canvas)
+ }
+ }
+
+ //
+
+ /**
+ * 4个角, 8个点 圆角配置
+ */
+ fun cornerRadii(radii: FloatArray) {
+ gradientRadii = radii
+ }
+
+ fun cornerRadius(radii: Float) {
+ Arrays.fill(gradientRadii, radii)
+ }
+
+ fun cornerRadius(
+ leftTop: Float = 0f,
+ rightTop: Float = 0f,
+ rightBottom: Float = 0f,
+ leftBottom: Float = 0f
+ ) {
+ gradientRadii[0] = leftTop
+ gradientRadii[1] = leftTop
+ gradientRadii[2] = rightTop
+ gradientRadii[3] = rightTop
+ gradientRadii[4] = rightBottom
+ gradientRadii[5] = rightBottom
+ gradientRadii[6] = leftBottom
+ gradientRadii[7] = leftBottom
+ }
+
+ /**
+ * 只配置左边的圆角
+ */
+ fun cornerRadiiLeft(radii: Float) {
+ gradientRadii[0] = radii
+ gradientRadii[1] = radii
+ gradientRadii[6] = radii
+ gradientRadii[7] = radii
+ }
+
+ fun cornerRadiiRight(radii: Float) {
+ gradientRadii[2] = radii
+ gradientRadii[3] = radii
+ gradientRadii[4] = radii
+ gradientRadii[5] = radii
+ }
+
+ fun cornerRadiiTop(radii: Float) {
+ gradientRadii[0] = radii
+ gradientRadii[1] = radii
+ gradientRadii[2] = radii
+ gradientRadii[3] = radii
+ }
+
+ fun cornerRadiiBottom(radii: Float) {
+ gradientRadii[4] = radii
+ gradientRadii[5] = radii
+ gradientRadii[6] = radii
+ gradientRadii[7] = radii
+ }
+
+ //
+
+ //
+ override fun setColorFilter(colorFilter: ColorFilter?) {
+ super.setColorFilter(colorFilter)
+ originDrawable?.colorFilter = colorFilter
+ }
+
+ override fun setTintList(tint: ColorStateList?) {
+ super.setTintList(tint)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ originDrawable?.setTintList(tint)
+ }
+ }
+
+ override fun setState(stateSet: IntArray): Boolean {
+ return originDrawable?.setState(stateSet) ?: super.setState(stateSet)
+ }
+
+ override fun getState(): IntArray {
+ return originDrawable?.state ?: super.getState()
+ }
+
+ //
+}
+
+@IntDef(
+ GradientDrawable.RECTANGLE,
+ GradientDrawable.OVAL,
+ GradientDrawable.LINE,
+ GradientDrawable.RING
+)
+@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
+annotation class Shape
+
+@IntDef(
+ GradientDrawable.LINEAR_GRADIENT,
+ GradientDrawable.RADIAL_GRADIENT,
+ GradientDrawable.SWEEP_GRADIENT
+)
+@kotlin.annotation.Retention(AnnotationRetention.SOURCE)
+annotation class GradientType
+
+/**快速创建[GradientDrawable]*/
+fun dslGradientDrawable(action: DslGradientDrawable.() -> Unit): GradientDrawable {
+ return DslGradientDrawable().run {
+ action()
+ updateOriginDrawable()!!
+ }
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslGravity.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslGravity.kt
new file mode 100644
index 000000000..66cc1d40e
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslGravity.kt
@@ -0,0 +1,215 @@
+package com.angcyo.tablayout
+
+import android.graphics.Rect
+import android.graphics.RectF
+import android.view.Gravity
+
+/**
+ * [android.view.Gravity] 辅助计算类
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/12/13
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+class DslGravity {
+
+ /**束缚范围*/
+ val gravityBounds: RectF = RectF()
+
+ /**束缚重力*/
+ var gravity: Int = Gravity.LEFT or Gravity.TOP
+
+ /**使用中心坐标作为定位参考, 否则就是四条边*/
+ var gravityRelativeCenter: Boolean = true
+
+ /**额外偏移距离, 会根据[Gravity]自动取负值*/
+ var gravityOffsetX: Int = 0
+ var gravityOffsetY: Int = 0
+
+ fun setGravityBounds(rectF: RectF) {
+ gravityBounds.set(rectF)
+ }
+
+ fun setGravityBounds(rect: Rect) {
+ gravityBounds.set(rect)
+ }
+
+ fun setGravityBounds(
+ left: Int,
+ top: Int,
+ right: Int,
+ bottom: Int
+ ) {
+ gravityBounds.set(left.toFloat(), top.toFloat(), right.toFloat(), bottom.toFloat())
+ }
+
+ fun setGravityBounds(
+ left: Float,
+ top: Float,
+ right: Float,
+ bottom: Float
+ ) {
+ gravityBounds.set(left, top, right, bottom)
+ }
+
+ //计算后的属性
+ var _horizontalGravity: Int = Gravity.LEFT
+ var _verticalGravity: Int = Gravity.TOP
+ var _isCenterGravity: Boolean = false
+ var _targetWidth = 0f
+ var _targetHeight = 0f
+ var _gravityLeft = 0
+ var _gravityTop = 0
+ var _gravityRight = 0
+ var _gravityBottom = 0
+
+ //根据gravity调整后的offset
+ var _gravityOffsetX = 0
+ var _gravityOffsetY = 0
+
+ /**根据[gravity]返回在[gravityBounds]中的[left] [top]位置*/
+ fun applyGravity(
+ width: Float = _targetWidth,
+ height: Float = _targetHeight,
+ callback: (centerX: Int, centerY: Int) -> Unit
+ ) {
+
+ _targetWidth = width
+ _targetHeight = height
+
+ val layoutDirection = 0
+ val absoluteGravity = Gravity.getAbsoluteGravity(gravity, layoutDirection)
+ val verticalGravity = gravity and Gravity.VERTICAL_GRAVITY_MASK
+ val horizontalGravity = absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK
+
+ //调整offset
+ _gravityOffsetX = when (horizontalGravity) {
+ Gravity.RIGHT -> -gravityOffsetX
+ Gravity.END -> -gravityOffsetX
+ else -> gravityOffsetX
+ }
+ _gravityOffsetY = when (verticalGravity) {
+ Gravity.BOTTOM -> -gravityOffsetY
+ else -> gravityOffsetY
+ }
+
+ //计算居中的坐标
+ val centerX = when (horizontalGravity) {
+ Gravity.CENTER_HORIZONTAL -> (gravityBounds.left + gravityBounds.width() / 2 + _gravityOffsetX).toInt()
+ Gravity.RIGHT -> (gravityBounds.right + _gravityOffsetX - if (gravityRelativeCenter) 0f else _targetWidth / 2).toInt()
+ Gravity.END -> (gravityBounds.right + _gravityOffsetX - if (gravityRelativeCenter) 0f else _targetWidth / 2).toInt()
+ else -> (gravityBounds.left + _gravityOffsetX + if (gravityRelativeCenter) 0f else _targetWidth / 2).toInt()
+ }
+
+ val centerY = when (verticalGravity) {
+ Gravity.CENTER_VERTICAL -> (gravityBounds.top + gravityBounds.height() / 2 + _gravityOffsetY).toInt()
+ Gravity.BOTTOM -> (gravityBounds.bottom + _gravityOffsetY - if (gravityRelativeCenter) 0f else _targetHeight / 2).toInt()
+ else -> (gravityBounds.top + _gravityOffsetY + if (gravityRelativeCenter) 0f else _targetHeight / 2).toInt()
+ }
+
+ //缓存
+ _horizontalGravity = horizontalGravity
+ _verticalGravity = verticalGravity
+ _isCenterGravity = horizontalGravity == Gravity.CENTER_HORIZONTAL &&
+ verticalGravity == Gravity.CENTER_VERTICAL
+
+ _gravityLeft = (centerX - _targetWidth / 2).toInt()
+ _gravityRight = (centerX + _targetWidth / 2).toInt()
+ _gravityTop = (centerY - _targetHeight / 2).toInt()
+ _gravityBottom = (centerY + _targetHeight / 2).toInt()
+
+ //回调
+ callback(centerX, centerY)
+ }
+}
+
+/**
+ * 默认计算出的是目标中心点坐标参考距离
+ * [com.angcyo.drawable.DslGravity.getGravityRelativeCenter]
+ * */
+fun dslGravity(
+ rect: RectF, //计算的矩形
+ gravity: Int, //重力
+ width: Float, //放置目标的宽度
+ height: Float, //放置目标的高度
+ offsetX: Int = 0, //额外的偏移,会根据[gravity]进行左右|上下取反
+ offsetY: Int = 0,
+ callback: (dslGravity: DslGravity, centerX: Int, centerY: Int) -> Unit
+): DslGravity {
+ val _dslGravity = DslGravity()
+ _dslGravity.setGravityBounds(rect)
+ _config(_dslGravity, gravity, width, height, offsetX, offsetY, callback)
+ return _dslGravity
+}
+
+/**
+ * 默认计算出的是目标中心点坐标参考距离
+ * [com.angcyo.drawable.DslGravity.getGravityRelativeCenter]
+ * */
+fun dslGravity(
+ rect: Rect, //计算的矩形
+ gravity: Int, //重力
+ width: Float, //放置目标的宽度
+ height: Float, //放置目标的高度
+ offsetX: Int = 0, //额外的偏移,会根据[gravity]进行左右|上下取反
+ offsetY: Int = 0,
+ callback: (dslGravity: DslGravity, centerX: Int, centerY: Int) -> Unit
+): DslGravity {
+ val _dslGravity = DslGravity()
+ _dslGravity.setGravityBounds(rect)
+ _config(_dslGravity, gravity, width, height, offsetX, offsetY, callback)
+ return _dslGravity
+}
+
+private fun _config(
+ _dslGravity: DslGravity,
+ gravity: Int, //重力
+ width: Float, //放置目标的宽度
+ height: Float, //放置目标的高度
+ offsetX: Int = 0, //额外的偏移,会根据[gravity]进行左右|上下取反
+ offsetY: Int = 0,
+ callback: (dslGravity: DslGravity, centerX: Int, centerY: Int) -> Unit
+) {
+ _dslGravity.gravity = gravity
+ _dslGravity.gravityOffsetX = offsetX
+ _dslGravity.gravityOffsetY = offsetY
+ _dslGravity.applyGravity(width, height) { centerX, centerY ->
+ callback(_dslGravity, centerX, centerY)
+ }
+}
+
+/**居中*/
+fun Int.isCenter(): Boolean {
+ val layoutDirection = 0
+ val absoluteGravity = Gravity.getAbsoluteGravity(this, layoutDirection)
+ val verticalGravity = this and Gravity.VERTICAL_GRAVITY_MASK
+ val horizontalGravity = absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK
+
+ return verticalGravity == Gravity.CENTER_VERTICAL && horizontalGravity == Gravity.CENTER_HORIZONTAL
+}
+
+fun Int.isLeft(): Boolean {
+ val layoutDirection = 0
+ val absoluteGravity = Gravity.getAbsoluteGravity(this, layoutDirection)
+ val horizontalGravity = absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK
+
+ return horizontalGravity == Gravity.LEFT
+}
+
+fun Int.isRight(): Boolean {
+ val layoutDirection = 0
+ val absoluteGravity = Gravity.getAbsoluteGravity(this, layoutDirection)
+ val horizontalGravity = absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK
+
+ return horizontalGravity == Gravity.RIGHT
+}
+
+fun Int.isTop(): Boolean {
+ val verticalGravity = this and Gravity.VERTICAL_GRAVITY_MASK
+ return verticalGravity == Gravity.TOP
+}
+
+fun Int.isBottom(): Boolean {
+ val verticalGravity = this and Gravity.VERTICAL_GRAVITY_MASK
+ return verticalGravity == Gravity.BOTTOM
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslSelector.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslSelector.kt
new file mode 100644
index 000000000..955680e86
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslSelector.kt
@@ -0,0 +1,438 @@
+package com.angcyo.tablayout
+
+import android.view.View
+import android.view.ViewGroup
+import android.widget.CompoundButton
+
+/**
+ * 用来操作[ViewGroup]中的[child], 支持单选, 多选, 拦截.
+ * 操作的都是可见性为[VISIBLE]的[View]
+ *
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/24
+ */
+
+open class DslSelector {
+
+ var parent: ViewGroup? = null
+ var dslSelectorConfig: DslSelectorConfig = DslSelectorConfig()
+
+ //可见view列表
+ val visibleViewList: MutableList = mutableListOf()
+
+ /**
+ * 选中的索引列表
+ * */
+ val selectorIndexList: MutableList = mutableListOf()
+ get() {
+ field.clear()
+ visibleViewList.forEachIndexed { index, view ->
+ if (view.isSe()) {
+ field.add(index)
+ }
+ }
+
+ return field
+ }
+
+ /**
+ * 选中的View列表
+ * */
+ val selectorViewList: MutableList = mutableListOf()
+ get() {
+ field.clear()
+ visibleViewList.forEachIndexed { index, view ->
+ if (view.isSe() || index == dslSelectIndex) {
+ field.add(view)
+ }
+ }
+ return field
+ }
+
+ //child 点击事件处理
+ val _onChildClickListener = View.OnClickListener {
+ val index = visibleViewList.indexOf(it)
+ val select =
+ if (dslSelectorConfig.dslMultiMode || dslSelectorConfig.dslMinSelectLimit < 1) {
+ !it.isSe()
+ } else {
+ true
+ }
+
+ if (!interceptSelector(index, select, true)) {
+ selector(
+ visibleViewList.indexOf(it),
+ select,
+ notify = true,
+ fromUser = true,
+ forceNotify = it is CompoundButton && dslSelectorConfig.dslMultiMode
+ )
+ }
+ }
+
+ /**兼容[CompoundButton]*/
+ val _onCheckedChangeListener = CompoundButton.OnCheckedChangeListener { buttonView, isChecked ->
+ buttonView.isChecked = buttonView.isSelected //恢复状态 不做任何处理, 在[OnClickListener]中处理
+ /*val index = visibleViewList.indexOf(buttonView)
+
+ if (interceptSelector(index, isChecked, false)) {
+ //拦截了此操作
+ buttonView.isChecked = !isChecked //恢复状态
+ }
+
+ val selectorViewList = selectorViewList
+ val sum = selectorViewList.size
+ //Limit 过滤
+ if (buttonView.isChecked) {
+ if (sum > dslSelectorConfig.dslMaxSelectLimit) {
+ //不允许选择
+ buttonView.isChecked = false //恢复状态
+ }
+ } else {
+ //取消选择, 检查是否达到了 limit
+ if (sum < dslSelectorConfig.dslMinSelectLimit) {
+ //不允许取消选择
+ buttonView.isChecked = true //恢复状态
+ }
+ }
+
+ if (isChecked) {
+ //已经选中了控件
+ } else {
+ //已经取消了控件
+ }*/
+ }
+
+ /**当前选中的索引*/
+ var dslSelectIndex = -1
+
+ /**安装*/
+ fun install(viewGroup: ViewGroup, config: DslSelectorConfig.() -> Unit = {}): DslSelector {
+ dslSelectIndex = -1
+ parent = viewGroup
+ updateVisibleList()
+ dslSelectorConfig.config()
+
+ updateStyle()
+ updateClickListener()
+
+ if (dslSelectIndex in 0 until visibleViewList.size) {
+ selector(dslSelectIndex)
+ }
+
+ return this
+ }
+
+ /**更新样式*/
+ fun updateStyle() {
+ visibleViewList.forEachIndexed { index, view ->
+ val selector = dslSelectIndex == index || view.isSe()
+ dslSelectorConfig.onStyleItemView(view, index, selector)
+ }
+ }
+
+ /**更新child的点击事件*/
+ fun updateClickListener() {
+ parent?.apply {
+ for (i in 0 until childCount) {
+ getChildAt(i)?.let {
+ it.setOnClickListener(_onChildClickListener)
+ if (it is CompoundButton) {
+ it.setOnCheckedChangeListener(_onCheckedChangeListener)
+ }
+ }
+ }
+ }
+ }
+
+ /**更新可见视图列表*/
+ fun updateVisibleList(): List {
+ visibleViewList.clear()
+ parent?.apply {
+ for (i in 0 until childCount) {
+ getChildAt(i)?.let {
+ if (it.visibility == View.VISIBLE) {
+ visibleViewList.add(it)
+ }
+ }
+ }
+ }
+ if (dslSelectIndex in visibleViewList.indices) {
+ if (!visibleViewList[dslSelectIndex].isSe()) {
+ visibleViewList[dslSelectIndex].setSe(true)
+ }
+ } else {
+ //如果当前选中的索引, 不在可见视图列表中
+ dslSelectIndex = -1
+ }
+ return visibleViewList
+ }
+
+ /**
+ * 操作单个
+ * @param index 操作目标的索引值
+ * @param select 选中 or 取消选中
+ * @param notify 是否需要通知事件
+ * @param forceNotify 是否强制通知事件.child使用[CompoundButton]时, 推荐使用
+ * */
+ fun selector(
+ index: Int,
+ select: Boolean = true,
+ notify: Boolean = true,
+ fromUser: Boolean = false,
+ forceNotify: Boolean = false
+ ) {
+ val oldSelectorList = selectorIndexList.toList()
+ val lastSelectorIndex: Int? = oldSelectorList.lastOrNull()
+ val reselect = !dslSelectorConfig.dslMultiMode &&
+ oldSelectorList.isNotEmpty() &&
+ oldSelectorList.contains(index)
+
+ var needNotify = _selector(index, select, fromUser) || forceNotify
+
+ if (!oldSelectorList.isChange(selectorIndexList)) {
+ //选中项, 未改变时不通知
+ needNotify = false
+ }
+
+ if (needNotify || reselect) {
+ dslSelectIndex = selectorIndexList.lastOrNull() ?: -1
+ if (notify) {
+ notifySelectChange(lastSelectorIndex ?: -1, reselect, fromUser)
+ }
+ }
+ }
+
+ /**选择所有
+ * [select] true:选择所有, false:取消所有*/
+ fun selectorAll(
+ select: Boolean = true,
+ notify: Boolean = true,
+ fromUser: Boolean = true
+ ) {
+ val indexList = visibleViewList.mapIndexedTo(mutableListOf()) { index, _ ->
+ index
+ }
+ selector(indexList, select, notify, fromUser)
+ }
+
+ /**
+ * 操作多个
+ * @param select 选中 or 取消选中
+ * [selector]
+ * */
+ fun selector(
+ indexList: MutableList,
+ select: Boolean = true,
+ notify: Boolean = true,
+ fromUser: Boolean = false
+ ) {
+ val oldSelectorIndexList = selectorIndexList
+ val lastSelectorIndex: Int? = oldSelectorIndexList.lastOrNull()
+
+ var result = false
+
+ indexList.forEach {
+ result = _selector(it, select, fromUser) || result
+ }
+
+ if (result) {
+ dslSelectIndex = selectorIndexList.lastOrNull() ?: -1
+ if (notify) {
+ notifySelectChange(lastSelectorIndex ?: -1, false, fromUser)
+ }
+ }
+ }
+
+ /**通知事件*/
+ fun notifySelectChange(lastSelectorIndex: Int, reselect: Boolean, fromUser: Boolean) {
+ val indexSelectorList = selectorIndexList
+ dslSelectorConfig.onSelectViewChange(
+ visibleViewList.getOrNull(lastSelectorIndex),
+ selectorViewList,
+ reselect,
+ fromUser
+ )
+ dslSelectorConfig.onSelectIndexChange(
+ lastSelectorIndex,
+ indexSelectorList,
+ reselect,
+ fromUser
+ )
+ }
+
+ /**当前的操作是否被拦截*/
+ fun interceptSelector(index: Int, select: Boolean, fromUser: Boolean): Boolean {
+ val visibleViewList = visibleViewList
+ if (index !in 0 until visibleViewList.size) {
+ return true
+ }
+ return dslSelectorConfig.onSelectItemView(visibleViewList[index], index, select, fromUser)
+ }
+
+ /**@return 是否发生过改变*/
+ fun _selector(index: Int, select: Boolean, fromUser: Boolean): Boolean {
+ val visibleViewList = visibleViewList
+ //超范围过滤
+ if (index !in 0 until visibleViewList.size) {
+ "index out of list.".logi()
+ return false
+ }
+
+ val selectorIndexList = selectorIndexList
+ val selectorViewList = selectorViewList
+
+ if (selectorIndexList.isNotEmpty()) {
+ if (select) {
+ //需要选中某项
+
+ if (dslSelectorConfig.dslMultiMode) {
+ //多选模式
+ if (selectorIndexList.contains(index)) {
+ //已经选中
+ return false
+ }
+ } else {
+ //单选模式
+
+ //取消之前选中
+ selectorIndexList.forEach {
+ if (it != index) {
+ visibleViewList[it].setSe(false)
+ }
+ }
+
+ if (selectorIndexList.contains(index)) {
+ //已经选中
+ return true
+ }
+ }
+
+ } else {
+ //需要取消选中
+ if (!selectorIndexList.contains(index)) {
+ //目标已经是未选中
+ return false
+ }
+ }
+ }
+
+ //Limit 过滤
+ if (select) {
+ val sum = selectorViewList.size + 1
+ if (sum > dslSelectorConfig.dslMaxSelectLimit) {
+ //不允许选择
+ return false
+ }
+ } else {
+ //取消选择, 检查是否达到了 limit
+ val sum = selectorViewList.size - 1
+ if (sum < dslSelectorConfig.dslMinSelectLimit) {
+ //不允许取消选择
+ return false
+ }
+ }
+
+ val selectorView = visibleViewList[index]
+
+ //更新选中样式
+ selectorView.setSe(select)
+
+ if (dslSelectorConfig.dslMultiMode) {
+ //多选
+ } else {
+ //单选
+
+ //取消之前
+ selectorViewList.forEach { view ->
+ //更新样式
+ val indexOf = visibleViewList.indexOf(view)
+ if (indexOf != index &&
+ !dslSelectorConfig.onSelectItemView(view, indexOf, false, fromUser)
+ ) {
+ view.setSe(false)
+ dslSelectorConfig.onStyleItemView(view, indexOf, false)
+ }
+ }
+ }
+
+ dslSelectorConfig.onStyleItemView(selectorView, index, select)
+
+ return true
+ }
+
+ /**是否选中状态*/
+ fun View.isSe(): Boolean {
+ return isSelected || if (this is CompoundButton) isChecked else false
+ }
+
+ fun View.setSe(se: Boolean) {
+ isSelected = se
+ if (this is CompoundButton) isChecked = se
+ }
+}
+
+/**
+ * Dsl配置项
+ * */
+open class DslSelectorConfig {
+
+ /**取消选择时, 最小需要保持多个选中. 可以决定单选时, 是否允许取消所有选中*/
+ var dslMinSelectLimit = 1
+
+ /**多选时, 最大允许多个选中*/
+ var dslMaxSelectLimit = Int.MAX_VALUE
+
+ /**是否是多选模式*/
+ var dslMultiMode: Boolean = false
+
+ /**
+ * 用来初始化[itemView]的样式
+ * [onSelectItemView]
+ * */
+ var onStyleItemView: (itemView: View, index: Int, select: Boolean) -> Unit =
+ { _, _, _ ->
+
+ }
+
+ /**
+ * 选中[View]改变回调, 优先于[onSelectIndexChange]触发, 区别在于参数类型不一样
+ * @param fromView 单选模式下有效, 表示之前选中的[View]
+ * @param reselect 是否是重复选择, 只在单选模式下有效
+ * @param fromUser 是否是用户产生的回调, 而非代码设置
+ * */
+ var onSelectViewChange: (fromView: View?, selectViewList: List, reselect: Boolean, fromUser: Boolean) -> Unit =
+ { _, _, _, _ ->
+
+ }
+
+ /**
+ * 选中改变回调
+ * [onSelectViewChange]
+ * @param fromIndex 单选模式下有效, 表示之前选中的[View], 在可见性[child]列表中的索引
+ * */
+ var onSelectIndexChange: (fromIndex: Int, selectIndexList: List, reselect: Boolean, fromUser: Boolean) -> Unit =
+ { fromIndex, selectList, reselect, fromUser ->
+ "选择:[$fromIndex]->${selectList} reselect:$reselect fromUser:$fromUser".logi()
+ }
+
+ /**
+ * 当需要选中[itemView]时回调, 返回[true]表示拦截默认处理
+ * @param itemView 操作的[View]
+ * @param index [itemView]在可见性view列表中的索引. 非ViewGroup中的索引
+ * @param select 选中 or 取消选中
+ * @return true 表示拦截默认处理
+ * */
+ var onSelectItemView: (itemView: View, index: Int, select: Boolean, fromUser: Boolean) -> Boolean =
+ { _, _, _, _ ->
+ false
+ }
+}
+
+/**[DslSelector]组件*/
+fun dslSelector(viewGroup: ViewGroup, config: DslSelectorConfig.() -> Unit = {}): DslSelector {
+ return DslSelector().apply {
+ install(viewGroup, config)
+ }
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslTabBadge.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabBadge.kt
new file mode 100644
index 000000000..0761d1d3a
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabBadge.kt
@@ -0,0 +1,222 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.graphics.Color
+import android.util.AttributeSet
+import android.view.Gravity
+import androidx.annotation.Px
+
+/**
+ * 角标
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/12/13
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+open class DslTabBadge : DslBadgeDrawable() {
+
+ /**角标默认配置项*/
+ val defaultBadgeConfig = TabBadgeConfig()
+
+ /**预览的角标属性*/
+ var xmlBadgeText: String? = null
+
+ override fun initAttribute(context: Context, attributeSet: AttributeSet?) {
+ val typedArray =
+ context.obtainStyledAttributes(attributeSet, R.styleable.DslTabLayout)
+ gradientSolidColor =
+ typedArray.getColor(
+ R.styleable.DslTabLayout_tab_badge_solid_color,
+ defaultBadgeConfig.badgeSolidColor
+ )
+ defaultBadgeConfig.badgeSolidColor = gradientSolidColor
+
+ badgeTextColor =
+ typedArray.getColor(
+ R.styleable.DslTabLayout_tab_badge_text_color,
+ defaultBadgeConfig.badgeTextColor
+ )
+ defaultBadgeConfig.badgeTextColor = badgeTextColor
+
+ gradientStrokeColor =
+ typedArray.getColor(
+ R.styleable.DslTabLayout_tab_badge_stroke_color,
+ defaultBadgeConfig.badgeStrokeColor
+ )
+ defaultBadgeConfig.badgeStrokeColor = gradientStrokeColor
+
+ gradientStrokeWidth =
+ typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_stroke_width,
+ defaultBadgeConfig.badgeStrokeWidth
+ )
+ defaultBadgeConfig.badgeStrokeWidth = gradientStrokeWidth
+
+ badgeGravity = typedArray.getInt(
+ R.styleable.DslTabLayout_tab_badge_gravity,
+ defaultBadgeConfig.badgeGravity
+ )
+ defaultBadgeConfig.badgeGravity = badgeGravity
+
+ badgeOffsetX = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_offset_x,
+ defaultBadgeConfig.badgeOffsetX
+ )
+ defaultBadgeConfig.badgeOffsetX = badgeOffsetX
+ badgeOffsetY = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_offset_y,
+ defaultBadgeConfig.badgeOffsetY
+ )
+ defaultBadgeConfig.badgeOffsetY = badgeOffsetY
+
+ badgeCircleOffsetX = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_circle_offset_x,
+ defaultBadgeConfig.badgeOffsetX
+ )
+ defaultBadgeConfig.badgeCircleOffsetX = badgeCircleOffsetX
+ badgeCircleOffsetY = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_circle_offset_y,
+ defaultBadgeConfig.badgeOffsetY
+ )
+ defaultBadgeConfig.badgeCircleOffsetY = badgeCircleOffsetY
+
+ badgeCircleRadius = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_circle_radius,
+ defaultBadgeConfig.badgeCircleRadius
+ )
+ defaultBadgeConfig.badgeCircleRadius = badgeCircleRadius
+
+ val badgeRadius = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_radius,
+ defaultBadgeConfig.badgeRadius
+ )
+ cornerRadius(badgeRadius.toFloat())
+ defaultBadgeConfig.badgeRadius = badgeRadius
+
+ badgePaddingLeft = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_padding_left,
+ defaultBadgeConfig.badgePaddingLeft
+ )
+ defaultBadgeConfig.badgePaddingLeft = badgePaddingLeft
+
+ badgePaddingRight = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_padding_right,
+ defaultBadgeConfig.badgePaddingRight
+ )
+ defaultBadgeConfig.badgePaddingRight = badgePaddingRight
+
+ badgePaddingTop = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_padding_top,
+ defaultBadgeConfig.badgePaddingTop
+ )
+ defaultBadgeConfig.badgePaddingTop = badgePaddingTop
+
+ badgePaddingBottom = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_padding_bottom,
+ defaultBadgeConfig.badgePaddingBottom
+ )
+ defaultBadgeConfig.badgePaddingBottom = badgePaddingBottom
+
+ xmlBadgeText = typedArray.getString(R.styleable.DslTabLayout_tab_badge_text)
+
+ badgeTextSize = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_badge_text_size,
+ defaultBadgeConfig.badgeTextSize.toInt()
+ ).toFloat()
+ defaultBadgeConfig.badgeTextSize = badgeTextSize
+
+ defaultBadgeConfig.badgeAnchorChildIndex =
+ typedArray.getInteger(
+ R.styleable.DslTabLayout_tab_badge_anchor_child_index,
+ defaultBadgeConfig.badgeAnchorChildIndex
+ )
+ defaultBadgeConfig.badgeIgnoreChildPadding = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_badge_ignore_child_padding,
+ defaultBadgeConfig.badgeIgnoreChildPadding
+ )
+
+ defaultBadgeConfig.badgeMinWidth = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_badge_min_width,
+ defaultBadgeConfig.badgeMinWidth
+ )
+
+ defaultBadgeConfig.badgeMinHeight = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_badge_min_height,
+ defaultBadgeConfig.badgeMinHeight
+ )
+
+ typedArray.recycle()
+ super.initAttribute(context, attributeSet)
+ }
+
+ /**使用指定配置, 更新[DslBadgeDrawable]*/
+ fun updateBadgeConfig(badgeConfig: TabBadgeConfig) {
+ gradientSolidColor = badgeConfig.badgeSolidColor
+ gradientStrokeColor = badgeConfig.badgeStrokeColor
+ gradientStrokeWidth = badgeConfig.badgeStrokeWidth
+ badgeTextColor = badgeConfig.badgeTextColor
+ badgeGravity = badgeConfig.badgeGravity
+ badgeOffsetX = badgeConfig.badgeOffsetX
+ badgeOffsetY = badgeConfig.badgeOffsetY
+ badgeCircleOffsetX = badgeConfig.badgeCircleOffsetX
+ badgeCircleOffsetY = badgeConfig.badgeCircleOffsetY
+ badgeCircleRadius = badgeConfig.badgeCircleRadius
+ badgePaddingLeft = badgeConfig.badgePaddingLeft
+ badgePaddingRight = badgeConfig.badgePaddingRight
+ badgePaddingTop = badgeConfig.badgePaddingTop
+ badgePaddingBottom = badgeConfig.badgePaddingBottom
+ badgeTextSize = badgeConfig.badgeTextSize
+ cornerRadius(badgeConfig.badgeRadius.toFloat())
+ badgeMinHeight = badgeConfig.badgeMinHeight
+ badgeMinWidth = badgeConfig.badgeMinWidth
+ badgeText = badgeConfig.badgeText
+ }
+}
+
+/**角标绘制参数配置*/
+data class TabBadgeConfig(
+ /**角标的文本(默认居中绘制文本,暂不支持修改), 空字符串会绘制成小圆点
+ * null 不绘制角标
+ * "" 空字符绘制圆点
+ * 其他 正常绘制
+ * */
+ var badgeText: String? = null,
+ /**重力*/
+ var badgeGravity: Int = Gravity.CENTER,
+ /**角标背景颜色*/
+ var badgeSolidColor: Int = Color.RED,
+ /**角标边框颜色*/
+ var badgeStrokeColor: Int = Color.TRANSPARENT,
+ /**角标边框宽度*/
+ var badgeStrokeWidth: Int = 0,
+
+ /**角标文本颜色*/
+ var badgeTextColor: Int = Color.WHITE,
+ /**角标文本字体大小*/
+ @Px
+ var badgeTextSize: Float = 12 * dp,
+ /**圆点状态时的半径大小*/
+ var badgeCircleRadius: Int = 4 * dpi,
+ /**圆角大小*/
+ var badgeRadius: Int = 10 * dpi,
+ /**额外偏移距离, 会根据[Gravity]自动取负值*/
+ var badgeOffsetX: Int = 0,
+ var badgeOffsetY: Int = 0,
+ var badgeCircleOffsetX: Int = 0,
+ var badgeCircleOffsetY: Int = 0,
+ /**圆点状态时无效*/
+ var badgePaddingLeft: Int = 4 * dpi,
+ var badgePaddingRight: Int = 4 * dpi,
+ var badgePaddingTop: Int = 0,
+ var badgePaddingBottom: Int = 0,
+
+ var badgeAnchorChildIndex: Int = -1,
+ var badgeIgnoreChildPadding: Boolean = true,
+
+ /**最小的高度大小, px. 大于0生效; 圆点时属性无效*/
+ var badgeMinHeight: Int = -2,
+
+ /**最小的宽度大小, px. 大于0生效; 圆点时属性无效;
+ * -1 表示使用使用计算出来的高度值*/
+ var badgeMinWidth: Int = -1
+)
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslTabBorder.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabBorder.kt
new file mode 100644
index 000000000..b1d4d6003
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabBorder.kt
@@ -0,0 +1,279 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import android.util.AttributeSet
+import android.view.View
+import androidx.core.view.ViewCompat
+
+/**
+ * 边框绘制, 支持首尾圆角中间不圆角的样式
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/27
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+open class DslTabBorder : DslGradientDrawable() {
+
+ /**
+ * 是否要接管[itemView]背景的绘制
+ * [updateItemBackground]
+ * */
+ var borderDrawItemBackground: Boolean = true
+
+ /**是否保持每个[itemView]的圆角都一样, 否则首尾有圆角, 中间没有圆角*/
+ var borderKeepItemRadius: Boolean = false
+
+ var borderBackgroundDrawable: Drawable? = null
+
+ /**宽度补偿*/
+ var borderBackgroundWidthOffset: Int = 0
+
+ /**高度补偿*/
+ var borderBackgroundHeightOffset: Int = 0
+
+ /**强制指定选中item的背景颜色*/
+ var borderItemBackgroundSolidColor: Int? = null
+
+ /**当item不可选中时的背景绘制颜色
+ * [com.angcyo.tablayout.DslTabLayout.itemEnableSelector]
+ * [borderItemBackgroundSolidColor]*/
+ var borderItemBackgroundSolidDisableColor: Int? = null
+
+ /**强制指定选中item的背景渐变颜色*/
+ var borderItemBackgroundGradientColors: IntArray? = null
+
+ override fun initAttribute(context: Context, attributeSet: AttributeSet?) {
+ val typedArray =
+ context.obtainStyledAttributes(attributeSet, R.styleable.DslTabLayout)
+
+ val borderBackgroundColor =
+ typedArray.getColor(R.styleable.DslTabLayout_tab_border_solid_color, gradientSolidColor)
+
+ gradientStrokeColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_border_stroke_color,
+ gradientStrokeColor
+ )
+ gradientStrokeWidth = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_border_stroke_width,
+ 2 * dpi
+ )
+ val radiusSize =
+ typedArray.getDimensionPixelOffset(R.styleable.DslTabLayout_tab_border_radius_size, 0)
+
+ cornerRadius(radiusSize.toFloat())
+
+ originDrawable = typedArray.getDrawable(R.styleable.DslTabLayout_tab_border_drawable)
+
+ borderDrawItemBackground = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_border_draw_item_background,
+ borderDrawItemBackground
+ )
+
+ borderKeepItemRadius = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_border_keep_item_radius,
+ borderKeepItemRadius
+ )
+
+ borderBackgroundWidthOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_border_item_background_width_offset,
+ borderBackgroundWidthOffset
+ )
+
+ borderBackgroundHeightOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_border_item_background_height_offset,
+ borderBackgroundHeightOffset
+ )
+
+ //
+ if (typedArray.hasValue(R.styleable.DslTabLayout_tab_border_item_background_solid_color)) {
+ borderItemBackgroundSolidColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_border_item_background_solid_color,
+ gradientStrokeColor
+ )
+ }
+ if (typedArray.hasValue(R.styleable.DslTabLayout_tab_border_item_background_solid_disable_color)) {
+ borderItemBackgroundSolidDisableColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_border_item_background_solid_disable_color,
+ borderItemBackgroundSolidColor ?: gradientStrokeColor
+ )
+ }
+
+ if (typedArray.hasValue(R.styleable.DslTabLayout_tab_border_item_background_gradient_start_color) ||
+ typedArray.hasValue(R.styleable.DslTabLayout_tab_border_item_background_gradient_end_color)
+ ) {
+ val startColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_border_item_background_gradient_start_color,
+ gradientStrokeColor
+ )
+ val endColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_border_item_background_gradient_end_color,
+ gradientStrokeColor
+ )
+ borderItemBackgroundGradientColors = intArrayOf(startColor, endColor)
+ }
+
+ typedArray.recycle()
+
+ if (originDrawable == null) {
+ //无自定义的drawable, 那么自绘.
+ borderBackgroundDrawable = DslGradientDrawable().configDrawable {
+ gradientSolidColor = borderBackgroundColor
+ gradientRadii = this@DslTabBorder.gradientRadii
+ }.originDrawable
+
+ updateOriginDrawable()
+ }
+ }
+
+ override fun draw(canvas: Canvas) {
+ super.draw(canvas)
+
+ originDrawable?.apply {
+ setBounds(
+ paddingLeft,
+ paddingBottom,
+ viewWidth - paddingRight,
+ viewHeight - paddingBottom
+ )
+ draw(canvas)
+ }
+ }
+
+ fun drawBorderBackground(canvas: Canvas) {
+ super.draw(canvas)
+
+ borderBackgroundDrawable?.apply {
+ setBounds(
+ paddingLeft,
+ paddingBottom,
+ viewWidth - paddingRight,
+ viewHeight - paddingBottom
+ )
+ draw(canvas)
+ }
+ }
+
+ var itemSelectBgDrawable: Drawable? = null
+ var itemDeselectBgDrawable: Drawable? = null
+
+ /**开启边框绘制后, [itemView]的背景也需要负责设置*/
+ open fun updateItemBackground(
+ tabLayout: DslTabLayout,
+ itemView: View,
+ index: Int,
+ select: Boolean
+ ) {
+
+ if (!borderDrawItemBackground) {
+ return
+ }
+
+ if (select) {
+
+ val isFirst = index == 0
+ val isLast = index == tabLayout.dslSelector.visibleViewList.size - 1
+
+ val drawable = DslGradientDrawable().configDrawable {
+ gradientWidthOffset = borderBackgroundWidthOffset
+ gradientHeightOffset = borderBackgroundHeightOffset
+
+ gradientSolidColor =
+ borderItemBackgroundSolidColor ?: this@DslTabBorder.gradientStrokeColor
+ if (!tabLayout.itemEnableSelector) {
+ if (borderItemBackgroundSolidDisableColor != null) {
+ gradientSolidColor = borderItemBackgroundSolidDisableColor!!
+ }
+ }
+
+ gradientColors = borderItemBackgroundGradientColors
+
+ if ((isFirst && isLast) || borderKeepItemRadius) {
+ //只有一个child
+ gradientRadii = this@DslTabBorder.gradientRadii
+ } else if (isFirst) {
+ if (tabLayout.isHorizontal()) {
+ if (tabLayout.isLayoutRtl) {
+ gradientRadii = floatArrayOf(
+ 0f,
+ 0f,
+ this@DslTabBorder.gradientRadii[2],
+ this@DslTabBorder.gradientRadii[3],
+ this@DslTabBorder.gradientRadii[4],
+ this@DslTabBorder.gradientRadii[5],
+ 0f,
+ 0f
+ )
+ } else {
+ gradientRadii = floatArrayOf(
+ this@DslTabBorder.gradientRadii[0],
+ this@DslTabBorder.gradientRadii[1],
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ this@DslTabBorder.gradientRadii[6],
+ this@DslTabBorder.gradientRadii[7]
+ )
+ }
+ } else {
+ gradientRadii = floatArrayOf(
+ this@DslTabBorder.gradientRadii[0],
+ this@DslTabBorder.gradientRadii[1],
+ this@DslTabBorder.gradientRadii[2],
+ this@DslTabBorder.gradientRadii[3],
+ 0f,
+ 0f,
+ 0f,
+ 0f
+ )
+ }
+ } else if (isLast) {
+ if (tabLayout.isHorizontal()) {
+ if (tabLayout.isLayoutRtl) {
+ gradientRadii = floatArrayOf(
+ this@DslTabBorder.gradientRadii[0],
+ this@DslTabBorder.gradientRadii[1],
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ this@DslTabBorder.gradientRadii[6],
+ this@DslTabBorder.gradientRadii[7]
+ )
+ } else {
+ gradientRadii = floatArrayOf(
+ 0f,
+ 0f,
+ this@DslTabBorder.gradientRadii[2],
+ this@DslTabBorder.gradientRadii[3],
+ this@DslTabBorder.gradientRadii[4],
+ this@DslTabBorder.gradientRadii[5],
+ 0f,
+ 0f
+ )
+ }
+ } else {
+ gradientRadii = floatArrayOf(
+ 0f,
+ 0f,
+ 0f,
+ 0f,
+ this@DslTabBorder.gradientRadii[4],
+ this@DslTabBorder.gradientRadii[5],
+ this@DslTabBorder.gradientRadii[6],
+ this@DslTabBorder.gradientRadii[7]
+ )
+ }
+ }
+ }
+
+ itemSelectBgDrawable = drawable
+
+ ViewCompat.setBackground(itemView, itemSelectBgDrawable)
+ } else {
+ ViewCompat.setBackground(itemView, itemDeselectBgDrawable)
+ }
+ }
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslTabDivider.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabDivider.kt
new file mode 100644
index 000000000..f0fe41c35
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabDivider.kt
@@ -0,0 +1,153 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.graphics.Canvas
+import android.util.AttributeSet
+import android.widget.LinearLayout
+
+/**
+ * 垂直分割线
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/27
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+open class DslTabDivider : DslGradientDrawable() {
+
+ var dividerWidth = 2 * dpi
+ var dividerHeight = 2 * dpi
+ var dividerMarginLeft = 0
+ var dividerMarginRight = 0
+ var dividerMarginTop = 0
+ var dividerMarginBottom = 0
+
+ /**
+ * [LinearLayout.SHOW_DIVIDER_BEGINNING]
+ * [LinearLayout.SHOW_DIVIDER_MIDDLE]
+ * [LinearLayout.SHOW_DIVIDER_END]
+ * */
+ var dividerShowMode = LinearLayout.SHOW_DIVIDER_MIDDLE
+
+ override fun initAttribute(context: Context, attributeSet: AttributeSet?) {
+ super.initAttribute(context, attributeSet)
+ val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.DslTabLayout)
+
+ dividerWidth = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_width,
+ dividerWidth
+ )
+ dividerHeight = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_height,
+ dividerHeight
+ )
+ dividerMarginLeft = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_margin_left,
+ dividerMarginLeft
+ )
+ dividerMarginRight = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_margin_right,
+ dividerMarginRight
+ )
+ dividerMarginTop = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_margin_top,
+ dividerMarginTop
+ )
+ dividerMarginBottom = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_margin_bottom,
+ dividerMarginBottom
+ )
+
+ if (typedArray.hasValue(R.styleable.DslTabLayout_tab_divider_solid_color)) {
+ gradientSolidColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_divider_solid_color,
+ gradientSolidColor
+ )
+ } else if (typedArray.hasValue(R.styleable.DslTabLayout_tab_border_stroke_color)) {
+ gradientSolidColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_border_stroke_color,
+ gradientSolidColor
+ )
+ } else {
+ gradientSolidColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_deselect_color,
+ gradientSolidColor
+ )
+ }
+
+ gradientStrokeColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_divider_stroke_color,
+ gradientStrokeColor
+ )
+ gradientStrokeWidth = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_stroke_width,
+ 0
+ )
+ val radiusSize =
+ typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_divider_radius_size,
+ 2 * dpi
+ )
+
+ cornerRadius(radiusSize.toFloat())
+
+ originDrawable = typedArray.getDrawable(R.styleable.DslTabLayout_tab_divider_drawable)
+
+ dividerShowMode =
+ typedArray.getInt(R.styleable.DslTabLayout_tab_divider_show_mode, dividerShowMode)
+
+ typedArray.recycle()
+
+ if (originDrawable == null) {
+ //无自定义的drawable, 那么自绘.
+
+ updateOriginDrawable()
+ }
+ }
+
+ override fun draw(canvas: Canvas) {
+ super.draw(canvas)
+
+ originDrawable?.apply {
+ bounds = this@DslTabDivider.bounds
+ draw(canvas)
+ }
+ }
+
+ val _tabLayout: DslTabLayout?
+ get() = if (callback is DslTabLayout) callback as DslTabLayout else null
+
+ /**
+ * [childIndex]位置前面是否需要分割线
+ * */
+ open fun haveBeforeDivider(childIndex: Int, childCount: Int): Boolean {
+ val tabLayout = _tabLayout
+ if (tabLayout != null && tabLayout.isHorizontal() && tabLayout.isLayoutRtl) {
+ if (childIndex == 0) {
+ return dividerShowMode and LinearLayout.SHOW_DIVIDER_END != 0
+ }
+ return dividerShowMode and LinearLayout.SHOW_DIVIDER_MIDDLE != 0
+ }
+
+ if (childIndex == 0) {
+ return dividerShowMode and LinearLayout.SHOW_DIVIDER_BEGINNING != 0
+ }
+ return dividerShowMode and LinearLayout.SHOW_DIVIDER_MIDDLE != 0
+ }
+
+ /**
+ * [childIndex]位置后面是否需要分割线
+ * */
+ open fun haveAfterDivider(childIndex: Int, childCount: Int): Boolean {
+ val tabLayout = _tabLayout
+ if (tabLayout != null && tabLayout.isHorizontal() && tabLayout.isLayoutRtl) {
+ if (childIndex == childCount - 1) {
+ return dividerShowMode and LinearLayout.SHOW_DIVIDER_BEGINNING != 0
+ }
+ }
+
+ if (childIndex == childCount - 1) {
+ return dividerShowMode and LinearLayout.SHOW_DIVIDER_END != 0
+ }
+ return false
+ }
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslTabHighlight.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabHighlight.kt
new file mode 100644
index 000000000..4c21bbd56
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabHighlight.kt
@@ -0,0 +1,118 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.util.AttributeSet
+import android.view.ViewGroup
+
+/**
+ *
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2021/05/19
+ * Copyright (c) 2020 ShenZhen Wayto Ltd. All rights reserved.
+ */
+open class DslTabHighlight(val tabLayout: DslTabLayout) : DslGradientDrawable() {
+
+ /**需要绘制的Drawable*/
+ var highlightDrawable: Drawable? = null
+
+ /**宽度测量模式*/
+ var highlightWidth = ViewGroup.LayoutParams.MATCH_PARENT
+
+ /**高度测量模式*/
+ var highlightHeight = ViewGroup.LayoutParams.MATCH_PARENT
+
+ /**宽度补偿*/
+ var highlightWidthOffset = 0
+
+ /**高度补偿*/
+ var highlightHeightOffset = 0
+
+ override fun initAttribute(context: Context, attributeSet: AttributeSet?) {
+ //super.initAttribute(context, attributeSet)
+
+ val typedArray =
+ context.obtainStyledAttributes(attributeSet, R.styleable.DslTabLayout)
+ highlightDrawable = typedArray.getDrawable(R.styleable.DslTabLayout_tab_highlight_drawable)
+
+ highlightWidth = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_highlight_width,
+ highlightWidth
+ )
+ highlightHeight = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_highlight_height,
+ highlightHeight
+ )
+
+ highlightWidthOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_highlight_width_offset,
+ highlightWidthOffset
+ )
+ highlightHeightOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_highlight_height_offset,
+ highlightHeightOffset
+ )
+
+ typedArray.recycle()
+
+ if (highlightDrawable == null && isValidConfig()) {
+ updateOriginDrawable()
+ }
+ }
+
+ override fun updateOriginDrawable(): GradientDrawable? {
+ val drawable = super.updateOriginDrawable()
+ highlightDrawable = originDrawable
+ return drawable
+ }
+
+ override fun draw(canvas: Canvas) {
+ //super.draw(canvas)
+ val itemView = tabLayout.currentItemView
+ if (itemView != null) {
+ val lp = itemView.layoutParams
+
+ if (lp is DslTabLayout.LayoutParams) {
+ lp.highlightDrawable ?: highlightDrawable
+ } else {
+ highlightDrawable
+ }?.apply {
+
+ val drawWidth: Int = when (highlightWidth) {
+ ViewGroup.LayoutParams.MATCH_PARENT -> itemView.measuredWidth
+ ViewGroup.LayoutParams.WRAP_CONTENT -> intrinsicWidth
+ else -> highlightWidth
+ } + highlightWidthOffset
+
+ val drawHeight: Int = when (highlightHeight) {
+ ViewGroup.LayoutParams.MATCH_PARENT -> itemView.measuredHeight
+ ViewGroup.LayoutParams.WRAP_CONTENT -> intrinsicHeight
+ else -> highlightHeight
+ } + highlightHeightOffset
+
+ val centerX: Int = itemView.left + (itemView.right - itemView.left) / 2
+ val centerY: Int = itemView.top + (itemView.bottom - itemView.top) / 2
+
+ setBounds(
+ centerX - drawWidth / 2,
+ centerY - drawHeight / 2,
+ centerX + drawWidth / 2,
+ centerY + drawHeight / 2
+ )
+
+ draw(canvas)
+ canvas.save()
+ if (tabLayout.isHorizontal()) {
+ canvas.translate(itemView.left.toFloat(), 0f)
+ } else {
+ canvas.translate(0f, itemView.top.toFloat())
+ }
+ itemView.draw(canvas)
+ canvas.restore()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslTabIndicator.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabIndicator.kt
new file mode 100644
index 000000000..316ced0ad
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabIndicator.kt
@@ -0,0 +1,931 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.drawable.Drawable
+import android.graphics.drawable.GradientDrawable
+import android.util.AttributeSet
+import android.view.View
+import android.view.ViewGroup
+import androidx.core.graphics.withSave
+import java.util.*
+import kotlin.math.absoluteValue
+import kotlin.math.max
+
+/**
+ * 指示器
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/25
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+open class DslTabIndicator(val tabLayout: DslTabLayout) : DslGradientDrawable() {
+
+ companion object {
+
+ /**非颜色值*/
+ const val NO_COLOR = -2
+
+ //---style---
+
+ /**不绘制指示器*/
+ const val INDICATOR_STYLE_NONE = 0
+
+ /**指示器绘制在[itemView]的顶部*/
+ const val INDICATOR_STYLE_TOP = 0x1
+
+ /**指示器绘制在[itemView]的底部*/
+ const val INDICATOR_STYLE_BOTTOM = 0x2
+
+ /**默认样式,指示器绘制在[itemView]的中心*/
+ const val INDICATOR_STYLE_CENTER = 0x4
+
+ /**前景绘制,
+ * 默认是背景绘制, 指示器绘制[itemView]的背部, [itemView] 请不要设置background, 否则可能看不见*/
+ const val INDICATOR_STYLE_FOREGROUND = 0x1000
+
+ //---gravity---
+
+ /**指示器重力在开始的位置(横向左边, 纵向上边)*/
+ const val INDICATOR_GRAVITY_START = 0x1
+
+ /**指示器重力在结束的位置(横向右边, 纵向下边)*/
+ const val INDICATOR_GRAVITY_END = 0x2
+
+ /**指示器重力在中间*/
+ const val INDICATOR_GRAVITY_CENTER = 0x4
+ }
+
+ /**指示器绘制的样式*/
+ var indicatorStyle = INDICATOR_STYLE_NONE //初始化
+
+ /**[indicatorStyle]*/
+ val _indicatorDrawStyle: Int
+ get() = indicatorStyle.remove(INDICATOR_STYLE_FOREGROUND)
+
+ /**优先将指示器显示在[DslTabLayout]的什么位置
+ * [INDICATOR_GRAVITY_START] 开始的位置
+ * [INDICATOR_GRAVITY_END] 结束的位置
+ * [INDICATOR_GRAVITY_CENTER] 中间的位置*/
+ var indicatorGravity = INDICATOR_GRAVITY_CENTER
+
+ /**
+ * 指示器在流向下一个位置时, 是否采用[Flow]流线的方式改变宽度
+ * */
+ var indicatorEnableFlow: Boolean = false
+
+ /**指示器闪现效果, 从当前位置直接跨越到目标位置*/
+ var indicatorEnableFlash: Boolean = false
+
+ /**使用clip的方式绘制闪现效果*/
+ var indicatorEnableFlashClip: Boolean = true
+
+ /**当目标和当前的索引差值<=此值时, [Flow]效果才有效*/
+ var indicatorFlowStep: Int = 1
+
+ /**指示器绘制实体*/
+ var indicatorDrawable: Drawable? = null
+ set(value) {
+ field = tintDrawableColor(value, indicatorColor)
+ }
+
+ /**过滤[indicatorDrawable]的颜色*/
+ var indicatorColor: Int = NO_COLOR
+ set(value) {
+ field = value
+ indicatorDrawable = indicatorDrawable
+ }
+
+ /**
+ * 指示器的宽度
+ * WRAP_CONTENT: [childView]内容的宽度,
+ * MATCH_PARENT: [childView]的宽度
+ * 40dp: 固定值
+ * */
+ var indicatorWidth = 0 //初始化
+
+ /**宽度补偿*/
+ var indicatorWidthOffset = 0
+
+ /**
+ * 指示器的高度
+ * WRAP_CONTENT: [childView]内容的高度,
+ * MATCH_PARENT: [childView]的高度
+ * 40dp: 固定值
+ * */
+ var indicatorHeight = 0 //初始化
+
+ /**高度补偿*/
+ var indicatorHeightOffset = 0
+
+ /**XY轴方向补偿*/
+ var indicatorXOffset = 0
+
+ /**会根据[indicatorStyle]自动取负值*/
+ var indicatorYOffset = 0
+
+ /**
+ * 宽高[WRAP_CONTENT]时, 内容view的定位索引
+ * */
+ var indicatorContentIndex = -1
+ var indicatorContentId = View.NO_ID
+
+ /**切换时是否需要动画的支持*/
+ var indicatorAnim = true
+
+ /**在获取锚点view的宽高时, 是否需要忽略对应的padding属性*/
+ var ignoreChildPadding: Boolean = true
+
+ init {
+ callback = tabLayout
+ }
+
+ override fun initAttribute(context: Context, attributeSet: AttributeSet?) {
+ val typedArray =
+ context.obtainStyledAttributes(attributeSet, R.styleable.DslTabLayout)
+
+ indicatorDrawable = typedArray.getDrawable(R.styleable.DslTabLayout_tab_indicator_drawable)
+ indicatorColor =
+ typedArray.getColor(R.styleable.DslTabLayout_tab_indicator_color, indicatorColor)
+ indicatorStyle = typedArray.getInt(
+ R.styleable.DslTabLayout_tab_indicator_style,
+ if (tabLayout.isHorizontal()) INDICATOR_STYLE_BOTTOM else INDICATOR_STYLE_TOP
+ )
+ indicatorGravity = typedArray.getInt(
+ R.styleable.DslTabLayout_tab_indicator_gravity,
+ indicatorGravity
+ )
+
+ //初始化指示器的高度和宽度
+ if (indicatorStyle.have(INDICATOR_STYLE_FOREGROUND)) {
+ //前景绘制
+ indicatorWidth = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_indicator_width,
+ if (tabLayout.isHorizontal()) ViewGroup.LayoutParams.MATCH_PARENT else 3 * dpi
+ )
+ indicatorHeight = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_indicator_height,
+ if (tabLayout.isHorizontal()) 3 * dpi else ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ indicatorXOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_x_offset,
+ if (tabLayout.isHorizontal()) 0 else 2 * dpi
+ )
+ indicatorYOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_y_offset,
+ if (tabLayout.isHorizontal()) 2 * dpi else 0
+ )
+ } else {
+ //背景绘制样式
+ if (tabLayout.isHorizontal()) {
+ indicatorWidth = ViewGroup.LayoutParams.MATCH_PARENT
+ indicatorHeight = ViewGroup.LayoutParams.MATCH_PARENT
+ } else {
+ indicatorHeight = ViewGroup.LayoutParams.MATCH_PARENT
+ indicatorWidth = ViewGroup.LayoutParams.MATCH_PARENT
+ }
+ indicatorWidth = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_indicator_width,
+ indicatorWidth
+ )
+ indicatorHeight = typedArray.getLayoutDimension(
+ R.styleable.DslTabLayout_tab_indicator_height,
+ indicatorHeight
+ )
+ indicatorXOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_x_offset,
+ indicatorXOffset
+ )
+ indicatorYOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_y_offset,
+ indicatorYOffset
+ )
+ }
+
+ ignoreChildPadding = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_indicator_ignore_child_padding,
+ !indicatorStyle.have(INDICATOR_STYLE_CENTER)
+ )
+
+ indicatorFlowStep =
+ typedArray.getInt(R.styleable.DslTabLayout_tab_indicator_flow_step, indicatorFlowStep)
+ indicatorEnableFlow = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_indicator_enable_flow,
+ indicatorEnableFlow
+ )
+ indicatorEnableFlash = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_indicator_enable_flash,
+ indicatorEnableFlash
+ )
+ indicatorEnableFlashClip = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_indicator_enable_flash_clip,
+ indicatorEnableFlashClip
+ )
+
+ indicatorWidthOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_width_offset,
+ indicatorWidthOffset
+ )
+ indicatorHeightOffset = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_height_offset,
+ indicatorHeightOffset
+ )
+ indicatorContentIndex = typedArray.getInt(
+ R.styleable.DslTabLayout_tab_indicator_content_index,
+ indicatorContentIndex
+ )
+ indicatorContentId = typedArray.getResourceId(
+ R.styleable.DslTabLayout_tab_indicator_content_id,
+ indicatorContentId
+ )
+ indicatorAnim = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_indicator_anim,
+ indicatorAnim
+ )
+
+ //代码构建Drawable
+ gradientShape =
+ typedArray.getInt(R.styleable.DslTabLayout_tab_indicator_shape, gradientShape)
+ gradientSolidColor =
+ typedArray.getColor(
+ R.styleable.DslTabLayout_tab_indicator_solid_color,
+ gradientSolidColor
+ )
+ gradientStrokeColor =
+ typedArray.getColor(
+ R.styleable.DslTabLayout_tab_indicator_stroke_color,
+ gradientStrokeColor
+ )
+ gradientStrokeWidth = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_stroke_width,
+ gradientStrokeWidth
+ )
+ gradientDashWidth = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_dash_width,
+ gradientDashWidth.toInt()
+ ).toFloat()
+ gradientDashGap = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_indicator_dash_gap,
+ gradientDashGap.toInt()
+ ).toFloat()
+
+ val gradientRadius =
+ typedArray.getDimensionPixelOffset(R.styleable.DslTabLayout_tab_indicator_radius, 0)
+ if (gradientRadius > 0) {
+ Arrays.fill(gradientRadii, gradientRadius.toFloat())
+ } else {
+ typedArray.getString(R.styleable.DslTabLayout_tab_indicator_radii)?.let {
+ _fillRadii(gradientRadii, it)
+ }
+ }
+
+ val gradientColors =
+ typedArray.getString(R.styleable.DslTabLayout_tab_indicator_gradient_colors)
+
+ this.gradientColors = if (gradientColors.isNullOrEmpty()) {
+ val startColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_indicator_gradient_start_color,
+ Color.TRANSPARENT
+ )
+ val endColor = typedArray.getColor(
+ R.styleable.DslTabLayout_tab_indicator_gradient_end_color,
+ Color.TRANSPARENT
+ )
+ if (startColor != endColor) {
+ intArrayOf(startColor, endColor)
+ } else {
+ this.gradientColors
+ }
+ } else {
+ _fillColor(gradientColors) ?: this.gradientColors
+ }
+ //...end
+
+ typedArray.recycle()
+
+ if (indicatorDrawable == null && isValidConfig()) {
+ updateOriginDrawable()
+ }
+ }
+
+ override fun updateOriginDrawable(): GradientDrawable? {
+ val drawable = super.updateOriginDrawable()
+ indicatorDrawable = originDrawable
+ return drawable
+ }
+
+ open fun tintDrawableColor(drawable: Drawable?, color: Int): Drawable? {
+ if (drawable == null || color == NO_COLOR) {
+ return drawable
+ }
+ return drawable.tintDrawableColor(color)
+ }
+
+ /**指示器需要参考的目标控件*/
+ open fun indicatorContentView(childView: View): View? {
+ val lp = childView.layoutParams as DslTabLayout.LayoutParams
+
+ val contentId =
+ if (lp.indicatorContentId != View.NO_ID) lp.indicatorContentId else indicatorContentId
+
+ if (contentId != View.NO_ID) {
+ return childView.findViewById(contentId)
+ }
+
+ //如果child强制指定了index, 就用指定的.
+ val contentIndex =
+ if (lp.indicatorContentIndex >= 0) lp.indicatorContentIndex else indicatorContentIndex
+
+ return if (contentIndex >= 0 && childView is ViewGroup && contentIndex in 0 until childView.childCount) {
+ //有指定
+ val contentChildView = childView.getChildAt(contentIndex)
+ contentChildView
+ } else {
+ //没有指定
+ null
+ }
+ }
+
+ /**根据指定[index]索引, 获取目标[View]*/
+ open fun targetChildView(
+ index: Int,
+ onChildView: (childView: View, contentChildView: View?) -> Unit
+ ) {
+ tabLayout.dslSelector.visibleViewList.getOrNull(index)?.also { childView ->
+ onChildView(childView, indicatorContentView(childView))
+ }
+ }
+
+ open fun getChildTargetPaddingLeft(childView: View): Int =
+ if (ignoreChildPadding) childView.paddingLeft else 0
+
+ open fun getChildTargetPaddingRight(childView: View): Int =
+ if (ignoreChildPadding) childView.paddingRight else 0
+
+ open fun getChildTargetPaddingTop(childView: View): Int =
+ if (ignoreChildPadding) childView.paddingTop else 0
+
+ open fun getChildTargetPaddingBottom(childView: View): Int =
+ if (ignoreChildPadding) childView.paddingBottom else 0
+
+ open fun getChildTargetWidth(childView: View): Int =
+ if (ignoreChildPadding) childView.viewDrawWidth else childView.measuredWidth
+
+ open fun getChildTargetHeight(childView: View): Int =
+ if (ignoreChildPadding) childView.viewDrawHeight else childView.measuredHeight
+
+ /**
+ * [childview]对应的中心x坐标
+ * */
+ open fun getChildTargetX(index: Int, gravity: Int = indicatorGravity): Int {
+
+ var result = if (index > 0) tabLayout.maxWidth else 0
+
+ targetChildView(index) { childView, contentChildView ->
+ result = if (contentChildView == null) {
+ when (gravity) {
+ INDICATOR_GRAVITY_START -> childView.left
+ INDICATOR_GRAVITY_END -> childView.right
+ else -> childView.left + getChildTargetPaddingLeft(childView) + getChildTargetWidth(
+ childView
+ ) / 2
+ }
+ } else {
+ when (gravity) {
+ INDICATOR_GRAVITY_START -> childView.left + contentChildView.left
+ INDICATOR_GRAVITY_END -> childView.left + contentChildView.right
+ else -> childView.left + contentChildView.left + getChildTargetPaddingLeft(
+ contentChildView
+ ) + getChildTargetWidth(
+ contentChildView
+ ) / 2
+ }
+ }
+ }
+
+ return result
+ }
+
+ open fun getChildTargetY(index: Int, gravity: Int = indicatorGravity): Int {
+
+ var result = if (index > 0) tabLayout.maxHeight else 0
+
+ targetChildView(index) { childView, contentChildView ->
+ result = if (contentChildView == null) {
+ when (gravity) {
+ INDICATOR_GRAVITY_START -> childView.top
+ INDICATOR_GRAVITY_END -> childView.bottom
+ else -> childView.top + getChildTargetPaddingTop(childView) + getChildTargetHeight(
+ childView
+ ) / 2
+ }
+ } else {
+ when (gravity) {
+ INDICATOR_GRAVITY_START -> childView.top + contentChildView.top
+ INDICATOR_GRAVITY_END -> childView.top + childView.bottom
+ else -> childView.top + contentChildView.top + getChildTargetPaddingTop(
+ contentChildView
+ ) + getChildTargetHeight(
+ contentChildView
+ ) / 2
+ }
+ }
+ }
+
+ return result
+ }
+
+ open fun getIndicatorDrawWidth(index: Int): Int {
+ var result = indicatorWidth
+
+ when (indicatorWidth) {
+ ViewGroup.LayoutParams.WRAP_CONTENT -> {
+ tabLayout.dslSelector.visibleViewList.getOrNull(index)?.also { childView ->
+ result = getChildTargetWidth(indicatorContentView(childView) ?: childView)
+ }
+ }
+ ViewGroup.LayoutParams.MATCH_PARENT -> {
+ tabLayout.dslSelector.visibleViewList.getOrNull(index)?.also { childView ->
+ result = childView.measuredWidth
+ }
+ }
+ }
+
+ return result + indicatorWidthOffset
+ }
+
+ open fun getIndicatorDrawHeight(index: Int): Int {
+ var result = indicatorHeight
+
+ when (indicatorHeight) {
+ ViewGroup.LayoutParams.WRAP_CONTENT -> {
+ tabLayout.dslSelector.visibleViewList.getOrNull(index)?.also { childView ->
+ result = getChildTargetHeight(indicatorContentView(childView) ?: childView)
+ }
+ }
+ ViewGroup.LayoutParams.MATCH_PARENT -> {
+ tabLayout.dslSelector.visibleViewList.getOrNull(index)?.also { childView ->
+ result = childView.measuredHeight
+ }
+ }
+ }
+
+ return result + indicatorHeightOffset
+ }
+
+ override fun draw(canvas: Canvas) {
+ //super.draw(canvas)
+ if (!isVisible || _indicatorDrawStyle == INDICATOR_STYLE_NONE || indicatorDrawable == null) {
+ //不绘制
+ return
+ }
+
+ if (tabLayout.isHorizontal()) {
+ drawHorizontal(canvas)
+ } else {
+ drawVertical(canvas)
+ }
+ }
+
+ fun drawHorizontal(canvas: Canvas) {
+ val childSize = tabLayout.dslSelector.visibleViewList.size
+
+ var currentIndex = currentIndex
+
+ if (_targetIndex in 0 until childSize) {
+ currentIndex = max(0, currentIndex)
+ }
+
+ if (currentIndex in 0 until childSize) {
+
+ } else {
+ //无效的index
+ return
+ }
+
+ //"绘制$currentIndex:$currentSelectIndex $positionOffset".logi()
+
+ val drawTargetX = getChildTargetX(currentIndex)
+ val drawWidth = getIndicatorDrawWidth(currentIndex)
+ val drawHeight = getIndicatorDrawHeight(currentIndex)
+
+ val drawLeft = drawTargetX - drawWidth / 2 + indicatorXOffset
+
+ //动画过程中的left
+ var animLeft = drawLeft
+ //width
+ var animWidth = drawWidth
+ //动画执行过程中, 高度额外变大的值
+ var animExHeight = 0
+
+ //end value
+ val nextDrawTargetX = getChildTargetX(_targetIndex)
+ val nextDrawWidth = getIndicatorDrawWidth(_targetIndex)
+ val nextDrawLeft = nextDrawTargetX - nextDrawWidth / 2 + indicatorXOffset
+
+ var animEndWidth = nextDrawWidth
+ var animEndLeft = nextDrawLeft
+
+ if (_targetIndex in 0 until childSize && _targetIndex != currentIndex) {
+
+ //动画过程参数计算变量
+ val animStartLeft = drawLeft
+ val animStartWidth = drawWidth
+
+ val animEndHeight = getIndicatorDrawHeight(_targetIndex)
+
+ if (indicatorEnableFlash) {
+ //闪现效果
+ animWidth = (animWidth * (1 - positionOffset)).toInt()
+ animEndWidth = (animEndWidth * positionOffset).toInt()
+
+ animLeft = drawTargetX - animWidth / 2 + indicatorXOffset
+ animEndLeft = nextDrawLeft
+ } else if (indicatorEnableFlow && (_targetIndex - currentIndex).absoluteValue <= indicatorFlowStep) {
+ //激活了流动效果
+
+ val flowEndWidth: Int
+ if (_targetIndex > currentIndex) {
+ flowEndWidth = animEndLeft - animStartLeft + animEndWidth
+
+ //目标在右边
+ animLeft = if (positionOffset >= 0.5) {
+ (animStartLeft + (animEndLeft - animStartLeft) * (positionOffset - 0.5) / 0.5f).toInt()
+ } else {
+ animStartLeft
+ }
+ } else {
+ flowEndWidth = animStartLeft - animEndLeft + animStartWidth
+
+ //目标在左边
+ animLeft = if (positionOffset >= 0.5) {
+ animEndLeft
+ } else {
+ (animStartLeft - (animStartLeft - animEndLeft) * positionOffset / 0.5f).toInt()
+ }
+ }
+
+ animWidth = if (positionOffset >= 0.5) {
+ (flowEndWidth - (flowEndWidth - animEndWidth) * (positionOffset - 0.5) / 0.5f).toInt()
+ } else {
+ (animStartWidth + (flowEndWidth - animStartWidth) * positionOffset / 0.5f).toInt()
+ }
+ } else {
+ //默认平移效果
+ if (_targetIndex > currentIndex) {
+ //目标在右边
+ animLeft =
+ (animStartLeft + (animEndLeft - animStartLeft) * positionOffset).toInt()
+ } else {
+ //目标在左边
+ animLeft =
+ (animStartLeft - (animStartLeft - animEndLeft) * positionOffset).toInt()
+ }
+
+ //动画过程中的宽度
+ animWidth =
+ (animStartWidth + (animEndWidth - animStartWidth) * positionOffset).toInt()
+ }
+
+ animExHeight = ((animEndHeight - drawHeight) * positionOffset).toInt()
+ }
+
+ //前景
+ val drawTop = when (_indicatorDrawStyle) {
+ //底部绘制
+ INDICATOR_STYLE_BOTTOM -> viewHeight - drawHeight - indicatorYOffset
+ //顶部绘制
+ INDICATOR_STYLE_TOP -> 0 + indicatorYOffset
+ //居中绘制
+ else -> paddingTop + viewDrawHeight / 2 - drawHeight / 2 + indicatorYOffset -
+ animExHeight +
+ (tabLayout._maxConvexHeight - _childConvexHeight(currentIndex)) / 2
+ }
+
+ indicatorDrawable?.apply {
+ if (indicatorEnableFlash) {
+ //flash
+ if (indicatorEnableFlashClip) {
+ drawIndicatorClipHorizontal(
+ this,
+ canvas,
+ drawLeft,
+ drawTop,
+ drawLeft + drawWidth,
+ drawTop + drawHeight + animExHeight,
+ animWidth,
+ 1 - positionOffset
+ )
+ } else {
+ drawIndicator(
+ this, canvas, animLeft,
+ drawTop,
+ animLeft + animWidth,
+ drawTop + drawHeight + animExHeight,
+ 1 - positionOffset
+ )
+ }
+
+ if (_targetIndex in 0 until childSize) {
+ if (indicatorEnableFlashClip) {
+ drawIndicatorClipHorizontal(
+ this,
+ canvas,
+ nextDrawLeft,
+ drawTop,
+ nextDrawLeft + nextDrawWidth,
+ drawTop + drawHeight + animExHeight,
+ animEndWidth,
+ positionOffset
+ )
+ } else {
+ drawIndicator(
+ this, canvas, animEndLeft,
+ drawTop,
+ animEndLeft + animEndWidth,
+ drawTop + drawHeight + animExHeight,
+ positionOffset
+ )
+ }
+ }
+ } else {
+ //normal
+ drawIndicator(
+ this, canvas, animLeft,
+ drawTop,
+ animLeft + animWidth,
+ drawTop + drawHeight + animExHeight,
+ 1 - positionOffset
+ )
+ }
+ }
+ }
+
+ fun drawIndicator(
+ indicator: Drawable,
+ canvas: Canvas,
+ l: Int,
+ t: Int,
+ r: Int,
+ b: Int,
+ offset: Float
+ ) {
+ indicator.apply {
+ if (this is ITabIndicatorDraw) {
+ setBounds(l, t, r, b)
+ onDrawTabIndicator(this@DslTabIndicator, canvas, offset)
+ } else {
+ val width = r - l
+ val height = b - t
+ setBounds(0, 0, width, height)
+ canvas.withSave {
+ translate(l.toFloat(), t.toFloat())
+ draw(canvas)
+ }
+ }
+ }
+ }
+
+ fun drawIndicatorClipHorizontal(
+ indicator: Drawable,
+ canvas: Canvas,
+ l: Int,
+ t: Int,
+ r: Int,
+ b: Int,
+ endWidth: Int,
+ offset: Float
+ ) {
+ indicator.apply {
+ canvas.save()
+ val dx = (r - l - endWidth) / 2
+ canvas.clipRect(l + dx, t, r - dx, b)
+ setBounds(l, t, r, b)
+ if (this is ITabIndicatorDraw) {
+ onDrawTabIndicator(this@DslTabIndicator, canvas, offset)
+ } else {
+ draw(canvas)
+ }
+ canvas.restore()
+ }
+ }
+
+ fun drawIndicatorClipVertical(
+ indicator: Drawable,
+ canvas: Canvas,
+ l: Int,
+ t: Int,
+ r: Int,
+ b: Int,
+ endHeight: Int,
+ offset: Float
+ ) {
+ indicator.apply {
+ canvas.save()
+ val dy = (b - t - endHeight) / 2
+ canvas.clipRect(l, t + dy, r, b - dy)
+ setBounds(l, t, r, b)
+ if (this is ITabIndicatorDraw) {
+ onDrawTabIndicator(this@DslTabIndicator, canvas, offset)
+ } else {
+ draw(canvas)
+ }
+ canvas.restore()
+ }
+ }
+
+ fun drawVertical(canvas: Canvas) {
+ val childSize = tabLayout.dslSelector.visibleViewList.size
+
+ var currentIndex = currentIndex
+
+ if (_targetIndex in 0 until childSize) {
+ currentIndex = max(0, currentIndex)
+ }
+
+ if (currentIndex in 0 until childSize) {
+
+ } else {
+ //无效的index
+ return
+ }
+
+ //"绘制$currentIndex:$currentSelectIndex $positionOffset".logi()
+
+ val drawTargetY = getChildTargetY(currentIndex)
+ val drawWidth = getIndicatorDrawWidth(currentIndex)
+ val drawHeight = getIndicatorDrawHeight(currentIndex)
+
+ val drawTop = drawTargetY - drawHeight / 2 + indicatorYOffset
+
+ //动画过程中的top
+ var animTop = drawTop
+ //height
+ var animHeight = drawHeight
+ //动画执行过程中, 宽度额外变大的值
+ var animExWidth = 0
+
+ //end value
+ val nextDrawTargetY = getChildTargetY(_targetIndex)
+ val nextDrawHeight = getIndicatorDrawHeight(_targetIndex)
+ val nextDrawTop = nextDrawTargetY - nextDrawHeight / 2 + indicatorYOffset
+
+ var animEndHeight = nextDrawHeight
+ var animEndTop = nextDrawTop
+
+ if (_targetIndex in 0 until childSize && _targetIndex != currentIndex) {
+
+ //动画过程参数计算变量
+ val animStartTop = drawTop
+ val animStartHeight = drawHeight
+
+ val animEndWidth = getIndicatorDrawWidth(_targetIndex)
+
+ if (indicatorEnableFlash) {
+ //闪现效果
+ animHeight = (animHeight * (1 - positionOffset)).toInt()
+ animEndHeight = (animEndHeight * positionOffset).toInt()
+
+ animTop = drawTargetY - animHeight / 2 + indicatorXOffset
+ animEndTop = nextDrawTargetY - animEndHeight / 2 + indicatorXOffset
+ } else if (indicatorEnableFlow && (_targetIndex - currentIndex).absoluteValue <= indicatorFlowStep) {
+ //激活了流动效果
+
+ val flowEndHeight: Int
+ if (_targetIndex > currentIndex) {
+ flowEndHeight = animEndTop - animStartTop + animEndHeight
+
+ //目标在下边
+ animTop = if (positionOffset >= 0.5) {
+ (animStartTop + (animEndTop - animStartTop) * (positionOffset - 0.5) / 0.5f).toInt()
+ } else {
+ animStartTop
+ }
+ } else {
+ flowEndHeight = animStartTop - animEndTop + animStartHeight
+
+ //目标在上边
+ animTop = if (positionOffset >= 0.5) {
+ animEndTop
+ } else {
+ (animStartTop - (animStartTop - animEndTop) * positionOffset / 0.5f).toInt()
+ }
+ }
+
+ animHeight = if (positionOffset >= 0.5) {
+ (flowEndHeight - (flowEndHeight - animEndHeight) * (positionOffset - 0.5) / 0.5f).toInt()
+ } else {
+ (animStartHeight + (flowEndHeight - animStartHeight) * positionOffset / 0.5f).toInt()
+ }
+ } else {
+ if (_targetIndex > currentIndex) {
+ //目标在下边
+ animTop = (animStartTop + (animEndTop - animStartTop) * positionOffset).toInt()
+ } else {
+ //目标在上边
+ animTop = (animStartTop - (animStartTop - animEndTop) * positionOffset).toInt()
+ }
+
+ //动画过程中的宽度
+ animHeight =
+ (animStartHeight + (animEndHeight - animStartHeight) * positionOffset).toInt()
+ }
+
+ animExWidth = ((animEndWidth - drawWidth) * positionOffset).toInt()
+ }
+
+ val drawLeft = when (_indicatorDrawStyle) {
+ INDICATOR_STYLE_BOTTOM -> {
+ //右边/底部绘制
+ viewWidth - drawWidth - indicatorXOffset
+ }
+ INDICATOR_STYLE_TOP -> {
+ //左边/顶部绘制
+ 0 + indicatorXOffset
+ }
+ else -> {
+ //居中绘制
+ paddingLeft + indicatorXOffset + (viewDrawWidth / 2 - drawWidth / 2) -
+ (tabLayout._maxConvexHeight - _childConvexHeight(currentIndex)) / 2
+ }
+ }
+
+ indicatorDrawable?.apply {
+ //flash
+ if (indicatorEnableFlash) {
+ if (indicatorEnableFlashClip) {
+ drawIndicatorClipVertical(
+ this, canvas, drawLeft,
+ drawTop,
+ drawLeft + drawWidth + animExWidth,
+ drawTop + drawHeight,
+ animHeight,
+ 1 - positionOffset
+ )
+ } else {
+ drawIndicator(
+ this, canvas, drawLeft,
+ animTop,
+ drawLeft + drawWidth + animExWidth,
+ animTop + animHeight,
+ 1 - positionOffset
+ )
+ }
+
+ if (_targetIndex in 0 until childSize) {
+ if (indicatorEnableFlashClip) {
+ drawIndicatorClipVertical(
+ this, canvas, drawLeft,
+ nextDrawTop,
+ drawLeft + drawWidth + animExWidth,
+ nextDrawTop + nextDrawHeight,
+ animEndHeight,
+ positionOffset
+ )
+ } else {
+ drawIndicator(
+ this, canvas, drawLeft,
+ animEndTop,
+ drawLeft + drawWidth + animExWidth,
+ animEndTop + animEndHeight,
+ positionOffset
+ )
+ }
+ }
+ } else {
+ drawIndicator(
+ this, canvas, drawLeft,
+ animTop,
+ drawLeft + drawWidth + animExWidth,
+ animTop + animHeight,
+ 1 - positionOffset
+ )
+ }
+ }
+ }
+
+ fun _childConvexHeight(index: Int): Int {
+ if (attachView is ViewGroup) {
+ ((attachView as ViewGroup).getChildAt(index).layoutParams as? DslTabLayout.LayoutParams)?.apply {
+ return layoutConvexHeight
+ }
+ }
+ return 0
+ }
+
+ /**
+ * 距离[_targetIndex]的偏移比例.[0->1]的过程
+ * */
+ var positionOffset: Float = 0f
+ set(value) {
+ field = value
+ invalidateSelf()
+ }
+
+ /**当前绘制的index*/
+ var currentIndex: Int = -1
+
+ /**滚动目标的index*/
+ var _targetIndex = -1
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslTabLayout.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabLayout.kt
new file mode 100644
index 000000000..d915fd1b2
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabLayout.kt
@@ -0,0 +1,2042 @@
+package com.angcyo.tablayout
+
+import android.animation.Animator
+import android.animation.AnimatorListenerAdapter
+import android.animation.ValueAnimator
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.os.Bundle
+import android.os.Parcelable
+import android.util.AttributeSet
+import android.view.*
+import android.view.animation.LinearInterpolator
+import android.widget.FrameLayout
+import android.widget.LinearLayout
+import android.widget.OverScroller
+import android.widget.TextView
+import androidx.core.view.GestureDetectorCompat
+import androidx.core.view.GravityCompat
+import androidx.core.view.ViewCompat
+import kotlin.math.abs
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * https://github.com/angcyo/DslTabLayout
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/23
+ */
+
+open class DslTabLayout(
+ context: Context,
+ val attributeSet: AttributeSet? = null
+) : ViewGroup(context, attributeSet) {
+
+ /**在未指定[minHeight]的[wrap_content]情况下的高度*/
+ var itemDefaultHeight = 40 * dpi
+
+ /**item是否等宽*/
+ var itemIsEquWidth = false
+
+ /**item是否支持选择, 只限制点击事件, 不限制滚动事件*/
+ var itemEnableSelector = true
+
+ /**当子Item数量在此范围内时,开启等宽,此属性优先级最高
+ * [~3] 小于等于3个
+ * [3~] 大于等于3个
+ * [3~5] 3<= <=5
+ * */
+ var itemEquWidthCountRange: IntRange? = null
+
+ /**智能判断Item是否等宽.
+ * 如果所有子项, 未撑满tab时, 则开启等宽模式.此属性会覆盖[itemIsEquWidth]*/
+ var itemAutoEquWidth = false
+
+ /**在等宽的情况下, 指定item的宽度, 小于0, 平分*/
+ var itemWidth = -3
+
+ /**是否绘制指示器*/
+ var drawIndicator = true
+
+ /**指示器*/
+ var tabIndicator: DslTabIndicator = DslTabIndicator(this)
+ set(value) {
+ field = value
+ field.initAttribute(context, attributeSet)
+ }
+
+ /**指示器动画时长*/
+ var tabIndicatorAnimationDuration = 240L
+
+ /**默认选中位置*/
+ var tabDefaultIndex = 0
+
+ /**回调监听器和样式配置器*/
+ var tabLayoutConfig: DslTabLayoutConfig? = null
+ set(value) {
+ field = value
+
+ field?.initAttribute(context, attributeSet)
+ }
+
+ /**边框绘制*/
+ var tabBorder: DslTabBorder? = null
+ set(value) {
+ field = value
+ field?.callback = this
+ field?.initAttribute(context, attributeSet)
+ }
+ var drawBorder = false
+
+ /**垂直分割线*/
+ var tabDivider: DslTabDivider? = null
+ set(value) {
+ field = value
+ field?.callback = this
+ field?.initAttribute(context, attributeSet)
+ }
+ var drawDivider = false
+
+ /**未读数角标*/
+ var tabBadge: DslTabBadge? = null
+ set(value) {
+ field = value
+ field?.callback = this
+ field?.initAttribute(context, attributeSet)
+ }
+ var drawBadge = false
+
+ /**快速角标配置项, 方便使用者*/
+ val tabBadgeConfigMap = mutableMapOf()
+
+ /**角标绘制配置*/
+ var onTabBadgeConfig: (child: View, tabBadge: DslTabBadge, index: Int) -> TabBadgeConfig? =
+ { _, tabBadge, index ->
+ val badgeConfig = getBadgeConfig(index)
+ if (!isInEditMode) {
+ tabBadge.updateBadgeConfig(badgeConfig)
+ }
+ badgeConfig
+ }
+
+ /**是否绘制突出*/
+ var drawHighlight = false
+
+ /**选中突出提示*/
+ var tabHighlight: DslTabHighlight? = null
+ set(value) {
+ field = value
+ field?.callback = this
+ field?.initAttribute(context, attributeSet)
+ }
+
+ /**如果使用了高凸模式. 请使用这个属性设置背景色*/
+ var tabConvexBackgroundDrawable: Drawable? = null
+
+ /**是否激活滑动选择模式*/
+ var tabEnableSelectorMode = false
+
+ /**布局的方向*/
+ var orientation: Int = LinearLayout.HORIZONTAL
+
+ /**布局时, 滚动到居中是否需要动画*/
+ var layoutScrollAnim: Boolean = false
+
+ /**滚动动画的时长*/
+ var scrollAnimDuration = 250
+
+ //
+
+ //fling 速率阈值
+ var _minFlingVelocity = 0
+ var _maxFlingVelocity = 0
+
+ //scroll 阈值
+ var _touchSlop = 0
+
+ //临时变量
+ val _tempRect = Rect()
+
+ //childView选择器
+ val dslSelector: DslSelector by lazy {
+ DslSelector().install(this) {
+ onStyleItemView = { itemView, index, select ->
+ tabLayoutConfig?.onStyleItemView?.invoke(itemView, index, select)
+ }
+ onSelectItemView = { itemView, index, select, fromUser ->
+ tabLayoutConfig?.onSelectItemView?.invoke(itemView, index, select, fromUser)
+ ?: false
+ }
+ onSelectViewChange = { fromView, selectViewList, reselect, fromUser ->
+ tabLayoutConfig?.onSelectViewChange?.invoke(
+ fromView,
+ selectViewList,
+ reselect,
+ fromUser
+ )
+ }
+ onSelectIndexChange = { fromIndex, selectList, reselect, fromUser ->
+ if (tabLayoutConfig == null) {
+ "选择:[$fromIndex]->${selectList} reselect:$reselect fromUser:$fromUser".logi()
+ }
+
+ val toIndex = selectList.lastOrNull() ?: -1
+ _animateToItem(fromIndex, toIndex)
+
+ _scrollToTarget(toIndex, tabIndicator.indicatorAnim)
+ postInvalidate()
+
+ //如果设置[tabLayoutConfig?.onSelectIndexChange], 那么会覆盖[_viewPagerDelegate]的操作.
+ tabLayoutConfig?.onSelectIndexChange?.invoke(
+ fromIndex,
+ selectList,
+ reselect,
+ fromUser
+ ) ?: _viewPagerDelegate?.onSetCurrentItem(fromIndex, toIndex, reselect, fromUser)
+ }
+ }
+ }
+
+ init {
+ val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.DslTabLayout)
+ itemIsEquWidth =
+ typedArray.getBoolean(R.styleable.DslTabLayout_tab_item_is_equ_width, itemIsEquWidth)
+ val maxEquWidthCount =
+ typedArray.getInt(R.styleable.DslTabLayout_tab_item_equ_width_count, -1)
+ if (maxEquWidthCount >= 0) {
+ itemEquWidthCountRange = IntRange(maxEquWidthCount, Int.MAX_VALUE)
+ }
+ if (typedArray.hasValue(R.styleable.DslTabLayout_tab_item_equ_width_count_range)) {
+ val equWidthCountRangeString =
+ typedArray.getString(R.styleable.DslTabLayout_tab_item_equ_width_count_range)
+ if (equWidthCountRangeString.isNullOrBlank()) {
+ itemEquWidthCountRange = null
+ } else {
+ val rangeList = equWidthCountRangeString.split("~")
+ if (rangeList.size() >= 2) {
+ val min = rangeList.getOrNull(0)?.toIntOrNull() ?: 0
+ val max = rangeList.getOrNull(1)?.toIntOrNull() ?: Int.MAX_VALUE
+ itemEquWidthCountRange = IntRange(min, max)
+ } else {
+ val min = rangeList.getOrNull(0)?.toIntOrNull() ?: Int.MAX_VALUE
+ itemEquWidthCountRange = IntRange(min, Int.MAX_VALUE)
+ }
+ }
+ }
+ itemAutoEquWidth = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_item_auto_equ_width,
+ itemAutoEquWidth
+ )
+ itemWidth =
+ typedArray.getDimensionPixelOffset(R.styleable.DslTabLayout_tab_item_width, itemWidth)
+ itemDefaultHeight = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_item_default_height,
+ itemDefaultHeight
+ )
+ tabDefaultIndex =
+ typedArray.getInt(R.styleable.DslTabLayout_tab_default_index, tabDefaultIndex)
+
+ drawIndicator =
+ typedArray.getBoolean(R.styleable.DslTabLayout_tab_draw_indicator, drawIndicator)
+ drawDivider =
+ typedArray.getBoolean(R.styleable.DslTabLayout_tab_draw_divider, drawDivider)
+ drawBorder =
+ typedArray.getBoolean(R.styleable.DslTabLayout_tab_draw_border, drawBorder)
+ drawBadge =
+ typedArray.getBoolean(R.styleable.DslTabLayout_tab_draw_badge, drawBadge)
+ drawHighlight =
+ typedArray.getBoolean(R.styleable.DslTabLayout_tab_draw_highlight, drawHighlight)
+
+ tabEnableSelectorMode =
+ typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_selector_mode,
+ tabEnableSelectorMode
+ )
+
+ tabConvexBackgroundDrawable =
+ typedArray.getDrawable(R.styleable.DslTabLayout_tab_convex_background)
+
+ orientation = typedArray.getInt(R.styleable.DslTabLayout_tab_orientation, orientation)
+
+ layoutScrollAnim =
+ typedArray.getBoolean(R.styleable.DslTabLayout_tab_layout_scroll_anim, layoutScrollAnim)
+ scrollAnimDuration =
+ typedArray.getInt(R.styleable.DslTabLayout_tab_scroll_anim_duration, scrollAnimDuration)
+
+ //preview
+ if (isInEditMode) {
+ val layoutId =
+ typedArray.getResourceId(R.styleable.DslTabLayout_tab_preview_item_layout_id, -1)
+ val layoutCount =
+ typedArray.getInt(R.styleable.DslTabLayout_tab_preview_item_count, 3)
+ if (layoutId != -1) {
+ for (i in 0 until layoutCount) {
+ inflate(layoutId, true).let {
+ if (it is TextView) {
+ if (it.text.isNullOrEmpty()) {
+ it.text = "Item $i"
+ } else {
+ it.text = "${it.text}/$i"
+ }
+ }
+ }
+ }
+ }
+ }
+
+ typedArray.recycle()
+
+ val vc = ViewConfiguration.get(context)
+ _minFlingVelocity = vc.scaledMinimumFlingVelocity
+ _maxFlingVelocity = vc.scaledMaximumFlingVelocity
+ //_touchSlop = vc.scaledTouchSlop
+
+ if (drawIndicator) {
+ //直接初始化的变量, 不会触发set方法.
+ tabIndicator.initAttribute(context, attributeSet)
+ }
+
+ if (drawBorder) {
+ tabBorder = DslTabBorder()
+ }
+ if (drawDivider) {
+ tabDivider = DslTabDivider()
+ }
+ if (drawBadge) {
+ tabBadge = DslTabBadge()
+ }
+ if (drawHighlight) {
+ tabHighlight = DslTabHighlight(this)
+ }
+
+ //样式配置器
+ tabLayoutConfig = DslTabLayoutConfig(this)
+
+ //开启绘制
+ setWillNotDraw(false)
+ }
+
+ //
+
+ //
+
+ /**当前选中item的索引*/
+ val currentItemIndex: Int
+ get() = dslSelector.dslSelectIndex
+
+ /**当前选中的itemView*/
+ val currentItemView: View?
+ get() = dslSelector.visibleViewList.getOrNull(currentItemIndex)
+
+ /**设置tab的位置*/
+ fun setCurrentItem(index: Int, notify: Boolean = true, fromUser: Boolean = false) {
+ if (currentItemIndex == index) {
+ _scrollToTarget(index, tabIndicator.indicatorAnim)
+ return
+ }
+ dslSelector.selector(index, true, notify, fromUser)
+ }
+
+ /**关联[ViewPagerDelegate]*/
+ fun setupViewPager(viewPagerDelegate: ViewPagerDelegate) {
+ _viewPagerDelegate = viewPagerDelegate
+ }
+
+ /**配置一个新的[DslTabLayoutConfig]给[DslTabLayout]*/
+ fun setTabLayoutConfig(
+ config: DslTabLayoutConfig = DslTabLayoutConfig(this),
+ doIt: DslTabLayoutConfig.() -> Unit = {}
+ ) {
+ tabLayoutConfig = config
+ configTabLayoutConfig(doIt)
+ }
+
+ /**配置[DslTabLayoutConfig]*/
+ fun configTabLayoutConfig(config: DslTabLayoutConfig.() -> Unit = {}) {
+ if (tabLayoutConfig == null) {
+ tabLayoutConfig = DslTabLayoutConfig(this)
+ }
+ tabLayoutConfig?.config()
+ dslSelector.updateStyle()
+ }
+
+ /**观察index的改变回调*/
+ fun observeIndexChange(
+ config: DslTabLayoutConfig.() -> Unit = {},
+ action: (fromIndex: Int, toIndex: Int, reselect: Boolean, fromUser: Boolean) -> Unit
+ ) {
+ configTabLayoutConfig {
+ config()
+ onSelectIndexChange = { fromIndex, selectIndexList, reselect, fromUser ->
+ action(fromIndex, selectIndexList.firstOrNull() ?: -1, reselect, fromUser)
+ }
+ }
+ }
+
+ fun getBadgeConfig(index: Int): TabBadgeConfig {
+ return tabBadgeConfigMap.getOrElse(index) {
+ tabBadge?.defaultBadgeConfig?.copy() ?: TabBadgeConfig()
+ }
+ }
+
+ fun updateTabBadge(index: Int, badgeText: String?) {
+ updateTabBadge(index) {
+ this.badgeText = badgeText
+ }
+ }
+
+ /**更新角标*/
+ fun updateTabBadge(index: Int, config: TabBadgeConfig.() -> Unit) {
+ val badgeConfig = getBadgeConfig(index)
+ tabBadgeConfigMap[index] = badgeConfig
+ badgeConfig.config()
+ postInvalidate()
+ }
+
+ //
+
+ //
+
+ override fun onAttachedToWindow() {
+ super.onAttachedToWindow()
+ }
+
+ override fun onDetachedFromWindow() {
+ super.onDetachedFromWindow()
+ }
+
+ override fun onFinishInflate() {
+ super.onFinishInflate()
+ }
+
+ override fun onViewAdded(child: View?) {
+ super.onViewAdded(child)
+ updateTabLayout()
+ }
+
+ override fun onViewRemoved(child: View?) {
+ super.onViewRemoved(child)
+ updateTabLayout()
+ }
+
+ open fun updateTabLayout() {
+ dslSelector.updateVisibleList()
+ dslSelector.updateStyle()
+ dslSelector.updateClickListener()
+ }
+
+ override fun draw(canvas: Canvas) {
+ //Log.e("angcyo", "...draw...")
+
+ if (drawIndicator) {
+ tabIndicator.setBounds(0, 0, measuredWidth, measuredHeight)
+ }
+
+ //自定义的背景
+ tabConvexBackgroundDrawable?.apply {
+ if (isHorizontal()) {
+ setBounds(0, _maxConvexHeight, right - left, bottom - top)
+ } else {
+ setBounds(0, 0, measuredWidth - _maxConvexHeight, bottom - top)
+ }
+
+ if (scrollX or scrollY == 0) {
+ draw(canvas)
+ } else {
+ canvas.holdLocation {
+ draw(canvas)
+ }
+ }
+ }
+
+ super.draw(canvas)
+
+ //突出显示
+ if (drawHighlight) {
+ tabHighlight?.draw(canvas)
+ }
+
+ val visibleChildCount = dslSelector.visibleViewList.size
+
+ //绘制在child的上面
+ if (drawDivider) {
+ if (isHorizontal()) {
+ if (isLayoutRtl) {
+ var right = 0
+ tabDivider?.apply {
+ val top = paddingTop + dividerMarginTop
+ val bottom = measuredHeight - paddingBottom - dividerMarginBottom
+ dslSelector.visibleViewList.forEachIndexed { index, view ->
+
+ if (haveBeforeDivider(index, visibleChildCount)) {
+ right = view.right + dividerMarginLeft + dividerWidth
+ setBounds(right - dividerWidth, top, right, bottom)
+ draw(canvas)
+ }
+
+ if (haveAfterDivider(index, visibleChildCount)) {
+ right = view.right - view.measuredWidth - dividerMarginRight
+ setBounds(right - dividerWidth, top, right, bottom)
+ draw(canvas)
+ }
+
+ }
+ }
+ } else {
+ var left = 0
+ tabDivider?.apply {
+ val top = paddingTop + dividerMarginTop
+ val bottom = measuredHeight - paddingBottom - dividerMarginBottom
+ dslSelector.visibleViewList.forEachIndexed { index, view ->
+
+ if (haveBeforeDivider(index, visibleChildCount)) {
+ left = view.left - dividerMarginRight - dividerWidth
+ setBounds(left, top, left + dividerWidth, bottom)
+ draw(canvas)
+ }
+
+ if (haveAfterDivider(index, visibleChildCount)) {
+ left = view.right + dividerMarginLeft
+ setBounds(left, top, left + dividerWidth, bottom)
+ draw(canvas)
+ }
+
+ }
+ }
+ }
+ } else {
+ var top = 0
+ tabDivider?.apply {
+ val left = paddingStart + dividerMarginLeft
+ val right = measuredWidth - paddingEnd - dividerMarginRight
+ dslSelector.visibleViewList.forEachIndexed { index, view ->
+
+ if (haveBeforeDivider(index, visibleChildCount)) {
+ top = view.top - dividerMarginBottom - dividerHeight
+ setBounds(left, top, right, top + dividerHeight)
+ draw(canvas)
+ }
+
+ if (haveAfterDivider(index, visibleChildCount)) {
+ top = view.bottom + dividerMarginTop
+ setBounds(left, top, right, top + dividerHeight)
+ draw(canvas)
+ }
+ }
+ }
+ }
+ }
+ if (drawBorder) {
+ //边框不跟随滚动
+ canvas.holdLocation {
+ tabBorder?.draw(canvas)
+ }
+ }
+ if (drawIndicator && tabIndicator.indicatorStyle.have(DslTabIndicator.INDICATOR_STYLE_FOREGROUND)) {
+ //前景显示
+ tabIndicator.draw(canvas)
+ }
+ if (drawBadge) {
+ tabBadge?.apply {
+ dslSelector.visibleViewList.forEachIndexed { index, child ->
+ val badgeConfig = onTabBadgeConfig(child, this, index)
+
+ var left: Int
+ var top: Int
+ var right: Int
+ var bottom: Int
+
+ var anchorView: View = child
+
+ if (badgeConfig != null && badgeConfig.badgeAnchorChildIndex >= 0) {
+ anchorView =
+ child.getChildOrNull(badgeConfig.badgeAnchorChildIndex) ?: child
+
+ anchorView.getLocationInParent(this@DslTabLayout, _tempRect)
+
+ left = _tempRect.left
+ top = _tempRect.top
+ right = _tempRect.right
+ bottom = _tempRect.bottom
+ } else {
+ left = anchorView.left
+ top = anchorView.top
+ right = anchorView.right
+ bottom = anchorView.bottom
+ }
+
+ if (badgeConfig != null && badgeConfig.badgeIgnoreChildPadding) {
+ left += anchorView.paddingStart
+ top += anchorView.paddingTop
+ right -= anchorView.paddingEnd
+ bottom -= anchorView.paddingBottom
+ }
+
+ setBounds(left, top, right, bottom)
+
+ updateOriginDrawable()
+
+ if (isInEditMode) {
+ badgeText = if (index == visibleChildCount - 1) {
+ //预览中, 强制最后一个角标为圆点类型, 方便查看预览.
+ ""
+ } else {
+ xmlBadgeText
+ }
+ }
+
+ draw(canvas)
+ }
+ }
+ }
+ }
+
+ override fun onDraw(canvas: Canvas) {
+ super.onDraw(canvas)
+
+ if (drawBorder) {
+ //边框不跟随滚动
+ canvas.holdLocation {
+ tabBorder?.drawBorderBackground(canvas)
+ }
+ }
+
+ //绘制在child的后面
+ if (drawIndicator && !tabIndicator.indicatorStyle.have(DslTabIndicator.INDICATOR_STYLE_FOREGROUND)) {
+ //背景绘制
+ tabIndicator.draw(canvas)
+ }
+ }
+
+ /**保持位置不变*/
+ fun Canvas.holdLocation(action: () -> Unit) {
+ translate(scrollX.toFloat(), scrollY.toFloat())
+ action()
+ translate(-scrollX.toFloat(), -scrollY.toFloat())
+ }
+
+ override fun drawChild(canvas: Canvas, child: View, drawingTime: Long): Boolean {
+ return super.drawChild(canvas, child, drawingTime)
+ }
+
+ override fun verifyDrawable(who: Drawable): Boolean {
+ return super.verifyDrawable(who) ||
+ who == tabIndicator /*||
+ who == tabBorder ||
+ who == tabDivider ||
+ who == tabConvexBackgroundDrawable*/ /*||
+ who == tabBadge 防止循环绘制*/
+ }
+
+ //
+
+ //
+
+ //所有child的总宽度, 不包含parent的padding
+ var _childAllWidthSum = 0
+
+ //最大的凸起高度
+ var _maxConvexHeight = 0
+
+ override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+
+ if (dslSelector.dslSelectIndex < 0) {
+ //还没有选中
+ setCurrentItem(tabDefaultIndex)
+ }
+
+ if (isHorizontal()) {
+ measureHorizontal(widthMeasureSpec, heightMeasureSpec)
+ } else {
+ measureVertical(widthMeasureSpec, heightMeasureSpec)
+ }
+ }
+
+ fun measureHorizontal(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ dslSelector.updateVisibleList()
+
+ val visibleChildList = dslSelector.visibleViewList
+ val visibleChildCount = visibleChildList.size
+
+ //控制最小大小
+ val tabMinHeight = if (suggestedMinimumHeight > 0) {
+ suggestedMinimumHeight
+ } else {
+ itemDefaultHeight
+ }
+
+ if (visibleChildCount == 0) {
+ setMeasuredDimension(
+ getDefaultSize(suggestedMinimumWidth, widthMeasureSpec),
+ getDefaultSize(tabMinHeight, heightMeasureSpec)
+ )
+ return
+ }
+
+ //super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ var widthSize = MeasureSpec.getSize(widthMeasureSpec)
+ val widthMode = MeasureSpec.getMode(widthMeasureSpec)
+ var heightSize = MeasureSpec.getSize(heightMeasureSpec)
+ val heightMode = MeasureSpec.getMode(heightMeasureSpec)
+
+ _maxConvexHeight = 0
+
+ var childWidthSpec: Int = -1
+
+ //记录child最大的height, 用来实现tabLayout wrap_content, 包括突出的大小
+ var childMaxHeight = tabMinHeight //child最大的高度
+
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ if (heightSize == 0) {
+ heightSize = Int.MAX_VALUE
+ }
+ }
+
+ if (widthMode == MeasureSpec.UNSPECIFIED) {
+ if (widthSize == 0) {
+ widthSize = Int.MAX_VALUE
+ }
+ }
+
+ //分割线需要排除的宽度
+ val dividerWidthExclude =
+ if (drawDivider) tabDivider?.run { dividerWidth + dividerMarginLeft + dividerMarginRight }
+ ?: 0 else 0
+
+ //智能等宽判断
+ if (itemAutoEquWidth) {
+ var childMaxWidth = 0 //所有child宽度总和
+ visibleChildList.forEachIndexed { index, child ->
+ val lp: LayoutParams = child.layoutParams as LayoutParams
+ measureChild(child, widthMeasureSpec, heightMeasureSpec)
+ childMaxWidth += lp.marginStart + lp.marginEnd + child.measuredWidth
+
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(index, visibleChildList.size) == true) {
+ childMaxWidth += dividerWidthExclude
+ }
+ if (tabDivider?.haveAfterDivider(index, visibleChildList.size) == true) {
+ childMaxWidth += dividerWidthExclude
+ }
+ }
+ }
+
+ itemIsEquWidth = childMaxWidth <= widthSize
+ }
+
+ itemEquWidthCountRange?.let {
+ itemIsEquWidth = it.contains(visibleChildCount)
+ }
+
+ //等宽时, child宽度的测量模式
+ val childEquWidthSpec = if (itemIsEquWidth) {
+ exactlyMeasure(
+ if (itemWidth > 0) {
+ itemWidth
+ } else {
+ var excludeWidth = paddingStart + paddingEnd
+ visibleChildList.forEachIndexed { index, child ->
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(
+ index,
+ visibleChildList.size
+ ) == true
+ ) {
+ excludeWidth += dividerWidthExclude
+ }
+ if (tabDivider?.haveAfterDivider(
+ index,
+ visibleChildList.size
+ ) == true
+ ) {
+ excludeWidth += dividerWidthExclude
+ }
+ }
+ val lp = child.layoutParams as LayoutParams
+ excludeWidth += lp.marginStart + lp.marginEnd
+ }
+ (widthSize - excludeWidth) / visibleChildCount
+ }
+ )
+ } else {
+ -1
+ }
+
+ //...end
+
+ _childAllWidthSum = 0
+
+ //没有设置weight属性的child宽度总和, 用于计算剩余空间
+ var allChildUsedWidth = 0
+
+ fun measureChild(childView: View, heightSpec: Int? = null) {
+ val lp = childView.layoutParams as LayoutParams
+
+ //child高度测量模式
+ var childHeightSpec: Int = -1
+
+ val widthHeight = calcLayoutWidthHeight(
+ lp.layoutWidth, lp.layoutHeight,
+ widthSize, heightSize, 0, 0
+ )
+
+ if (heightMode == MeasureSpec.EXACTLY) {
+ //固定高度
+ childHeightSpec =
+ exactlyMeasure(heightSize - paddingTop - paddingBottom - lp.topMargin - lp.bottomMargin)
+ } else {
+ if (widthHeight[1] > 0) {
+ heightSize = widthHeight[1]
+ childHeightSpec = exactlyMeasure(heightSize)
+ heightSize += paddingTop + paddingBottom
+ } else {
+ childHeightSpec = if (lp.height == ViewGroup.LayoutParams.MATCH_PARENT) {
+ exactlyMeasure(tabMinHeight)
+ } else {
+ atmostMeasure(Int.MAX_VALUE)
+ }
+ }
+ }
+
+ val childConvexHeight = lp.layoutConvexHeight
+
+ //...end
+
+ //计算宽度测量模式
+ childWidthSpec //no op
+
+ if (heightSpec != null) {
+ childView.measure(childWidthSpec, heightSpec)
+ } else {
+ childView.measure(childWidthSpec, childHeightSpec)
+ }
+ if (childConvexHeight > 0) {
+ _maxConvexHeight = max(_maxConvexHeight, childConvexHeight)
+ //需要凸起
+ val spec = exactlyMeasure(childView.measuredHeight + childConvexHeight)
+ childView.measure(childWidthSpec, spec)
+ }
+ childMaxHeight = max(childMaxHeight, childView.measuredHeight)
+ }
+
+ visibleChildList.forEachIndexed { index, childView ->
+ val lp = childView.layoutParams as LayoutParams
+ var childUsedWidth = 0
+ if (lp.weight < 0) {
+ val widthHeight = calcLayoutWidthHeight(
+ lp.layoutWidth, lp.layoutHeight,
+ widthSize, heightSize, 0, 0
+ )
+
+ //计算宽度测量模式
+ childWidthSpec = when {
+ itemIsEquWidth -> childEquWidthSpec
+ widthHeight[0] > 0 -> exactlyMeasure(widthHeight[0])
+ lp.width == ViewGroup.LayoutParams.MATCH_PARENT -> exactlyMeasure(widthSize - paddingStart - paddingEnd)
+ lp.width > 0 -> exactlyMeasure(lp.width)
+ else -> atmostMeasure(widthSize - paddingStart - paddingEnd)
+ }
+
+ measureChild(childView)
+
+ childUsedWidth = childView.measuredWidth + lp.marginStart + lp.marginEnd
+ } else {
+ childUsedWidth = lp.marginStart + lp.marginEnd
+ }
+
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(index, visibleChildList.size) == true) {
+ childUsedWidth += dividerWidthExclude
+ }
+ if (tabDivider?.haveAfterDivider(index, visibleChildList.size) == true) {
+ childUsedWidth += dividerWidthExclude
+ }
+ }
+
+ childMaxHeight = max(childMaxHeight, childView.measuredHeight)
+ allChildUsedWidth += childUsedWidth
+ _childAllWidthSum += childUsedWidth
+ }
+
+ //剩余空间
+ val spaceSize = widthSize - allChildUsedWidth
+
+ //计算weight
+ visibleChildList.forEach { childView ->
+ val lp = childView.layoutParams as LayoutParams
+ if (lp.weight > 0) {
+ val widthHeight = calcLayoutWidthHeight(
+ lp.layoutWidth, lp.layoutHeight,
+ widthSize, heightSize, 0, 0
+ )
+
+ //计算宽度测量模式
+ childWidthSpec = when {
+ itemIsEquWidth -> childEquWidthSpec
+ spaceSize > 0 -> exactlyMeasure(spaceSize * lp.weight)
+ widthHeight[0] > 0 -> exactlyMeasure(allChildUsedWidth)
+ lp.width == ViewGroup.LayoutParams.MATCH_PARENT -> exactlyMeasure(widthSize - paddingStart - paddingEnd)
+ lp.width > 0 -> exactlyMeasure(lp.width)
+ else -> atmostMeasure(widthSize - paddingStart - paddingEnd)
+ }
+
+ measureChild(childView)
+
+ childMaxHeight = max(childMaxHeight, childView.measuredHeight)
+
+ //上面已经处理了分割线和margin的距离了
+ _childAllWidthSum += childView.measuredWidth
+ }
+ }
+ //...end
+
+ if (heightMode == MeasureSpec.AT_MOST) {
+ //wrap_content 情况下, 重新测量所有子view
+ val childHeightSpec = exactlyMeasure(
+ max(
+ childMaxHeight - _maxConvexHeight,
+ suggestedMinimumHeight - paddingTop - paddingBottom
+ )
+ )
+ visibleChildList.forEach { childView ->
+ measureChild(childView, childHeightSpec)
+ }
+ }
+
+ if (widthMode != MeasureSpec.EXACTLY) {
+ widthSize = min(_childAllWidthSum + paddingStart + paddingEnd, widthSize)
+ }
+
+ if (visibleChildList.isEmpty()) {
+ heightSize = if (suggestedMinimumHeight > 0) {
+ suggestedMinimumHeight
+ } else {
+ itemDefaultHeight
+ }
+ } else if (heightMode != MeasureSpec.EXACTLY) {
+ heightSize = max(
+ childMaxHeight - _maxConvexHeight + paddingTop + paddingBottom,
+ suggestedMinimumHeight
+ )
+ }
+
+ setMeasuredDimension(widthSize, heightSize + _maxConvexHeight)
+ }
+
+ fun measureVertical(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+ dslSelector.updateVisibleList()
+
+ val visibleChildList = dslSelector.visibleViewList
+ val visibleChildCount = visibleChildList.size
+
+ if (visibleChildCount == 0) {
+ setMeasuredDimension(
+ getDefaultSize(
+ if (suggestedMinimumHeight > 0) {
+ suggestedMinimumHeight
+ } else {
+ itemDefaultHeight
+ }, widthMeasureSpec
+ ),
+ getDefaultSize(suggestedMinimumHeight, heightMeasureSpec)
+ )
+ return
+ }
+
+ //super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+ var widthSize = MeasureSpec.getSize(widthMeasureSpec)
+ val widthMode = MeasureSpec.getMode(widthMeasureSpec)
+ var heightSize = MeasureSpec.getSize(heightMeasureSpec)
+ val heightMode = MeasureSpec.getMode(heightMeasureSpec)
+
+ _maxConvexHeight = 0
+
+ //child高度测量模式
+ var childHeightSpec: Int = -1
+ var childWidthSpec: Int = -1
+
+ if (heightMode == MeasureSpec.UNSPECIFIED) {
+ if (heightSize == 0) {
+ heightSize = Int.MAX_VALUE
+ }
+ }
+
+ if (widthMode == MeasureSpec.EXACTLY) {
+ //固定宽度
+ childWidthSpec = exactlyMeasure(widthSize - paddingStart - paddingEnd)
+ } else if (widthMode == MeasureSpec.UNSPECIFIED) {
+ if (widthSize == 0) {
+ widthSize = Int.MAX_VALUE
+ }
+ }
+
+ //分割线需要排除的宽度
+ val dividerHeightExclude =
+ if (drawDivider) tabDivider?.run { dividerHeight + dividerMarginTop + dividerMarginBottom }
+ ?: 0 else 0
+
+ //智能等宽判断
+ if (itemAutoEquWidth) {
+ var childMaxHeight = 0 //所有child高度总和
+ visibleChildList.forEachIndexed { index, child ->
+ val lp: LayoutParams = child.layoutParams as LayoutParams
+ measureChild(child, widthMeasureSpec, heightMeasureSpec)
+ childMaxHeight += lp.topMargin + lp.bottomMargin + child.measuredHeight
+
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(index, visibleChildList.size) == true) {
+ childMaxHeight += dividerHeightExclude
+ }
+ if (tabDivider?.haveAfterDivider(index, visibleChildList.size) == true) {
+ childMaxHeight += dividerHeightExclude
+ }
+ }
+ }
+
+ itemIsEquWidth = childMaxHeight <= heightSize
+ }
+
+ itemEquWidthCountRange?.let {
+ itemIsEquWidth = it.contains(visibleChildCount)
+ }
+
+ //等宽时, child高度的测量模式
+ val childEquHeightSpec = if (itemIsEquWidth) {
+ exactlyMeasure(
+ if (itemWidth > 0) {
+ itemWidth
+ } else {
+ var excludeHeight = paddingTop + paddingBottom
+ visibleChildList.forEachIndexed { index, child ->
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(index, visibleChildList.size) == true
+ ) {
+ excludeHeight += dividerHeightExclude
+ }
+ if (tabDivider?.haveAfterDivider(index, visibleChildList.size) == true
+ ) {
+ excludeHeight += dividerHeightExclude
+ }
+ }
+ val lp = child.layoutParams as LayoutParams
+ excludeHeight += lp.topMargin + lp.bottomMargin
+ }
+ (heightSize - excludeHeight) / visibleChildCount
+ }
+ )
+ } else {
+ -1
+ }
+
+ //...end
+
+ _childAllWidthSum = 0
+
+ var wrapContentWidth = false
+
+ //没有设置weight属性的child宽度总和, 用于计算剩余空间
+ var allChildUsedHeight = 0
+
+ fun measureChild(childView: View) {
+ val lp = childView.layoutParams as LayoutParams
+
+ //纵向布局, 不支持横向margin支持
+ lp.marginStart = 0
+ lp.marginEnd = 0
+
+ val childConvexHeight = lp.layoutConvexHeight
+ _maxConvexHeight = max(_maxConvexHeight, childConvexHeight)
+
+ val widthHeight = calcLayoutWidthHeight(
+ lp.layoutWidth, lp.layoutHeight,
+ widthSize, heightSize, 0, 0
+ )
+
+ //计算宽度测量模式
+ wrapContentWidth = false
+ if (childWidthSpec == -1) {
+ if (widthHeight[0] > 0) {
+ widthSize = widthHeight[0]
+ childWidthSpec = exactlyMeasure(widthSize)
+ widthSize += paddingStart + paddingEnd
+ }
+ }
+
+ if (childWidthSpec == -1) {
+ if (lp.width == ViewGroup.LayoutParams.MATCH_PARENT) {
+
+ widthSize = if (suggestedMinimumWidth > 0) {
+ suggestedMinimumWidth
+ } else {
+ itemDefaultHeight
+ }
+
+ childWidthSpec = exactlyMeasure(widthSize)
+
+ widthSize += paddingStart + paddingEnd
+ } else {
+ childWidthSpec = atmostMeasure(widthSize)
+ wrapContentWidth = true
+ }
+ }
+ //...end
+
+ //计算高度测量模式
+ childHeightSpec //no op
+
+ if (childConvexHeight > 0) {
+ //需要凸起
+ val childConvexWidthSpec = MeasureSpec.makeMeasureSpec(
+ MeasureSpec.getSize(childWidthSpec) + childConvexHeight,
+ MeasureSpec.getMode(childWidthSpec)
+ )
+ childView.measure(childConvexWidthSpec, childHeightSpec)
+ } else {
+ childView.measure(childWidthSpec, childHeightSpec)
+ }
+
+ if (wrapContentWidth) {
+ widthSize = childView.measuredWidth
+ childWidthSpec = exactlyMeasure(widthSize)
+ widthSize += paddingStart + paddingEnd
+ }
+ }
+
+ visibleChildList.forEachIndexed { index, childView ->
+ val lp = childView.layoutParams as LayoutParams
+ var childUsedHeight = 0
+ if (lp.weight < 0) {
+ val widthHeight = calcLayoutWidthHeight(
+ lp.layoutWidth, lp.layoutHeight,
+ widthSize, heightSize, 0, 0
+ )
+
+ //计算高度测量模式
+ childHeightSpec = when {
+ itemIsEquWidth -> childEquHeightSpec
+ widthHeight[1] > 0 -> exactlyMeasure(widthHeight[1])
+ lp.height == ViewGroup.LayoutParams.MATCH_PARENT -> exactlyMeasure(heightSize - paddingTop - paddingBottom)
+ lp.height > 0 -> exactlyMeasure(lp.height)
+ else -> atmostMeasure(heightSize - paddingTop - paddingBottom)
+ }
+
+ measureChild(childView)
+
+ childUsedHeight = childView.measuredHeight + lp.topMargin + lp.bottomMargin
+ } else {
+ childUsedHeight = lp.topMargin + lp.bottomMargin
+ }
+
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(index, visibleChildList.size) == true) {
+ childUsedHeight += dividerHeightExclude
+ }
+ if (tabDivider?.haveAfterDivider(index, visibleChildList.size) == true) {
+ childUsedHeight += dividerHeightExclude
+ }
+ }
+
+ allChildUsedHeight += childUsedHeight
+ _childAllWidthSum += childUsedHeight
+ }
+
+ //剩余空间
+ val spaceSize = heightSize - allChildUsedHeight
+
+ //计算weight
+ visibleChildList.forEach { childView ->
+ val lp = childView.layoutParams as LayoutParams
+ if (lp.weight > 0) {
+ val widthHeight = calcLayoutWidthHeight(
+ lp.layoutWidth, lp.layoutHeight,
+ widthSize, heightSize, 0, 0
+ )
+
+ //计算高度测量模式
+ childHeightSpec = when {
+ itemIsEquWidth -> childEquHeightSpec
+ spaceSize > 0 -> exactlyMeasure(spaceSize * lp.weight)
+ widthHeight[1] > 0 -> exactlyMeasure(allChildUsedHeight)
+ lp.height == ViewGroup.LayoutParams.MATCH_PARENT -> exactlyMeasure(heightSize - paddingTop - paddingBottom)
+ lp.height > 0 -> exactlyMeasure(lp.height)
+ else -> atmostMeasure(heightSize - paddingTop - paddingBottom)
+ }
+
+ measureChild(childView)
+
+ //上面已经处理了分割线和margin的距离了
+ _childAllWidthSum += childView.measuredHeight
+ }
+ }
+ //...end
+
+ if (heightMode != MeasureSpec.EXACTLY) {
+ heightSize = min(_childAllWidthSum + paddingTop + paddingBottom, heightSize)
+ }
+
+ if (visibleChildList.isEmpty()) {
+ widthSize = if (suggestedMinimumWidth > 0) {
+ suggestedMinimumWidth
+ } else {
+ itemDefaultHeight
+ }
+ }
+
+ setMeasuredDimension(widthSize + _maxConvexHeight, heightSize)
+ }
+
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ if (isHorizontal()) {
+ layoutHorizontal(changed, l, t, r, b)
+ } else {
+ layoutVertical(changed, l, t, r, b)
+ }
+ }
+
+ override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) {
+ super.onSizeChanged(w, h, oldw, oldh)
+
+ //check
+ restoreScroll()
+
+ if (dslSelector.dslSelectIndex < 0) {
+ //还没有选中
+ setCurrentItem(tabDefaultIndex)
+ } else {
+ if (_overScroller.isFinished) {
+ _scrollToTarget(dslSelector.dslSelectIndex, layoutScrollAnim)
+ }
+ }
+ }
+
+ val isLayoutRtl: Boolean
+ get() = ViewCompat.getLayoutDirection(this) == ViewCompat.LAYOUT_DIRECTION_RTL
+
+ var _layoutDirection: Int = -1
+
+ //API 17
+ override fun onRtlPropertiesChanged(layoutDirection: Int) {
+ super.onRtlPropertiesChanged(layoutDirection)
+
+ if (layoutDirection != _layoutDirection) {
+ _layoutDirection = layoutDirection
+ if (orientation == LinearLayout.HORIZONTAL) {
+ updateTabLayout() //更新样式
+ requestLayout() //重新布局
+ }
+ }
+ }
+
+ fun layoutHorizontal(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ val isRtl = isLayoutRtl
+
+ var left = paddingStart
+ var right = measuredWidth - paddingEnd
+
+ var childBottom = measuredHeight - paddingBottom
+
+ val dividerExclude = if (drawDivider) tabDivider?.run {
+ dividerWidth + dividerMarginLeft + dividerMarginRight
+ } ?: 0 else 0
+
+ val visibleChildList = dslSelector.visibleViewList
+ visibleChildList.forEachIndexed { index, childView ->
+
+ val lp = childView.layoutParams as LayoutParams
+ val verticalGravity = lp.gravity and Gravity.VERTICAL_GRAVITY_MASK
+
+ if (isRtl) {
+ right -= lp.marginEnd
+ } else {
+ left += lp.marginStart
+ }
+
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(index, visibleChildList.size) == true) {
+
+ if (isRtl) {
+ right -= dividerExclude
+ } else {
+ left += dividerExclude
+ }
+ }
+ }
+
+ childBottom = when (verticalGravity) {
+ Gravity.CENTER_VERTICAL -> measuredHeight - paddingBottom -
+ ((measuredHeight - paddingTop - paddingBottom - _maxConvexHeight) / 2 -
+ childView.measuredHeight / 2)
+
+ Gravity.BOTTOM -> measuredHeight - paddingBottom
+ else -> paddingTop + lp.topMargin + childView.measuredHeight
+ }
+
+ if (isRtl) {
+ childView.layout(
+ right - childView.measuredWidth,
+ childBottom - childView.measuredHeight,
+ right,
+ childBottom
+ )
+ right -= childView.measuredWidth + lp.marginStart
+ } else {
+ childView.layout(
+ left,
+ childBottom - childView.measuredHeight,
+ left + childView.measuredWidth,
+ childBottom
+ )
+ left += childView.measuredWidth + lp.marginEnd
+ }
+ }
+
+ //check
+ restoreScroll()
+
+ if (dslSelector.dslSelectIndex < 0) {
+ //还没有选中
+ setCurrentItem(tabDefaultIndex)
+ } else {
+ if (_overScroller.isFinished) {
+ _scrollToTarget(dslSelector.dslSelectIndex, layoutScrollAnim)
+ }
+ }
+ }
+
+ fun layoutVertical(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ var top = paddingTop
+ var childLeft = paddingStart
+
+ val dividerExclude =
+ if (drawDivider) tabDivider?.run { dividerHeight + dividerMarginTop + dividerMarginBottom }
+ ?: 0 else 0
+
+ val visibleChildList = dslSelector.visibleViewList
+ visibleChildList.forEachIndexed { index, childView ->
+
+ val lp = childView.layoutParams as LayoutParams
+ val layoutDirection = 0
+ val absoluteGravity = GravityCompat.getAbsoluteGravity(lp.gravity, layoutDirection)
+ val horizontalGravity = absoluteGravity and Gravity.HORIZONTAL_GRAVITY_MASK
+
+ top += lp.topMargin
+
+ if (drawDivider) {
+ if (tabDivider?.haveBeforeDivider(index, visibleChildList.size) == true) {
+ top += dividerExclude
+ }
+ }
+
+ childLeft = when (horizontalGravity) {
+ Gravity.CENTER_HORIZONTAL -> paddingStart + ((measuredWidth - paddingStart - paddingEnd - _maxConvexHeight) / 2 -
+ childView.measuredWidth / 2)
+
+ Gravity.RIGHT -> measuredWidth - paddingRight - childView.measuredWidth - lp.rightMargin
+ else -> paddingLeft + lp.leftMargin
+ }
+
+ /*默认水平居中显示*/
+ childView.layout(
+ childLeft,
+ top,
+ childLeft + childView.measuredWidth,
+ top + childView.measuredHeight
+ )
+
+ top += childView.measuredHeight + lp.bottomMargin
+ }
+ }
+
+ /**是否是横向布局*/
+ fun isHorizontal() = orientation.isHorizontal()
+
+ //
+
+ //
+
+ override fun generateDefaultLayoutParams(): ViewGroup.LayoutParams {
+ return LayoutParams(
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT,
+ Gravity.CENTER
+ )
+ }
+
+ override fun generateLayoutParams(attrs: AttributeSet?): ViewGroup.LayoutParams {
+ return LayoutParams(context, attrs)
+ }
+
+ override fun generateLayoutParams(p: ViewGroup.LayoutParams?): ViewGroup.LayoutParams {
+ return p?.run { LayoutParams(p) } ?: generateDefaultLayoutParams()
+ }
+
+ class LayoutParams : FrameLayout.LayoutParams {
+
+ /**
+ * 支持格式0.3pw 0.5ph, 表示[parent]的多少倍数
+ * */
+ var layoutWidth: String? = null
+ var layoutHeight: String? = null
+
+ /**凸出的高度*/
+ var layoutConvexHeight: Int = 0
+
+ /**
+ * 宽高[WRAP_CONTENT]时, 内容view的定位索引
+ * [TabIndicator.indicatorContentIndex]
+ * */
+ var indicatorContentIndex = -1
+ var indicatorContentId = View.NO_ID
+
+ /**[com.angcyo.tablayout.DslTabLayoutConfig.getOnGetTextStyleView]*/
+ var contentTextViewIndex = -1
+
+ /**[com.angcyo.tablayout.DslTabLayoutConfig.getTabTextViewId]*/
+ var contentTextViewId = View.NO_ID
+
+ /**[com.angcyo.tablayout.DslTabLayoutConfig.getOnGetIcoStyleView]*/
+ var contentIconViewIndex = -1
+
+ /**[com.angcyo.tablayout.DslTabLayoutConfig.getTabIconViewId]*/
+ var contentIconViewId = View.NO_ID
+
+ /**
+ * 剩余空间占比, 1f表示占满剩余空间, 0.5f表示使用剩余空间的0.5倍
+ * [android.widget.LinearLayout.LayoutParams.weight]*/
+ var weight: Float = -1f
+
+ /**突出需要绘制的Drawable
+ * [com.angcyo.tablayout.DslTabHighlight.highlightDrawable]*/
+ var highlightDrawable: Drawable? = null
+
+ constructor(c: Context, attrs: AttributeSet?) : super(c, attrs) {
+ val a = c.obtainStyledAttributes(attrs, R.styleable.DslTabLayout_Layout)
+ layoutWidth = a.getString(R.styleable.DslTabLayout_Layout_layout_tab_width)
+ layoutHeight = a.getString(R.styleable.DslTabLayout_Layout_layout_tab_height)
+ layoutConvexHeight =
+ a.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_Layout_layout_tab_convex_height,
+ layoutConvexHeight
+ )
+ indicatorContentIndex = a.getInt(
+ R.styleable.DslTabLayout_Layout_layout_tab_indicator_content_index,
+ indicatorContentIndex
+ )
+ indicatorContentId = a.getResourceId(
+ R.styleable.DslTabLayout_Layout_layout_tab_indicator_content_id,
+ indicatorContentId
+ )
+ weight = a.getFloat(R.styleable.DslTabLayout_Layout_layout_tab_weight, weight)
+ highlightDrawable =
+ a.getDrawable(R.styleable.DslTabLayout_Layout_layout_highlight_drawable)
+
+ contentTextViewIndex = a.getInt(
+ R.styleable.DslTabLayout_Layout_layout_tab_text_view_index,
+ contentTextViewIndex
+ )
+ contentIconViewIndex = a.getInt(
+ R.styleable.DslTabLayout_Layout_layout_tab_text_view_index,
+ contentIconViewIndex
+ )
+ contentTextViewId = a.getResourceId(
+ R.styleable.DslTabLayout_Layout_layout_tab_text_view_id,
+ contentTextViewId
+ )
+ contentIconViewId = a.getResourceId(
+ R.styleable.DslTabLayout_Layout_layout_tab_icon_view_id,
+ contentIconViewIndex
+ )
+
+ a.recycle()
+
+ if (gravity == UNSPECIFIED_GRAVITY) {
+ gravity = if (layoutConvexHeight > 0) {
+ Gravity.BOTTOM
+ } else {
+ Gravity.CENTER
+ }
+ }
+ }
+
+ constructor(source: ViewGroup.LayoutParams) : super(source) {
+ if (source is LayoutParams) {
+ this.layoutWidth = source.layoutWidth
+ this.layoutHeight = source.layoutHeight
+ this.layoutConvexHeight = source.layoutConvexHeight
+ this.weight = source.weight
+ this.highlightDrawable = source.highlightDrawable
+ }
+ }
+
+ constructor(width: Int, height: Int) : super(width, height)
+
+ constructor(width: Int, height: Int, gravity: Int) : super(width, height, gravity)
+ }
+
+ //
+
+ //
+
+ //滚动支持
+ val _overScroller: OverScroller by lazy {
+ OverScroller(context)
+ }
+
+ //手势检测
+ val _gestureDetector: GestureDetectorCompat by lazy {
+ GestureDetectorCompat(context, object : GestureDetector.SimpleOnGestureListener() {
+ override fun onFling(
+ e1: MotionEvent,
+ e2: MotionEvent,
+ velocityX: Float,
+ velocityY: Float
+ ): Boolean {
+ if (isHorizontal()) {
+ val absX = abs(velocityX)
+ if (absX > _minFlingVelocity) {
+ onFlingChange(velocityX)
+ }
+ } else {
+ val absY = abs(velocityY)
+ if (absY > _minFlingVelocity) {
+ onFlingChange(velocityY)
+ }
+ }
+
+ return true
+ }
+
+ override fun onScroll(
+ e1: MotionEvent,
+ e2: MotionEvent,
+ distanceX: Float,
+ distanceY: Float
+ ): Boolean {
+ var handle = false
+ if (isHorizontal()) {
+ val absX = abs(distanceX)
+ if (absX > _touchSlop) {
+ handle = onScrollChange(distanceX)
+ }
+ } else {
+ val absY = abs(distanceY)
+ if (absY > _touchSlop) {
+ handle = onScrollChange(distanceY)
+ }
+ }
+ return handle
+ }
+ })
+ }
+
+ override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
+ var intercept = false
+ if (needScroll) {
+ if (ev.actionMasked == MotionEvent.ACTION_DOWN) {
+ _overScroller.abortAnimation()
+ _scrollAnimator.cancel()
+ }
+ if (isEnabled) {
+ intercept = super.onInterceptTouchEvent(ev) || _gestureDetector.onTouchEvent(ev)
+ }
+ } else {
+ if (isEnabled) {
+ intercept = super.onInterceptTouchEvent(ev)
+ }
+ }
+ return if (isEnabled) {
+ if (itemEnableSelector) {
+ intercept
+ } else {
+ true
+ }
+ } else {
+ false
+ }
+ }
+
+ override fun onTouchEvent(event: MotionEvent): Boolean {
+ if (isEnabled) {
+ if (needScroll) {
+ _gestureDetector.onTouchEvent(event)
+ if (event.actionMasked == MotionEvent.ACTION_CANCEL ||
+ event.actionMasked == MotionEvent.ACTION_UP
+ ) {
+ parent.requestDisallowInterceptTouchEvent(false)
+ } else if (event.actionMasked == MotionEvent.ACTION_DOWN) {
+ _overScroller.abortAnimation()
+ }
+ return true
+ } else {
+ return (isEnabled && super.onTouchEvent(event))
+ }
+ } else {
+ return false
+ }
+ }
+
+ /**是否需要滚动*/
+ val needScroll: Boolean
+ get() = if (tabEnableSelectorMode) true else {
+ if (isHorizontal()) {
+ if (isLayoutRtl) {
+ minScrollX < 0
+ } else {
+ maxScrollX > 0
+ }
+ } else {
+ maxScrollY > 0
+ }
+ }
+
+ /**[parent]宽度外的滚动距离*/
+ val maxScrollX: Int
+ get() = if (isLayoutRtl && isHorizontal()) {
+ if (tabEnableSelectorMode) viewDrawWidth / 2 else 0
+ } else {
+ max(
+ maxWidth - measuredWidth + if (tabEnableSelectorMode) viewDrawWidth / 2 else 0,
+ 0
+ )
+ }
+
+ val maxScrollY: Int
+ get() = max(
+ maxHeight - measuredHeight + if (tabEnableSelectorMode) viewDrawHeight / 2 else 0,
+ 0
+ )
+
+ /**最小滚动的值*/
+ val minScrollX: Int
+ get() = if (isLayoutRtl && isHorizontal()) {
+ min(
+ -(maxWidth - measuredWidth + if (tabEnableSelectorMode) viewDrawWidth / 2 else 0),
+ 0
+ )
+ } else {
+ if (tabEnableSelectorMode) -viewDrawWidth / 2 else 0
+ }
+
+ val minScrollY: Int
+ get() = if (tabEnableSelectorMode) -viewDrawHeight / 2 else 0
+
+ /**view最大的宽度*/
+ val maxWidth: Int
+ get() = _childAllWidthSum + paddingStart + paddingEnd
+
+ val maxHeight: Int
+ get() = _childAllWidthSum + paddingTop + paddingBottom
+
+ open fun onFlingChange(velocity: Float /*瞬时值*/) {
+ if (needScroll) {
+
+ //速率小于0 , 手指向左滑动
+ //速率大于0 , 手指向右滑动
+
+ if (tabEnableSelectorMode) {
+ if (isHorizontal() && isLayoutRtl) {
+ if (velocity < 0) {
+ setCurrentItem(dslSelector.dslSelectIndex - 1)
+ } else if (velocity > 0) {
+ setCurrentItem(dslSelector.dslSelectIndex + 1)
+ }
+ } else {
+ if (velocity < 0) {
+ setCurrentItem(dslSelector.dslSelectIndex + 1)
+ } else if (velocity > 0) {
+ setCurrentItem(dslSelector.dslSelectIndex - 1)
+ }
+ }
+ } else {
+ if (isHorizontal()) {
+ if (isLayoutRtl) {
+ startFling(-velocity.toInt(), minScrollX, 0)
+ } else {
+ startFling(-velocity.toInt(), 0, maxScrollX)
+ }
+ } else {
+ startFling(-velocity.toInt(), 0, maxHeight)
+ }
+ }
+ }
+ }
+
+ fun startFling(velocity: Int, min: Int, max: Int) {
+
+ fun velocity(velocity: Int): Int {
+ return if (velocity > 0) {
+ clamp(velocity, _minFlingVelocity, _maxFlingVelocity)
+ } else {
+ clamp(velocity, -_maxFlingVelocity, -_minFlingVelocity)
+ }
+ }
+
+ val v = velocity(velocity)
+
+ _overScroller.abortAnimation()
+
+ if (isHorizontal()) {
+ _overScroller.fling(
+ scrollX,
+ scrollY,
+ v,
+ 0,
+ min,
+ max,
+ 0,
+ 0,
+ measuredWidth,
+ 0
+ )
+ } else {
+ _overScroller.fling(
+ scrollX,
+ scrollY,
+ 0,
+ v,
+ 0,
+ 0,
+ min,
+ max,
+ 0,
+ measuredHeight
+ )
+ }
+ postInvalidate()
+ }
+
+ fun startScroll(dv: Int) {
+ _overScroller.abortAnimation()
+ if (isHorizontal()) {
+ _overScroller.startScroll(scrollX, scrollY, dv, 0, scrollAnimDuration)
+ } else {
+ _overScroller.startScroll(scrollX, scrollY, 0, dv, scrollAnimDuration)
+ }
+ ViewCompat.postInvalidateOnAnimation(this)
+ }
+
+ /**检查是否需要重置滚动的位置*/
+ fun restoreScroll() {
+ if (itemIsEquWidth || !needScroll) {
+ if (scrollX != 0 || scrollY != 0) {
+ scrollTo(0, 0)
+ }
+ }
+ }
+
+ open fun onScrollChange(distance: Float): Boolean {
+ if (needScroll) {
+
+ //distance小于0 , 手指向右滑动
+ //distance大于0 , 手指向左滑动
+
+ parent.requestDisallowInterceptTouchEvent(true)
+
+ if (tabEnableSelectorMode) {
+ //滑动选择模式下, 不响应scroll事件
+ } else {
+ if (isHorizontal()) {
+ scrollBy(distance.toInt(), 0)
+ } else {
+ scrollBy(0, distance.toInt())
+ }
+ }
+ return true
+ }
+ return false
+ }
+
+ override fun scrollTo(x: Int, y: Int) {
+ if (isHorizontal()) {
+ when {
+ x > maxScrollX -> super.scrollTo(maxScrollX, 0)
+ x < minScrollX -> super.scrollTo(minScrollX, 0)
+ else -> super.scrollTo(x, 0)
+ }
+ } else {
+ when {
+ y > maxScrollY -> super.scrollTo(0, maxScrollY)
+ y < minScrollY -> super.scrollTo(0, minScrollY)
+ else -> super.scrollTo(0, y)
+ }
+ }
+ }
+
+ override fun computeScroll() {
+ if (_overScroller.computeScrollOffset()) {
+ scrollTo(_overScroller.currX, _overScroller.currY)
+ invalidate()
+ if (_overScroller.currX < minScrollX || _overScroller.currX > maxScrollX) {
+ _overScroller.abortAnimation()
+ }
+ }
+ }
+
+ fun _getViewTargetX(): Int {
+ return when (tabIndicator.indicatorGravity) {
+ DslTabIndicator.INDICATOR_GRAVITY_START -> paddingStart
+ DslTabIndicator.INDICATOR_GRAVITY_END -> measuredWidth - paddingEnd
+ else -> paddingStart + viewDrawWidth / 2
+ }
+ }
+
+ fun _getViewTargetY(): Int {
+ return when (tabIndicator.indicatorGravity) {
+ DslTabIndicator.INDICATOR_GRAVITY_START -> paddingTop
+ DslTabIndicator.INDICATOR_GRAVITY_END -> measuredHeight - paddingBottom
+ else -> paddingTop + viewDrawHeight / 2
+ }
+ }
+
+ /**将[index]位置显示在TabLayout的中心*/
+ fun _scrollToTarget(index: Int, scrollAnim: Boolean) {
+ if (!needScroll) {
+ return
+ }
+
+ dslSelector.visibleViewList.getOrNull(index)?.let {
+ if (!ViewCompat.isLaidOut(it)) {
+ //没有布局
+ return
+ }
+ }
+
+ val dv = if (isHorizontal()) {
+ val childTargetX = tabIndicator.getChildTargetX(index)
+ val viewDrawTargetX = _getViewTargetX()
+ when {
+ tabEnableSelectorMode -> {
+ val viewCenterX = measuredWidth / 2
+ childTargetX - viewCenterX - scrollX
+ }
+
+ isLayoutRtl -> {
+ if (childTargetX < viewDrawTargetX) {
+ childTargetX - viewDrawTargetX - scrollX
+ } else {
+ -scrollX
+ }
+ }
+
+ else -> {
+ if (childTargetX > viewDrawTargetX) {
+ childTargetX - viewDrawTargetX - scrollX
+ } else {
+ -scrollX
+ }
+ }
+ }
+ } else {
+ //竖向
+ val childTargetY = tabIndicator.getChildTargetY(index)
+ val viewDrawTargetY = _getViewTargetY()
+ when {
+ tabEnableSelectorMode -> {
+ val viewCenterY = measuredHeight / 2
+ childTargetY - viewCenterY - scrollY
+ }
+
+ childTargetY > viewDrawTargetY -> {
+ childTargetY - viewDrawTargetY - scrollY
+ }
+
+ else -> {
+ if (tabIndicator.indicatorGravity == DslTabIndicator.INDICATOR_GRAVITY_END &&
+ childTargetY < viewDrawTargetY
+ ) {
+ childTargetY - viewDrawTargetY - scrollY
+ } else {
+ -scrollY
+ }
+ }
+ }
+ }
+
+ if (isHorizontal()) {
+ if (isInEditMode || !scrollAnim) {
+ _overScroller.abortAnimation()
+ scrollBy(dv, 0)
+ } else {
+ startScroll(dv)
+ }
+ } else {
+ if (isInEditMode || !scrollAnim) {
+ _overScroller.abortAnimation()
+ scrollBy(0, dv)
+ } else {
+ startScroll(dv)
+ }
+ }
+ }
+
+ //
+
+ //
+
+ val _scrollAnimator: ValueAnimator by lazy {
+ ValueAnimator().apply {
+ interpolator = LinearInterpolator()
+ duration = tabIndicatorAnimationDuration
+ addUpdateListener {
+ _onAnimateValue(it.animatedValue as Float)
+ }
+ addListener(object : AnimatorListenerAdapter() {
+ override fun onAnimationCancel(animation: Animator) {
+ _onAnimateValue(1f)
+ onAnimationEnd(animation)
+ }
+
+ override fun onAnimationEnd(animation: Animator) {
+ _onAnimateEnd()
+ }
+ })
+ }
+ }
+
+ val isAnimatorStart: Boolean
+ get() = _scrollAnimator.isStarted
+
+ fun _animateToItem(fromIndex: Int, toIndex: Int) {
+ if (toIndex == fromIndex) {
+ return
+ }
+
+ //取消之前的动画
+ _scrollAnimator.cancel()
+
+ if (!tabIndicator.indicatorAnim) {
+ //不需要动画
+ _onAnimateEnd()
+ return
+ }
+
+ if (fromIndex < 0) {
+ //从一个不存在的位置, 到目标位置
+ tabIndicator.currentIndex = toIndex
+ } else {
+ tabIndicator.currentIndex = fromIndex
+ }
+ tabIndicator._targetIndex = toIndex
+
+ if (isInEditMode) {
+ tabIndicator.currentIndex = toIndex
+ return
+ }
+
+ if (tabIndicator.currentIndex == tabIndicator._targetIndex) {
+ return
+ }
+ //"_animateToItem ${tabIndicator.currentIndex} ${tabIndicator._targetIndex}".loge()
+ _scrollAnimator.setFloatValues(tabIndicator.positionOffset, 1f)
+ _scrollAnimator.start()
+ }
+
+ fun _onAnimateValue(value: Float) {
+ tabIndicator.positionOffset = value
+ tabLayoutConfig?.onPageIndexScrolled(
+ tabIndicator.currentIndex,
+ tabIndicator._targetIndex,
+ value
+ )
+ tabLayoutConfig?.let {
+ dslSelector.visibleViewList.apply {
+ val targetView = getOrNull(tabIndicator._targetIndex)
+ if (targetView != null) {
+ it.onPageViewScrolled(
+ getOrNull(tabIndicator.currentIndex),
+ targetView,
+ value
+ )
+ }
+ }
+ }
+ }
+
+ fun _onAnimateEnd() {
+ tabIndicator.currentIndex = dslSelector.dslSelectIndex
+ tabIndicator._targetIndex = tabIndicator.currentIndex
+ tabIndicator.positionOffset = 0f
+ //结束_viewPager的滚动动画, 系统没有直接结束的api, 固用此方法代替.
+ //_viewPager?.setCurrentItem(tabIndicator.currentIndex, false)
+ }
+
+ //
+
+ //
+
+ var _viewPagerDelegate: ViewPagerDelegate? = null
+ var _viewPagerScrollState = 0
+
+ fun onPageScrollStateChanged(state: Int) {
+ //"$state".logi()
+ _viewPagerScrollState = state
+ if (state == ViewPagerDelegate.SCROLL_STATE_IDLE) {
+ _onAnimateEnd()
+ dslSelector.updateStyle()
+ }
+ }
+
+ fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
+ if (isAnimatorStart) {
+ //动画已经开始了
+ return
+ }
+
+ val currentItem = _viewPagerDelegate?.onGetCurrentItem() ?: 0
+ //"$currentItem:$position $positionOffset $positionOffsetPixels state:$_viewPagerScrollState".logw()
+
+ if (position < currentItem) {
+ //Page 目标在左
+ if (_viewPagerScrollState == ViewPagerDelegate.SCROLL_STATE_DRAGGING) {
+ tabIndicator.currentIndex = position + 1
+ tabIndicator._targetIndex = position
+ }
+ _onAnimateValue(1 - positionOffset)
+ } else {
+ //Page 目标在右
+ if (_viewPagerScrollState == ViewPagerDelegate.SCROLL_STATE_DRAGGING) {
+ tabIndicator.currentIndex = position
+ tabIndicator._targetIndex = position + 1
+ }
+ _onAnimateValue(positionOffset)
+ }
+ }
+
+ fun onPageSelected(position: Int) {
+ setCurrentItem(position, true, false)
+ }
+
+ //
+
+ //
+
+ override fun onRestoreInstanceState(state: Parcelable?) {
+ if (state is Bundle) {
+ val oldState: Parcelable? = state.getParcelable("old")
+ super.onRestoreInstanceState(oldState)
+
+ tabDefaultIndex = state.getInt("defaultIndex", tabDefaultIndex)
+ val currentItemIndex = state.getInt("currentIndex", -1)
+ dslSelector.dslSelectIndex = -1
+ if (currentItemIndex > 0) {
+ setCurrentItem(currentItemIndex, true, false)
+ }
+ } else {
+ super.onRestoreInstanceState(state)
+ }
+ }
+
+ override fun onSaveInstanceState(): Parcelable? {
+ val state = super.onSaveInstanceState()
+ val bundle = Bundle()
+ bundle.putParcelable("old", state)
+ bundle.putInt("defaultIndex", tabDefaultIndex)
+ bundle.putInt("currentIndex", currentItemIndex)
+ return bundle
+ }
+
+ //
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/DslTabLayoutConfig.kt b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabLayoutConfig.kt
new file mode 100644
index 000000000..3d4a48dec
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/DslTabLayoutConfig.kt
@@ -0,0 +1,526 @@
+package com.angcyo.tablayout
+
+import android.content.Context
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Typeface
+import android.util.AttributeSet
+import android.util.TypedValue
+import android.view.View
+import android.view.ViewGroup
+import android.widget.TextView
+import androidx.annotation.IdRes
+import com.angcyo.tablayout.DslTabIndicator.Companion.NO_COLOR
+import kotlin.math.max
+import kotlin.math.min
+
+/**
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/26
+ * Copyright (c) 2019 ShenZhen O&M Cloud Co., Ltd. All rights reserved.
+ */
+open class DslTabLayoutConfig(val tabLayout: DslTabLayout) : DslSelectorConfig() {
+
+ /**是否开启文本颜色*/
+ var tabEnableTextColor = true
+ set(value) {
+ field = value
+ if (field) {
+ tabEnableIcoColor = true
+ }
+ }
+
+ /**是否开启颜色渐变效果*/
+ var tabEnableGradientColor = false
+ set(value) {
+ field = value
+ if (field) {
+ tabEnableIcoGradientColor = true
+ }
+ }
+
+ /**是否激活指示器的颜色渐变效果*/
+ var tabEnableIndicatorGradientColor = false
+
+ /**选中的文本颜色*/
+ var tabSelectColor: Int = Color.WHITE //Color.parseColor("#333333")
+
+ /**未选中的文本颜色*/
+ var tabDeselectColor: Int = Color.parseColor("#999999")
+
+ /**是否开启Bold, 文本加粗*/
+ var tabEnableTextBold = false
+
+ /**是否使用粗体字体的方式设置粗体, 否则使用[Paint.FAKE_BOLD_TEXT_FLAG]
+ * 需要先激活[tabEnableTextBold]*/
+ var tabUseTypefaceBold = false
+
+ /**是否开启图标颜色*/
+ var tabEnableIcoColor = true
+
+ /**是否开启图标颜色渐变效果*/
+ var tabEnableIcoGradientColor = false
+
+ /**选中的图标颜色*/
+ var tabIcoSelectColor: Int = NO_COLOR
+ get() {
+ return if (field == NO_COLOR) tabSelectColor else field
+ }
+
+ /**未选中的图标颜色*/
+ var tabIcoDeselectColor: Int = NO_COLOR
+ get() {
+ return if (field == NO_COLOR) tabDeselectColor else field
+ }
+
+ /**是否开启scale渐变效果*/
+ var tabEnableGradientScale = false
+
+ /**最小缩放的比例*/
+ var tabMinScale = 0.8f
+
+ /**最大缩放的比例*/
+ var tabMaxScale = 1.2f
+
+ /**是否开启字体大小渐变效果*/
+ var tabEnableGradientTextSize = true
+
+ /**tab中文本字体未选中时的字体大小, >0时激活*/
+ var tabTextMinSize = -1f
+
+ /**tab中文本字体选中时的字体大小, >0时激活*/
+ var tabTextMaxSize = -1f
+
+ /**渐变效果实现的回调*/
+ var tabGradientCallback = TabGradientCallback()
+
+ /**指定文本控件的id, 所有文本属性改变, 将会发生在这个控件上.
+ * 如果指定的控件不存在, 控件会降权至[ItemView]*/
+ @IdRes
+ var tabTextViewId: Int = View.NO_ID
+
+ /**指定图标控件的id*/
+ @IdRes
+ var tabIconViewId: Int = View.NO_ID
+
+ /**返回用于配置文本样式的控件*/
+ var onGetTextStyleView: (itemView: View, index: Int) -> TextView? = { itemView, _ ->
+ if (tabTextViewId == View.NO_ID) {
+ var tv: TextView? = if (itemView is TextView) itemView else null
+
+ if (tabLayout.tabIndicator.indicatorContentIndex != -1) {
+ itemView.getChildOrNull(tabLayout.tabIndicator.indicatorContentIndex)?.let {
+ if (it is TextView) {
+ tv = it
+ }
+ }
+ }
+
+ if (tabLayout.tabIndicator.indicatorContentId != View.NO_ID) {
+ itemView.findViewById(tabLayout.tabIndicator.indicatorContentId)?.let {
+ if (it is TextView) {
+ tv = it
+ }
+ }
+ }
+
+ val lp = itemView.layoutParams
+ if (lp is DslTabLayout.LayoutParams) {
+ if (lp.indicatorContentIndex != -1 && itemView is ViewGroup) {
+ itemView.getChildOrNull(lp.indicatorContentIndex)?.let {
+ if (it is TextView) {
+ tv = it
+ }
+ }
+ }
+
+ if (lp.indicatorContentId != View.NO_ID) {
+ itemView.findViewById(lp.indicatorContentId)?.let {
+ if (it is TextView) {
+ tv = it
+ }
+ }
+ }
+
+ if (lp.contentTextViewIndex != -1 && itemView is ViewGroup) {
+ itemView.getChildOrNull(lp.contentTextViewIndex)?.let {
+ if (it is TextView) {
+ tv = it
+ }
+ }
+ }
+
+ if (lp.contentTextViewId != View.NO_ID) {
+ itemView.findViewById(lp.contentTextViewId)?.let {
+ if (it is TextView) {
+ tv = it
+ }
+ }
+ }
+ }
+ tv
+ } else {
+ itemView.findViewById(tabTextViewId)
+ }
+ }
+
+ /**返回用于配置ico样式的控件*/
+ var onGetIcoStyleView: (itemView: View, index: Int) -> View? = { itemView, _ ->
+ if (tabIconViewId == View.NO_ID) {
+ var iv: View? = itemView
+
+ if (tabLayout.tabIndicator.indicatorContentIndex != -1) {
+ itemView.getChildOrNull(tabLayout.tabIndicator.indicatorContentIndex)?.let {
+ iv = it
+ }
+ }
+
+ if (tabLayout.tabIndicator.indicatorContentId != View.NO_ID) {
+ itemView.findViewById(tabLayout.tabIndicator.indicatorContentId)?.let {
+ iv = it
+ }
+ }
+
+ val lp = itemView.layoutParams
+ if (lp is DslTabLayout.LayoutParams) {
+ if (lp.indicatorContentIndex != -1 && itemView is ViewGroup) {
+ iv = itemView.getChildOrNull(lp.indicatorContentIndex)
+ }
+
+ if (lp.indicatorContentId != View.NO_ID) {
+ itemView.findViewById(lp.indicatorContentId)?.let {
+ iv = it
+ }
+ }
+
+ if (lp.contentIconViewIndex != -1 && itemView is ViewGroup) {
+ iv = itemView.getChildOrNull(lp.contentIconViewIndex)
+ }
+
+ if (lp.contentIconViewId != View.NO_ID) {
+ itemView.findViewById(lp.contentIconViewId)?.let {
+ iv = it
+ }
+ }
+ }
+ iv
+ } else {
+ itemView.findViewById(tabIconViewId)
+ }
+ }
+
+ /**获取渐变结束时,指示器的颜色.*/
+ var onGetGradientIndicatorColor: (fromIndex: Int, toIndex: Int, positionOffset: Float) -> Int =
+ { fromIndex, toIndex, positionOffset ->
+ tabLayout.tabIndicator.indicatorColor
+ }
+
+ init {
+ onStyleItemView = { itemView, index, select ->
+ onUpdateItemStyle(itemView, index, select)
+ }
+ onSelectIndexChange = { fromIndex, selectIndexList, reselect, fromUser ->
+ val toIndex = selectIndexList.last()
+ tabLayout._viewPagerDelegate?.onSetCurrentItem(fromIndex, toIndex, reselect, fromUser)
+ }
+ }
+
+ /**xml属性读取*/
+ open fun initAttribute(context: Context, attributeSet: AttributeSet? = null) {
+ val typedArray = context.obtainStyledAttributes(attributeSet, R.styleable.DslTabLayout)
+
+ tabSelectColor =
+ typedArray.getColor(R.styleable.DslTabLayout_tab_select_color, tabSelectColor)
+ tabDeselectColor =
+ typedArray.getColor(
+ R.styleable.DslTabLayout_tab_deselect_color,
+ tabDeselectColor
+ )
+ tabIcoSelectColor =
+ typedArray.getColor(R.styleable.DslTabLayout_tab_ico_select_color, NO_COLOR)
+ tabIcoDeselectColor =
+ typedArray.getColor(R.styleable.DslTabLayout_tab_ico_deselect_color, NO_COLOR)
+
+ tabEnableTextColor = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_text_color,
+ tabEnableTextColor
+ )
+ tabEnableIndicatorGradientColor = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_indicator_gradient_color,
+ tabEnableIndicatorGradientColor
+ )
+ tabEnableGradientColor = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_gradient_color,
+ tabEnableGradientColor
+ )
+ tabEnableIcoColor = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_ico_color,
+ tabEnableIcoColor
+ )
+ tabEnableIcoGradientColor = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_ico_gradient_color,
+ tabEnableIcoGradientColor
+ )
+
+ tabEnableTextBold = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_text_bold,
+ tabEnableTextBold
+ )
+
+ tabUseTypefaceBold = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_use_typeface_bold,
+ tabUseTypefaceBold
+ )
+
+ tabEnableGradientScale = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_gradient_scale,
+ tabEnableGradientScale
+ )
+ tabMinScale = typedArray.getFloat(R.styleable.DslTabLayout_tab_min_scale, tabMinScale)
+ tabMaxScale = typedArray.getFloat(R.styleable.DslTabLayout_tab_max_scale, tabMaxScale)
+
+ tabEnableGradientTextSize = typedArray.getBoolean(
+ R.styleable.DslTabLayout_tab_enable_gradient_text_size,
+ tabEnableGradientTextSize
+ )
+ if (typedArray.hasValue(R.styleable.DslTabLayout_tab_text_min_size)) {
+ tabTextMinSize = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_text_min_size,
+ tabTextMinSize.toInt()
+ ).toFloat()
+ }
+ if (typedArray.hasValue(R.styleable.DslTabLayout_tab_text_max_size)) {
+ tabTextMaxSize = typedArray.getDimensionPixelOffset(
+ R.styleable.DslTabLayout_tab_text_max_size,
+ tabTextMaxSize.toInt()
+ ).toFloat()
+ }
+
+ tabTextViewId =
+ typedArray.getResourceId(R.styleable.DslTabLayout_tab_text_view_id, tabTextViewId)
+ tabIconViewId =
+ typedArray.getResourceId(R.styleable.DslTabLayout_tab_icon_view_id, tabIconViewId)
+
+ typedArray.recycle()
+ }
+
+ /**更新item的样式*/
+ open fun onUpdateItemStyle(itemView: View, index: Int, select: Boolean) {
+ //"$itemView\n$index\n$select".logw()
+
+ (onGetTextStyleView(itemView, index))?.apply {
+ //文本加粗
+ paint?.apply {
+ if (tabEnableTextBold && select) {
+ //设置粗体
+ if (tabUseTypefaceBold) {
+ typeface = Typeface.defaultFromStyle(Typeface.BOLD)
+ } else {
+ flags = flags or Paint.FAKE_BOLD_TEXT_FLAG
+ isFakeBoldText = true
+ }
+ } else {
+ //取消粗体
+ if (tabUseTypefaceBold) {
+ typeface = Typeface.defaultFromStyle(Typeface.NORMAL)
+ } else {
+ flags = flags and Paint.FAKE_BOLD_TEXT_FLAG.inv()
+ isFakeBoldText = false
+ }
+ }
+ }
+
+ if (tabEnableTextColor) {
+ //文本颜色
+ setTextColor(if (select) tabSelectColor else tabDeselectColor)
+ }
+
+ if (tabTextMaxSize > 0 || tabTextMinSize > 0) {
+ //文本字体大小
+ val minTextSize = min(tabTextMinSize, tabTextMaxSize)
+ val maxTextSize = max(tabTextMinSize, tabTextMaxSize)
+ setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ if (select) maxTextSize else minTextSize
+ )
+ }
+ }
+
+ if (tabEnableIcoColor) {
+ onGetIcoStyleView(itemView, index)?.apply {
+ _updateIcoColor(this, if (select) tabIcoSelectColor else tabIcoDeselectColor)
+ }
+ }
+
+ if (tabEnableGradientScale) {
+ itemView.scaleX = if (select) tabMaxScale else tabMinScale
+ itemView.scaleY = if (select) tabMaxScale else tabMinScale
+ }
+
+ if (tabLayout.drawBorder) {
+ tabLayout.tabBorder?.updateItemBackground(tabLayout, itemView, index, select)
+ }
+ }
+
+ /**
+ * [DslTabLayout]滚动时回调.
+ * */
+ open fun onPageIndexScrolled(fromIndex: Int, toIndex: Int, positionOffset: Float) {
+
+ }
+
+ /**
+ * [onPageIndexScrolled]
+ * */
+ open fun onPageViewScrolled(fromView: View?, toView: View, positionOffset: Float) {
+ //"$fromView\n$toView\n$positionOffset".logi()
+
+ if (fromView != toView) {
+
+ val fromIndex = tabLayout.tabIndicator.currentIndex
+ val toIndex = tabLayout.tabIndicator._targetIndex
+
+ if (tabEnableIndicatorGradientColor) {
+ val startColor = onGetGradientIndicatorColor(fromIndex, fromIndex, 0f)
+ val endColor = onGetGradientIndicatorColor(fromIndex, toIndex, positionOffset)
+
+ tabLayout.tabIndicator.indicatorColor =
+ evaluateColor(positionOffset, startColor, endColor)
+ }
+
+ if (tabEnableGradientColor) {
+ //文本渐变
+ fromView?.apply {
+ _gradientColor(
+ onGetTextStyleView(this, fromIndex),
+ tabSelectColor,
+ tabDeselectColor,
+ positionOffset
+ )
+ }
+ _gradientColor(
+ onGetTextStyleView(toView, toIndex),
+ tabDeselectColor,
+ tabSelectColor,
+ positionOffset
+ )
+ }
+
+ if (tabEnableIcoGradientColor) {
+ //图标渐变
+ fromView?.apply {
+ _gradientIcoColor(
+ onGetIcoStyleView(this, fromIndex),
+ tabIcoSelectColor,
+ tabIcoDeselectColor,
+ positionOffset
+ )
+ }
+
+ _gradientIcoColor(
+ onGetIcoStyleView(toView, toIndex),
+ tabIcoDeselectColor,
+ tabIcoSelectColor,
+ positionOffset
+ )
+ }
+
+ if (tabEnableGradientScale) {
+ //scale渐变
+ _gradientScale(fromView, tabMaxScale, tabMinScale, positionOffset)
+ _gradientScale(toView, tabMinScale, tabMaxScale, positionOffset)
+ }
+
+ if (tabEnableGradientTextSize &&
+ tabTextMaxSize > 0 &&
+ tabTextMinSize > 0 &&
+ tabTextMinSize != tabTextMaxSize
+ ) {
+
+ //文本字体大小渐变
+ _gradientTextSize(
+ fromView?.run { onGetTextStyleView(this, fromIndex) },
+ tabTextMaxSize,
+ tabTextMinSize,
+ positionOffset
+ )
+ _gradientTextSize(
+ onGetTextStyleView(toView, toIndex),
+ tabTextMinSize,
+ tabTextMaxSize,
+ positionOffset
+ )
+
+ if (toIndex == tabLayout.dslSelector.visibleViewList.lastIndex || toIndex == 0) {
+ tabLayout._scrollToTarget(toIndex, false)
+ }
+ }
+ }
+ }
+
+ open fun _gradientColor(view: View?, startColor: Int, endColor: Int, percent: Float) {
+ tabGradientCallback.onGradientColor(view, startColor, endColor, percent)
+ }
+
+ open fun _gradientIcoColor(view: View?, startColor: Int, endColor: Int, percent: Float) {
+ tabGradientCallback.onGradientIcoColor(view, startColor, endColor, percent)
+ }
+
+ open fun _gradientScale(view: View?, startScale: Float, endScale: Float, percent: Float) {
+ tabGradientCallback.onGradientScale(view, startScale, endScale, percent)
+ }
+
+ open fun _gradientTextSize(
+ view: TextView?,
+ startTextSize: Float,
+ endTextSize: Float,
+ percent: Float
+ ) {
+ tabGradientCallback.onGradientTextSize(view, startTextSize, endTextSize, percent)
+ }
+
+ open fun _updateIcoColor(view: View?, color: Int) {
+ tabGradientCallback.onUpdateIcoColor(view, color)
+ }
+}
+
+open class TabGradientCallback {
+
+ open fun onGradientColor(view: View?, startColor: Int, endColor: Int, percent: Float) {
+ (view as? TextView)?.apply {
+ setTextColor(evaluateColor(percent, startColor, endColor))
+ }
+ }
+
+ open fun onGradientIcoColor(view: View?, startColor: Int, endColor: Int, percent: Float) {
+ onUpdateIcoColor(view, evaluateColor(percent, startColor, endColor))
+ }
+
+ open fun onUpdateIcoColor(view: View?, color: Int) {
+ view?.tintDrawableColor(color)
+ }
+
+ open fun onGradientScale(view: View?, startScale: Float, endScale: Float, percent: Float) {
+ view?.apply {
+ (startScale + (endScale - startScale) * percent).let {
+ scaleX = it
+ scaleY = it
+ }
+ }
+ }
+
+ open fun onGradientTextSize(
+ view: TextView?,
+ startTextSize: Float,
+ endTextSize: Float,
+ percent: Float
+ ) {
+ view?.apply {
+ setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ (startTextSize + (endTextSize - startTextSize) * percent)
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/ITabIndicatorDraw.kt b/TabLayout/src/main/java/com/angcyo/tablayout/ITabIndicatorDraw.kt
new file mode 100644
index 000000000..1b7f74926
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/ITabIndicatorDraw.kt
@@ -0,0 +1,22 @@
+package com.angcyo.tablayout
+
+import android.graphics.Canvas
+
+/**
+ * 用来实现[DslTabIndicator]的自绘
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2022/02/21
+ * Copyright (c) 2020 ShenZhen Wayto Ltd. All rights reserved.
+ */
+interface ITabIndicatorDraw {
+
+ /**绘制指示器
+ * [positionOffset] 页面偏移量*/
+ fun onDrawTabIndicator(
+ tabIndicator: DslTabIndicator,
+ canvas: Canvas,
+ positionOffset: Float
+ )
+
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/LibEx.kt b/TabLayout/src/main/java/com/angcyo/tablayout/LibEx.kt
new file mode 100644
index 000000000..2d040a90e
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/LibEx.kt
@@ -0,0 +1,334 @@
+package com.angcyo.tablayout
+
+import android.app.Activity
+import android.content.Context
+import android.content.res.Resources
+import android.graphics.Paint
+import android.graphics.PorterDuff
+import android.graphics.Rect
+import android.graphics.drawable.Drawable
+import android.os.Build
+import android.text.TextUtils
+import android.util.Log
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.view.Window
+import android.widget.ImageView
+import android.widget.LinearLayout
+import android.widget.TextView
+import androidx.annotation.LayoutRes
+import androidx.core.graphics.drawable.DrawableCompat
+import androidx.core.math.MathUtils
+
+/**
+ *
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/11/23
+ */
+internal val dpi: Int
+ get() = dp.toInt()
+
+internal val dp: Float
+ get() = Resources.getSystem().displayMetrics.density
+
+internal val View.dpi: Int
+ get() = context.resources.displayMetrics.density.toInt()
+
+internal val View.screenWidth: Int
+ get() = context.resources.displayMetrics.widthPixels
+
+internal val View.screenHeight: Int
+ get() = context.resources.displayMetrics.heightPixels
+
+internal val View.viewDrawWidth: Int
+ get() = measuredWidth - paddingLeft - paddingRight
+
+internal val View.viewDrawHeight: Int
+ get() = measuredHeight - paddingTop - paddingBottom
+
+/**Match_Parent*/
+internal fun exactlyMeasure(size: Int): Int =
+ View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.EXACTLY)
+
+internal fun exactlyMeasure(size: Float): Int = exactlyMeasure(size.toInt())
+
+/**Wrap_Content*/
+internal fun atmostMeasure(size: Int): Int =
+ View.MeasureSpec.makeMeasureSpec(size, View.MeasureSpec.AT_MOST)
+
+internal fun Int.have(value: Int): Boolean = if (this == 0 || value == 0) {
+ false
+} else if (this == 0 && value == 0) {
+ true
+} else {
+ ((this > 0 && value > 0) || (this < 0 && value < 0)) && this and value == value
+}
+
+internal fun Int.remove(value: Int): Int = this and value.inv()
+
+internal fun clamp(value: Float, min: Float, max: Float): Float {
+ if (value < min) {
+ return min
+ } else if (value > max) {
+ return max
+ }
+ return value
+}
+
+internal fun clamp(value: Int, min: Int, max: Int): Int {
+ if (value < min) {
+ return min
+ } else if (value > max) {
+ return max
+ }
+ return value
+}
+
+internal fun Any.logi() {
+ Log.i("DslTabLayout", "$this")
+}
+
+internal fun Any.logw() {
+ Log.w("DslTabLayout", "$this")
+}
+
+internal fun Any.loge() {
+ Log.e("DslTabLayout", "$this")
+}
+
+internal fun View.calcLayoutWidthHeight(
+ rLayoutWidth: String?, rLayoutHeight: String?,
+ parentWidth: Int, parentHeight: Int,
+ rLayoutWidthExclude: Int = 0, rLayoutHeightExclude: Int = 0
+): IntArray {
+ val size = intArrayOf(-1, -1)
+ if (TextUtils.isEmpty(rLayoutWidth) && TextUtils.isEmpty(rLayoutHeight)) {
+ return size
+ }
+ if (!TextUtils.isEmpty(rLayoutWidth)) {
+ if (rLayoutWidth!!.contains("sw", true)) {
+ val ratio = rLayoutWidth.replace("sw", "", true).toFloatOrNull()
+ ratio?.let {
+ size[0] = (ratio * (screenWidth - rLayoutWidthExclude)).toInt()
+ }
+ } else if (rLayoutWidth!!.contains("pw", true)) {
+ val ratio = rLayoutWidth.replace("pw", "", true).toFloatOrNull()
+ ratio?.let {
+ size[0] = (ratio * (parentWidth - rLayoutWidthExclude)).toInt()
+ }
+ }
+ }
+ if (!TextUtils.isEmpty(rLayoutHeight)) {
+ if (rLayoutHeight!!.contains("sh", true)) {
+ val ratio = rLayoutHeight.replace("sh", "", true).toFloatOrNull()
+ ratio?.let {
+ size[1] = (ratio * (screenHeight - rLayoutHeightExclude)).toInt()
+ }
+ } else if (rLayoutHeight!!.contains("ph", true)) {
+ val ratio = rLayoutHeight.replace("ph", "", true).toFloatOrNull()
+ ratio?.let {
+ size[1] = (ratio * (parentHeight - rLayoutHeightExclude)).toInt()
+ }
+ }
+ }
+ return size
+}
+
+internal fun evaluateColor(fraction: Float /*0-1*/, startColor: Int, endColor: Int): Int {
+ val fr = MathUtils.clamp(fraction, 0f, 1f)
+ val startA = startColor shr 24 and 0xff
+ val startR = startColor shr 16 and 0xff
+ val startG = startColor shr 8 and 0xff
+ val startB = startColor and 0xff
+ val endA = endColor shr 24 and 0xff
+ val endR = endColor shr 16 and 0xff
+ val endG = endColor shr 8 and 0xff
+ val endB = endColor and 0xff
+ return startA + (fr * (endA - startA)).toInt() shl 24 or
+ (startR + (fr * (endR - startR)).toInt() shl 16) or
+ (startG + (fr * (endG - startG)).toInt() shl 8) or
+ startB + (fr * (endB - startB)).toInt()
+}
+
+internal fun Drawable?.tintDrawableColor(color: Int): Drawable? {
+
+ if (this == null) {
+ return this
+ }
+
+ val wrappedDrawable =
+ DrawableCompat.wrap(this).mutate()
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
+ DrawableCompat.setTint(wrappedDrawable, color)
+ } else {
+ wrappedDrawable.setColorFilter(color, PorterDuff.Mode.SRC_ATOP)
+ }
+
+ return wrappedDrawable
+}
+
+internal fun View?.tintDrawableColor(color: Int) {
+ when (this) {
+ is TextView -> {
+ val drawables = arrayOfNulls(4)
+ compoundDrawables.forEachIndexed { index, drawable ->
+ drawables[index] = drawable?.tintDrawableColor(color)
+ }
+ setCompoundDrawables(drawables[0], drawables[1], drawables[2], drawables[3])
+ }
+ is ImageView -> {
+ setImageDrawable(drawable?.tintDrawableColor(color))
+ }
+ }
+}
+
+internal fun Paint?.textWidth(text: String?): Float {
+ if (TextUtils.isEmpty(text)) {
+ return 0f
+ }
+ return this?.run {
+ measureText(text)
+ } ?: 0f
+}
+
+internal fun Paint?.textHeight(): Float = this?.run { descent() - ascent() } ?: 0f
+
+internal fun View.getChildOrNull(index: Int): View? {
+ return if (this is ViewGroup) {
+ return if (index in 0 until childCount) {
+ getChildAt(index)
+ } else {
+ null
+ }
+ } else {
+ this
+ }
+}
+
+/**获取[View]在指定[parent]中的矩形坐标*/
+internal fun View.getLocationInParent(parentView: View? = null, result: Rect = Rect()): Rect {
+ val parent: View? = parentView ?: (parent as? View)
+
+ if (parent == null) {
+ getViewRect(result)
+ } else {
+ result.set(0, 0, 0, 0)
+ if (this != parent) {
+ fun doIt(view: View, parent: View, rect: Rect) {
+ val viewParent = view.parent
+ if (viewParent is View) {
+ rect.left += view.left
+ rect.top += view.top
+ if (viewParent != parent) {
+ doIt(viewParent, parent, rect)
+ }
+ }
+ }
+ doIt(this, parent, result)
+ }
+ result.right = result.left + this.measuredWidth
+ result.bottom = result.top + this.measuredHeight
+ }
+
+ return result
+}
+
+/**
+ * 获取View, 相对于手机屏幕的矩形
+ * */
+internal fun View.getViewRect(result: Rect = Rect()): Rect {
+ var offsetX = 0
+ var offsetY = 0
+
+ //横屏, 并且显示了虚拟导航栏的时候. 需要左边偏移
+ //只计算一次
+ (context as? Activity)?.let {
+ it.window.decorView.getGlobalVisibleRect(result)
+ if (result.width() > result.height()) {
+ //横屏了
+ offsetX = navBarHeight(it)
+ }
+ }
+
+ return getViewRect(offsetX, offsetY, result)
+}
+
+/**
+ * 获取View, 相对于手机屏幕的矩形, 带皮阿尼一
+ * */
+internal fun View.getViewRect(offsetX: Int, offsetY: Int, result: Rect = Rect()): Rect {
+ //可见位置的坐标, 超出屏幕的距离会被剃掉
+ //getGlobalVisibleRect(r)
+ val r2 = IntArray(2)
+ //val r3 = IntArray(2)
+ //相对于屏幕的坐标
+ getLocationOnScreen(r2)
+ //相对于窗口的坐标
+ //getLocationInWindow(r3)
+
+ val left = r2[0] + offsetX
+ val top = r2[1] + offsetY
+
+ result.set(left, top, left + measuredWidth, top + measuredHeight)
+ return result
+}
+
+
+/**
+ * 导航栏的高度(如果显示了)
+ */
+internal fun navBarHeight(context: Context): Int {
+ var result = 0
+
+ if (context is Activity) {
+ val decorRect = Rect()
+ val windowRect = Rect()
+
+ context.window.decorView.getGlobalVisibleRect(decorRect)
+ context.window.findViewById(Window.ID_ANDROID_CONTENT)
+ .getGlobalVisibleRect(windowRect)
+
+ if (decorRect.width() > decorRect.height()) {
+ //横屏
+ result = decorRect.width() - windowRect.width()
+ } else {
+ //竖屏
+ result = decorRect.bottom - windowRect.bottom
+ }
+ }
+
+ return result
+}
+
+fun Collection<*>?.size() = this?.size ?: 0
+
+/**判断2个列表中的数据是否改变过*/
+internal fun List?.isChange(other: List?): Boolean {
+ if (this.size() != other.size()) {
+ return true
+ }
+ this?.forEachIndexed { index, t ->
+ if (t != other?.getOrNull(index)) {
+ return true
+ }
+ }
+ return false
+}
+
+fun Int.isHorizontal() = this == LinearLayout.HORIZONTAL
+
+fun Int.isVertical() = this == LinearLayout.VERTICAL
+
+internal fun ViewGroup.inflate(@LayoutRes layoutId: Int, attachToRoot: Boolean = true): View {
+ if (layoutId == -1) {
+ return this
+ }
+ val rootView = LayoutInflater.from(context).inflate(layoutId, this, false)
+ if (attachToRoot) {
+ addView(rootView)
+ }
+ return rootView
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/java/com/angcyo/tablayout/ViewPagerDelegate.kt b/TabLayout/src/main/java/com/angcyo/tablayout/ViewPagerDelegate.kt
new file mode 100644
index 000000000..a40df8500
--- /dev/null
+++ b/TabLayout/src/main/java/com/angcyo/tablayout/ViewPagerDelegate.kt
@@ -0,0 +1,21 @@
+package com.angcyo.tablayout
+
+/**
+ * 不依赖ViewPager和ViewPager2
+ * Email:angcyo@126.com
+ * @author angcyo
+ * @date 2019/12/14
+ */
+interface ViewPagerDelegate {
+ companion object {
+ const val SCROLL_STATE_IDLE = 0
+ const val SCROLL_STATE_DRAGGING = 1
+ const val SCROLL_STATE_SETTLING = 2
+ }
+
+ /**获取当前页面索引*/
+ fun onGetCurrentItem(): Int
+
+ /**设置当前的页面*/
+ fun onSetCurrentItem(fromIndex: Int, toIndex: Int, reselect: Boolean, fromUser: Boolean)
+}
\ No newline at end of file
diff --git a/TabLayout/src/main/res/values/attr_dsl_tab_layout.xml b/TabLayout/src/main/res/values/attr_dsl_tab_layout.xml
new file mode 100644
index 000000000..423d215c8
--- /dev/null
+++ b/TabLayout/src/main/res/values/attr_dsl_tab_layout.xml
@@ -0,0 +1,299 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/build.gradle b/common/build.gradle
index 39c0f0dd1..664aa81d8 100644
--- a/common/build.gradle
+++ b/common/build.gradle
@@ -107,6 +107,7 @@ dependencies {
//svga播放器
//api rootProject.ext.dependencies["SVGAPlayer"]
+ // api project(path:':SVGAlibrary')//svga
implementation 'com.squareup.wire:wire-runtime:4.4.1'
//七牛云存储
@@ -237,4 +238,7 @@ dependencies {
api 'com.google.android.exoplayer:exoplayer-core:2.18.2@aar'
api rootProject.ext.dependencies["blank-utilcode"]
+
+ //下标切换器 https://github.com/angcyo/DslTabLayout
+ api project(':TabLayout')
}
diff --git a/common/src/main/assets/gift_wall_gift_info_light.svga b/common/src/main/assets/gift_wall_gift_info_light.svga
new file mode 100644
index 000000000..6965e8c72
Binary files /dev/null and b/common/src/main/assets/gift_wall_gift_info_light.svga differ
diff --git a/common/src/main/assets/gift_wall_light_up.svga b/common/src/main/assets/gift_wall_light_up.svga
new file mode 100644
index 000000000..d62328461
Binary files /dev/null and b/common/src/main/assets/gift_wall_light_up.svga differ
diff --git a/common/src/main/java/com/yunbao/common/Constants.java b/common/src/main/java/com/yunbao/common/Constants.java
index 58bbd92e6..a2bab93b2 100644
--- a/common/src/main/java/com/yunbao/common/Constants.java
+++ b/common/src/main/java/com/yunbao/common/Constants.java
@@ -20,6 +20,7 @@ public class Constants {
public static final String AVATAR = "avatar";
public static final String SIGN = "sign";
public static final String TO_UID = "toUid";
+ public static final String TO_UNAME = "toUserName";
public static final String INTOINDEX = "intoIndex";
public static final String FROM_LIVE_ROOM = "fromLiveRoom";
public static final String TO_NAME = "toName";
diff --git a/common/src/main/java/com/yunbao/common/adapter/GiftWallGiftInfoListItemAdapter.java b/common/src/main/java/com/yunbao/common/adapter/GiftWallGiftInfoListItemAdapter.java
new file mode 100644
index 000000000..1720818ca
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/adapter/GiftWallGiftInfoListItemAdapter.java
@@ -0,0 +1,77 @@
+package com.yunbao.common.adapter;
+
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.makeramen.roundedimageview.RoundedImageView;
+import com.yunbao.common.R;
+import com.yunbao.common.bean.GiftWallInfoBean;
+import com.yunbao.common.glide.ImgLoader;
+import com.yunbao.common.utils.WordUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class GiftWallGiftInfoListItemAdapter extends RecyclerView.Adapter {
+ List data = new ArrayList<>();
+
+ public void setData(List data) {
+ if (data == null) {
+ data = new ArrayList<>();
+ }
+ this.data = data;
+ notifyDataSetChanged();
+ }
+
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ return new VH(LayoutInflater.from(parent.getContext()).inflate(R.layout.item_dialog_gift_wall_gift_info, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ holder.setData(data.get(position), position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return data.size();
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ TextView tv_rank, user_name, tv_rename;
+ RoundedImageView avatar;
+
+ public VH(@NonNull View itemView) {
+ super(itemView);
+ tv_rank = itemView.findViewById(R.id.tv_rank);
+ user_name = itemView.findViewById(R.id.user_name);
+ tv_rename = itemView.findViewById(R.id.tv_rename);
+ avatar = itemView.findViewById(R.id.avatar);
+ }
+
+ public void setData(GiftWallInfoBean.Data data, int position) {
+ tv_rank.setText(String.format(Locale.getDefault(), "%d", (position + 1)));
+ tv_rank.setTextColor(Color.parseColor("#FCC755"));
+ tv_rank.setTextSize(20);
+ tv_rename.setText(String.format(Locale.getDefault(), "%d", (data.getGift_hall_send_num())));
+ avatar.setVisibility(View.VISIBLE);
+ if (data.getActive_rank_hide() == 1) {
+ user_name.setText(WordUtil.getNewString(R.string.mystery_man));
+ avatar.setImageResource(R.mipmap.hide);
+ } else {
+ user_name.setText(data.getUser_name());
+ ImgLoader.display(itemView.getContext(), data.getAvatar(), avatar);
+ }
+ }
+
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/adapter/GiftWallMainTab1List2Adapter.java b/common/src/main/java/com/yunbao/common/adapter/GiftWallMainTab1List2Adapter.java
new file mode 100644
index 000000000..3d8f9c07c
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/adapter/GiftWallMainTab1List2Adapter.java
@@ -0,0 +1,180 @@
+package com.yunbao.common.adapter;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.BitmapDrawable;
+import android.graphics.drawable.Drawable;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewTreeObserver;
+import android.view.WindowInsets;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.bumptech.glide.Glide;
+import com.opensource.svgaplayer.SVGACallback;
+import com.opensource.svgaplayer.SVGADrawable;
+import com.opensource.svgaplayer.SVGAImageView;
+import com.opensource.svgaplayer.SVGAParser;
+import com.opensource.svgaplayer.SVGAVideoEntity;
+import com.yunbao.common.R;
+import com.yunbao.common.bean.GiftWallBean;
+import com.yunbao.common.dialog.GiftWallGiftInfoDialog;
+import com.yunbao.common.glide.ImgLoader;
+import com.yunbao.common.manager.base.ACache;
+import com.yunbao.common.utils.DpUtil;
+import com.yunbao.common.utils.SVGAViewUtils;
+import com.yunbao.common.utils.WordUtil;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GiftWallMainTab1List2Adapter extends RecyclerView.Adapter {
+ Context mContext;
+ List list;
+ boolean isStar;
+ SVGAVideoEntity drawable;
+ String toUid;
+ boolean isAnchor;
+ private boolean isLiveRoom;
+
+ public GiftWallMainTab1List2Adapter(Context mContext) {
+ this.mContext = mContext;
+ list = new ArrayList<>();
+ }
+
+ public void setToUid(String toUid) {
+ this.toUid = toUid;
+ }
+
+ public void setAnchor(boolean anchor) {
+ isAnchor = anchor;
+ }
+
+ public void setList(List list) {
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+ this.list = list;
+ notifyDataSetChanged();
+ }
+
+ public void setDrawable(SVGAVideoEntity drawable) {
+ this.drawable = drawable;
+ }
+
+ public void setStar(boolean star) {
+ isStar = star;
+ }
+
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+
+ return new VH(LayoutInflater.from(mContext).inflate(R.layout.item_gift_wall_man_tab1_list_2, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ holder.setData(list.get(position), position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ @Override
+ public void onViewAttachedToWindow(@NonNull VH holder) {
+ super.onViewAttachedToWindow(holder);
+ holder.giftBg.startAnimation();
+ }
+
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull VH holder) {
+ super.onViewDetachedFromWindow(holder);
+ holder.giftBg.stopAnimation();
+
+ }
+
+ public void setLiveRoom(boolean isLiveRoom) {
+ this.isLiveRoom = isLiveRoom;
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ SVGAImageView giftBg;
+ ImageView gift_soles;
+ ImageView giftImage;
+ TextView gift_name;
+ TextView gift_status;
+ ProgressBar progressBar;
+
+ public VH(View itemView) {
+ super(itemView);
+ giftBg = itemView.findViewById(R.id.gift_bg);
+ gift_soles = itemView.findViewById(R.id.gift_soles);
+ giftImage = itemView.findViewById(R.id.gift);
+ gift_name = itemView.findViewById(R.id.gift_name);
+ gift_status = itemView.findViewById(R.id.gift_status);
+ progressBar = itemView.findViewById(R.id.progressBar);
+ }
+
+ @SuppressLint("DefaultLocale")
+ public void setData(GiftWallBean.Gift gift, int position) {
+ gift_name.setText(WordUtil.isNewZh() ? gift.getGift_name() : gift.getGift_name_en());
+ ImgLoader.display(itemView.getContext(), gift.getGift_icon(), giftImage, 40, 40);
+ giftBg.setClearsAfterDetached(false);
+ giftBg.setClearsAfterStop(false);
+ if (gift.getIlluminate_status() == 1) {
+ gift_status.setText(String.format("%s%d", WordUtil.getNewString(R.string.dialog_gift_wall_list_spinner_up), gift.getGift_hall_send_num()));
+ gift_status.setTextColor(Color.parseColor("#FFFFFF"));
+ gift_soles.setImageResource(getSolesrRes());
+ if (drawable != null) {
+ giftBg.post(() -> {
+ giftBg.setImageDrawable(new SVGADrawable(drawable));
+ giftBg.setLoops(0);
+ giftBg.startAnimation();
+ });
+ }
+ progressBar.setMax(gift.getIlluminate_num());
+ progressBar.setProgress(gift.getGift_hall_send_num());
+ } else {
+ gift_status.setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_spinner_down));
+ gift_status.setTextColor(Color.parseColor("#01071A"));
+ gift_soles.setImageResource(getUnSolesrRes());
+ giftBg.setImageResource(R.mipmap.gift_wall_main_item_bg1);
+ }
+ ViewClicksAntiShake.clicksAntiShake(itemView, new ViewClicksAntiShake.ViewClicksCallBack() {
+ @Override
+ public void onViewClicks() {
+ new GiftWallGiftInfoDialog(mContext, gift.getGift_id() + "", toUid, isAnchor)
+ .setFullWindows(!isLiveRoom)
+ .setLiveRoom(isLiveRoom)
+ .setStar(isStar)
+ .showDialog();
+ }
+ });
+ }
+
+ private int getSolesrRes() {
+ return isStar ? R.mipmap.gift_wall_main_item_select : R.mipmap.gift_wall_main_item_select1;
+ }
+
+ private int getUnSolesrRes() {
+ return isStar ? R.mipmap.gift_wall_main_item_unselect : R.mipmap.gift_wall_main_item_unselect1;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/adapter/GiftWallMainTab2ListAdapter.java b/common/src/main/java/com/yunbao/common/adapter/GiftWallMainTab2ListAdapter.java
new file mode 100644
index 000000000..1c11ab4dd
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/adapter/GiftWallMainTab2ListAdapter.java
@@ -0,0 +1,169 @@
+package com.yunbao.common.adapter;
+
+import android.annotation.SuppressLint;
+import android.content.Context;
+import android.graphics.Color;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.makeramen.roundedimageview.RoundedImageView;
+import com.opensource.svgaplayer.SVGADrawable;
+import com.opensource.svgaplayer.SVGAImageView;
+import com.opensource.svgaplayer.SVGAVideoEntity;
+import com.yunbao.common.R;
+import com.yunbao.common.bean.GiftWallBean;
+import com.yunbao.common.bean.GiftWallTab2Bean;
+import com.yunbao.common.dialog.GiftWallGiftInfoDialog;
+import com.yunbao.common.dialog.GiftWallMainTab2ClassicInfoDialog;
+import com.yunbao.common.glide.ImgLoader;
+import com.yunbao.common.utils.StringUtil;
+import com.yunbao.common.utils.WordUtil;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class GiftWallMainTab2ListAdapter extends RecyclerView.Adapter {
+ Context mContext;
+ List list;
+ boolean isStar;
+ String toUid;
+ boolean isAnchor;
+ private boolean isLiveRoom;
+
+ public GiftWallMainTab2ListAdapter(Context mContext) {
+ this.mContext = mContext;
+ list = new ArrayList<>();
+ }
+
+ public void setToUid(String toUid) {
+ this.toUid = toUid;
+ }
+
+ public void setAnchor(boolean anchor) {
+ isAnchor = anchor;
+ }
+
+ public void setList(List list) {
+ if (list == null) {
+ list = new ArrayList<>();
+ }
+ this.list = list;
+ notifyDataSetChanged();
+ }
+
+
+ public void setStar(boolean star) {
+ isStar = star;
+ }
+
+ @NonNull
+ @Override
+ public VH onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
+ if (isStar) {
+ return new VH(LayoutInflater.from(mContext).inflate(R.layout.item_gift_wall_man_tab2_list_1, parent, false));
+ }
+ return new VH(LayoutInflater.from(mContext).inflate(R.layout.item_gift_wall_man_tab2_list_2, parent, false));
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull VH holder, int position) {
+ holder.setData(list.get(position), position);
+ }
+
+ @Override
+ public int getItemCount() {
+ return list.size();
+ }
+
+ @Override
+ public void onViewAttachedToWindow(@NonNull VH holder) {
+ super.onViewAttachedToWindow(holder);
+ }
+
+
+ @Override
+ public void onViewDetachedFromWindow(@NonNull VH holder) {
+ super.onViewDetachedFromWindow(holder);
+
+ }
+
+ public void setLiveRoom(boolean isLiveRoom) {
+ this.isLiveRoom = isLiveRoom;
+ }
+
+ public class VH extends RecyclerView.ViewHolder {
+ ImageView gift;
+ TextView gift_name;
+ TextView gift_number;
+ RoundedImageView user1Avatar, user2Avatar;
+ TextView anchor_nickname;
+ TextView user_nickname;
+ TextView tv_wait;
+ View imageView8;
+
+ public VH(View itemView) {
+ super(itemView);
+
+ gift = itemView.findViewById(R.id.gift);
+ gift_name = itemView.findViewById(R.id.gift_name);
+ gift_number = itemView.findViewById(R.id.gift_number);
+ user1Avatar = itemView.findViewById(R.id.user1_avatar);
+ user2Avatar = itemView.findViewById(R.id.user2_avatar);
+ anchor_nickname = itemView.findViewById(R.id.anchor_nickname);
+ user_nickname = itemView.findViewById(R.id.user_nickname);
+ tv_wait = itemView.findViewById(R.id.tv_wait);
+ imageView8 = itemView.findViewById(R.id.imageView8);
+ }
+
+ @SuppressLint("DefaultLocale")
+ public void setData(GiftWallTab2Bean.Gift giftData, int position) {
+ gift_name.setText(WordUtil.isNewZh() ? giftData.getGiftName() : giftData.getGiftNameEn());
+ ImgLoader.display(itemView.getContext(), giftData.getGiftIcon(), gift, 60, 60);
+ if (!StringUtil.isEmpty(giftData.getNamingLiveNicename(), giftData.getNamingUserNicename())) {
+ anchor_nickname.setVisibility(View.VISIBLE);
+ user_nickname.setVisibility(View.VISIBLE);
+ gift_number.setVisibility(View.VISIBLE);
+ user1Avatar.setVisibility(View.VISIBLE);
+ user2Avatar.setVisibility(View.VISIBLE);
+ imageView8.setVisibility(View.VISIBLE);
+ tv_wait.setVisibility(View.GONE);
+ gift_number.setText(String.format(Locale.getDefault(), "%d", giftData.getNeedCoinTotal()));
+ ImgLoader.display(mContext, giftData.getNamingLiveAvatar(), user1Avatar, 35, 35);
+ ImgLoader.display(mContext, giftData.getNamingUserAvatar(), user2Avatar, 35, 35);
+ anchor_nickname.setText(giftData.getNamingLiveNicename());
+ user_nickname.setText(giftData.getNamingUserNicename());
+ } else {
+ user1Avatar.setVisibility(View.GONE);
+ user2Avatar.setVisibility(View.GONE);
+ anchor_nickname.setVisibility(View.GONE);
+ user_nickname.setVisibility(View.GONE);
+ gift_number.setVisibility(View.GONE);
+ imageView8.setVisibility(View.GONE);
+ tv_wait.setVisibility(View.VISIBLE);
+ }
+ ViewClicksAntiShake.clicksAntiShake(itemView, new ViewClicksAntiShake.ViewClicksCallBack() {
+ @Override
+ public void onViewClicks() {
+ new GiftWallMainTab2ClassicInfoDialog(mContext, giftData, isAnchor).setFullWindows(!isLiveRoom).show();
+ }
+ });
+ }
+
+ private int getSolesrRes() {
+ return isStar ? R.mipmap.gift_wall_main_item_select : R.mipmap.gift_wall_main_item_select1;
+ }
+
+ private int getUnSolesrRes() {
+ return isStar ? R.mipmap.gift_wall_main_item_unselect : R.mipmap.gift_wall_main_item_unselect1;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/bean/GiftWallBean.java b/common/src/main/java/com/yunbao/common/bean/GiftWallBean.java
new file mode 100644
index 000000000..32d35b354
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/bean/GiftWallBean.java
@@ -0,0 +1,253 @@
+package com.yunbao.common.bean;
+
+import com.google.gson.annotations.SerializedName;
+import com.yunbao.common.utils.StringUtil;
+
+import java.util.List;
+
+public class GiftWallBean extends BaseModel {
+ private IlluminateData illuminate_data;
+ private int active_rank_hide;
+
+
+ public GiftWallBean() {
+ }
+
+ public IlluminateData getIlluminate_data() {
+ return illuminate_data;
+ }
+
+ public void setIlluminate_data(IlluminateData illuminate_data) {
+ this.illuminate_data = illuminate_data;
+ }
+
+ public int getActive_rank_hide() {
+ return active_rank_hide;
+ }
+
+ public void setActive_rank_hide(int active_rank_hide) {
+ this.active_rank_hide = active_rank_hide;
+ }
+
+ public static class IlluminateData {
+ private List week_star_data;
+ private List gift_data;
+ private int week_gift_illuminate_num;
+ private int week_star_gift_num;
+ private int gift_illuminate_num;
+ private int gift_num;
+ private String gift_hall_start_date;
+ private String gift_hall_end_date;
+
+
+ public IlluminateData() {
+ }
+
+ public String getGift_hall_start_date() {
+ if(!StringUtil.isEmpty("gift_hall_start_date")){
+ gift_hall_start_date=gift_hall_start_date.replace("-","/");
+ }
+ return gift_hall_start_date;
+ }
+
+ public void setGift_hall_start_date(String gift_hall_start_date) {
+ this.gift_hall_start_date = gift_hall_start_date;
+ }
+
+ public String getGift_hall_end_date() {
+ if(!StringUtil.isEmpty("gift_hall_end_date")){
+ gift_hall_end_date=gift_hall_end_date.replace("-","/");
+ }
+ return gift_hall_end_date;
+ }
+
+ public void setGift_hall_end_date(String gift_hall_end_date) {
+ this.gift_hall_end_date = gift_hall_end_date;
+ }
+
+ public List getWeek_star_data() {
+ return week_star_data;
+ }
+
+ public void setWeek_star_data(List week_star_data) {
+ this.week_star_data = week_star_data;
+ }
+
+ public List getGift_data() {
+ return gift_data;
+ }
+
+ public void setGift_data(List gift_data) {
+ this.gift_data = gift_data;
+ }
+
+ public int getWeek_gift_illuminate_num() {
+ return week_gift_illuminate_num;
+ }
+
+ public void setWeek_gift_illuminate_num(int week_gift_illuminate_num) {
+ this.week_gift_illuminate_num = week_gift_illuminate_num;
+ }
+
+ public int getWeek_star_gift_num() {
+ return week_star_gift_num;
+ }
+
+ public void setWeek_star_gift_num(int week_star_gift_num) {
+ this.week_star_gift_num = week_star_gift_num;
+ }
+
+ public int getGift_illuminate_num() {
+ return gift_illuminate_num;
+ }
+
+ public void setGift_illuminate_num(int gift_illuminate_num) {
+ this.gift_illuminate_num = gift_illuminate_num;
+ }
+
+ public int getGift_num() {
+ return gift_num;
+ }
+
+ public void setGift_num(int gift_num) {
+ this.gift_num = gift_num;
+ }
+ }
+
+ public static class Gift {
+ private int gift_id;
+ private int sendtype;
+ private String gift_name;
+ private long need_coin; // 注意:这里可能需要更改为long类型,因为666666超过了int的最大值
+ private String gift_icon;
+ private int week_start_level;
+ private int illuminate_num;
+ private int gift_hall_type;
+ private String gift_name_en;
+ private String gift_hall_start; // 使用Java 8的LocalDate来处理日期
+ private String gift_hall_end;
+ private int gift_hall_send_num;
+ private int illuminate_status;
+ private long needcoin_total;
+
+ public Gift() {
+ }
+
+ public String getGift_hall_start() {
+ if(!StringUtil.isEmpty(gift_hall_start)){
+ gift_hall_start=gift_hall_start.replace("-","/");
+ }
+ return gift_hall_start;
+ }
+ public String getGift_hall_end() {
+ if(!StringUtil.isEmpty(gift_hall_end)){
+ gift_hall_end=gift_hall_end.replace("-","/");
+ }
+ return gift_hall_end;
+ }
+ public int getGift_id() {
+ return gift_id;
+ }
+
+ public void setGift_id(int gift_id) {
+ this.gift_id = gift_id;
+ }
+
+ public int getSendtype() {
+ return sendtype;
+ }
+
+ public void setSendtype(int sendtype) {
+ this.sendtype = sendtype;
+ }
+
+ public String getGift_name() {
+ return gift_name;
+ }
+
+ public void setGift_name(String gift_name) {
+ this.gift_name = gift_name;
+ }
+
+ public long getNeed_coin() {
+ return need_coin;
+ }
+
+ public void setNeed_coin(long need_coin) {
+ this.need_coin = need_coin;
+ }
+
+ public String getGift_icon() {
+ return gift_icon;
+ }
+
+ public void setGift_icon(String gift_icon) {
+ this.gift_icon = gift_icon;
+ }
+
+ public int getWeek_start_level() {
+ return week_start_level;
+ }
+
+ public void setWeek_start_level(int week_start_level) {
+ this.week_start_level = week_start_level;
+ }
+
+ public int getIlluminate_num() {
+ return illuminate_num;
+ }
+
+ public void setIlluminate_num(int illuminate_num) {
+ this.illuminate_num = illuminate_num;
+ }
+
+ public int getGift_hall_type() {
+ return gift_hall_type;
+ }
+
+ public void setGift_hall_type(int gift_hall_type) {
+ this.gift_hall_type = gift_hall_type;
+ }
+
+ public String getGift_name_en() {
+ return gift_name_en;
+ }
+
+ public void setGift_name_en(String gift_name_en) {
+ this.gift_name_en = gift_name_en;
+ }
+
+ public void setGift_hall_start(String gift_hall_start) {
+ this.gift_hall_start = gift_hall_start;
+ }
+
+
+ public void setGift_hall_end(String gift_hall_end) {
+ this.gift_hall_end = gift_hall_end;
+ }
+
+ public int getGift_hall_send_num() {
+ return gift_hall_send_num;
+ }
+
+ public void setGift_hall_send_num(int gift_hall_send_num) {
+ this.gift_hall_send_num = gift_hall_send_num;
+ }
+
+ public int getIlluminate_status() {
+ return illuminate_status;
+ }
+
+ public void setIlluminate_status(int illuminate_status) {
+ this.illuminate_status = illuminate_status;
+ }
+
+ public long getNeedcoin_total() {
+ return needcoin_total;
+ }
+
+ public void setNeedcoin_total(long needcoin_total) {
+ this.needcoin_total = needcoin_total;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/bean/GiftWallForUserBean.java b/common/src/main/java/com/yunbao/common/bean/GiftWallForUserBean.java
new file mode 100644
index 000000000..efe70ed51
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/bean/GiftWallForUserBean.java
@@ -0,0 +1,114 @@
+package com.yunbao.common.bean;
+
+import java.util.List;
+
+public class GiftWallForUserBean extends BaseModel {
+ private List illuminate_data;
+ private int active_rank_hide;
+
+ public GiftWallForUserBean() {
+ }
+
+ public List getIlluminate_data() {
+ return illuminate_data;
+ }
+
+ public void setIlluminate_data(List illuminate_data) {
+ this.illuminate_data = illuminate_data;
+ }
+
+ public int getActive_rank_hide() {
+ return active_rank_hide;
+ }
+
+ public void setActive_rank_hide(int active_rank_hide) {
+ this.active_rank_hide = active_rank_hide;
+ }
+
+ public static class Gift {
+ private String giftname;
+ private String giftname_en;
+ private String gifticon;
+ private int needcoin;
+ private int needcoin_total;
+ private int gift_hall_send_num;
+ private int illuminate_status;
+ private int gift_id;
+ private int id;
+
+ public Gift() {
+ }
+
+ public String getGiftname() {
+ return giftname;
+ }
+
+ public void setGiftname(String giftname) {
+ this.giftname = giftname;
+ }
+
+ public String getGiftname_en() {
+ return giftname_en;
+ }
+
+ public void setGiftname_en(String giftname_en) {
+ this.giftname_en = giftname_en;
+ }
+
+ public String getGifticon() {
+ return gifticon;
+ }
+
+ public void setGifticon(String gifticon) {
+ this.gifticon = gifticon;
+ }
+
+ public int getNeedcoin() {
+ return needcoin;
+ }
+
+ public void setNeedcoin(int needcoin) {
+ this.needcoin = needcoin;
+ }
+
+ public int getNeedcoin_total() {
+ return needcoin_total;
+ }
+
+ public void setNeedcoin_total(int needcoin_total) {
+ this.needcoin_total = needcoin_total;
+ }
+
+ public int getGift_hall_send_num() {
+ return gift_hall_send_num;
+ }
+
+ public void setGift_hall_send_num(int gift_hall_send_num) {
+ this.gift_hall_send_num = gift_hall_send_num;
+ }
+
+ public int getIlluminate_status() {
+ return illuminate_status;
+ }
+
+ public void setIlluminate_status(int illuminate_status) {
+ this.illuminate_status = illuminate_status;
+ }
+
+ public int getGift_id() {
+ return gift_id;
+ }
+
+ public void setGift_id(int gift_id) {
+ this.gift_id = gift_id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/bean/GiftWallInfoBean.java b/common/src/main/java/com/yunbao/common/bean/GiftWallInfoBean.java
new file mode 100644
index 000000000..686263037
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/bean/GiftWallInfoBean.java
@@ -0,0 +1,258 @@
+package com.yunbao.common.bean;
+
+import com.alibaba.fastjson.annotation.JSONField;
+import com.google.gson.annotations.SerializedName;
+import com.yunbao.common.utils.RandomUtil;
+import com.yunbao.common.utils.StringUtil;
+import com.yunbao.common.utils.WordUtil;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class GiftWallInfoBean extends BaseModel {
+ @SerializedName("gift_info")
+ private GiftInfo gift_info;
+ @SerializedName("data")
+ private List data;
+ @SerializedName("is_me")
+ private int is_me;
+
+ // 一般情况下,我们会添加getter和setter方法,但根据你的要求,这里省略
+
+
+ @JSONField(name = "gift_info")
+ public GiftInfo getGift_info() {
+ return gift_info;
+ }
+
+ @JSONField(name = "gift_info")
+ public void setGift_info(GiftInfo gift_info) {
+ this.gift_info = gift_info;
+ }
+
+ @JSONField(name = "data")
+ public List getData() {
+ /* if (data == null || data.isEmpty()) {
+ data = new ArrayList<>();
+ for (int i = 0; i < 20; i++) {
+ Data item = new Data();
+ item.setId(i);
+ item.setActive_rank_hide(i % 4 == 0 ? 0 : 1);
+ item.setUser_name("用户:" + i);
+ item.setGift_hall_send_num(RandomUtil.nextInt(100000));
+ item.setAvatar("https://downs.yaoulive.com/manfive.png");
+ data.add(item);
+ }
+ }*/
+ return data;
+ }
+
+ @JSONField(name = "data")
+ public void setData(List data) {
+ this.data = data;
+ }
+
+ public int getIs_me() {
+ return is_me;
+ }
+
+ public void setIs_me(int is_me) {
+ this.is_me = is_me;
+ }
+
+ // 嵌套类:GiftInfo
+ public static class GiftInfo {
+ @SerializedName("gift_name")
+ private String giftname;
+ @SerializedName("gift_name_en")
+ private String giftname_en;
+ @SerializedName("gift_icon")
+ private String gifticon;
+ @SerializedName("need_coin")
+ private int needcoin;
+ @SerializedName("needcoin_total")
+ private int needcoin_total;
+ @SerializedName("gift_hall_send_num")
+ private String gift_hall_send_num;
+ @SerializedName("illuminate_num")
+ private int illuminate_num;
+ @SerializedName("illuminate_status")
+ private int illuminate_status;
+ @SerializedName("gift_id")
+ private int gift_id;
+ @SerializedName("id")
+ private int id;
+ @SerializedName("gift_hall_start_date")
+ private String gift_hall_start; // 使用Java 8的LocalDate来处理日期
+ @SerializedName("gift_hall_end_date")
+ private String gift_hall_end;
+
+ @JSONField(name = "gift_hall_start_date")
+ public void setGift_hall_start(String gift_hall_start) {
+ this.gift_hall_start = gift_hall_start;
+ }
+ @JSONField(name = "gift_hall_start_date")
+ public String getGift_hall_start() {
+ if(!StringUtil.isEmpty(gift_hall_start)){
+ gift_hall_start=gift_hall_start.replace("-","/");
+ }
+ return gift_hall_start;
+ }
+ @JSONField(name = "gift_hall_end_date")
+ public String getGift_hall_end() {
+ if(!StringUtil.isEmpty(gift_hall_end)){
+ gift_hall_end=gift_hall_end.replace("-","/");
+ }
+ return gift_hall_end;
+ }
+
+ public void setGift_hall_end(String gift_hall_end) {
+ this.gift_hall_end = gift_hall_end;
+ }
+
+ // 同样地,这里省略getter和setter方法
+
+ public String getGiftname() {
+ return giftname;
+ }
+
+ public void setGiftname(String giftname) {
+ this.giftname = giftname;
+ }
+
+ public String getGiftname_en() {
+ return giftname_en;
+ }
+
+ public void setGiftname_en(String giftname_en) {
+ this.giftname_en = giftname_en;
+ }
+
+ public String getGifticon() {
+ return gifticon;
+ }
+
+ public void setGifticon(String gifticon) {
+ this.gifticon = gifticon;
+ }
+
+ public int getNeedcoin() {
+ return needcoin;
+ }
+
+ public void setNeedcoin(int needcoin) {
+ this.needcoin = needcoin;
+ }
+
+ public int getNeedcoin_total() {
+ return needcoin_total;
+ }
+
+ public void setNeedcoin_total(int needcoin_total) {
+ this.needcoin_total = needcoin_total;
+ }
+
+ public int getIlluminate_num() {
+ return illuminate_num;
+ }
+
+ public void setIlluminate_num(int illuminate_num) {
+ this.illuminate_num = illuminate_num;
+ }
+
+ public String getGift_hall_send_num() {
+ if (StringUtil.isEmpty(gift_hall_send_num)) {
+ gift_hall_send_num = "0";
+ }
+ return gift_hall_send_num;
+ }
+
+ public void setGift_hall_send_num(String gift_hall_send_num) {
+ this.gift_hall_send_num = gift_hall_send_num;
+ }
+
+ public int getIlluminate_status() {
+ return illuminate_status;
+ }
+
+ public void setIlluminate_status(int illuminate_status) {
+ this.illuminate_status = illuminate_status;
+ }
+
+ public int getGift_id() {
+ return gift_id;
+ }
+
+ public void setGift_id(int gift_id) {
+ this.gift_id = gift_id;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+ }
+
+ // 嵌套类:Data
+ public static class Data {
+ private int gift_hall_send_num;
+ private String user_name;
+ private String avatar;
+ private int id;
+ private int active_rank_hide;
+ private String create_time;
+
+ // 同样地,这里省略getter和setter方法
+
+ public int getGift_hall_send_num() {
+ return gift_hall_send_num;
+ }
+
+ public void setGift_hall_send_num(int gift_hall_send_num) {
+ this.gift_hall_send_num = gift_hall_send_num;
+ }
+
+ public String getUser_name() {
+ return user_name;
+ }
+
+ public void setUser_name(String user_name) {
+ this.user_name = user_name;
+ }
+
+ public String getAvatar() {
+ return avatar;
+ }
+
+ public void setAvatar(String avatar) {
+ this.avatar = avatar;
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(int id) {
+ this.id = id;
+ }
+
+ public int getActive_rank_hide() {
+ return active_rank_hide;
+ }
+
+ public void setActive_rank_hide(int active_rank_hide) {
+ this.active_rank_hide = active_rank_hide;
+ }
+
+ public String getCreate_time() {
+ return create_time;
+ }
+
+ public void setCreate_time(String create_time) {
+ this.create_time = create_time;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/bean/GiftWallTab2Bean.java b/common/src/main/java/com/yunbao/common/bean/GiftWallTab2Bean.java
new file mode 100644
index 000000000..f46f12e83
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/bean/GiftWallTab2Bean.java
@@ -0,0 +1,312 @@
+package com.yunbao.common.bean;
+
+import com.google.gson.annotations.SerializedName;
+
+import java.util.List;
+
+public class GiftWallTab2Bean extends BaseModel{
+
+
+ @SerializedName("illuminate_data")
+ private IlluminateData illuminateData;
+ @SerializedName("gift_hall_start_date")
+ private String gift_hall_start_date;
+ @SerializedName("gift_hall_end_date")
+ private String gift_hall_end_date;
+
+ public GiftWallTab2Bean() {
+ }
+
+ public IlluminateData getIlluminateData() {
+ return illuminateData;
+ }
+
+ public void setIlluminateData(IlluminateData illuminateData) {
+ this.illuminateData = illuminateData;
+ }
+
+ public String getGift_hall_start_date() {
+ return gift_hall_start_date;
+ }
+
+ public void setGift_hall_start_date(String gift_hall_start_date) {
+ this.gift_hall_start_date = gift_hall_start_date;
+ }
+
+ public String getGift_hall_end_date() {
+ return gift_hall_end_date;
+ }
+
+ public void setGift_hall_end_date(String gift_hall_end_date) {
+ this.gift_hall_end_date = gift_hall_end_date;
+ }
+
+ public static class IlluminateData{
+ @SerializedName("week_start_data")
+ private List weekStartData;
+ @SerializedName("gift_data")
+ private List giftData;
+
+ public IlluminateData() {
+ }
+
+ public List getWeekStartData() {
+ return weekStartData;
+ }
+
+ public void setWeekStartData(List weekStartData) {
+ this.weekStartData = weekStartData;
+ }
+
+ public List getGiftData() {
+ return giftData;
+ }
+
+ public void setGiftData(List giftData) {
+ this.giftData = giftData;
+ }
+ }
+ public static class Gift{
+ @SerializedName("gift_id")
+ public int giftId;
+
+ @SerializedName("sendtype")
+ public int sendType;
+
+ @SerializedName("gift_name")
+ public String giftName;
+
+ @SerializedName("need_coin")
+ public int needCoin;
+
+ @SerializedName("gift_icon")
+ public String giftIcon;
+
+ @SerializedName("week_star_level")
+ public int weekStarLevel;
+
+ @SerializedName("illuminate_num")
+ public int illuminateNum;
+
+ @SerializedName("gift_hall_type")
+ public int giftHallType;
+
+ @SerializedName("gift_name_en")
+ public String giftNameEn;
+
+ @SerializedName("gift_hall_start")
+ public String giftHallStart;
+
+ @SerializedName("gift_hall_end")
+ public String giftHallEnd;
+
+ @SerializedName("naming_live_id")
+ public int namingLiveId;
+
+ @SerializedName("naming_live_nicename")
+ public String namingLiveNicename;
+
+ @SerializedName("gift_hall_send_num")
+ public int giftHallSendNum;
+
+ @SerializedName("naming_live_avatar")
+ public String namingLiveAvatar;
+
+ @SerializedName("naming_live_active_rank_hide")
+ public int namingLiveActiveRankHide;
+
+ @SerializedName("illuminate_status")
+ public int illuminateStatus;
+
+ @SerializedName("naming_user_id")
+ public int namingUserId;
+
+ @SerializedName("naming_user_nicename")
+ public String namingUserNicename;
+
+ @SerializedName("naming_user_avatar")
+ public String namingUserAvatar;
+
+ @SerializedName("naming_user_active_rank_hide")
+ public int namingUserActiveRankHide;
+
+ @SerializedName("needcoin_total")
+ public int needCoinTotal;
+
+ public int getGiftId() {
+ return giftId;
+ }
+
+ public void setGiftId(int giftId) {
+ this.giftId = giftId;
+ }
+
+ public int getSendType() {
+ return sendType;
+ }
+
+ public void setSendType(int sendType) {
+ this.sendType = sendType;
+ }
+
+ public String getGiftName() {
+ return giftName;
+ }
+
+ public void setGiftName(String giftName) {
+ this.giftName = giftName;
+ }
+
+ public int getNeedCoin() {
+ return needCoin;
+ }
+
+ public void setNeedCoin(int needCoin) {
+ this.needCoin = needCoin;
+ }
+
+ public String getGiftIcon() {
+ return giftIcon;
+ }
+
+ public void setGiftIcon(String giftIcon) {
+ this.giftIcon = giftIcon;
+ }
+
+ public int getWeekStarLevel() {
+ return weekStarLevel;
+ }
+
+ public void setWeekStarLevel(int weekStarLevel) {
+ this.weekStarLevel = weekStarLevel;
+ }
+
+ public int getIlluminateNum() {
+ return illuminateNum;
+ }
+
+ public void setIlluminateNum(int illuminateNum) {
+ this.illuminateNum = illuminateNum;
+ }
+
+ public int getGiftHallType() {
+ return giftHallType;
+ }
+
+ public void setGiftHallType(int giftHallType) {
+ this.giftHallType = giftHallType;
+ }
+
+ public String getGiftNameEn() {
+ return giftNameEn;
+ }
+
+ public void setGiftNameEn(String giftNameEn) {
+ this.giftNameEn = giftNameEn;
+ }
+
+ public String getGiftHallStart() {
+ return giftHallStart;
+ }
+
+ public void setGiftHallStart(String giftHallStart) {
+ this.giftHallStart = giftHallStart;
+ }
+
+ public String getGiftHallEnd() {
+ return giftHallEnd;
+ }
+
+ public void setGiftHallEnd(String giftHallEnd) {
+ this.giftHallEnd = giftHallEnd;
+ }
+
+ public int getNamingLiveId() {
+ return namingLiveId;
+ }
+
+ public void setNamingLiveId(int namingLiveId) {
+ this.namingLiveId = namingLiveId;
+ }
+
+ public String getNamingLiveNicename() {
+ return namingLiveNicename;
+ }
+
+ public void setNamingLiveNicename(String namingLiveNicename) {
+ this.namingLiveNicename = namingLiveNicename;
+ }
+
+ public int getGiftHallSendNum() {
+ return giftHallSendNum;
+ }
+
+ public void setGiftHallSendNum(int giftHallSendNum) {
+ this.giftHallSendNum = giftHallSendNum;
+ }
+
+ public String getNamingLiveAvatar() {
+ return namingLiveAvatar;
+ }
+
+ public void setNamingLiveAvatar(String namingLiveAvatar) {
+ this.namingLiveAvatar = namingLiveAvatar;
+ }
+
+ public int getNamingLiveActiveRankHide() {
+ return namingLiveActiveRankHide;
+ }
+
+ public void setNamingLiveActiveRankHide(int namingLiveActiveRankHide) {
+ this.namingLiveActiveRankHide = namingLiveActiveRankHide;
+ }
+
+ public int getIlluminateStatus() {
+ return illuminateStatus;
+ }
+
+ public void setIlluminateStatus(int illuminateStatus) {
+ this.illuminateStatus = illuminateStatus;
+ }
+
+ public int getNamingUserId() {
+ return namingUserId;
+ }
+
+ public void setNamingUserId(int namingUserId) {
+ this.namingUserId = namingUserId;
+ }
+
+ public String getNamingUserNicename() {
+ return namingUserNicename;
+ }
+
+ public void setNamingUserNicename(String namingUserNicename) {
+ this.namingUserNicename = namingUserNicename;
+ }
+
+ public String getNamingUserAvatar() {
+ return namingUserAvatar;
+ }
+
+ public void setNamingUserAvatar(String namingUserAvatar) {
+ this.namingUserAvatar = namingUserAvatar;
+ }
+
+ public int getNamingUserActiveRankHide() {
+ return namingUserActiveRankHide;
+ }
+
+ public void setNamingUserActiveRankHide(int namingUserActiveRankHide) {
+ this.namingUserActiveRankHide = namingUserActiveRankHide;
+ }
+
+ public int getNeedCoinTotal() {
+ return needCoinTotal;
+ }
+
+ public void setNeedCoinTotal(int needCoinTotal) {
+ this.needCoinTotal = needCoinTotal;
+ }
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/dialog/AbsDialogPopupWindow.java b/common/src/main/java/com/yunbao/common/dialog/AbsDialogPopupWindow.java
index be1adad33..aa385d22f 100644
--- a/common/src/main/java/com/yunbao/common/dialog/AbsDialogPopupWindow.java
+++ b/common/src/main/java/com/yunbao/common/dialog/AbsDialogPopupWindow.java
@@ -6,6 +6,11 @@ import androidx.annotation.NonNull;
import com.lxj.xpopup.XPopup;
import com.lxj.xpopup.core.BottomPopupView;
+import com.yunbao.common.event.ClosePopupDialogEvent;
+import com.yunbao.common.utils.Bus;
+
+import org.greenrobot.eventbus.Subscribe;
+import org.greenrobot.eventbus.ThreadMode;
/**
* 底部弹窗
@@ -16,12 +21,20 @@ public abstract class AbsDialogPopupWindow extends BottomPopupView {
public AbsDialogPopupWindow(@NonNull Context context) {
super(context);
this.mContext = context;
+ Bus.getOn(this);
+ }
+
+ @Override
+ public void dismiss() {
+ super.dismiss();
+ Bus.getOff(this);
}
/**
* 参考配置
*/
public abstract void buildDialog(XPopup.Builder builder);
+
public abstract int bindLayoutId();
@Override
@@ -36,4 +49,16 @@ public abstract class AbsDialogPopupWindow extends BottomPopupView {
buildDialog(builder);
builder.asCustom(this).show();
}
+
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onWishSendGift(ClosePopupDialogEvent bean) {
+ if(bean.getId()==null) {
+ dismiss();
+ return;
+ }
+ if(bean.getId().equals(getTag())) {
+ dismiss();
+ }
+ }
+
}
diff --git a/common/src/main/java/com/yunbao/common/dialog/GiftWallDialog.java b/common/src/main/java/com/yunbao/common/dialog/GiftWallDialog.java
index 099fff9ee..45482b1ea 100644
--- a/common/src/main/java/com/yunbao/common/dialog/GiftWallDialog.java
+++ b/common/src/main/java/com/yunbao/common/dialog/GiftWallDialog.java
@@ -29,6 +29,7 @@ import com.yunbao.common.views.weight.ViewClicksAntiShake;
import java.util.ArrayList;
import java.util.List;
+import java.util.Locale;
/**
* 礼物墙
@@ -46,9 +47,15 @@ public class GiftWallDialog extends AbsDialogPopupWindow {
private List fragments = new ArrayList<>();
private boolean isFullWindows;
+ String toUserId;
+ String userName;
+ boolean isAnchor;
- public GiftWallDialog(@NonNull Context context) {
+ public GiftWallDialog(@NonNull Context context, String toUserId, String userName, boolean isAnchor) {
super(context);
+ this.toUserId = toUserId;
+ this.isAnchor = isAnchor;
+ this.userName = userName;
}
public GiftWallDialog setFullWindows(boolean fullWindows) {
@@ -86,8 +93,13 @@ public class GiftWallDialog extends AbsDialogPopupWindow {
mViewPager = findViewById(R.id.viewPager2);
mIvTabsLayout = findViewById(R.id.tab_layout);
- fragments.add(new GiftWallMainTab1Fragment());
- fragments.add(new GiftWallMainTab2Fragment());
+ mTvUserName.setText(String.format(Locale.getDefault(), "%s%s",
+ userName,
+ WordUtil.isNewZh() ? "的禮物展館" : "'s Gift Hall"
+ ));
+
+ fragments.add(new GiftWallMainTab1Fragment().setToUserId(toUserId).setAnchor(isAnchor).setLiveRoom(!isFullWindows));
+ fragments.add(new GiftWallMainTab2Fragment().setToUserId(toUserId).setAnchor(isAnchor).setLiveRoom(!isFullWindows));
mViewPager.setAdapter(new FragmentStateAdapter((FragmentActivity) mContext) {
@NonNull
@Override
@@ -101,6 +113,18 @@ public class GiftWallDialog extends AbsDialogPopupWindow {
}
});
mViewPager.setUserInputEnabled(false);
+ mViewPager.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
+ @Override
+ public void onPageSelected(int position) {
+ super.onPageSelected(position);
+ fragments.get(position).updateData();
+ if(position==0){
+ mTvUserName.setTextColor(Color.parseColor("#6BCDFF"));
+ }else{
+ mTvUserName.setTextColor(Color.parseColor("#FFCF94"));
+ }
+ }
+ });
ViewClicksAntiShake.clicksAntiShake(mTvTab1, () -> {
@@ -113,7 +137,7 @@ public class GiftWallDialog extends AbsDialogPopupWindow {
mTvTab2.setTextColor(Color.parseColor("#FFFFFF"));
mTvTab2.setTextSize(14);
mTvTab2.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
- mViewPager.setCurrentItem(0,false);
+ mViewPager.setCurrentItem(0, false);
});
ViewClicksAntiShake.clicksAntiShake(mTvTab2, () -> {
@@ -124,13 +148,13 @@ public class GiftWallDialog extends AbsDialogPopupWindow {
mTvTab1.setTypeface(Typeface.defaultFromStyle(Typeface.NORMAL));
mTvTab2.setTextColor(Color.parseColor("#FFC593"));
- if(WordUtil.isNewZh()) {
+ if (WordUtil.isNewZh()) {
mTvTab2.setTextSize(16);
- }else{
+ } else {
mTvTab2.setTextSize(14.5f);
}
mTvTab2.setTypeface(Typeface.defaultFromStyle(Typeface.BOLD));
- mViewPager.setCurrentItem(1,false);
+ mViewPager.setCurrentItem(1, false);
});
}
@@ -141,11 +165,11 @@ public class GiftWallDialog extends AbsDialogPopupWindow {
initView();
ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mIvBack.getLayoutParams();
if (isFullWindows) {
- params.width=DpUtil.dp2px(20);
+ params.width = DpUtil.dp2px(20);
mIvBack.setVisibility(View.VISIBLE);
mIvBg.setScaleType(ImageView.ScaleType.CENTER_CROP);
} else {
- params.width=DpUtil.dp2px(1);
+ params.width = DpUtil.dp2px(1);
mIvBack.setVisibility(View.INVISIBLE);
mIvBg.setScaleType(ImageView.ScaleType.FIT_XY);
}
diff --git a/common/src/main/java/com/yunbao/common/dialog/GiftWallGiftInfoDialog.java b/common/src/main/java/com/yunbao/common/dialog/GiftWallGiftInfoDialog.java
new file mode 100644
index 000000000..801756145
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/dialog/GiftWallGiftInfoDialog.java
@@ -0,0 +1,392 @@
+package com.yunbao.common.dialog;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ObjectAnimator;
+import android.content.Context;
+import android.graphics.Color;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.lxj.xpopup.XPopup;
+import com.makeramen.roundedimageview.RoundedImageView;
+import com.opensource.svgaplayer.SVGADrawable;
+import com.opensource.svgaplayer.SVGAImageView;
+import com.opensource.svgaplayer.SVGAParser;
+import com.opensource.svgaplayer.SVGAVideoEntity;
+import com.yunbao.common.R;
+import com.yunbao.common.adapter.GiftWallGiftInfoListItemAdapter;
+import com.yunbao.common.bean.GiftWallInfoBean;
+import com.yunbao.common.bean.JsWishBean;
+import com.yunbao.common.custom.ItemDecoration;
+import com.yunbao.common.event.ClosePopupDialogEvent;
+import com.yunbao.common.glide.ImgLoader;
+import com.yunbao.common.http.base.HttpCallback;
+import com.yunbao.common.http.live.LiveNetManager;
+import com.yunbao.common.interfaces.OnItemClickListener;
+import com.yunbao.common.manager.IMLoginManager;
+import com.yunbao.common.utils.Bus;
+import com.yunbao.common.utils.ScreenDimenUtil;
+import com.yunbao.common.utils.WordUtil;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+import java.util.Locale;
+
+public class GiftWallGiftInfoDialog extends AbsDialogPopupWindow {
+ private boolean isFullWindows;
+ private ImageView mIvBg;
+ private ImageView mIvBack;
+ TextView giftName;
+ ImageView gift;
+ SVGAImageView gift_bg;
+ TextView diamond_text;
+ TextView gift_tv_progress;
+ TextView gift_tv_max;
+ ProgressBar gift_progress;
+ Button gift_btn;
+ Button tab1, tab2;
+ ImageView tips_timer;
+ TextView tv_list_title;
+ RoundedImageView avatar;
+ TextView user_name;
+ TextView send_num;
+ TextView btn_one;
+ Button btn_one_tips;
+ Button btn_lighten;
+ View tab_layout;
+ View bottom_layout;
+
+ GiftWallGiftInfoListItemAdapter adapter;
+ RecyclerView recyclerView;
+
+ String giftId;
+ String toUserId;
+ boolean isAnchor;
+ int gift_hall_type = 1;
+ int list_type = 1;
+ private boolean isLiveRoom;
+ String time;
+ private boolean isStar;
+
+
+ public GiftWallGiftInfoDialog(Context context, String giftId, String toUserId, boolean isAnchor) {
+ super(context);
+ this.giftId = giftId;
+ this.toUserId = toUserId;
+ this.isAnchor = isAnchor;
+ }
+
+ public GiftWallGiftInfoDialog setFullWindows(boolean fullWindows) {
+ isFullWindows = fullWindows;
+ return this;
+ }
+
+
+ public GiftWallGiftInfoDialog setLiveRoom(boolean isLiveRoom) {
+ this.isLiveRoom = isLiveRoom;
+ return this;
+ }
+
+ public GiftWallGiftInfoDialog setStar(boolean isStar) {
+ this.isStar = isStar;
+ return this;
+ }
+
+ @Override
+ protected int getPopupHeight() {
+ if (isFullWindows) {
+ return super.getPopupHeight();
+ }
+ int screenHeight = ScreenDimenUtil.getInstance().getScreenHeight();
+ return (int) (screenHeight * 0.8);
+ }
+
+ @Override
+ public void buildDialog(XPopup.Builder builder) {
+
+ }
+
+ @Override
+ public int bindLayoutId() {
+ return R.layout.dialog_gift_wall_gift_info;
+ }
+
+ @Override
+ protected void onCreate() {
+ super.onCreate();
+ initView();
+ initData();
+ }
+
+ void initView() {
+ mIvBg = findViewById(R.id.iv_root_bg);
+ mIvBack = findViewById(R.id.iv_back);
+ giftName = findViewById(R.id.gift_name);
+ gift = findViewById(R.id.gift);
+ diamond_text = findViewById(R.id.diamond_text);
+ gift_tv_progress = findViewById(R.id.gift_tv_progress);
+ gift_tv_max = findViewById(R.id.gift_tv_max);
+ gift_progress = findViewById(R.id.gift_progress);
+ gift_btn = findViewById(R.id.gift_btn);
+ tab1 = findViewById(R.id.tab1);
+ tab2 = findViewById(R.id.tab2);
+ tips_timer = findViewById(R.id.tips_timer);
+ tv_list_title = findViewById(R.id.tv_list_title);
+ avatar = findViewById(R.id.bottom_avatar);
+ user_name = findViewById(R.id.bottom_user_name);
+ send_num = findViewById(R.id.send_num);
+ btn_one = findViewById(R.id.btn_one);
+ btn_one_tips = findViewById(R.id.btn_one_tips);
+ gift_bg = findViewById(R.id.gift_bg);
+ btn_lighten = findViewById(R.id.btn_lighten);
+ recyclerView = findViewById(R.id.recyclerView);
+ tab_layout = findViewById(R.id.tab_layout);
+ bottom_layout = findViewById(R.id.bottom_layout);
+ adapter = new GiftWallGiftInfoListItemAdapter();
+ recyclerView.setAdapter(adapter);
+ recyclerView.addItemDecoration(new ItemDecoration(mContext, 0x00000000, 0, 10));
+
+ if (isStar) {
+ tab2.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_title_star));
+ ((TextView) findViewById(R.id.user_name)).setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_info_list_header_rename1));
+ ((TextView) findViewById(R.id.tv_rename)).setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_info_list_header_rename_value1));
+ tv_list_title.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_title_star));
+ } else {
+ tab2.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_title_champion));
+ tv_list_title.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_title_champion));
+ ((TextView) findViewById(R.id.user_name)).setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_info_list_header_rename));
+ ((TextView) findViewById(R.id.tv_rename)).setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_info_list_header_rename_value));
+ }
+ btn_one.setVisibility(View.GONE);
+ btn_one_tips.setVisibility(View.GONE);
+ ViewClicksAntiShake.clicksAntiShake(tab1, () -> {
+
+ btn_one.setVisibility(View.GONE);
+ btn_one_tips.setVisibility(View.GONE);
+
+ tab1.setBackgroundResource(R.drawable.gift_wall_gift_info_list_btn_up);
+ tab1.setTextColor(Color.parseColor("#31326D"));
+
+ tab2.setBackgroundResource(R.drawable.gift_wall_gift_info_list_btn_down);
+ tab2.setTextColor(Color.parseColor("#FFFFFF"));
+ list_type = 1;
+ initData();
+ });
+ ViewClicksAntiShake.clicksAntiShake(tab2, () -> {
+ btn_one.setVisibility(View.VISIBLE);
+ btn_one_tips.setVisibility(View.VISIBLE);
+
+ tab2.setBackgroundResource(R.drawable.gift_wall_gift_info_list_btn_up);
+ tab2.setTextColor(Color.parseColor("#31326D"));
+
+ tab1.setBackgroundResource(R.drawable.gift_wall_gift_info_list_btn_down);
+ tab1.setTextColor(Color.parseColor("#FFFFFF"));
+ list_type = 2;
+ initData();
+ });
+ XPopup.Builder builder = new XPopup.Builder(getContext())
+ .atView(tips_timer);
+ builder.hasShadowBg(false);
+ tips_timer.setTag(builder);
+ ViewClicksAntiShake.clicksAntiShake(tips_timer, new ViewClicksAntiShake.ViewClicksCallBack() {
+ @Override
+ public void onViewClicks() {
+ XPopup.Builder b = (XPopup.Builder) tips_timer.getTag();
+ b.asCustom(new GiftWallMainTab1TipsDialog(mContext, new OnItemClickListener() {
+ @Override
+ public void onItemClick(Integer bean, int position) {
+
+ }
+ }).setTime(time)).show();
+ }
+ });
+ ViewClicksAntiShake.clicksAntiShake(btn_one, new ViewClicksAntiShake.ViewClicksCallBack() {
+ @Override
+ public void onViewClicks() {
+ Bus.get().post(new JsWishBean(giftId));//setUname==by
+ Bus.get().post(new ClosePopupDialogEvent());
+ }
+ });
+ ViewClicksAntiShake.clicksAntiShake(gift_btn, new ViewClicksAntiShake.ViewClicksCallBack() {
+ @Override
+ public void onViewClicks() {
+ Bus.get().post(new JsWishBean(giftId));//setUname==by
+ Bus.get().post(new ClosePopupDialogEvent());
+ }
+ });
+ }
+
+ void initData() {
+ if (isAnchor) {
+ LiveNetManager.get(mContext)
+ .liveGiftHallDetail(toUserId, giftId, gift_hall_type, list_type, new HttpCallback() {
+ @Override
+ public void onSuccess(GiftWallInfoBean data) {
+ initData(data);
+ }
+
+ @Override
+ public void onError(String error) {
+
+ }
+ });
+ } else {
+ LiveNetManager.get(mContext)
+ .singleUserGiftHallDetail(toUserId, giftId, new HttpCallback() {
+ @Override
+ public void onSuccess(GiftWallInfoBean data) {
+ initData(data);
+ }
+
+ @Override
+ public void onError(String error) {
+
+ }
+ });
+ }
+
+
+ }
+
+ void initData(GiftWallInfoBean giftBean) {
+ giftName.setText(WordUtil.isNewZh() ? giftBean.getGift_info().getGiftname() : giftBean.getGift_info().getGiftname_en());
+ ImgLoader.display(mContext, giftBean.getGift_info().getGifticon(), gift);
+ diamond_text.setText(String.format(Locale.getDefault(), "%d", giftBean.getGift_info().getNeedcoin()));
+ gift_tv_max.setText(String.format(Locale.getDefault(), "/%s", giftBean.getGift_info().getIlluminate_num()));
+ gift_tv_progress.setText(String.format(Locale.getDefault(), "%s", giftBean.getGift_info().getGift_hall_send_num()));
+ gift_progress.setMax(Integer.parseInt(giftBean.getGift_info().getGift_hall_send_num()));
+ gift_progress.setProgress(giftBean.getGift_info().getNeedcoin_total());
+ if (giftBean.getGift_info().getIlluminate_status() == 1) {
+ gift_btn.setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_info_top_btn_continue));
+ btn_lighten.setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_spinner_up));
+ btn_lighten.setBackgroundResource(R.drawable.gift_wall_gift_info_lighten);
+ } else {
+ gift_btn.setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_info_top_btn_to));
+ btn_lighten.setText(WordUtil.getNewString(R.string.dialog_gift_wall_list_spinner_down));
+ btn_lighten.setBackgroundResource(R.drawable.gift_wall_gift_info_un_lighten);
+ }
+ if (isAnchor) {
+ tab_layout.setVisibility(View.VISIBLE);
+ bottom_layout.setVisibility(View.VISIBLE);
+ tv_list_title.setVisibility(View.GONE);
+ } else {
+ tab_layout.setVisibility(View.GONE);
+ bottom_layout.setVisibility(View.GONE);
+ tv_list_title.setVisibility(View.VISIBLE);
+ gift_btn.setVisibility(View.GONE);
+ }
+ if (!isLiveRoom) {
+ gift_btn.setEnabled(false);
+ }
+ adapter.setData(giftBean.getData());
+ time = (WordUtil.isNewZh() ? "榜單結算時間" : "Settlement time") + giftBean.getGift_info().getGift_hall_start() + " - " + giftBean.getGift_info().getGift_hall_end();
+ ImgLoader.display(mContext, IMLoginManager.get(mContext).getUserInfo().getAvatar(), avatar);
+ user_name.setText(IMLoginManager.get(mContext).getUserInfo().getUserNicename());
+ send_num.setText(String.format(Locale.getDefault(), "%s", giftBean.getGift_info().getGift_hall_send_num()));
+
+ String tmp = "";
+ if (giftBean.getGift_info().getIlluminate_status() == 0) {//未點亮
+ btn_one.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_bottom_btn_one_light));
+ tmp = String.format(Locale.getDefault(), "%s", (giftBean.getGift_info().getIlluminate_num() - Integer.parseInt(giftBean.getGift_info().getGift_hall_send_num())));
+ setTips(tmp);
+ } else {//冠名/摘星
+ if (isStar) {
+ loadStar(giftBean);
+ } else {
+ loadChampion(giftBean);
+ }
+ }
+
+
+ initAnim();
+ }
+
+ private void setTips(String tmp) {
+ btn_one_tips.setText(String.format(Locale.getDefault(), "%s%s%s"
+ , WordUtil.isNewZh() ? "需" : "Need",
+ tmp,
+ WordUtil.isNewZh() ? "個" : ""));
+ }
+
+ private void loadChampion(GiftWallInfoBean giftBean) {
+ int tmp = 0;
+ int mySend = Integer.parseInt(giftBean.getGift_info().getGift_hall_send_num());
+ if (giftBean.getData() != null && !giftBean.getData().isEmpty()) {
+ tmp = giftBean.getData().get(0).getGift_hall_send_num() - Integer.parseInt(giftBean.getGift_info().getGift_hall_send_num()) + 1;
+ }
+ if (mySend > tmp) {
+ btn_one.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_bottom_btn_one_champion_get));
+ btn_one_tips.setVisibility(View.GONE);
+ } else {
+ btn_one.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_bottom_btn_one_champion));
+ setTips(String.valueOf(tmp + 1));
+ }
+ }
+
+ private void loadStar(GiftWallInfoBean giftBean) {
+ int tmp = 0;
+ int mySend = Integer.parseInt(giftBean.getGift_info().getGift_hall_send_num());
+ if (giftBean.getData() != null && !giftBean.getData().isEmpty()) {
+ tmp = giftBean.getData().get(0).getGift_hall_send_num() - Integer.parseInt(giftBean.getGift_info().getGift_hall_send_num()) + 1;
+ }
+ if (mySend > tmp) {
+ btn_one.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_bottom_btn_one_star_get));
+ btn_one_tips.setVisibility(View.GONE);
+ } else {
+ btn_one.setText(WordUtil.getNewString(R.string.dialog_gift_wall_gfit_info_list_bottom_btn_one_star));
+ setTips(String.valueOf(tmp + 1));
+ }
+ }
+
+ void initAnim() {
+ if (gift.getTag() != null) {
+ return;
+ }
+ // 创建一个向上移动的动画
+ ObjectAnimator upAnimator = ObjectAnimator.ofFloat(gift, "translationY", 0f, -10f); // 假设 10f 是你想要移动的距离(注意:这里使用的是像素值,而不是 dp)
+ upAnimator.setDuration(1000); // 设置动画时长为 1000 毫秒(即 1 秒)
+ // 创建一个向下移动的动画
+ ObjectAnimator downAnimator = ObjectAnimator.ofFloat(gift, "translationY", -10f, 0f);
+ downAnimator.setDuration(1000); // 同样设置动画时长为 1 秒
+ // 设置动画监听器以在向上动画结束后开始向下动画(如果需要的话)
+ upAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ // 在这里开始向下动画
+ downAnimator.start();
+ }
+ });
+ downAnimator.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ gift.postDelayed(upAnimator::start, 1000);
+ }
+ });
+ // 开始向上动画
+ upAnimator.start();
+ gift.setTag("start");
+
+ new SVGAParser(getContext()).decodeFromAssets("gift_wall_gift_info_light.svga", new SVGAParser.ParseCompletion() {
+ @Override
+ public void onComplete(@NonNull SVGAVideoEntity videoItem) {
+ gift_bg.setImageDrawable(new SVGADrawable(videoItem));
+ gift_bg.setLoops(0);
+ gift_bg.startAnimation();
+ }
+
+ @Override
+ public void onError() {
+ System.err.println("-------------SVGA报错了");
+ }
+ }, null);
+ }
+
+}
diff --git a/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab1List2SpinnerDialog.java b/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab1List2SpinnerDialog.java
new file mode 100644
index 000000000..b2d07a45c
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab1List2SpinnerDialog.java
@@ -0,0 +1,54 @@
+package com.yunbao.common.dialog;
+
+import android.content.Context;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.lxj.xpopup.core.AttachPopupView;
+import com.yunbao.common.R;
+import com.yunbao.common.interfaces.OnItemClickListener;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+public class GiftWallMainTab1List2SpinnerDialog extends AttachPopupView {
+ TextView all, up, down;
+ OnItemClickListener listener;
+
+ public GiftWallMainTab1List2SpinnerDialog(@NonNull Context context, OnItemClickListener listener) {
+ super(context);
+ this.listener = listener;
+ }
+
+ @Override
+ protected void onDismiss() {
+ super.onDismiss();
+ listener.onItemClick(-1, 0);
+ }
+
+ @Override
+ protected int getImplLayoutId() {
+ return R.layout.dialog_gift_wall_main_tab1_list2_spinner;
+ }
+
+ @Override
+ protected void onCreate() {
+ super.onCreate();
+ all = findViewById(R.id.spinner_all);
+ up = findViewById(R.id.spinner_up);
+ down = findViewById(R.id.spinner_down);
+ ViewClicksAntiShake.clicksAntiShake(all, () -> {
+ listener.onItemClick(0, 0);
+ dismiss();
+ });
+
+ ViewClicksAntiShake.clicksAntiShake(up, () -> {
+ listener.onItemClick(1, 0);
+ dismiss();
+ });
+
+ ViewClicksAntiShake.clicksAntiShake(down, () -> {
+ listener.onItemClick(2, 0);
+ dismiss();
+ });
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab1TipsDialog.java b/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab1TipsDialog.java
new file mode 100644
index 000000000..72a1794c5
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab1TipsDialog.java
@@ -0,0 +1,48 @@
+package com.yunbao.common.dialog;
+
+import android.content.Context;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.lxj.xpopup.core.AttachPopupView;
+import com.yunbao.common.R;
+import com.yunbao.common.interfaces.OnItemClickListener;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+public class GiftWallMainTab1TipsDialog extends AttachPopupView {
+ TextView tips;
+ OnItemClickListener listener;
+ String time;
+
+ public GiftWallMainTab1TipsDialog(@NonNull Context context, OnItemClickListener listener) {
+ super(context);
+ this.listener = listener;
+ }
+
+ public GiftWallMainTab1TipsDialog setTime(String time) {
+ this.time = time;
+ return this;
+ }
+
+ @Override
+ protected void onDismiss() {
+ super.onDismiss();
+ listener.onItemClick(-1, 0);
+ }
+
+ @Override
+ protected int getImplLayoutId() {
+ return R.layout.dialog_gift_wall_main_tab1_tips;
+ }
+
+ @Override
+ protected void onCreate() {
+ super.onCreate();
+ tips = findViewById(R.id.tips);
+ tips.setText(time);
+ ViewClicksAntiShake.clicksAntiShake(tips, () -> {
+ listener.onItemClick(0, 0);
+ });
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab2ClassicInfoDialog.java b/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab2ClassicInfoDialog.java
new file mode 100644
index 000000000..a0a6bfdd8
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/dialog/GiftWallMainTab2ClassicInfoDialog.java
@@ -0,0 +1,116 @@
+package com.yunbao.common.dialog;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.graphics.Typeface;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+import androidx.viewpager2.adapter.FragmentStateAdapter;
+import androidx.viewpager2.widget.ViewPager2;
+
+import com.lxj.xpopup.XPopup;
+import com.yunbao.common.R;
+import com.yunbao.common.bean.GiftWallTab2Bean;
+import com.yunbao.common.fragment.BaseFragment;
+import com.yunbao.common.fragment.GiftWallMainTab1Fragment;
+import com.yunbao.common.fragment.GiftWallMainTab2Fragment;
+import com.yunbao.common.utils.DpUtil;
+import com.yunbao.common.utils.ScreenDimenUtil;
+import com.yunbao.common.utils.WordUtil;
+import com.yunbao.common.views.CustomEllipsizeTextView;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Locale;
+
+public class GiftWallMainTab2ClassicInfoDialog extends AbsDialogPopupWindow {
+
+ private ImageView mIvBg;
+ private ImageView mIvTips;
+ private ImageView mIvBack;
+ private CustomEllipsizeTextView mTvUserName;
+ private View mAchievement;
+ private TextView mTvTab1, mTvTab2;
+ private ViewPager2 mViewPager;
+ private LinearLayout mIvTabsLayout;
+ private List fragments = new ArrayList<>();
+
+ private boolean isFullWindows;
+ GiftWallTab2Bean.Gift gift;
+ String userName;
+ boolean isAnchor;
+
+ public GiftWallMainTab2ClassicInfoDialog(@NonNull Context context, GiftWallTab2Bean.Gift gift,boolean isAnchor) {
+ super(context);
+ this.gift = gift;
+ this.isAnchor = isAnchor;
+ }
+
+ public GiftWallMainTab2ClassicInfoDialog setFullWindows(boolean fullWindows) {
+ isFullWindows = fullWindows;
+ return this;
+ }
+
+ @Override
+ public void buildDialog(XPopup.Builder builder) {
+
+ }
+
+ @Override
+ public int bindLayoutId() {
+ return R.layout.dialog_gift_wall_tab2_classic_info;
+ }
+
+ @Override
+ protected int getPopupHeight() {
+ if (isFullWindows) {
+ return super.getPopupHeight();
+ }
+ int screenHeight = ScreenDimenUtil.getInstance().getScreenHeight();
+ return (int) (screenHeight * 0.8);
+ }
+
+ void initView() {
+ mIvBg = findViewById(R.id.iv_root_bg);
+ mIvTips = findViewById(R.id.v_tips);
+ mIvBack = findViewById(R.id.iv_back);
+ mTvUserName = findViewById(R.id.user_name);
+ mAchievement = findViewById(R.id.v_achievement);
+ mTvTab1 = findViewById(R.id.tab1);
+ mTvTab2 = findViewById(R.id.tab2);
+ mViewPager = findViewById(R.id.viewPager2);
+ mIvTabsLayout = findViewById(R.id.tab_layout);
+
+ mTvUserName.setText(String.format(Locale.getDefault(), "%s%s",
+ userName,
+ WordUtil.isNewZh() ? "的禮物展館" : "'s Gift Hall"
+ ));
+
+ }
+
+ @Override
+ protected void onCreate() {
+ super.onCreate();
+ initView();
+ ConstraintLayout.LayoutParams params = (ConstraintLayout.LayoutParams) mIvBack.getLayoutParams();
+ if (isFullWindows) {
+ params.width = DpUtil.dp2px(20);
+ mIvBack.setVisibility(View.VISIBLE);
+ mIvBg.setScaleType(ImageView.ScaleType.CENTER_CROP);
+ } else {
+ params.width = DpUtil.dp2px(1);
+ mIvBack.setVisibility(View.INVISIBLE);
+ mIvBg.setScaleType(ImageView.ScaleType.FIT_XY);
+ }
+ mIvBack.setLayoutParams(params);
+
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/event/ClosePopupDialogEvent.java b/common/src/main/java/com/yunbao/common/event/ClosePopupDialogEvent.java
new file mode 100644
index 000000000..7dab53f00
--- /dev/null
+++ b/common/src/main/java/com/yunbao/common/event/ClosePopupDialogEvent.java
@@ -0,0 +1,22 @@
+package com.yunbao.common.event;
+
+import com.yunbao.common.bean.BaseModel;
+
+public class ClosePopupDialogEvent extends BaseModel {
+ public Object id=null;
+
+ public ClosePopupDialogEvent(Object id) {
+ this.id = id;
+ }
+
+ public Object getId() {
+ return id;
+ }
+
+ public void setId(Object id) {
+ this.id = id;
+ }
+
+ public ClosePopupDialogEvent() {
+ }
+}
diff --git a/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab1Fragment.java b/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab1Fragment.java
index 46ace5f33..516ffde17 100644
--- a/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab1Fragment.java
+++ b/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab1Fragment.java
@@ -1,20 +1,58 @@
package com.yunbao.common.fragment;
import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Shader;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.TextView;
+import androidx.annotation.NonNull;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.lxj.xpopup.XPopup;
+import com.opensource.svgaplayer.SVGAParser;
+import com.opensource.svgaplayer.SVGAVideoEntity;
+import com.yunbao.common.R;
+import com.yunbao.common.adapter.GiftWallMainTab1List2Adapter;
+import com.yunbao.common.bean.GiftWallBean;
+import com.yunbao.common.custom.ItemDecoration;
+import com.yunbao.common.dialog.GiftWallMainTab1List2SpinnerDialog;
+import com.yunbao.common.http.base.HttpCallback;
+import com.yunbao.common.http.live.LiveNetManager;
+import com.yunbao.common.interfaces.OnItemClickListener;
+import com.yunbao.common.manager.IMLoginManager;
+import com.yunbao.common.utils.DpUtil;
+import com.yunbao.common.utils.WordUtil;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+import java.util.Locale;
+
public class GiftWallMainTab1Fragment extends BaseFragment {
+ TextView classicGiftsTitle, starGiftsTitle;
+ TextView classicGiftsNumber, starGiftsNumber;
+ RecyclerView recyclerView1, recyclerView2;
+ View spinner;
+ TextView spinnerText;
+ ImageView spinnerImage;
+
+ GiftWallMainTab1List2Adapter list1Adapter, list2Adapter;
+
+ int screen;
+ String toUserId;
+ boolean isAnchor;
+ boolean isLiveRoom;
+ SVGAVideoEntity drawable;
+ View classic_layout, star_layout;
+ TextView show_time;
+ View show_time_layout;
+
@Override
public View createView(LayoutInflater inflater, ViewGroup container) {
- TextView textView = new TextView(getActivity());
- textView.setText("第一页");
- textView.setTextColor(Color.WHITE);
- textView.setTextSize(24);
- return textView;
+ return inflater.inflate(R.layout.fragment_gift_wall_man_tab_1, container, false);
}
@Override
@@ -22,9 +60,172 @@ public class GiftWallMainTab1Fragment extends BaseFragment {
}
+ public GiftWallMainTab1Fragment setToUserId(String toUserId) {
+ this.toUserId = toUserId;
+ return this;
+ }
+
+ public GiftWallMainTab1Fragment setAnchor(boolean anchor) {
+ isAnchor = anchor;
+ return this;
+ }
+
+ public GiftWallMainTab1Fragment setLiveRoom(boolean liveRoom) {
+ isLiveRoom = liveRoom;
+ return this;
+ }
+
+ @Override
+ public void updateData() {
+ super.updateData();
+ list1Adapter.notifyDataSetChanged();
+ list2Adapter.notifyDataSetChanged();
+
+ }
+
+ private void initData() {
+ if (isAnchor) {
+ LiveNetManager.get(getContext())
+ .liveGiftHall(toUserId, screen, new HttpCallback() {
+ @Override
+ public void onSuccess(GiftWallBean data) {
+ initData(data);
+ }
+
+ @Override
+ public void onError(String error) {
+
+ }
+ });
+ } else {
+ LiveNetManager.get(getContext())
+ .singleUserGiftHall(toUserId, screen, new HttpCallback() {
+ @Override
+ public void onSuccess(GiftWallBean data) {
+ initData(data);
+ }
+
+ @Override
+ public void onError(String error) {
+
+ }
+ });
+ }
+ }
+
+ private void initData(GiftWallBean bean) {
+ list1Adapter.setDrawable(drawable);
+ list1Adapter.setList(bean.getIlluminate_data().getWeek_star_data());
+ list2Adapter.setList(bean.getIlluminate_data().getGift_data());
+ list1Adapter.setLiveRoom(isLiveRoom);
+ list2Adapter.setLiveRoom(isLiveRoom);
+
+ starGiftsNumber.setText(String.format(Locale.getDefault(), "%s%d/%d",
+ WordUtil.getNewString(R.string.dialog_gift_wall_list_spinner_up),
+ bean.getIlluminate_data().getWeek_gift_illuminate_num(),
+ bean.getIlluminate_data().getWeek_star_gift_num()
+ ));
+ classicGiftsNumber.setText(String.format(Locale.getDefault(), "%s%d/%d",
+ WordUtil.getNewString(R.string.dialog_gift_wall_list_spinner_up),
+ bean.getIlluminate_data().getGift_illuminate_num(),
+ bean.getIlluminate_data().getGift_num()
+ ));
+ if (bean.getIlluminate_data().getWeek_star_data() != null) {
+ show_time.setText(String.format(Locale.getDefault(), "%s - %s",
+ bean.getIlluminate_data().getGift_hall_start_date(),
+ bean.getIlluminate_data().getGift_hall_end_date()));
+ }
+ }
+
@Override
protected void initViews(Bundle savedInstanceState, View contentView) {
+ classicGiftsTitle = contentView.findViewById(R.id.classic_gifts_title);
+ classicGiftsNumber = contentView.findViewById(R.id.classic_gifts_number);
+ recyclerView1 = contentView.findViewById(R.id.recyclerView1);
+ recyclerView2 = contentView.findViewById(R.id.recyclerView2);
+ starGiftsTitle = contentView.findViewById(R.id.star_gifts_title);
+ starGiftsNumber = contentView.findViewById(R.id.star_gifts_number);
+ spinner = contentView.findViewById(R.id.classic_gift_spinner);
+ spinnerText = contentView.findViewById(R.id.classic_gift_spinner_text);
+ spinnerImage = contentView.findViewById(R.id.classic_gift_spinner_ic);
+ classic_layout = contentView.findViewById(R.id.classic_layout);
+ star_layout = contentView.findViewById(R.id.star_layout);
+ show_time = contentView.findViewById(R.id.show_time);
+ show_time_layout = contentView.findViewById(R.id.show_time_layout);
+ list1Adapter = new GiftWallMainTab1List2Adapter(getContext());
+ list2Adapter = new GiftWallMainTab1List2Adapter(getContext());
+
+ list1Adapter.setToUid(toUserId);
+ list2Adapter.setToUid(toUserId);
+ list1Adapter.setAnchor(isAnchor);
+ list2Adapter.setAnchor(isAnchor);
+ list1Adapter.setStar(true);
+
+ recyclerView1.setAdapter(list1Adapter);
+ recyclerView2.setAdapter(list2Adapter);
+
+ recyclerView1.addItemDecoration(new ItemDecoration(getContext(), 0x00000000, DpUtil.dp2px(10), 1));
+ recyclerView2.addItemDecoration(new ItemDecoration(getContext(), 0x00000000, DpUtil.dp2px(2), 1));
+
+ starGiftsTitle.getPaint().setShader(new LinearGradient(0, 0, 0, classicGiftsTitle.getPaint().getTextSize(),
+ Color.parseColor("#FEE8C6"), Color.parseColor("#FFD5A3"), Shader.TileMode.CLAMP));
+
+ classicGiftsTitle.getPaint().setShader(new LinearGradient(0, 0, 0, classicGiftsTitle.getPaint().getTextSize(),
+ Color.parseColor("#A0E9FF"), Color.parseColor("#72D1FF"), Shader.TileMode.CLAMP));
+ XPopup.Builder builder = new XPopup.Builder(getContext())
+ .atView(spinner);
+ spinner.setTag(builder);
+
+ if (!isAnchor) {
+ show_time_layout.setVisibility(View.GONE);
+ star_layout.setVisibility(View.GONE);
+ }
+ if (!isLiveRoom) {
+
+ }
+
+
+ ViewClicksAntiShake.clicksAntiShake(spinner, new ViewClicksAntiShake.ViewClicksCallBack() {
+ @Override
+ public void onViewClicks() {
+ spinnerImage.setRotation(180);
+ ((XPopup.Builder) spinner.getTag()).asCustom(new GiftWallMainTab1List2SpinnerDialog(getContext(), new OnItemClickListener() {
+ @Override
+ public void onItemClick(Integer bean, int position) {
+ if (bean > -1) {
+ screen = bean;
+ initData();
+ }
+ switch (bean) {
+ case 0:
+ spinnerText.setText(R.string.dialog_gift_wall_list_spinner_all);
+ break;
+ case 1:
+ spinnerText.setText(R.string.dialog_gift_wall_list_spinner_up);
+ break;
+ case 2:
+ spinnerText.setText(R.string.dialog_gift_wall_list_spinner_down);
+ break;
+ }
+ spinnerImage.setRotation(0);
+ }
+ })).show();
+
+ }
+ });
+ new SVGAParser(getContext()).decodeFromAssets("gift_wall_light_up.svga", new SVGAParser.ParseCompletion() {
+ @Override
+ public void onComplete(@NonNull SVGAVideoEntity videoItem) {
+ GiftWallMainTab1Fragment.this.drawable = videoItem;
+ initData();
+ }
+
+ @Override
+ public void onError() {
+ System.err.println("-------------SVGA报错了");
+ }
+ }, null);
}
@Override
diff --git a/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab2Fragment.java b/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab2Fragment.java
index 9c4b7f12d..7cf250cb6 100644
--- a/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab2Fragment.java
+++ b/common/src/main/java/com/yunbao/common/fragment/GiftWallMainTab2Fragment.java
@@ -1,20 +1,75 @@
package com.yunbao.common.fragment;
import android.graphics.Color;
+import android.graphics.LinearGradient;
+import android.graphics.Shader;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
+import android.widget.ImageView;
import android.widget.TextView;
+import androidx.recyclerview.widget.RecyclerView;
+
+import com.angcyo.tablayout.DslTabLayout;
+import com.angcyo.tablayout.DslTabLayoutConfig;
+import com.lxj.xpopup.XPopup;
+import com.yunbao.common.R;
+import com.yunbao.common.adapter.GiftWallMainTab2ListAdapter;
+import com.yunbao.common.bean.GiftWallTab2Bean;
+import com.yunbao.common.custom.ItemDecoration;
+import com.yunbao.common.dialog.GiftWallMainTab1List2SpinnerDialog;
+import com.yunbao.common.http.base.HttpCallback;
+import com.yunbao.common.http.live.LiveNetManager;
+import com.yunbao.common.interfaces.OnItemClickListener;
+import com.yunbao.common.utils.DpUtil;
+import com.yunbao.common.views.weight.ViewClicksAntiShake;
+
+import java.util.Iterator;
+import java.util.List;
+import java.util.Locale;
+
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
+import kotlin.jvm.functions.Function4;
+
public class GiftWallMainTab2Fragment extends BaseFragment {
+ TextView classicGiftsTitle, starGiftsTitle;
+ RecyclerView recyclerView1, recyclerView2;
+ View spinner;
+ TextView spinnerText;
+ ImageView spinnerImage;
+ GiftWallMainTab2ListAdapter list1Adapter, list2Adapter;
+ TextView show_time;
+ DslTabLayout tabLayout;
+ TextView dslTab1, dslTab2;
+
+
+ int screen;
+ int type = 1;
+ String toUserId;
+ boolean isAnchor;
+ private boolean isLiveRoom;
+
+ public GiftWallMainTab2Fragment setToUserId(String toUserId) {
+ this.toUserId = toUserId;
+ return this;
+ }
+
+ public GiftWallMainTab2Fragment setAnchor(boolean anchor) {
+ isAnchor = anchor;
+ return this;
+ }
+
+ public GiftWallMainTab2Fragment setLiveRoom(boolean liveRoom) {
+ isLiveRoom = liveRoom;
+ return this;
+ }
+
@Override
public View createView(LayoutInflater inflater, ViewGroup container) {
- TextView textView = new TextView(getActivity());
- textView.setText("第二页");
- textView.setTextColor(Color.WHITE);
- textView.setTextSize(24);
- return textView;
+ return inflater.inflate(R.layout.fragment_gift_wall_man_tab_2, container, false);
}
@Override
@@ -24,7 +79,156 @@ public class GiftWallMainTab2Fragment extends BaseFragment {
@Override
protected void initViews(Bundle savedInstanceState, View contentView) {
+ classicGiftsTitle = contentView.findViewById(R.id.classic_gifts_title);
+ recyclerView1 = contentView.findViewById(R.id.recyclerView1);
+ recyclerView2 = contentView.findViewById(R.id.recyclerView2);
+ starGiftsTitle = contentView.findViewById(R.id.star_gifts_title);
+ spinner = contentView.findViewById(R.id.classic_gift_spinner);
+ spinnerText = contentView.findViewById(R.id.classic_gift_spinner_text);
+ spinnerImage = contentView.findViewById(R.id.classic_gift_spinner_ic);
+ show_time = contentView.findViewById(R.id.show_time);
+ tabLayout = findViewById(R.id.tab_layout);
+ dslTab1 = findViewById(R.id.dsl_tab1);
+ dslTab2 = findViewById(R.id.dsl_tab2);
+ list1Adapter = new GiftWallMainTab2ListAdapter(getContext());
+ list2Adapter = new GiftWallMainTab2ListAdapter(getContext());
+
+ list1Adapter.setToUid(toUserId);
+ list2Adapter.setToUid(toUserId);
+ list1Adapter.setAnchor(isAnchor);
+ list2Adapter.setAnchor(isAnchor);
+ list1Adapter.setStar(true);
+
+ recyclerView1.setAdapter(list1Adapter);
+ recyclerView2.setAdapter(list2Adapter);
+
+
+ recyclerView1.addItemDecoration(new ItemDecoration(getContext(), 0x00000000, DpUtil.dp2px(2), 1));
+ recyclerView2.addItemDecoration(new ItemDecoration(getContext(), 0x00000000, DpUtil.dp2px(2), 1));
+
+ starGiftsTitle.getPaint().setShader(new LinearGradient(0, 0, 0, classicGiftsTitle.getPaint().getTextSize(),
+ Color.parseColor("#FEE8C6"), Color.parseColor("#FFD5A3"), Shader.TileMode.CLAMP));
+
+ classicGiftsTitle.getPaint().setShader(new LinearGradient(0, 0, 0, classicGiftsTitle.getPaint().getTextSize(),
+ Color.parseColor("#A0E9FF"), Color.parseColor("#72D1FF"), Shader.TileMode.CLAMP));
+
+ setDslTabColor(dslTab1, dslTab2);
+
+ tabLayout.configTabLayoutConfig(new Function1() {
+ @Override
+ public Unit invoke(DslTabLayoutConfig dslTabLayoutConfig) {
+ dslTabLayoutConfig.setOnSelectItemView(new Function4() {
+ @Override
+ public Boolean invoke(View itemView, Integer index, Boolean select, Boolean fromUser) {
+ //当需要选中itemView时, 触发的回调.
+ //返回true, 拦截库中的默认处理.
+ setDslTabColor(index == 1 ? dslTab1 : dslTab2, index == 0 ? dslTab1 : dslTab2);
+ type = (index + 1);
+ screen=0;
+ spinnerText.setText(R.string.dialog_gift_wall_list_spinner_all);
+ initData();
+ return false;
+ }
+ });
+ dslTabLayoutConfig.setOnSelectViewChange(new Function4, Boolean, Boolean, Unit>() {
+ @Override
+ public Unit invoke(View fromView, List extends View> selectViewList, Boolean select, Boolean fromUser) {
+ //fromView 表示之前选中的view, 多选模式不支持.
+ //selectViewList 表示目前选中view的集合, 单选模式列表里面只有一个,可以使用selectViewList.get(0)获取.
+ return null;
+ }
+ });
+ dslTabLayoutConfig.setOnSelectIndexChange(new Function4, Boolean, Boolean, Unit>() {
+ @Override
+ public Unit invoke(Integer fromIndex, List selectIndexList, Boolean select, Boolean fromUser) {
+ //参考setOnSelectViewChange
+ //只不过对象从view,变成了view在ViewGroup中的索引
+ return null;
+ }
+ });
+ return null;
+ }
+ });
+
+ XPopup.Builder builder = new XPopup.Builder(getContext())
+ .atView(spinner);
+ spinner.setTag(builder);
+
+ ViewClicksAntiShake.clicksAntiShake(spinner, new ViewClicksAntiShake.ViewClicksCallBack() {
+ @Override
+ public void onViewClicks() {
+ spinnerImage.setRotation(180);
+ ((XPopup.Builder) spinner.getTag()).asCustom(new GiftWallMainTab1List2SpinnerDialog(getContext(), new OnItemClickListener() {
+ @Override
+ public void onItemClick(Integer bean, int position) {
+ if (bean > -1) {
+ screen = bean;
+ initData();
+ }
+ switch (bean) {
+ case 0:
+ spinnerText.setText(R.string.dialog_gift_wall_list_spinner_all);
+ break;
+ case 1:
+ spinnerText.setText(R.string.dialog_gift_wall_list_spinner_up);
+ break;
+ case 2:
+ spinnerText.setText(R.string.dialog_gift_wall_list_spinner_down);
+ break;
+ }
+ spinnerImage.setRotation(0);
+ }
+ })).show();
+
+ }
+ });
+ initData();
+ }
+
+ void setDslTabColor(TextView select, TextView unSelect) {
+ select.getPaint().setShader(new LinearGradient(0, 0, 0, select.getPaint().getTextSize(),
+ Color.parseColor("#FEE0B6"), Color.parseColor("#FFCF95"), Shader.TileMode.CLAMP));
+ unSelect.setTextColor(Color.parseColor("#FFEED8"));
+ unSelect.getPaint().setShader(null);
+ }
+
+ void initData() {
+ LiveNetManager.get(getContext())
+ .allGiftHall(String.valueOf(type), new HttpCallback() {
+ @Override
+ public void onSuccess(GiftWallTab2Bean data) {
+ if (screen != 0) {
+ filtrationData(data);
+ }
+ initData(data);
+ }
+
+ @Override
+ public void onError(String error) {
+
+ }
+ });
+ }
+
+ void filtrationData(GiftWallTab2Bean bean) {
+ Iterator iterator = bean.getIlluminateData().getGiftData().iterator();
+ while (iterator.hasNext()){
+ GiftWallTab2Bean.Gift next = iterator.next();
+ if (screen == 1 && next.getIlluminateStatus() != 1) {//已点亮
+ iterator.remove();
+ } else if (screen == 2 && next.getIlluminateStatus() == 1) {//未点亮
+ iterator.remove();
+ }
+ }
+ }
+
+ void initData(GiftWallTab2Bean bean) {
+ list1Adapter.setList(bean.getIlluminateData().getWeekStartData());
+ list2Adapter.setList(bean.getIlluminateData().getGiftData());
+ show_time.setText(String.format(Locale.getDefault(), "%s - %s",
+ bean.getGift_hall_start_date(),
+ bean.getGift_hall_end_date()));
}
@Override
diff --git a/common/src/main/java/com/yunbao/common/http/PDLiveApi.java b/common/src/main/java/com/yunbao/common/http/PDLiveApi.java
index cda5437c1..2418c5a91 100644
--- a/common/src/main/java/com/yunbao/common/http/PDLiveApi.java
+++ b/common/src/main/java/com/yunbao/common/http/PDLiveApi.java
@@ -26,8 +26,11 @@ import com.yunbao.common.bean.FansGroupGiftPackInfo;
import com.yunbao.common.bean.GiftAlreadyWallModel;
import com.yunbao.common.bean.GiftGuideModel;
import com.yunbao.common.bean.GiftNamingInfoModel;
+import com.yunbao.common.bean.GiftWallBean;
import com.yunbao.common.bean.GiftWallGiftDetail;
+import com.yunbao.common.bean.GiftWallInfoBean;
import com.yunbao.common.bean.GiftWallModel;
+import com.yunbao.common.bean.GiftWallTab2Bean;
import com.yunbao.common.bean.GuardGetGuardOpenInfoModel;
import com.yunbao.common.bean.GuardGetGuardUserInfoModel;
import com.yunbao.common.bean.HourRank;
@@ -53,7 +56,6 @@ import com.yunbao.common.bean.NobleRankHideUserListModel;
import com.yunbao.common.bean.NobleTrumpetModel;
import com.yunbao.common.bean.OpenAdModel;
import com.yunbao.common.bean.PkRankBean;
-import com.yunbao.common.bean.PrankGiftBean;
import com.yunbao.common.bean.PrankGiftResultBean;
import com.yunbao.common.bean.PrankHttpTurntableBean;
import com.yunbao.common.bean.PrankProgressBean;
@@ -1266,4 +1268,50 @@ public interface PDLiveApi {
Observable> getPrankList(
@Query("type")String type
);
+
+ /**
+ * 个人展馆
+ */
+ @GET("/api/public/?service=Gift.singleUserGiftHall")
+ Observable> singleUserGiftHall(
+ @Query("user_id")String userId,
+ @Query("screen")int screen
+ );
+
+ /**
+ * 主播展馆
+ */
+ @GET("/api/public/?service=Gift.liveGiftHall")
+ Observable> liveGiftHall(
+ @Query("live_id")String userId,
+ @Query("screen")int screen
+ );
+
+ /**
+ * 个人展馆详情
+ */
+ @GET("/api/public/?service=Gift.singleUserGiftHallDetail")
+ Observable> singleUserGiftHallDetail(
+ @Query("user_id")String userId,
+ @Query("gift_id")String gift_id
+ );
+
+ /**
+ * 主播展馆详情
+ */
+ @GET("/api/public/?service=Gift.liveGiftHallDetail")
+ Observable> liveGiftHallDetail(
+ @Query("live_id")String live_id,
+ @Query("gift_id")String gift_id,
+ @Query("gift_hall_type")int gift_hall_type,
+ @Query("list_type")int list_type
+ );
+
+ /**
+ * 全站展馆
+ */
+ @GET("/api/public/?service=Gift.allGiftHall")
+ Observable> allGiftHall(
+ @Query("type")String type
+ );
}
diff --git a/common/src/main/java/com/yunbao/common/http/live/LiveNetManager.java b/common/src/main/java/com/yunbao/common/http/live/LiveNetManager.java
index 0aa79bb7d..50a6093f9 100644
--- a/common/src/main/java/com/yunbao/common/http/live/LiveNetManager.java
+++ b/common/src/main/java/com/yunbao/common/http/live/LiveNetManager.java
@@ -29,8 +29,11 @@ import com.yunbao.common.bean.FansGroupGiftPackInfo;
import com.yunbao.common.bean.GiftAlreadyWallModel;
import com.yunbao.common.bean.GiftGuideModel;
import com.yunbao.common.bean.GiftNamingInfoModel;
+import com.yunbao.common.bean.GiftWallBean;
import com.yunbao.common.bean.GiftWallGiftDetail;
+import com.yunbao.common.bean.GiftWallInfoBean;
import com.yunbao.common.bean.GiftWallModel;
+import com.yunbao.common.bean.GiftWallTab2Bean;
import com.yunbao.common.bean.GuardGetGuardOpenInfoModel;
import com.yunbao.common.bean.GuardGetGuardUserInfoModel;
import com.yunbao.common.bean.HttpCallbackModel;
@@ -53,7 +56,6 @@ import com.yunbao.common.bean.NobleRankHideUserListModel;
import com.yunbao.common.bean.NobleTrumpetModel;
import com.yunbao.common.bean.OpenAdModel;
import com.yunbao.common.bean.PkRankBean;
-import com.yunbao.common.bean.PrankGiftBean;
import com.yunbao.common.bean.PrankGiftResultBean;
import com.yunbao.common.bean.PrankHttpTurntableBean;
import com.yunbao.common.bean.PrankProgressBean;
@@ -99,7 +101,6 @@ import io.reactivex.schedulers.Schedulers;
import okhttp3.MediaType;
import okhttp3.MultipartBody;
import okhttp3.RequestBody;
-import retrofit2.http.Query;
/**
@@ -3262,6 +3263,106 @@ public class LiveNetManager {
}
}).isDisposed();
+ }
+ public void singleUserGiftHall(String userId,int screen, HttpCallbackcallback) {
+ API.get().pdLiveApi(mContext)
+ .singleUserGiftHall(userId,screen)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(listResponseModel -> {
+ if (callback != null) {
+ callback.onSuccess(listResponseModel.getData().getInfo());
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ throwable.printStackTrace();
+ if (callback != null) {
+ callback.onError(mContext.getString(R.string.net_error));
+ }
+ }
+ }).isDisposed();
+
+ }
+ public void liveGiftHall(String userId,int screen, HttpCallbackcallback) {
+ API.get().pdLiveApi(mContext)
+ .liveGiftHall(userId,screen)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(listResponseModel -> {
+ if (callback != null) {
+ callback.onSuccess(listResponseModel.getData().getInfo());
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ throwable.printStackTrace();
+ if (callback != null) {
+ callback.onError(mContext.getString(R.string.net_error));
+ }
+ }
+ }).isDisposed();
+
+ }
+ public void singleUserGiftHallDetail(String userId,String giftId, HttpCallbackcallback) {
+ API.get().pdLiveApi(mContext)
+ .singleUserGiftHallDetail(userId,giftId)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(listResponseModel -> {
+ if (callback != null) {
+ callback.onSuccess(listResponseModel.getData().getInfo());
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ throwable.printStackTrace();
+ if (callback != null) {
+ callback.onError(mContext.getString(R.string.net_error));
+ }
+ }
+ }).isDisposed();
+
+ }
+ public void liveGiftHallDetail(String liveId,String giftId,int gift_hall_type,int list_type, HttpCallbackcallback) {
+ API.get().pdLiveApi(mContext)
+ .liveGiftHallDetail(liveId,giftId,gift_hall_type,list_type)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(listResponseModel -> {
+ if (callback != null) {
+ callback.onSuccess(listResponseModel.getData().getInfo());
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ throwable.printStackTrace();
+ if (callback != null) {
+ callback.onError(mContext.getString(R.string.net_error));
+ }
+ }
+ }).isDisposed();
+
+ }
+ public void allGiftHall(String type, HttpCallbackcallback) {
+ API.get().pdLiveApi(mContext)
+ .allGiftHall(type)
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(listResponseModel -> {
+ if (callback != null) {
+ callback.onSuccess(listResponseModel.getData().getInfo());
+ }
+ }, new Consumer() {
+ @Override
+ public void accept(Throwable throwable) throws Exception {
+ throwable.printStackTrace();
+ if (callback != null) {
+ callback.onError(mContext.getString(R.string.net_error));
+ }
+ }
+ }).isDisposed();
+
}
public void updateFile(File file, HttpCallback callback) {
diff --git a/common/src/main/java/com/yunbao/common/utils/SVGAViewUtils.java b/common/src/main/java/com/yunbao/common/utils/SVGAViewUtils.java
index 7cefb2e2f..1e72292da 100644
--- a/common/src/main/java/com/yunbao/common/utils/SVGAViewUtils.java
+++ b/common/src/main/java/com/yunbao/common/utils/SVGAViewUtils.java
@@ -13,13 +13,14 @@ import java.util.List;
public class SVGAViewUtils {
private final static List SVGA_CACHE = new ArrayList<>();
- public static void playEndClear(SVGAImageView svga, boolean isClear, SVGACallback callback) {
+ public static void playEndClear(SVGAImageView svga, boolean isClear,int loop, SVGACallback callback) {
if (!isClear) {
if (!SVGA_CACHE.contains(svga)) {
SVGA_CACHE.add(svga);
}
}
CrashSaveBean.getInstance().addPlaySvga();
+ svga.setLoops(loop);
svga.setCallback(new SVGACallback() {
@Override
public void onPause() {
@@ -72,9 +73,11 @@ public class SVGAViewUtils {
}
SVGA_CACHE.clear();
}
-
+ public static void playEndClear(SVGAImageView svga, boolean isClear, SVGACallback callback) {
+ playEndClear(svga, isClear,1, null);
+ }
public static void playEndClear(SVGAImageView svga, boolean isClear) {
- playEndClear(svga, isClear, null);
+ playEndClear(svga, isClear,null);
}
public static void playEndClear(SVGAImageView svga) {
diff --git a/common/src/main/res/drawable/gift_wall_gift_info_bottom_btn_tips.xml b/common/src/main/res/drawable/gift_wall_gift_info_bottom_btn_tips.xml
new file mode 100644
index 000000000..a8934c264
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_gift_info_bottom_btn_tips.xml
@@ -0,0 +1,14 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_gift_info_btn.xml b/common/src/main/res/drawable/gift_wall_gift_info_btn.xml
new file mode 100644
index 000000000..83a17d467
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_gift_info_btn.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_gift_info_lighten.xml b/common/src/main/res/drawable/gift_wall_gift_info_lighten.xml
new file mode 100644
index 000000000..775388468
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_gift_info_lighten.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_gift_info_list_btn_down.xml b/common/src/main/res/drawable/gift_wall_gift_info_list_btn_down.xml
new file mode 100644
index 000000000..faf44ecf9
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_gift_info_list_btn_down.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_gift_info_list_btn_up.xml b/common/src/main/res/drawable/gift_wall_gift_info_list_btn_up.xml
new file mode 100644
index 000000000..06cade22e
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_gift_info_list_btn_up.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_gift_info_progres.xml b/common/src/main/res/drawable/gift_wall_gift_info_progres.xml
new file mode 100644
index 000000000..2a8fb6f44
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_gift_info_progres.xml
@@ -0,0 +1,23 @@
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_gift_info_un_lighten.xml b/common/src/main/res/drawable/gift_wall_gift_info_un_lighten.xml
new file mode 100644
index 000000000..dc4761119
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_gift_info_un_lighten.xml
@@ -0,0 +1,13 @@
+
+
+ -
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_main_tab_list_item_progres.xml b/common/src/main/res/drawable/gift_wall_main_tab_list_item_progres.xml
new file mode 100644
index 000000000..3e1da33e2
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_main_tab_list_item_progres.xml
@@ -0,0 +1,23 @@
+
+
+
+ -
+
+
+
+
+
+
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_man_tab2_dsl.xml b/common/src/main/res/drawable/gift_wall_man_tab2_dsl.xml
new file mode 100644
index 000000000..4caa9da97
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_man_tab2_dsl.xml
@@ -0,0 +1,11 @@
+
+
+ -
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/drawable/gift_wall_man_tab_list_title.xml b/common/src/main/res/drawable/gift_wall_man_tab_list_title.xml
new file mode 100644
index 000000000..e62c38a90
--- /dev/null
+++ b/common/src/main/res/drawable/gift_wall_man_tab_list_title.xml
@@ -0,0 +1,8 @@
+
+
+ -
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/dialog_gift_wall_gift_info.xml b/common/src/main/res/layout/dialog_gift_wall_gift_info.xml
new file mode 100644
index 000000000..06bb03eee
--- /dev/null
+++ b/common/src/main/res/layout/dialog_gift_wall_gift_info.xml
@@ -0,0 +1,371 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/dialog_gift_wall_main_tab1_list2_spinner.xml b/common/src/main/res/layout/dialog_gift_wall_main_tab1_list2_spinner.xml
new file mode 100644
index 000000000..7bc49e745
--- /dev/null
+++ b/common/src/main/res/layout/dialog_gift_wall_main_tab1_list2_spinner.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/dialog_gift_wall_main_tab1_tips.xml b/common/src/main/res/layout/dialog_gift_wall_main_tab1_tips.xml
new file mode 100644
index 000000000..bce1065c7
--- /dev/null
+++ b/common/src/main/res/layout/dialog_gift_wall_main_tab1_tips.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/dialog_gift_wall_tab2_classic_info.xml b/common/src/main/res/layout/dialog_gift_wall_tab2_classic_info.xml
new file mode 100644
index 000000000..30b8c653a
--- /dev/null
+++ b/common/src/main/res/layout/dialog_gift_wall_tab2_classic_info.xml
@@ -0,0 +1,348 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/fragment_gift_wall_man_tab_1.xml b/common/src/main/res/layout/fragment_gift_wall_man_tab_1.xml
new file mode 100644
index 000000000..59106a9e8
--- /dev/null
+++ b/common/src/main/res/layout/fragment_gift_wall_man_tab_1.xml
@@ -0,0 +1,260 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/fragment_gift_wall_man_tab_2.xml b/common/src/main/res/layout/fragment_gift_wall_man_tab_2.xml
new file mode 100644
index 000000000..2777ed51f
--- /dev/null
+++ b/common/src/main/res/layout/fragment_gift_wall_man_tab_2.xml
@@ -0,0 +1,206 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/item_dialog_gift_wall_gift_info.xml b/common/src/main/res/layout/item_dialog_gift_wall_gift_info.xml
new file mode 100644
index 000000000..65d6cd629
--- /dev/null
+++ b/common/src/main/res/layout/item_dialog_gift_wall_gift_info.xml
@@ -0,0 +1,59 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/item_gift_wall_man_tab1_list_1.xml b/common/src/main/res/layout/item_gift_wall_man_tab1_list_1.xml
new file mode 100644
index 000000000..88d8fb768
--- /dev/null
+++ b/common/src/main/res/layout/item_gift_wall_man_tab1_list_1.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/item_gift_wall_man_tab1_list_2.xml b/common/src/main/res/layout/item_gift_wall_man_tab1_list_2.xml
new file mode 100644
index 000000000..21a4d2ff9
--- /dev/null
+++ b/common/src/main/res/layout/item_gift_wall_man_tab1_list_2.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/item_gift_wall_man_tab2_list_1.xml b/common/src/main/res/layout/item_gift_wall_man_tab2_list_1.xml
new file mode 100644
index 000000000..bf9c1c278
--- /dev/null
+++ b/common/src/main/res/layout/item_gift_wall_man_tab2_list_1.xml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/layout/item_gift_wall_man_tab2_list_2.xml b/common/src/main/res/layout/item_gift_wall_man_tab2_list_2.xml
new file mode 100644
index 000000000..1951e30bf
--- /dev/null
+++ b/common/src/main/res/layout/item_gift_wall_man_tab2_list_2.xml
@@ -0,0 +1,121 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/common/src/main/res/mipmap-xxhdpi/bg_dialog_gift_wall_main_tab1_tips.png b/common/src/main/res/mipmap-xxhdpi/bg_dialog_gift_wall_main_tab1_tips.png
new file mode 100644
index 000000000..3c3e48ef3
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/bg_dialog_gift_wall_main_tab1_tips.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_bg.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_bg.png
new file mode 100644
index 000000000..92cf89145
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_bg.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_botton.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_botton.png
new file mode 100644
index 000000000..326474bc0
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_botton.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_light.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_light.png
new file mode 100644
index 000000000..d8eb05d38
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_light.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_list.9.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_list.9.png
new file mode 100644
index 000000000..3052c22ba
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_list.9.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_list_time.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_list_time.png
new file mode 100644
index 000000000..2f8b56130
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_gift_info_list_time.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_bg.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_bg.png
new file mode 100644
index 000000000..84c6d0f1a
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_bg.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_bg1.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_bg1.png
new file mode 100644
index 000000000..6aae1876f
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_bg1.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_select.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_select.png
new file mode 100644
index 000000000..b695a99f7
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_select.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_select1.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_select1.png
new file mode 100644
index 000000000..c2f01f0c1
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_select1.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_unselect.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_unselect.png
new file mode 100644
index 000000000..e0bf3ad40
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_unselect.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_unselect1.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_unselect1.png
new file mode 100644
index 000000000..f12e8442a
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_item_unselect1.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_bg.9.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_bg.9.png
new file mode 100644
index 000000000..d0006c322
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_bg.9.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_bottom.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_bottom.png
new file mode 100644
index 000000000..3d2c154f3
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_bottom.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_top.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_top.png
new file mode 100644
index 000000000..2476baf10
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_list_top.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_tab1_down_bg.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_tab1_down_bg.png
new file mode 100644
index 000000000..9c42e6ed2
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_tab1_down_bg.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_main_tab1_down_ic.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_tab1_down_ic.png
new file mode 100644
index 000000000..9f5e8f38a
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_main_tab1_down_ic.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_avatar1.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_avatar1.png
new file mode 100644
index 000000000..70256e7c7
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_avatar1.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_avatar2.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_avatar2.png
new file mode 100644
index 000000000..296ae900a
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_avatar2.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg.png
new file mode 100644
index 000000000..6a968701b
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_one.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_one.png
new file mode 100644
index 000000000..1a67ecf89
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_one.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_three.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_three.png
new file mode 100644
index 000000000..5b26aa719
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_three.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_two.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_two.png
new file mode 100644
index 000000000..4d77c5dea
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_bg_two.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_list_bg.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_list_bg.png
new file mode 100644
index 000000000..d5a8b8bae
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_list_bg.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_one.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_one.png
new file mode 100644
index 000000000..55eaa4aa0
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_one.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_star.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_star.png
new file mode 100644
index 000000000..0978ae71d
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_star.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_three.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_three.png
new file mode 100644
index 000000000..76da9c82a
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_three.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_tips.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_tips.png
new file mode 100644
index 000000000..a02bc6c36
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_tips.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_twwo.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_twwo.png
new file mode 100644
index 000000000..c85fcbdcf
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_classic_info_twwo.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_bg1.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_bg1.png
new file mode 100644
index 000000000..17cc2c3cc
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_bg1.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_bg2.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_bg2.png
new file mode 100644
index 000000000..a4a5b5479
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_bg2.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_icon1.png b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_icon1.png
new file mode 100644
index 000000000..2dfd614eb
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/gift_wall_tab2_item_icon1.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/ic_gift_wall_time2.png b/common/src/main/res/mipmap-xxhdpi/ic_gift_wall_time2.png
new file mode 100644
index 000000000..38529ee75
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/ic_gift_wall_time2.png differ
diff --git a/common/src/main/res/mipmap-xxhdpi/icon_gift_wall_main_tips2.png b/common/src/main/res/mipmap-xxhdpi/icon_gift_wall_main_tips2.png
new file mode 100644
index 000000000..59706c0c5
Binary files /dev/null and b/common/src/main/res/mipmap-xxhdpi/icon_gift_wall_main_tips2.png differ
diff --git a/main/src/main/res/mipmap-xxxhdpi/hide.png b/common/src/main/res/mipmap-xxxhdpi/hide.png
similarity index 100%
rename from main/src/main/res/mipmap-xxxhdpi/hide.png
rename to common/src/main/res/mipmap-xxxhdpi/hide.png
diff --git a/common/src/main/res/values-zh/strings.xml b/common/src/main/res/values-zh/strings.xml
index 0fae9ad0e..32d4f066e 100644
--- a/common/src/main/res/values-zh/strings.xml
+++ b/common/src/main/res/values-zh/strings.xml
@@ -1501,4 +1501,30 @@
個人展館
全站展館
榮譽成就
+ 全部
+ 已點亮
+ 未點亮
+ 繼續點亮
+ 去點亮
+ 助力榜
+ 冠名榜
+ 周星榜
+ 已送:
+ 一鍵冠名
+ 一键摘星
+ 一鍵點亮
+ 已點亮
+ 已摘星
+ 已冠名
+ 排名
+ 冠名歸屬
+ 周星歸屬
+ 冠名值
+ 星光值
+ 周星禮物
+ 經典禮物
+ 等待點亮
+ 本期角逐
+ 上期冠名
+
diff --git a/common/src/main/res/values/strings.xml b/common/src/main/res/values/strings.xml
index 9e2e998aa..5d5b4f53a 100644
--- a/common/src/main/res/values/strings.xml
+++ b/common/src/main/res/values/strings.xml
@@ -1509,4 +1509,29 @@
Personal hal
Whole site hal
Honors
+ All
+ Lit up
+ Not Lit
+ keep lighting
+ Go light up
+ Assistance List
+ Title list
+ Weekly star list
+ Sent:
+ One-click Title
+ One-click Stars
+ One-click Lighting
+ Lit up
+ Picked
+ Titled
+ Rank
+ Title attribution
+ Weekly star attribution
+ Title value
+ Star value
+ Star Gifts
+ Classic Gifts
+ Waiting for titling
+ This competition
+ Previous Title
diff --git a/live/build.gradle b/live/build.gradle
index 2fca16a93..216cbdb52 100644
--- a/live/build.gradle
+++ b/live/build.gradle
@@ -1 +1 @@
-apply plugin: 'com.android.library'
apply plugin: 'img-optimizer'
apply plugin: 'kotlin-android'
apply from: "../package_config.gradle"
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
aaptOptions.cruncherEnabled = false
aaptOptions.useNewCruncher = false
packagingOptions {
pickFirst "lib/armeabi/libyuvutils.so"
pickFirst "lib/arm64-v8a/libyuvutils.so"
pickFirst "lib/armeabi-v7a/libyuvutils.so"
pickFirst "lib/armeabi/libyuvtools.so"
pickFirst "lib/arm64-v8a/libyuvtools.so"
pickFirst "lib/armeabi-v7a/libyuvtools.so"
exclude "lib/arm64-v8a/libmmcv_api_handgesture.so"
exclude "lib/arm64-v8a/libmmcv_api_express.so"
exclude "lib/arm64-v8a/libMediaEncoder.so"
exclude "lib/arm64-v8a/libarcore_sdk_c.so"
exclude "lib/arm64-v8a/libmediadecoder.so"
exclude "lib/arm64-v8a/libMediaMuxer.so"
exclude "lib/arm64-v8a/libarcore_sdk_jni.so"
exclude "lib/arm64-v8a/libMediaUtils.so"
exclude "lib/arm64-v8a/libcosmosffmpeg.so"
}
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
manifestPlaceholders = rootProject.ext.manifestPlaceholders
ndk {
abiFilters "armeabi-v7a", "arm64-v8a","x86","x86_64"
}
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
aaptOptions {
cruncherEnabled = false
useNewCruncher = false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
kotlinOptions {
allWarningsAsErrors = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
flatDir {
dirs 'libs', '../libs'
}
mavenCentral()
}
dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation (name:'../libs/beautysdk-202202241203',ext:'aar')
implementation (name:'../libs/svgaplayer-release-v1.2.1',ext:'aar')
//socket.io
implementation('io.socket:socket.io-client:1.0.0') {
exclude group: 'org.json', module: 'json'
}
//common
api project(path:':lib_faceunity')//声网-新娱美颜
api project(path:':FaceUnity')//融云-新娱美颜
api project(path:':common')//公共库
api project(':Share')//分享
annotationProcessor rootProject.ext.dependencies["arouter-compiler"]
//工具
implementation 'com.eightbitlab:blurview:1.6.6'
implementation 'com.google.code.gson:gson:2.8.6'
implementation "com.getkeepsafe.relinker:relinker:1.4.4"
}
\ No newline at end of file
+apply plugin: 'com.android.library'
apply plugin: 'img-optimizer'
apply plugin: 'kotlin-android'
apply from: "../package_config.gradle"
android {
compileSdkVersion rootProject.ext.android.compileSdkVersion
buildToolsVersion rootProject.ext.android.buildToolsVersion
aaptOptions.cruncherEnabled = false
aaptOptions.useNewCruncher = false
packagingOptions {
pickFirst "lib/armeabi/libyuvutils.so"
pickFirst "lib/arm64-v8a/libyuvutils.so"
pickFirst "lib/armeabi-v7a/libyuvutils.so"
pickFirst "lib/armeabi/libyuvtools.so"
pickFirst "lib/arm64-v8a/libyuvtools.so"
pickFirst "lib/armeabi-v7a/libyuvtools.so"
exclude "lib/arm64-v8a/libmmcv_api_handgesture.so"
exclude "lib/arm64-v8a/libmmcv_api_express.so"
exclude "lib/arm64-v8a/libMediaEncoder.so"
exclude "lib/arm64-v8a/libarcore_sdk_c.so"
exclude "lib/arm64-v8a/libmediadecoder.so"
exclude "lib/arm64-v8a/libMediaMuxer.so"
exclude "lib/arm64-v8a/libarcore_sdk_jni.so"
exclude "lib/arm64-v8a/libMediaUtils.so"
exclude "lib/arm64-v8a/libcosmosffmpeg.so"
}
defaultConfig {
minSdkVersion rootProject.ext.android.minSdkVersion
targetSdkVersion rootProject.ext.android.targetSdkVersion
versionCode rootProject.ext.android.versionCode
versionName rootProject.ext.android.versionName
manifestPlaceholders = rootProject.ext.manifestPlaceholders
ndk {
abiFilters "armeabi-v7a", "arm64-v8a","x86","x86_64"
}
javaCompileOptions {
annotationProcessorOptions {
arguments = [AROUTER_MODULE_NAME: project.getName()]
}
}
}
aaptOptions {
cruncherEnabled = false
useNewCruncher = false
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
kotlinOptions {
allWarningsAsErrors = true
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}
}
repositories {
flatDir {
dirs 'libs', '../libs'
}
mavenCentral()
}
dependencies {
implementation 'androidx.constraintlayout:constraintlayout:2.0.0'
implementation fileTree(dir: 'libs', include: ['*.jar', '*.aar'])
implementation (name:'../libs/beautysdk-202202241203',ext:'aar')
implementation (name:'../libs/svgaplayer-release-v1.2.1',ext:'aar')
//socket.io
implementation('io.socket:socket.io-client:1.0.0') {
exclude group: 'org.json', module: 'json'
}
//common
api project(path:':lib_faceunity')//声网-新娱美颜
api project(path:':FaceUnity')//融云-新娱美颜
api project(path:':common')//公共库
// api project(path:':SVGAlibrary')//svga
api project(':Share')//分享
annotationProcessor rootProject.ext.dependencies["arouter-compiler"]
//工具
implementation 'com.eightbitlab:blurview:1.6.6'
implementation 'com.google.code.gson:gson:2.8.6'
implementation "com.getkeepsafe.relinker:relinker:1.4.4"
}
\ No newline at end of file
diff --git a/live/src/main/java/com/yunbao/live/activity/LiveAudienceActivity.java b/live/src/main/java/com/yunbao/live/activity/LiveAudienceActivity.java
index 46de579c3..ed8ae2ae2 100644
--- a/live/src/main/java/com/yunbao/live/activity/LiveAudienceActivity.java
+++ b/live/src/main/java/com/yunbao/live/activity/LiveAudienceActivity.java
@@ -38,6 +38,7 @@ import com.lxj.xpopup.enums.PopupPosition;
import com.lxj.xpopup.interfaces.XPopupCallback;
import com.lzf.easyfloat.EasyFloat;
import com.lzf.easyfloat.enums.ShowPattern;
+import com.yunbao.common.bean.JsWishBean;
import com.yunbao.common.dialog.DebugDialog;
import com.yunbao.common.interfaces.OnItemClickListener;
import com.yunbao.common.utils.MobclickAgent;
@@ -549,6 +550,13 @@ public class LiveAudienceActivity extends LiveActivity {
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ public void onWishSendGift(JsWishBean bean) {
+ if (bean != null) {
+ openGiftWindow(bean.getGiftId());
+ }
+ }
+
@Override
protected void release() {
if (mPayPresenter != null) {
diff --git a/live/src/main/java/com/yunbao/live/dialog/LiveUserDialogFragment.java b/live/src/main/java/com/yunbao/live/dialog/LiveUserDialogFragment.java
index da6d5452b..526b502ba 100644
--- a/live/src/main/java/com/yunbao/live/dialog/LiveUserDialogFragment.java
+++ b/live/src/main/java/com/yunbao/live/dialog/LiveUserDialogFragment.java
@@ -281,12 +281,14 @@ public class LiveUserDialogFragment extends AbsDialogFragment implements View.On
@Override
public void onViewClicks() {
if (TextUtils.equals(mLiveUid, "uid")) return;
+ /* // 老礼物墙
Bus.get().post(new LiveAudienceEvent()
.setType(LiveAudienceEvent.LiveAudienceType.GIFT_WALL)
.setUid(mToUid)
.setVoicePress(isAnchor)
.setUname(mToName)
- .setAvatar(mAvatarUrl));
+ .setAvatar(mAvatarUrl));*/
+ new com.yunbao.common.dialog.GiftWallDialog(mContext, mToUid + "",mToName, mToUid.equals(mLiveUid)).setFullWindows(false).showDialog();
dismiss();
}
});
@@ -663,7 +665,7 @@ public class LiveUserDialogFragment extends AbsDialogFragment implements View.On
LiveBean liveBean = JSON.parseObject(info[0], LiveBean.class);
new LiveRoomCheckLivePresenter(mContext, liveBean.getUid(), liveBean.getStream(), new LiveRoomCheckLivePresenter.NewActionListener() {
@Override
- public void onLiveRoomChanged(String liveUid, String stream, int liveType, String liveTypeVal, String liveSdk,boolean isSw) {
+ public void onLiveRoomChanged(String liveUid, String stream, int liveType, String liveTypeVal, String liveSdk, boolean isSw) {
if (liveBean == null) {
return;
}
diff --git a/live/src/main/java/com/yunbao/live/views/LiveRoomViewHolder.java b/live/src/main/java/com/yunbao/live/views/LiveRoomViewHolder.java
index 72dfc078f..96fb6d5f7 100644
--- a/live/src/main/java/com/yunbao/live/views/LiveRoomViewHolder.java
+++ b/live/src/main/java/com/yunbao/live/views/LiveRoomViewHolder.java
@@ -4233,6 +4233,7 @@ public class LiveRoomViewHolder extends AbsViewHolder implements View.OnClickLis
bundle.putString(Constants.LIVE_UID, mLiveUid);
bundle.putString(Constants.STREAM, mStream);
bundle.putString(Constants.TO_UID, toUid);
+ bundle.putString(Constants.TO_UNAME, toUid);
fragment.setArguments(bundle);
fragment.show(((LiveActivity) mContext).getSupportFragmentManager(), "LiveUserDialogFragment");
}
diff --git a/main/build.gradle b/main/build.gradle
index 4d8833cfe..d1b0f355a 100644
--- a/main/build.gradle
+++ b/main/build.gradle
@@ -75,6 +75,7 @@ dependencies {
api project(':common')
api project(':lib_faceunity')//新娱美颜
api project(':Share')//分享
+ // api project(':SVGAlibrary')//分享
implementation 'androidx.appcompat:appcompat:1.3.0'
implementation 'com.google.android.material:material:1.4.0'
annotationProcessor rootProject.ext.dependencies["arouter-compiler"]
diff --git a/main/src/main/java/com/yunbao/main/views/MainHomeViewHolder.java b/main/src/main/java/com/yunbao/main/views/MainHomeViewHolder.java
index 11d54abeb..c8e9d9579 100644
--- a/main/src/main/java/com/yunbao/main/views/MainHomeViewHolder.java
+++ b/main/src/main/java/com/yunbao/main/views/MainHomeViewHolder.java
@@ -71,7 +71,9 @@ public class MainHomeViewHolder extends AbsMainHomeParentViewHolder {
AppManager.runDebugCode(new Runnable() {
@Override
public void run() {
- new GiftWallDialog(mContext).setFullWindows(false).showDialog();
+ new GiftWallDialog(mContext, IMLoginManager.get(mContext).getUserInfo().getId() + "",
+ IMLoginManager.get(mContext).getUserInfo().getUserNicename()
+ , false).setFullWindows(true).showDialog();
}
});
return true;
diff --git a/settings.gradle b/settings.gradle
index 9620d2b17..c759e0b49 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -8,3 +8,5 @@ include ':lib_huawei'
include ':lib_google'
include ':IAP6Helper'
include ':lib_faceunity'
+//include ':SVGAlibrary'
+include ':TabLayout'