鸿蒙主题切换器:基于Cordova与动态资源管理的实现

本文将介绍如何开发一个能够动态切换鸿蒙主题颜色的Cordova应用,结合鸿蒙的ResourceManager API和跨设备同步技术,实现类似《鸿蒙跨端U同步》中多设备显示一致性的主题切换功能。

一、技术架构概述

我们的主题切换器将采用以下技术架构:

  1. ​Cordova框架​​:作为跨平台应用的基础
  2. ​自定义Cordova插件​​:桥接JS与鸿蒙原生API
  3. ​鸿蒙ResourceManager​​:动态管理主题资源
  4. ​U同步技术​​:实现多设备主题同步

二、自定义Cordova插件实现

首先,我们需要创建一个自定义Cordova插件来调用鸿蒙的ResourceManager API。

1. 插件JavaScript接口 (www/HarmonyThemeSwitcher.js)

var exec = require('cordova/exec');

var HarmonyThemeSwitcher = {
    setThemeColor: function(color, successCallback, errorCallback) {
        exec(successCallback, errorCallback, 'HarmonyThemeSwitcher', 'setThemeColor', [color]);
    },
    getCurrentTheme: function(successCallback, errorCallback) {
        exec(successCallback, errorCallback, 'HarmonyThemeSwitcher', 'getCurrentTheme', []);
    },
    registerThemeChangeListener: function(callback) {
        exec(callback, null, 'HarmonyThemeSwitcher', 'registerThemeChangeListener', []);
    }
};

module.exports = HarmonyThemeSwitcher;

2. 鸿蒙原生实现 (HarmonyThemeSwitcher.java)

package org.apache.cordova.harmonytheme;

import org.apache.cordova.CordovaPlugin;
import org.apache.cordova.CallbackContext;
import org.apache.cordova.PluginResult;
import org.json.JSONArray;
import org.json.JSONException;

import ohos.app.Context;
import ohos.app.AbilityContext;
import ohos.global.resource.ResourceManager;
import ohos.global.resource.Resource;
import ohos.global.resource.NotExistException;
import ohos.global.resource.WrongTypeException;
import ohos.hiviewdfx.HiLog;
import ohos.hiviewdfx.HiLogLabel;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class HarmonyThemeSwitcher extends CordovaPlugin {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, "HarmonyThemeSwitcher");
    private CallbackContext themeChangeCallback;
    
    @Override
    public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
        if ("setThemeColor".equals(action)) {
            String color = args.getString(0);
            this.setThemeColor(color, callbackContext);
            return true;
        } else if ("getCurrentTheme".equals(action)) {
            this.getCurrentTheme(callbackContext);
            return true;
        } else if ("registerThemeChangeListener".equals(action)) {
            this.registerThemeChangeListener(callbackContext);
            return true;
        }
        return false;
    }
    
    private void setThemeColor(String color, CallbackContext callbackContext) {
        try {
            AbilityContext context = (AbilityContext) cordova.getContext();
            ResourceManager resourceManager = context.getResourceManager();
            
            // 这里简化处理,实际应修改资源文件或使用动态资源管理
            int colorValue = parseColor(color);
            
            // 保存主题设置
            saveThemePreference(color);
            
            // 通知所有设备主题变更
            notifyThemeChanged(color);
            
            callbackContext.success(color);
        } catch (Exception e) {
            HiLog.error(LABEL, "setThemeColor error: " + e.getMessage());
            callbackContext.error(e.getMessage());
        }
    }
    
    private void getCurrentTheme(CallbackContext callbackContext) {
        try {
            String currentTheme = loadThemePreference();
            callbackContext.success(currentTheme);
        } catch (Exception e) {
            callbackContext.error(e.getMessage());
        }
    }
    
    private void registerThemeChangeListener(CallbackContext callbackContext) {
        this.themeChangeCallback = callbackContext;
        // 保持回调活跃
        PluginResult pluginResult = new PluginResult(PluginResult.Status.NO_RESULT);
        pluginResult.setKeepCallback(true);
        callbackContext.sendPluginResult(pluginResult);
    }
    
    // 模拟U同步通知主题变更
    private void notifyThemeChanged(String newColor) {
        if (themeChangeCallback != null) {
            PluginResult result = new PluginResult(PluginResult.Status.OK, newColor);
            result.setKeepCallback(true);
            themeChangeCallback.sendPluginResult(result);
        }
    }
    
    // 解析颜色字符串
    private int parseColor(String color) {
        // 实现颜色解析逻辑
        return 0; // 返回实际颜色值
    }
    
    // 保存主题偏好设置
    private void saveThemePreference(String color) {
        // 实现保存逻辑
    }
    
    // 加载主题偏好设置
    private String loadThemePreference() {
        // 实现加载逻辑
        return "#FFFFFF"; // 默认返回白色
    }
}

