IMKit Theme Manager
融云主题管理器
用于管理融云 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);
Content copied to clipboard
快速开始
// 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)) {
// 传统主题
}
Content copied to clipboard
Types
Link copied to clipboard
主题变化监听器接口 用于监听主题切换事件,当主题发生变化时会收到回调通知。 使用示例:
// 创建监听器
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);
Content copied to clipboard
Properties
Functions
Link copied to clipboard
添加主题(支持浅色和深色样式) 此方法用于添加自定义主题,支持为主题指定浅色和深色两种样式。系统会根据当前的深浅色模式自动应用对应的样式。 使用场景:注意事项:
// 添加自定义主题(支持深浅色模式)
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);
Content copied to clipboard
- 多次调用此方法添加同一主题时,样式会按顺序叠加应用
- 后添加的样式属性会覆盖先前的同名属性
- lightStyleResId 和 darkStyleResId 不能为 0
- 主题会自动跟随系统深浅色模式切换
Link copied to clipboard
添加主题变化监听器 注册一个监听器以接收主题切换事件通知。当调用 changeInnerTheme 或 changeCustomTheme 方法且主题实际发生变化时,所有已注册的监听器都会收到 onThemeChanged 回调。 使用场景:注意事项:
- 需要在主题切换时更新自定义 UI 组件
- 需要在主题变化时重新加载资源或刷新界面
- 需要同步主题状态到其他模块
- 监听器无需自己持有 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);
}
}
Content copied to clipboard
- 同一个监听器对象不会被重复添加
- 请在合适的时机(如 Activity/Fragment 的 onDestroy)移除监听器,避免内存泄漏
- 监听器回调在主题切换所在的线程执行,通常是主线程
Link copied to clipboard
切换自定义主题(基于内置主题扩展) 此方法用于切换到自定义主题,并指定一个基础主题。会先应用基础主题的配置,然后再应用自定义主题的配置,从而实现主题的叠加和扩展。 应用顺序:注意事项:
- 先应用 baseOnTheme 对应的所有主题配置
- 再应用 customThemeType 对应的所有主题配置
- 后应用的配置会覆盖先前的同名属性
// 先添加自定义主题
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);
Content copied to clipboard
- baseOnTheme 只能传递 TRADITION_THEME 或 LIVELY_THEME
- customThemeType 必须是已通过 addTheme 方法添加的主题
- 主题切换是全局性的,会影响所有融云 SDK 的 UI 组件
Link copied to clipboard
切换应用内置主题 切换到指定的内置主题并立即应用到当前上下文。该方法会自动处理以下操作: 注意:主题切换是全局性的,会影响所有融云 SDK 的 UI 组件。如果当前已经是目标主题,方法会自动跳过,避免不必要的重复应用。
- 验证并设置新的主题类型
- 根据系统深浅色模式自动选择对应的样式
- 应用主题到 Application Context(全局生效)
- 应用主题到当前 Context(立即生效)
- 通过 ActivityLifecycleCallbacks 自动应用到后续创建的所有 Activity
// 切换到欢快主题(自动跟随系统深浅色)
IMKitThemeManager.changeInnerTheme(context, IMKitThemeManager.LIVELY_THEME);
// 切换到传统主题
IMKitThemeManager.changeInnerTheme(context, IMKitThemeManager.TRADITION_THEME);
Content copied to clipboard
Link copied to clipboard
动态获取主题资源(无需 Context 的快速版本) 此方法是 dynamicResource 的轻量级版本,适用于 XML 布局文件或无需立即解析的场景。 注意:此方法返回的是属性 ID(现代化主题)或资源 ID(传统主题),如果需要立即使用解析后的实际资源 ID,请使用 dynamicResource 方法。
- 现代化主题:直接返回主题属性 ID,由系统在渲染时自动解析
- 传统主题:返回固定资源 ID
- XML 布局文件中需要根据主题类型选择资源的场景(如 android:background)
- 延迟解析场景,资源 ID 会在后续使用时由 Android 系统自动解析
- 无法获取 Context 但需要资源 ID 的场景
// 在代码中使用(系统会自动解析属性)
view.setBackgroundResource(
IMKitThemeManager.dynamicResource(R.attr.rc_bg, R.drawable.rc_old_bg)
);
// 注意:返回的可能是属性 ID,需要系统自动解析
// 如果需要立即获取解析后的资源,请使用带 Context 参数的版本
Content copied to clipboard
动态获取主题资源(基于 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));
Content copied to clipboard
Link copied to clipboard
从主题属性中解析实际的资源 ID 此方法用于将主题属性(Theme Attribute)解析为实际的资源 ID。 使用场景:需要从主题属性中获取实际资源 ID 时使用。
// 解析主题属性得到实际的资源 ID
int drawableResId = IMKitThemeManager.getAttrResId(context, R.attr.rc_conversation_bg);
// drawableResId 现在是实际的 R.drawable.xxx 值
Content copied to clipboard
Link copied to clipboard
从主题属性中直接获取颜色值 此方法是获取颜色的便捷方法,会自动完成"属性→资源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);
Content copied to clipboard
Link copied to clipboard
获取当前激活的主题类型 使用示例:
String theme = IMKitThemeManager.getCurrentThemeName();
if (IMKitThemeManager.LIVELY_THEME.equals(theme)) {
// 当前是欢快主题
}
Content copied to clipboard
Link copied to clipboard
判断当前是否为传统主题(内部使用,不推荐外部调用) ⚠️ 注意:此方法仅供 SDK 内部使用,外部不推荐直接调用。 推荐做法:外部应用请使用 getCurrentThemeName 获取当前主题名称,然后自行判断:
String theme = IMKitThemeManager.getCurrentThemeName();
if (IMKitThemeManager.TRADITION_THEME.equals(theme)) {
// 传统主题逻辑
} else if (IMKitThemeManager.LIVELY_THEME.equals(theme)) {
// 欢快主题逻辑
} else {
// 自定义主题逻辑
}
Content copied to clipboard
Link copied to clipboard
移除主题变化监听器 移除之前通过 addThemeListener 添加的监听器。 建议在 Activity/Fragment 的生命周期结束时(如 onDestroy)调用此方法,避免内存泄漏。 使用示例:
@Override
protected void onDestroy() {
super.onDestroy();
IMKitThemeManager.removeThemeListener(themeListener);
}
Content copied to clipboard