IMKitThemeManager

public class IMKitThemeManager

融云主题管理器

用于管理融云 IM SDK 的主题切换和资源适配,支持多主题动态切换。

核心功能

  • 内置主题:提供传统主题和欢快主题,欢快主题自动跟随系统深浅色模式
  • 自定义主题:支持开发者扩展和注册自定义主题,支持深浅色模式和主题叠加
  • 系统跟随:自动跟随系统深浅色模式切换(欢快主题及自定义主题)
  • 动态资源:提供动态资源获取方法,根据当前主题和深浅色模式自动选择合适的资源
  • 自动应用:通过 Activity 生命周期回调自动应用主题到所有页面
  • 主题监听:支持监听主题切换事件,方便进行自定义处理

重要说明

关于系统深浅色模式切换:

在部分机型上,系统设置切换深浅色模式后,返回 APP 可能无法及时切换到对应的模式。 这是由于系统原因导致,当前 APP 通过系统方法获取的深浅色状态仍然是旧值,需要重启 APP 才能生效。

解决方案(推荐):

如果您的应用使用了 AppCompat 库,建议在 Application 中通过 AppCompatDelegate.setDefaultNightMode(nightMode) 来设置应用的深浅色模式, 这样可以保证深浅色模式的及时切换。

// 在 Application 的 onCreate 中设置
// 跟随系统
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM);

// 或强制使用浅色模式
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);

// 或强制使用深色模式
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES);

快速开始

// 1. 切换到欢快主题(自动跟随系统深浅色)
IMKitThemeManager.changeInnerTheme(context, IMKitThemeManager.LIVELY_THEME);

// 2. 切换到传统主题
IMKitThemeManager.changeInnerTheme(context, IMKitThemeManager.TRADITION_THEME);

// 3. 添加自定义主题(支持深浅色模式)
IMKitThemeManager.addTheme("CUSTOM_THEME",
    R.style.MyCustomLightTheme,  // 浅色模式样式
    R.style.MyCustomDarkTheme    // 深色模式样式
);

// 4. 切换到自定义主题(基于欢快主题)
IMKitThemeManager.changeCustomTheme(context, "CUSTOM_THEME", IMKitThemeManager.LIVELY_THEME);

// 5. 在代码中动态获取主题资源
int bgResId = IMKitThemeManager.dynamicResource(
    context,
    R.attr.rc_conversation_bg,  // 主题属性
    R.drawable.rc_old_bg        // 默认资源
);
view.setBackgroundResource(bgResId);

// 6. 获取当前主题
String currentTheme = IMKitThemeManager.getCurrentThemeName();

// 7. 判断当前主题类型
if (IMKitThemeManager.TRADITION_THEME.equals(currentTheme)) {
    // 传统主题
}

Types

Link copied to clipboard
public interface OnThemeListener
主题变化监听器接口 用于监听主题切换事件,当主题发生变化时会收到回调通知。 使用示例:
// 创建监听器
OnThemeListener listener = new OnThemeListener() {
    @Override
    public void onThemeChanged(Context context, String oldTheme, String newTheme) {
        Log.d("Theme", "主题从 " + oldTheme + " 切换到 " + newTheme);
        // 可以直接使用传入的 context,无需自己持有引用
        if (context instanceof Activity) {
            ((Activity) context).recreate(); // 重建 Activity 应用新主题
        }
    }
};

// 添加监听器
IMKitThemeManager.addThemeListener(listener);

// 不需要时移除监听器(避免内存泄漏)
IMKitThemeManager.removeThemeListener(listener);

Properties

Link copied to clipboard
public final static String LIVELY_THEME
欢快主题 - 现代化界面,自动跟随系统深浅色模式
Link copied to clipboard
public final static String TRADITION_THEME
传统主题 - 经典的界面设计风格

Functions

Link copied to clipboard
public static void addTheme(String themeType, int lightStyleResId, int darkStyleResId)
添加主题(支持浅色和深色样式) 此方法用于添加自定义主题,支持为主题指定浅色和深色两种样式。系统会根据当前的深浅色模式自动应用对应的样式。 使用场景:
// 添加自定义主题(支持深浅色模式)
IMKitThemeManager.addTheme(
    "CUSTOM_THEME",
    R.style.MyCustomLightTheme,  // 浅色模式样式
    R.style.MyCustomDarkTheme    // 深色模式样式
);

// 添加单一样式主题(浅色和深色使用相同样式)
IMKitThemeManager.addTheme(
    "SIMPLE_THEME",
    R.style.MyTheme,  // 浅色模式样式
    R.style.MyTheme   // 深色模式样式(相同)
);

