diff --git a/app/src/main/java/com/haodustudio/DailyNotes/utils/BitmapUtils.kt b/app/src/main/java/com/haodustudio/DailyNotes/utils/BitmapUtils.kt index 0f61568..be7352a 100644 --- a/app/src/main/java/com/haodustudio/DailyNotes/utils/BitmapUtils.kt +++ b/app/src/main/java/com/haodustudio/DailyNotes/utils/BitmapUtils.kt @@ -20,6 +20,70 @@ import java.io.InputStream object BitmapUtils { + + /** + * 安全地从资源中加载图片,自动计算采样率避免 OOM 和 Canvas 绘制过大 Bitmap 错误 + * @param resId 图片资源ID + * @param reqWidth 需要的宽度(如果为0则不限制) + * @param reqHeight 需要的高度(如果为0则不限制) + * @return 采样后的 Bitmap,如果加载失败返回 null + */ + fun decodeSampledBitmapFromResource(resId: Int, reqWidth: Int = 0, reqHeight: Int = 0): Bitmap? { + return try { + val options = BitmapFactory.Options().apply { + inJustDecodeBounds = true + } + BitmapFactory.decodeResource(BaseApplication.instance.resources, resId, options) + + // 计算采样率 + options.inSampleSize = calculateInSampleSize(options, reqWidth, reqHeight) + + // 实际解码图片 + options.inJustDecodeBounds = false + options.inPreferredConfig = Bitmap.Config.RGB_565 // 使用更少内存 + + BitmapFactory.decodeResource(BaseApplication.instance.resources, resId, options) + } catch (e: Exception) { + Log.e("BitmapUtils", "Failed to decode bitmap from resource: $resId", e) + null + } + } + + /** + * 计算合适的采样率 + */ + private fun calculateInSampleSize( + options: BitmapFactory.Options, + reqWidth: Int, + reqHeight: Int + ): Int { + val height = options.outHeight + val width = options.outWidth + var inSampleSize = 1 + + // 如果没有指定需求尺寸,使用屏幕尺寸作为限制 + val finalReqWidth = if (reqWidth <= 0) { + BaseApplication.instance.resources.displayMetrics.widthPixels + } else reqWidth + + val finalReqHeight = if (reqHeight <= 0) { + BaseApplication.instance.resources.displayMetrics.heightPixels + } else reqHeight + + if (height > finalReqHeight || width > finalReqWidth) { + val halfHeight = height / 2 + val halfWidth = width / 2 + + // 计算最大的 inSampleSize 值,保证采样后的图片尺寸大于需求尺寸 + while (halfHeight / inSampleSize >= finalReqHeight && + halfWidth / inSampleSize >= finalReqWidth) { + inSampleSize *= 2 + } + } + + return inSampleSize + } + fun getImageFromAssetsFile(filePath: String): Bitmap { val am: AssetManager = BaseApplication.instance.resources.assets val mIs: InputStream = am.open(filePath) diff --git a/app/src/main/java/com/haodustudio/DailyNotes/view/activities/AboutSoftware.kt b/app/src/main/java/com/haodustudio/DailyNotes/view/activities/AboutSoftware.kt index 2fd0158..f274d8b 100644 --- a/app/src/main/java/com/haodustudio/DailyNotes/view/activities/AboutSoftware.kt +++ b/app/src/main/java/com/haodustudio/DailyNotes/view/activities/AboutSoftware.kt @@ -9,6 +9,7 @@ import com.haodustudio.DailyNotes.BaseApplication import com.haodustudio.DailyNotes.R import com.haodustudio.DailyNotes.databinding.ActivityAboutSoftwareBinding import com.haodustudio.DailyNotes.helper.makeToast +import com.haodustudio.DailyNotes.utils.BitmapUtils import com.haodustudio.DailyNotes.view.activities.base.BaseActivity import com.haodustudio.DailyNotes.view.activities.ViewImage import com.haodustudio.DailyNotes.view.activities.PrivacySettingsActivity @@ -20,10 +21,36 @@ class AboutSoftware : BaseActivity() { super.onCreate(savedInstanceState) setContentView(binding.root) + loadBannerImageSafely() configureBanner() configureCards() } + private fun loadBannerImageSafely() { + try { + binding.bannerImage.post { + val viewWidth = binding.bannerImage.width + val viewHeight = binding.bannerImage.height + + val bitmap = BitmapUtils.decodeSampledBitmapFromResource( + R.drawable.about_banner, + viewWidth, + viewHeight + ) + + if (bitmap != null) { + binding.bannerImage.setImageBitmap(bitmap) + } else { + // 如果加载失败,使用占位符 + binding.bannerImage.setImageResource(android.R.color.darker_gray) + } + } + } catch (e: Exception) { + e.printStackTrace() + binding.bannerImage.setImageResource(android.R.color.darker_gray) + } + } + private fun configureBanner() { binding.bannerBadge.text = getString(R.string.about_codename_label) binding.bannerTitle.text = getString(R.string.about_banner_title) diff --git a/app/src/main/java/com/haodustudio/DailyNotes/view/activities/PrivacySettingsActivity.kt b/app/src/main/java/com/haodustudio/DailyNotes/view/activities/PrivacySettingsActivity.kt index 5c2f067..c9124f6 100644 --- a/app/src/main/java/com/haodustudio/DailyNotes/view/activities/PrivacySettingsActivity.kt +++ b/app/src/main/java/com/haodustudio/DailyNotes/view/activities/PrivacySettingsActivity.kt @@ -12,6 +12,7 @@ import com.haodustudio.DailyNotes.R import com.haodustudio.DailyNotes.databinding.ActivityPrivacySettingsBinding import com.haodustudio.DailyNotes.helper.PrivacySettingsManager import com.haodustudio.DailyNotes.helper.makeToast +import com.haodustudio.DailyNotes.utils.BitmapUtils import com.haodustudio.DailyNotes.view.activities.base.BaseActivity class PrivacySettingsActivity : BaseActivity() { @@ -22,10 +23,32 @@ class PrivacySettingsActivity : BaseActivity() { super.onCreate(savedInstanceState) setContentView(binding.root) + loadBannerImageSafely() setupListeners() refreshIndicators() } + private fun loadBannerImageSafely() { + try { + binding.privacyBannerImage.post { + val viewWidth = binding.privacyBannerImage.width + val viewHeight = binding.privacyBannerImage.height + + val bitmap = BitmapUtils.decodeSampledBitmapFromResource( + R.drawable.privacy_banner, + viewWidth, + viewHeight + ) + + bitmap?.let { + binding.privacyBannerImage.setImageBitmap(it) + } + } + } catch (e: Exception) { + e.printStackTrace() + } + } + private fun setupListeners() { binding.privacyCardCrash.setOnClickListener { handleSettingClick(PrivacySetting.CRASH_REPORTING) diff --git a/app/src/main/res/drawable/about_banner.jpg b/app/src/main/res/drawable/about_banner.jpg new file mode 100644 index 0000000..72d9b08 Binary files /dev/null and b/app/src/main/res/drawable/about_banner.jpg differ diff --git a/app/src/main/res/drawable/about_banner.png b/app/src/main/res/drawable/about_banner.png deleted file mode 100644 index ddf5c48..0000000 Binary files a/app/src/main/res/drawable/about_banner.png and /dev/null differ diff --git a/app/src/main/res/layout/activity_about_software.xml b/app/src/main/res/layout/activity_about_software.xml index 9e82ca0..e875d30 100644 --- a/app/src/main/res/layout/activity_about_software.xml +++ b/app/src/main/res/layout/activity_about_software.xml @@ -62,7 +62,6 @@ android:adjustViewBounds="true" android:contentDescription="@string/about_banner_content_description" android:scaleType="centerCrop" - android:src="@drawable/about_banner" app:layout_constraintTop_toTopOf="parent" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" diff --git a/app/src/main/res/layout/activity_privacy_settings.xml b/app/src/main/res/layout/activity_privacy_settings.xml index de05cce..84d8a21 100644 --- a/app/src/main/res/layout/activity_privacy_settings.xml +++ b/app/src/main/res/layout/activity_privacy_settings.xml @@ -56,8 +56,7 @@ android:layout_height="wrap_content" android:adjustViewBounds="true" android:contentDescription="@string/privacy_banner_content_desc" - android:scaleType="centerCrop" - android:src="@drawable/privacy_banner" /> + android:scaleType="centerCrop" /> diff --git a/app/src/main/res/layout/dialog_privacy_confirmation.xml b/app/src/main/res/layout/dialog_privacy_confirmation.xml index 93e46f8..14b3e43 100644 --- a/app/src/main/res/layout/dialog_privacy_confirmation.xml +++ b/app/src/main/res/layout/dialog_privacy_confirmation.xml @@ -66,7 +66,8 @@ android:layout_height="48dp" android:background="@drawable/bg_privacy_dialog_button_cancel" android:contentDescription="@string/privacy_confirm_cancel" - android:padding="14dp" + android:padding="8dp" + android:scaleType="fitCenter" android:src="@drawable/privacy_dialog_close" />