三、前端界面实现

创建一个简单的HTML界面来切换和显示主题:

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>鸿蒙主题切换器</title>
    <style>
        body {
            transition: background-color 0.3s ease;
            padding: 20px;
        }
        .color-picker {
            display: flex;
            flex-wrap: wrap;
            gap: 10px;
            margin: 20px 0;
        }
        .color-option {
            width: 50px;
            height: 50px;
            border-radius: 50%;
            cursor: pointer;
            border: 2px solid #eee;
        }
        .device-list {
            margin-top: 30px;
        }
        .device-item {
            padding: 10px;
            margin: 5px 0;
            background: #f5f5f5;
            border-radius: 5px;
        }
    </style>
</head>
<body>
    <h1>鸿蒙主题切换器</h1>
    
    <div>
        <h3>当前主题颜色: <span id="currentTheme">#FFFFFF</span></h3>
    </div>
    
    <div class="color-picker">
        <div class="color-option" style="background: #FF5252;" data-color="#FF5252"></div>
        <div class="color-option" style="background: #FF4081;" data-color="#FF4081"></div>
        <div class="color-option" style="background: #E040FB;" data-color="#E040FB"></div>
        <div class="color-option" style="background: #7C4DFF;" data-color="#7C4DFF"></div>
        <div class="color-option" style="background: #536DFE;" data-color="#536DFE"></div>
        <div class="color-option" style="background: #448AFF;" data-color="#448AFF"></div>
        <div class="color-option" style="background: #40C4FF;" data-color="#40C4FF"></div>
        <div class="color-option" style="background: #18FFFF;" data-color="#18FFFF"></div>
        <div class="color-option" style="background: #64FFDA;" data-color="#64FFDA"></div>
        <div class="color-option" style="background: #69F0AE;" data-color="#69F0AE"></div>
        <div class="color-option" style="background: #B2FF59;" data-color="#B2FF59"></div>
        <div class="color-option" style="background: #EEFF41;" data-color="#EEFF41"></div>
    </div>
    
    <div class="device-list">
        <h3>已连接设备</h3>
        <div id="devicesContainer">
            <!-- 设备列表将通过JS动态生成 -->
        </div>
    </div>
    
    <script src="cordova.js"></script>
    <script>
        document.addEventListener('deviceready', onDeviceReady, false);
        
        function onDeviceReady() {
            console.log('Cordova已准备好');
            
            // 获取当前主题
            HarmonyThemeSwitcher.getCurrentTheme(
                function(currentColor) {
                    document.getElementById('currentTheme').textContent = currentColor;
                    document.body.style.backgroundColor = currentColor;
                },
                function(error) {
                    console.error('获取当前主题失败:', error);
                }
            );
            
            // 注册主题变更监听器
            HarmonyThemeSwitcher.registerThemeChangeListener(function(newColor) {
                document.getElementById('currentTheme').textContent = newColor;
                document.body.style.backgroundColor = newColor;
                console.log('主题已更新为:', newColor);
            });
            
            // 设置颜色选择器点击事件
            const colorOptions = document.querySelectorAll('.color-option');
            colorOptions.forEach(option => {
                option.addEventListener('click', function() {
                    const color = this.getAttribute('data-color');
                    HarmonyThemeSwitcher.setThemeColor(
                        color,
                        function(success) {
                            console.log('主题颜色已设置为:', color);
                        },
                        function(error) {
                            console.error('设置主题颜色失败:', error);
                        }
                    );
                });
            });
            
            // 模拟发现设备
            simulateDeviceDiscovery();
        }
        
        // 模拟U同步设备发现
        function simulateDeviceDiscovery() {
            const devices = [
                { id: 'device1', name: '我的手机', theme: '#FFFFFF' },
                { id: 'device2', name: '我的平板', theme: '#FFFFFF' },
                { id: 'device3', name: '我的手表', theme: '#FFFFFF' }
            ];
            
            const devicesContainer = document.getElementById('devicesContainer');
            devicesContainer.innerHTML = '';
            
            devices.forEach(device => {
                const deviceElement = document.createElement('div');
                deviceElement.className = 'device-item';
                deviceElement.innerHTML = `
                    <h4>${device.name}</h4>
                    <p>主题颜色: <span class="device-theme">${device.theme}</span></p>
                `;
                devicesContainer.appendChild(deviceElement);
            });
        }
    </script>
