针对readme部分做性能优化,目标帧率120达成
This commit is contained in:
@@ -11,6 +11,7 @@ import android.os.Build
|
|||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.provider.DocumentsContract
|
import android.provider.DocumentsContract
|
||||||
import android.provider.OpenableColumns
|
import android.provider.OpenableColumns
|
||||||
|
import android.view.Window // 🟢 必须包含
|
||||||
import androidx.activity.ComponentActivity
|
import androidx.activity.ComponentActivity
|
||||||
import androidx.annotation.NonNull
|
import androidx.annotation.NonNull
|
||||||
import io.flutter.embedding.android.FlutterActivity
|
import io.flutter.embedding.android.FlutterActivity
|
||||||
@@ -32,6 +33,9 @@ class MainActivity : FlutterActivity() {
|
|||||||
private val PREF_NAME = "battery_health_prefs"
|
private val PREF_NAME = "battery_health_prefs"
|
||||||
private val KEY_SAF_URI = "saf_uri"
|
private val KEY_SAF_URI = "saf_uri"
|
||||||
|
|
||||||
|
// 🟢 刷新率控制通道
|
||||||
|
private val FRAME_RATE_CHANNEL = "com.lxh.battery_health/frame_rate"
|
||||||
|
|
||||||
private val chargeReceiver = object : BroadcastReceiver() {
|
private val chargeReceiver = object : BroadcastReceiver() {
|
||||||
override fun onReceive(context: Context?, intent: Intent?) {
|
override fun onReceive(context: Context?, intent: Intent?) {
|
||||||
intent ?: return
|
intent ?: return
|
||||||
@@ -74,7 +78,6 @@ class MainActivity : FlutterActivity() {
|
|||||||
try {
|
try {
|
||||||
safUri = Uri.parse(uriString)
|
safUri = Uri.parse(uriString)
|
||||||
android.util.Log.d("BatteryHealth", "通过 MethodChannel 恢复 URI: $safUri")
|
android.util.Log.d("BatteryHealth", "通过 MethodChannel 恢复 URI: $safUri")
|
||||||
// 同时保存到 SharedPreferences
|
|
||||||
val prefs: SharedPreferences = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
val prefs: SharedPreferences = getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
|
||||||
prefs.edit().putString(KEY_SAF_URI, uriString).apply()
|
prefs.edit().putString(KEY_SAF_URI, uriString).apply()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
@@ -222,9 +225,34 @@ class MainActivity : FlutterActivity() {
|
|||||||
return fileUri?.let { readFile(it) }
|
return fileUri?.let { readFile(it) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🟢 刷新率控制方法
|
||||||
|
// 🟢 刷新率控制方法(使用 preferredRefreshRate,兼容性更好)
|
||||||
|
private fun setHighRefreshRate() {
|
||||||
|
try {
|
||||||
|
val params = window.attributes
|
||||||
|
params.preferredRefreshRate = 120f
|
||||||
|
window.attributes = params
|
||||||
|
android.util.Log.d("BatteryHealth", "setHighRefreshRate: 设置 120Hz")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.util.Log.e("BatteryHealth", "setHighRefreshRate 失败: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun clearRefreshRate() {
|
||||||
|
try {
|
||||||
|
val params = window.attributes
|
||||||
|
params.preferredRefreshRate = 0f // 0 表示让系统自动决定
|
||||||
|
window.attributes = params
|
||||||
|
android.util.Log.d("BatteryHealth", "clearRefreshRate: 恢复默认")
|
||||||
|
} catch (e: Exception) {
|
||||||
|
android.util.Log.e("BatteryHealth", "clearRefreshRate 失败: ${e.message}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
override fun configureFlutterEngine(@NonNull flutterEngine: FlutterEngine) {
|
||||||
super.configureFlutterEngine(flutterEngine)
|
super.configureFlutterEngine(flutterEngine)
|
||||||
|
|
||||||
|
// 充电状态通道
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL)
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHARGE_CHANNEL)
|
||||||
.setMethodCallHandler { call, result ->
|
.setMethodCallHandler { call, result ->
|
||||||
if (call.method == "getChargeState") {
|
if (call.method == "getChargeState") {
|
||||||
@@ -247,6 +275,7 @@ class MainActivity : FlutterActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SAF 通道
|
||||||
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL)
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, SAF_CHANNEL)
|
||||||
.setMethodCallHandler { call, result ->
|
.setMethodCallHandler { call, result ->
|
||||||
when (call.method) {
|
when (call.method) {
|
||||||
@@ -283,6 +312,23 @@ class MainActivity : FlutterActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 🟢 刷新率控制通道
|
||||||
|
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, FRAME_RATE_CHANNEL)
|
||||||
|
.setMethodCallHandler { call, result ->
|
||||||
|
when (call.method) {
|
||||||
|
"setHighRefreshRate" -> {
|
||||||
|
setHighRefreshRate()
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
"clearRefreshRate" -> {
|
||||||
|
clearRefreshRate()
|
||||||
|
result.success(true)
|
||||||
|
}
|
||||||
|
else -> result.notImplemented()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 事件通道
|
||||||
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL)
|
EventChannel(flutterEngine.dartExecutor.binaryMessenger, EVENT_CHANNEL)
|
||||||
.setStreamHandler(
|
.setStreamHandler(
|
||||||
object : EventChannel.StreamHandler {
|
object : EventChannel.StreamHandler {
|
||||||
|
|||||||
+134
-30
@@ -1,4 +1,5 @@
|
|||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
|
import 'package:flutter/services.dart'; // 🟢 添加这一行
|
||||||
import 'package:flutter/services.dart' show rootBundle;
|
import 'package:flutter/services.dart' show rootBundle;
|
||||||
import 'package:flutter_markdown/flutter_markdown.dart';
|
import 'package:flutter_markdown/flutter_markdown.dart';
|
||||||
|
|
||||||
@@ -9,15 +10,85 @@ class ChangelogPage extends StatefulWidget {
|
|||||||
State<ChangelogPage> createState() => _ChangelogPageState();
|
State<ChangelogPage> createState() => _ChangelogPageState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class _ChangelogPageState extends State<ChangelogPage> {
|
class _ChangelogPageState extends State<ChangelogPage>
|
||||||
|
with SingleTickerProviderStateMixin {
|
||||||
|
// ---------- 数据 ----------
|
||||||
String _markdownContent = '';
|
String _markdownContent = '';
|
||||||
bool _isLoading = true;
|
bool _isLoading = true;
|
||||||
String? _error;
|
String? _error;
|
||||||
|
Widget? _cachedMarkdown;
|
||||||
|
|
||||||
|
// ---------- 滚动 ----------
|
||||||
|
final ScrollController _scrollController = ScrollController();
|
||||||
|
double _scrollProgress = 0.0;
|
||||||
|
bool _isScrolling = false;
|
||||||
|
|
||||||
|
// ---------- 呼吸动画(维持高刷感知) ----------
|
||||||
|
late AnimationController _breathController;
|
||||||
|
late Animation<double> _breathAnimation;
|
||||||
|
|
||||||
|
// ---------- 原生刷新率控制 ----------
|
||||||
|
// 🟢 改用 static final(不是 const)
|
||||||
|
static final _frameRateChannel = MethodChannel(
|
||||||
|
'com.lxh.battery_health/frame_rate',
|
||||||
|
);
|
||||||
|
|
||||||
|
// ---------- 生命周期 ----------
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
_loadChangelog();
|
_loadChangelog();
|
||||||
|
|
||||||
|
// 进入页面时申请高刷(原生层)
|
||||||
|
_frameRateChannel.invokeMethod('setHighRefreshRate');
|
||||||
|
|
||||||
|
// 呼吸动画:让进度条在滚动时轻微抖动,维持高刷感知
|
||||||
|
_breathController = AnimationController(
|
||||||
|
vsync: this,
|
||||||
|
duration: const Duration(milliseconds: 500),
|
||||||
|
)..repeat(reverse: true);
|
||||||
|
|
||||||
|
_breathAnimation = Tween<double>(begin: 0.0, end: 0.03).animate(
|
||||||
|
CurvedAnimation(parent: _breathController, curve: Curves.easeInOut),
|
||||||
|
);
|
||||||
|
|
||||||
|
_scrollController.addListener(() {
|
||||||
|
_updateProgress();
|
||||||
|
final isScrolling = _scrollController.position.isScrollingNotifier.value;
|
||||||
|
if (isScrolling != _isScrolling) {
|
||||||
|
setState(() {
|
||||||
|
_isScrolling = isScrolling;
|
||||||
|
});
|
||||||
|
if (isScrolling) {
|
||||||
|
_breathController.repeat(reverse: true);
|
||||||
|
} else {
|
||||||
|
_breathController.stop();
|
||||||
|
_breathController.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
@override
|
||||||
|
void dispose() {
|
||||||
|
// 离开页面时恢复默认刷新率
|
||||||
|
_frameRateChannel.invokeMethod('clearRefreshRate');
|
||||||
|
|
||||||
|
_scrollController.removeListener(_updateProgress);
|
||||||
|
_scrollController.dispose();
|
||||||
|
_breathController.dispose();
|
||||||
|
super.dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- 方法 ----------
|
||||||
|
void _updateProgress() {
|
||||||
|
if (!_scrollController.hasClients) return;
|
||||||
|
final maxScroll = _scrollController.position.maxScrollExtent;
|
||||||
|
final currentScroll = _scrollController.position.pixels;
|
||||||
|
final progress = maxScroll > 0 ? currentScroll / maxScroll : 0.0;
|
||||||
|
setState(() {
|
||||||
|
_scrollProgress = progress;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _loadChangelog() async {
|
Future<void> _loadChangelog() async {
|
||||||
@@ -26,6 +97,7 @@ class _ChangelogPageState extends State<ChangelogPage> {
|
|||||||
setState(() {
|
setState(() {
|
||||||
_markdownContent = content;
|
_markdownContent = content;
|
||||||
_isLoading = false;
|
_isLoading = false;
|
||||||
|
_cachedMarkdown = _buildMarkdown(content);
|
||||||
});
|
});
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
setState(() {
|
setState(() {
|
||||||
@@ -35,8 +107,56 @@ class _ChangelogPageState extends State<ChangelogPage> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Widget _buildMarkdown(String content) {
|
||||||
|
return Markdown(
|
||||||
|
data: content,
|
||||||
|
shrinkWrap: true,
|
||||||
|
physics: const NeverScrollableScrollPhysics(),
|
||||||
|
styleSheet: MarkdownStyleSheet(
|
||||||
|
h1: const TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 24,
|
||||||
|
fontWeight: FontWeight.w700,
|
||||||
|
color: Colors.black,
|
||||||
|
),
|
||||||
|
h2: const TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 20,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
h3: const TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 18,
|
||||||
|
fontWeight: FontWeight.w600,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
p: const TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
listBullet: const TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 16,
|
||||||
|
color: Colors.black87,
|
||||||
|
),
|
||||||
|
code: const TextStyle(
|
||||||
|
fontFamily: 'HarmonyOS_Sans_SC',
|
||||||
|
fontSize: 14,
|
||||||
|
color: Colors.black87,
|
||||||
|
backgroundColor: Color(0xFFF5F5F5),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
// ---------- Build ----------
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
|
final breathOffset = _isScrolling ? _breathAnimation.value : 0.0;
|
||||||
|
final displayProgress = (_scrollProgress + breathOffset).clamp(0.0, 1.0);
|
||||||
|
|
||||||
return Scaffold(
|
return Scaffold(
|
||||||
backgroundColor: Colors.white,
|
backgroundColor: Colors.white,
|
||||||
appBar: AppBar(
|
appBar: AppBar(
|
||||||
@@ -51,6 +171,17 @@ class _ChangelogPageState extends State<ChangelogPage> {
|
|||||||
elevation: 0,
|
elevation: 0,
|
||||||
foregroundColor: Colors.black,
|
foregroundColor: Colors.black,
|
||||||
iconTheme: const IconThemeData(color: Colors.black),
|
iconTheme: const IconThemeData(color: Colors.black),
|
||||||
|
bottom: PreferredSize(
|
||||||
|
preferredSize: const Size.fromHeight(2.0),
|
||||||
|
child: LinearProgressIndicator(
|
||||||
|
value: displayProgress,
|
||||||
|
backgroundColor: Colors.grey.shade200,
|
||||||
|
valueColor: const AlwaysStoppedAnimation<Color>(
|
||||||
|
Color.fromRGBO(157, 212, 237, 1.0),
|
||||||
|
),
|
||||||
|
minHeight: 2.0,
|
||||||
|
),
|
||||||
|
),
|
||||||
),
|
),
|
||||||
body: _isLoading
|
body: _isLoading
|
||||||
? const Center(child: CircularProgressIndicator())
|
? const Center(child: CircularProgressIndicator())
|
||||||
@@ -65,36 +196,9 @@ class _ChangelogPageState extends State<ChangelogPage> {
|
|||||||
),
|
),
|
||||||
)
|
)
|
||||||
: SingleChildScrollView(
|
: SingleChildScrollView(
|
||||||
|
controller: _scrollController,
|
||||||
padding: const EdgeInsets.all(16.0),
|
padding: const EdgeInsets.all(16.0),
|
||||||
child: Markdown(
|
child: _cachedMarkdown ?? const SizedBox.shrink(),
|
||||||
data: _markdownContent,
|
|
||||||
shrinkWrap: true,
|
|
||||||
physics: const NeverScrollableScrollPhysics(),
|
|
||||||
styleSheet: MarkdownStyleSheet(
|
|
||||||
h1: const TextStyle(
|
|
||||||
fontFamily: 'HarmonyOS_Sans_SC',
|
|
||||||
fontSize: 24,
|
|
||||||
fontWeight: FontWeight.w700,
|
|
||||||
color: Colors.black,
|
|
||||||
),
|
|
||||||
h2: const TextStyle(
|
|
||||||
fontFamily: 'HarmonyOS_Sans_SC',
|
|
||||||
fontSize: 20,
|
|
||||||
fontWeight: FontWeight.w600,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
p: const TextStyle(
|
|
||||||
fontFamily: 'HarmonyOS_Sans_SC',
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
listBullet: const TextStyle(
|
|
||||||
fontFamily: 'HarmonyOS_Sans_SC',
|
|
||||||
fontSize: 16,
|
|
||||||
color: Colors.black87,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user