From 06b1050938ba71066a075f84224c653f561b18d9 Mon Sep 17 00:00:00 2001 From: yujiankang <15535958281@163.com> Date: Sun, 28 Sep 2025 20:14:39 +0800 Subject: [PATCH] =?UTF-8?q?1.=E5=A4=9A=E6=B8=A0=E9=81=93=E6=89=93=E5=8C=85?= =?UTF-8?q?=202.app=E6=9B=B4=E6=96=B0=E9=A1=B5=E9=9D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 48 ++++- gradle.properties | 2 +- .../moduleutil/adapter/AppUpdateDialog.java | 9 +- .../xscm/moduleutil/utils/TextViewUtils.java | 171 ++++++++++++++++++ .../src/main/res/layout/dialog_app_update.xml | 78 +++++--- .../modulemain/activity/MainActivity.java | 6 +- 6 files changed, 280 insertions(+), 34 deletions(-) create mode 100644 moduleUtil/src/main/java/com/xscm/moduleutil/utils/TextViewUtils.java diff --git a/app/build.gradle b/app/build.gradle index 9c42717..8f2cd61 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -4,9 +4,17 @@ plugins { } android { + // 1. 定义渠道维度(必须配置,否则报错) + flavorDimensions "environment" + + namespace 'com.xscm.midi' compileSdk 35 + + + + defaultConfig { applicationId "com.xscm.midi" minSdk 24 @@ -19,7 +27,7 @@ android { ndk { //设置支持的so库 // abiFilters 'arm64-v8a', 'armeabi','arm64' - abiFilters 'arm64-v8a','armeabi-v7a' + abiFilters 'arm64-v8a', 'armeabi-v7a' } javaCompileOptions { @@ -30,12 +38,50 @@ android { ] } } + // 【默认图标占位符】后续会被渠道配置覆盖 + manifestPlaceholders = [ + icon: "@mipmap/ic_launcher_app" // 默认图标(main目录下的图标) + ] // signingConfig signingConfigs.release // proguardFiles 'proguard-rules.pro' } + // 2. 配置测试版和正式版渠道 + productFlavors { + // 正式版配置 + releas { + dimension "environment" + // 正式版包名:使用基础包名(com.example.myapp) + applicationIdSuffix "" + + // 【正式版应用名称】通过resValue动态生成string资源 + resValue "string", "app_name", "秘地" + + // 【正式版图标】替换manifest中的占位符(使用main目录下的正式图标) + manifestPlaceholders = [ + appIcon: "@mipmap/ic_launcher_app" // 需在main/res/mipmap放置该图标 + ] + } + + // 测试版配置 + beta { + dimension "environment" + // 测试版包名:基础包名 + .beta(com.example.myapp.beta) + applicationIdSuffix ".beta" + // 测试版版本名:1.0-beta + versionNameSuffix "-beta" + + // 【测试版应用名称】动态生成带标识的名称 + resValue "string", "app_name", "秘地-测试版" + + // 【测试版图标】替换为测试专用图标 + manifestPlaceholders = [ + appIcon: "@mipmap/ic_launcher_app_bat" // 需在main/res/mipmap放置该图标 + ] + } + } signingConfigs { diff --git a/gradle.properties b/gradle.properties index 429524b..ed84208 100644 --- a/gradle.properties +++ b/gradle.properties @@ -29,7 +29,7 @@ isBuildModule=false android.injected.testOnly=false APP_VERSION_NAME=1.1.3 -APP_VERSION_CODE=166 +APP_VERSION_CODE=165 org.gradle.jvm.toolchain.useLegacyAdapters=false #org.gradle.java.home=C\:\\Users\\qx\\.jdks\\ms-17.0.15 diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/adapter/AppUpdateDialog.java b/moduleUtil/src/main/java/com/xscm/moduleutil/adapter/AppUpdateDialog.java index 0a0d6a0..c48ddc2 100644 --- a/moduleUtil/src/main/java/com/xscm/moduleutil/adapter/AppUpdateDialog.java +++ b/moduleUtil/src/main/java/com/xscm/moduleutil/adapter/AppUpdateDialog.java @@ -9,12 +9,14 @@ import androidx.annotation.NonNull; import android.text.Html; import android.text.TextUtils; import android.text.method.ScrollingMovementMethod; +import android.util.Log; import android.view.KeyEvent; import android.view.View; import android.view.Window; import android.view.WindowManager; import com.blankj.utilcode.util.AppUtils; +import com.blankj.utilcode.util.LogUtils; import com.blankj.utilcode.util.ScreenUtils; import com.blankj.utilcode.util.ToastUtils; import com.xscm.moduleutil.R; @@ -22,6 +24,7 @@ import com.xscm.moduleutil.bean.AppUpdateModel; import com.xscm.moduleutil.databinding.DialogAppUpdateBinding; import com.xscm.moduleutil.utils.DownloadListener; import com.xscm.moduleutil.utils.DownloadUtil; +import com.xscm.moduleutil.utils.TextViewUtils; import com.xscm.moduleutil.utils.logger.Logger; import com.xscm.moduleutil.widget.dialog.BaseDialog; @@ -77,7 +80,11 @@ public class AppUpdateDialog extends BaseDialog implemen public void setAppUpdateModel(AppUpdateModel appUpdateModel) { this.appUpdateModel = appUpdateModel; - mBinding.tvContent.setText(TextUtils.isEmpty(appUpdateModel.getContent()) ? "修复旧版本已知bug" : Html.fromHtml(appUpdateModel.getContent())); + TextViewUtils.setHtmlText(mBinding.tvContent,appUpdateModel.getContent(),false); + //mBinding.tvContent.setText(TextUtils.isEmpty(appUpdateModel.getContent()) ? "修复旧版本已知bug" : Html.fromHtml(appUpdateModel.getContent())); + LogUtils.d("AppUpdateDialog", "setAppUpdateModel " + appUpdateModel.getContent().toString()); +// mBinding.tvContent.setHtmlText(appUpdateModel.getContent()); + } diff --git a/moduleUtil/src/main/java/com/xscm/moduleutil/utils/TextViewUtils.java b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/TextViewUtils.java new file mode 100644 index 0000000..d25a532 --- /dev/null +++ b/moduleUtil/src/main/java/com/xscm/moduleutil/utils/TextViewUtils.java @@ -0,0 +1,171 @@ +package com.xscm.moduleutil.utils; +import android.graphics.Color; +import android.graphics.Typeface; +import android.os.Build; +import android.text.Html; +import android.text.Spannable; +import android.text.SpannableString; +import android.text.method.LinkMovementMethod; +import android.text.style.BackgroundColorSpan; +import android.text.style.ClickableSpan; +import android.text.style.ForegroundColorSpan; +import android.text.style.RelativeSizeSpan; +import android.text.style.StrikethroughSpan; +import android.text.style.StyleSpan; +import android.text.style.UnderlineSpan; +import android.view.View; +import android.widget.TextView; + +import androidx.annotation.ColorInt; +import androidx.annotation.NonNull; + +/** + * TextView 富文本工具类(Java实现) + * 支持HTML解析、部分文本样式、点击事件等功能 + */ +public class TextViewUtils { + + /** + * 显示HTML格式文本 + * @param textView 目标TextView + * @param htmlContent HTML内容字符串 + */ + public static void setHtmlText(TextView textView, String htmlContent) { + setHtmlText(textView, htmlContent, true); + } + + /** + * 显示HTML格式文本(可控制链接点击) + * @param textView 目标TextView + * @param htmlContent HTML内容字符串 + * @param enableLinks 是否启用链接点击 + */ + public static void setHtmlText(TextView textView, String htmlContent, boolean enableLinks) { + if (textView == null || htmlContent == null) return; + + // 处理不同Android版本的HTML解析 + CharSequence spannedText; + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + spannedText = Html.fromHtml(htmlContent, Html.FROM_HTML_MODE_COMPACT); + } else { + // 兼容Android N以下版本 + spannedText = Html.fromHtml(htmlContent); + } + + textView.setText(spannedText); + + // 启用链接点击功能 + if (enableLinks) { + textView.setMovementMethod(LinkMovementMethod.getInstance()); + textView.setHighlightColor(Color.TRANSPARENT); // 去除点击高亮 + } + } + + /** + * 给部分文本设置样式 + * @param textView 目标TextView + * @param fullText 完整文本 + * @param targetText 需要设置样式的子文本 + * @param spans 样式集合(可传入多个) + */ + public static void setPartialStyle(TextView textView, String fullText, + String targetText, Object... spans) { + if (textView == null || fullText == null || targetText == null) return; + + int startIndex = fullText.indexOf(targetText); + if (startIndex == -1) { + textView.setText(fullText); + return; + } + + int endIndex = startIndex + targetText.length(); + SpannableString spannable = new SpannableString(fullText); + + // 应用所有样式 + for (Object span : spans) { + spannable.setSpan(span, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } + + textView.setText(spannable); + } + + /** + * 设置可点击文本 + * @param textView 目标TextView + * @param fullText 完整文本 + * @param clickText 可点击的子文本 + * @param linkColor 链接颜色 + * @param isUnderline 是否显示下划线 + * @param listener 点击事件监听器 + */ + public static void setClickableText(TextView textView, String fullText, String clickText, + @ColorInt int linkColor, boolean isUnderline, + OnClickableTextListener listener) { + if (textView == null || fullText == null || clickText == null || listener == null) return; + + int startIndex = fullText.indexOf(clickText); + if (startIndex == -1) { + textView.setText(fullText); + return; + } + + int endIndex = startIndex + clickText.length(); + SpannableString spannable = new SpannableString(fullText); + + // 创建可点击样式 + ClickableSpan clickableSpan = new ClickableSpan() { + @Override + public void onClick(@NonNull View widget) { + listener.onClick(); + } + + @Override + public void updateDrawState(@NonNull android.text.TextPaint ds) { + super.updateDrawState(ds); + ds.setColor(linkColor); + ds.setUnderlineText(isUnderline); + } + }; + + spannable.setSpan(clickableSpan, startIndex, endIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + textView.setText(spannable); + textView.setMovementMethod(LinkMovementMethod.getInstance()); + textView.setHighlightColor(Color.TRANSPARENT); + } + + // 快捷创建样式的工具方法 + public static StyleSpan createBoldSpan() { + return new StyleSpan(Typeface.BOLD); + } + + public static StyleSpan createItalicSpan() { + return new StyleSpan(Typeface.ITALIC); + } + + public static ForegroundColorSpan createTextColorSpan(@ColorInt int color) { + return new ForegroundColorSpan(color); + } + + public static BackgroundColorSpan createBgColorSpan(@ColorInt int color) { + return new BackgroundColorSpan(color); + } + + public static UnderlineSpan createUnderlineSpan() { + return new UnderlineSpan(); + } + + public static StrikethroughSpan createStrikethroughSpan() { + return new StrikethroughSpan(); + } + + public static RelativeSizeSpan createTextSizeSpan(float proportion) { + return new RelativeSizeSpan(proportion); + } + + /** + * 可点击文本的监听器接口 + */ + public interface OnClickableTextListener { + void onClick(); + } +} diff --git a/moduleUtil/src/main/res/layout/dialog_app_update.xml b/moduleUtil/src/main/res/layout/dialog_app_update.xml index d952a4f..68da3a9 100644 --- a/moduleUtil/src/main/res/layout/dialog_app_update.xml +++ b/moduleUtil/src/main/res/layout/dialog_app_update.xml @@ -11,12 +11,24 @@ android:layout_width="match_parent" android:layout_height="wrap_content"> + + + android:layout_width="0dp" + android:background="@drawable/bg_bttom_fff" + android:layout_height="wrap_content" + android:paddingBottom="@dimen/dp_10" + app:layout_constraintLeft_toLeftOf="@+id/iv_bg" + app:layout_constraintRight_toRightOf="@+id/iv_bg" + app:layout_constraintTop_toBottomOf="@+id/iv_bg"> + - + /> + + + + + + +