</body>
</html>

四、鸿蒙U同步实现多设备主题同步

为了实现类似游戏中多设备显示一致性的功能,我们需要利用鸿蒙的分布式能力来同步主题设置。

1. 修改HarmonyThemeSwitcher.java添加分布式能力

// 在HarmonyThemeSwitcher类中添加以下方法

private void setupDistributedDataSync() {
    try {
        AbilityContext context = (AbilityContext) cordova.getContext();
        
        // 创建分布式数据管理器
        DistributedDataManager distributedDataManager = DistributedDataManager.getInstance(context);
        
        // 创建主题同步回调
        DistributedDataManager.SyncCallback syncCallback = new DistributedDataManager.SyncCallback() {
            @Override
            public void onSyncCompleted(String deviceId, String key, SyncStatus status) {
                if (status == SyncStatus.SUCCESS) {
                    HiLog.info(LABEL, "主题同步成功到设备: " + deviceId);
                } else {
                    HiLog.warn(LABEL, "主题同步到设备 " + deviceId + " 失败");
                }
            }
        };
        
        // 注册主题同步
        distributedDataManager.registerSyncCallback("app_theme_color", syncCallback);
        
    } catch (Exception e) {
        HiLog.error(LABEL, "设置分布式数据同步失败: " + e.getMessage());
    }
}

private void syncThemeToAllDevices(String color) {
    try {
        AbilityContext context = (AbilityContext) cordova.getContext();
        DistributedDataManager distributedDataManager = DistributedDataManager.getInstance(context);
        
        // 将主题颜色同步到所有设备
        distributedDataManager.putString("app_theme_color", color);
        distributedDataManager.sync("app_theme_color", DistributedDataManager.SyncMode.PUSH);
        
    } catch (Exception e) {
        HiLog.error(LABEL, "同步主题到所有设备失败: " + e.getMessage());
    }
}

// 在setThemeColor方法中调用同步
private void setThemeColor(String color, CallbackContext callbackContext) {
    try {
        // ...原有代码...
        
        // 同步到所有设备
        syncThemeToAllDevices(color);
        
        callbackContext.success(color);
    } catch (Exception e) {
        HiLog.error(LABEL, "setThemeColor error: " + e.getMessage());
        callbackContext.error(e.getMessage());
    }
}

2. 添加设备发现和状态更新

// 在前端JavaScript中添加设备状态更新功能

function updateDeviceTheme(deviceId, themeColor) {
    const devices = document.querySelectorAll('.device-item');
    devices.forEach(device => {
        if (device.dataset.id === deviceId) {
            const themeSpan = device.querySelector('.device-theme');
            if (themeSpan) {
                themeSpan.textContent = themeColor;
                device.style.backgroundColor = themeColor + '20'; // 添加透明度
            }
        }
    });
}