// 为已有主题追加额外样式(样式叠加)
IMKitThemeManager.addTheme(
    IMKitThemeManager.LIVELY_THEME,
    R.style.MyCustomOverrideLight,  // 浅色模式扩展样式
    R.style.MyCustomOverrideDark    // 深色模式扩展样式
);

// 切换到自定义主题(基于欢快主题)
IMKitThemeManager.changeCustomTheme(context, "CUSTOM_THEME", IMKitThemeManager.LIVELY_THEME);
注意事项:
  • 多次调用此方法添加同一主题时,样式会按顺序叠加应用
  • 后添加的样式属性会覆盖先前的同名属性
  • lightStyleResId 和 darkStyleResId 不能为 0
  • 主题会自动跟随系统深浅色模式切换
Link copied to clipboard
添加主题变化监听器 注册一个监听器以接收主题切换事件通知。当调用 changeInnerThemechangeCustomTheme 方法且主题实际发生变化时,所有已注册的监听器都会收到 onThemeChanged 回调。 使用场景:
  • 需要在主题切换时更新自定义 UI 组件
  • 需要在主题变化时重新加载资源或刷新界面
  • 需要同步主题状态到其他模块
Context 参数的优势:
  • 监听器无需自己持有 Context 引用,避免潜在的内存泄漏
  • 可以直接使用传入的 Context 进行 UI 操作(如 Activity.recreate())
  • 通过 Context 获取资源或调用其他需要 Context 的方法
使用示例:
public class MyActivity extends AppCompatActivity {
    private OnThemeListener themeListener;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // 创建监听器
        themeListener = new OnThemeListener() {
            @Override
            public void onThemeChanged(Context context, String oldTheme, String newTheme) {
                // 使用传入的 context 重新创建 Activity
                if (context instanceof Activity) {
                    ((Activity) context).recreate();
                }
            }
        };

        // 添加监听器
        IMKitThemeManager.addThemeListener(themeListener);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // 移除监听器,避免内存泄漏
        IMKitThemeManager.removeThemeListener(themeListener);
    }
}
注意事项:
  • 同一个监听器对象不会被重复添加
  • 请在合适的时机(如 Activity/Fragment 的 onDestroy)移除监听器,避免内存泄漏
  • 监听器回调在主题切换所在的线程执行,通常是主线程
Link copied to clipboard
public static void changeCustomTheme(Context context, String customThemeType, String baseOnTheme)
切换自定义主题(基于内置主题扩展) 此方法用于切换到自定义主题,并指定一个基础主题。会先应用基础主题的配置,然后再应用自定义主题的配置,从而实现主题的叠加和扩展。 应用顺序:
  1. 先应用 baseOnTheme 对应的所有主题配置
  2. 再应用 customThemeType 对应的所有主题配置
  3. 后应用的配置会覆盖先前的同名属性
使用示例:
// 先添加自定义主题
IMKitThemeManager.addTheme(
    "MY_BLUE_THEME",
    R.style.MyBlueLightTheme,
    R.style.MyBlueDarkTheme
);

// 基于欢快主题,应用自定义主题(先应用欢快主题,再应用自定义主题)
IMKitThemeManager.changeCustomTheme(context, "MY_BLUE_THEME", IMKitThemeManager.LIVELY_THEME);

// 基于传统主题,应用自定义主题
IMKitThemeManager.changeCustomTheme(context, "MY_BLUE_THEME", IMKitThemeManager.TRADITION_THEME);
注意事项:
  • baseOnTheme 只能传递 TRADITION_THEME 或 LIVELY_THEME
  • customThemeType 必须是已通过 addTheme 方法添加的主题
  • 主题切换是全局性的,会影响所有融云 SDK 的 UI 组件
Link copied to clipboard
public static void changeInnerTheme(Context context, String themeType)
切换应用内置主题 切换到指定的内置主题并立即应用到当前上下文。该方法会自动处理以下操作:
  • 验证并设置新的主题类型
  • 根据系统深浅色模式自动选择对应的样式
  • 应用主题到 Application Context(全局生效)
  • 应用主题到当前 Context(立即生效)
  • 通过 ActivityLifecycleCallbacks 自动应用到后续创建的所有 Activity
使用示例:
// 切换到欢快主题(自动跟随系统深浅色)
IMKitThemeManager.changeInnerTheme(context, IMKitThemeManager.LIVELY_THEME);

// 切换到传统主题
IMKitThemeManager.changeInnerTheme(context, IMKitThemeManager.TRADITION_THEME);
注意:主题切换是全局性的,会影响所有融云 SDK 的 UI 组件。如果当前已经是目标主题,方法会自动跳过,避免不必要的重复应用。
Link copied to clipboard
public static int dynamicResource(int newVersionResId, int oldVersionResId)
动态获取主题资源(无需 Context 的快速版本) 此方法是 dynamicResource 的轻量级版本,适用于 XML 布局文件或无需立即解析的场景。
  • 现代化主题:直接返回主题属性 ID,由系统在渲染时自动解析
  • 传统主题:返回固定资源 ID
适用场景:
  • XML 布局文件中需要根据主题类型选择资源的场景(如 android:background)
  • 延迟解析场景,资源 ID 会在后续使用时由 Android 系统自动解析
  • 无法获取 Context 但需要资源 ID 的场景
使用示例:
// 在代码中使用(系统会自动解析属性)
view.setBackgroundResource(
    IMKitThemeManager.dynamicResource(R.attr.rc_bg, R.drawable.rc_old_bg)
);

// 注意:返回的可能是属性 ID,需要系统自动解析
// 如果需要立即获取解析后的资源,请使用带 Context 参数的版本
注意:此方法返回的是属性 ID(现代化主题)或资源 ID(传统主题),如果需要立即使用解析后的实际资源 ID,请使用 dynamicResource 方法。
public static int dynamicResource(Context context, int newVersionAttrId, int oldVersionResId)
动态获取主题资源(基于 Context 解析) 此方法根据当前激活的主题自动选择对应的资源:
  • 现代化主题:从主题属性(Theme Attribute)中动态解析资源 ID,支持多主题切换
  • 传统主题:直接返回指定的固定资源 ID,保持向后兼容
适用场景:
  • 在 Java/Kotlin 代码中动态获取需要主题适配的资源
  • 需要在运行时根据当前主题选择不同资源的场景
  • 自定义 View 或 Adapter 中需要加载主题相关的图片、颜色等资源
使用示例:
// 获取背景资源:现代化主题使用主题属性,传统主题使用固定资源
int bgResId = IMKitThemeManager.dynamicResource(
    context,
    R.attr.rc_conversation_bg,      // 主题属性
    R.drawable.rc_old_bg            // 固定资源(用于传统主题)
);
view.setBackgroundResource(bgResId);

// 获取颜色资源
int colorResId = IMKitThemeManager.dynamicResource(
    context,
    R.attr.rc_text_primary,         // 主题属性
    R.color.rc_old_text_color       // 固定颜色(用于传统主题)
);
textView.setTextColor(getResources().getColor(colorResId));
Link copied to clipboard
public static int getAttrResId(Context context, int attrId)
从主题属性中解析实际的资源 ID 此方法用于将主题属性(Theme Attribute)解析为实际的资源 ID。 使用场景:需要从主题属性中获取实际资源 ID 时使用。
// 解析主题属性得到实际的资源 ID
int drawableResId = IMKitThemeManager.getAttrResId(context, R.attr.rc_conversation_bg);
// drawableResId 现在是实际的 R.drawable.xxx 值
Link copied to clipboard
public static int getColorFromAttrId(Context context, int attrId)
从主题属性中直接获取颜色值 此方法是获取颜色的便捷方法,会自动完成"属性→资源ID→颜色值"的完整解析过程。 使用场景:需要从主题属性中直接获取可用的颜色 int 值。
// 直接获取主题颜色值
int textColor = IMKitThemeManager.getColorFromAttrId(context, R.attr.rc_text_primary);
textView.setTextColor(textColor);

// 等价于以下两步操作:
// int colorResId = IMKitThemeManager.getAttrResId(context, R.attr.rc_text_primary);
// int textColor = context.getResources().getColor(colorResId);
Link copied to clipboard
public static String getCurrentThemeName()
获取当前激活的主题类型 使用示例:
String theme = IMKitThemeManager.getCurrentThemeName();
if (IMKitThemeManager.LIVELY_THEME.equals(theme)) {
    // 当前是欢快主题
}
Link copied to clipboard
public static boolean isTraditionTheme()
判断当前是否为传统主题(内部使用,不推荐外部调用) ⚠️ 注意:此方法仅供 SDK 内部使用,外部不推荐直接调用。 推荐做法:外部应用请使用 getCurrentThemeName 获取当前主题名称,然后自行判断:
String theme = IMKitThemeManager.getCurrentThemeName();
if (IMKitThemeManager.TRADITION_THEME.equals(theme)) {
    // 传统主题逻辑
} else if (IMKitThemeManager.LIVELY_THEME.equals(theme)) {
    // 欢快主题逻辑
} else {
    // 自定义主题逻辑
}
Link copied to clipboard
移除主题变化监听器 移除之前通过 addThemeListener 添加的监听器。 建议在 Activity/Fragment 的生命周期结束时(如 onDestroy)调用此方法,避免内存泄漏。 使用示例:
@Override
protected void onDestroy() {
    super.onDestroy();
    IMKitThemeManager.removeThemeListener(themeListener);
}