// 模拟设备主题更新
function simulateDeviceThemeUpdates() {
    const devices = [
        { id: 'device1', name: '我的手机' },
        { id: 'device2', name: '我的平板' },
        { id: 'device3', name: '我的手表' }
    ];
    
    // 每隔一段时间随机更新一个设备的主题
    setInterval(() => {
        const randomDevice = devices[Math.floor(Math.random() * devices.length)];
        const randomColor = '#' + Math.floor(Math.random()*16777215).toString(16);
        updateDeviceTheme(randomDevice.id, randomColor);
    }, 5000);
}

// 在onDeviceReady中调用
simulateDeviceThemeUpdates();

五、动态资源管理实现

为了实现真正的主题切换,我们需要在鸿蒙端实现动态资源管理:

1. 创建资源管理工具类

public class DynamicResourceManager {
    private static final HiLogLabel LABEL = new HiLogLabel(HiLog.LOG_APP, 0, "DynamicResourceManager");
    private static DynamicResourceManager instance;
    private ResourceManager resourceManager;
    private Map<String, Integer> dynamicColors = new HashMap<>();
    
    private DynamicResourceManager(AbilityContext context) {
        this.resourceManager = context.getResourceManager();
    }
    
    public static synchronized DynamicResourceManager getInstance(AbilityContext context) {
        if (instance == null) {
            instance = new DynamicResourceManager(context);
        }
        return instance;
    }
    
    public void addDynamicColor(String colorName, String colorValue) {
        try {
            int color = Color.parseColor(colorValue);
            dynamicColors.put(colorName, color);
        } catch (Exception e) {
            HiLog.error(LABEL, "添加动态颜色失败: " + e.getMessage());
        }
    }
    
    public int getColor(String colorName) {
        if (dynamicColors.containsKey(colorName)) {
            return dynamicColors.get(colorName);
        }
        
        try {
            return resourceManager.getElement(colorName).getColor();
        } catch (Exception e) {
            HiLog.error(LABEL, "获取颜色资源失败: " + e.getMessage());
            return 0; // 返回默认颜色
        }
    }
    
    public void applyDynamicTheme() {
        // 这里可以实现更复杂的主题应用逻辑
        // 例如更新所有Activity的界面等
    }
}

2. 修改插件使用动态资源

// 在HarmonyThemeSwitcher.java中修改setThemeColor方法

private void setThemeColor(String color, CallbackContext callbackContext) {
    try {
        AbilityContext context = (AbilityContext) cordova.getContext();
        DynamicResourceManager drm = DynamicResourceManager.getInstance(context);
        
        // 添加动态颜色
        drm.addDynamicColor("primary_color", color);
        drm.addDynamicColor("secondary_color", calculateSecondaryColor(color));
        drm.applyDynamicTheme();
        
        // 保存和同步
        saveThemePreference(color);
        syncThemeToAllDevices(color);
        
        callbackContext.success(color);
    } catch (Exception e) {
        HiLog.error(LABEL, "setThemeColor error: " + e.getMessage());
        callbackContext.error(e.getMessage());
    }
}

private String calculateSecondaryColor(String primaryColor) {
    // 实现计算辅助颜色的逻辑
    return "#" + Integer.toHexString(Color.parseColor(primaryColor) & 0x00FFFFFF | 0x77000000);
}

六、总结与扩展

通过上述实现,我们创建了一个完整的鸿蒙主题切换器,具有以下特点:

  1. ​跨平台能力​​:基于Cordova框架,可扩展至其他平台
  2. ​动态主题切换​​:利用鸿蒙ResourceManager实现实时主题变更
  3. ​多设备同步​​:借助鸿蒙分布式能力实现主题跨设备同步
  4. ​可扩展性​​:可轻松添加更多主题属性和样式

进一步改进方向:

  1. 实现更复杂的主题系统,包括字体、间距等更多属性
  2. 添加主题持久化功能,记录用户偏好
  3. 优化分布式同步性能,减少网络开销
  4. 添加主题预览和自定义主题创建功能

这个实现展示了如何结合Cordova的跨平台能力和鸿蒙的原生特性,创造出既灵活又强大的应用功能。

Logo

讨论HarmonyOS开发技术,专注于API与组件、DevEco Studio、测试、元服务和应用上架分发等。

更多推荐