进阶用法

大约 8 分钟

进阶用法

Chat UIKit SDK UI 组件库提供主题、国际化、常用 UI 组件等。除提供默认使用方式外,该组件库支持自定义组件样式和行为。

入口组件

Container 组件提供全局配置和初始化。如果没有使用该组件,则可能导致其它 UI 组件无法正常使用。

Container 提供了自定义参数,如下所示:

属性类型是否必填描述
optionsChatOptionsType复合参数集合,和 ChatOptions 一致,必填参数只有 appKey
languageLanguageCode语言码。指定 UI 组件显示内容的语言。例如:中文、英文。
translateLanguageLanguageCode语言码。指定 消息翻译的目标语言。例如:中文、英文。
palettePalette主题调色板。主题会从调色板中选择颜色和样式搭配组成主题。
themeTheme主题。默认提供明暗两种主题。
fontFamilystring自定义 UI 组件字体的样式。
emojiFontFamilystring自定义 emoji 表情字体的样式。
headerFontFamilystring自定义 导航栏 字体的样式。
releaseAreaReleaseArea设置发布区域。例如:全球、中国。
formatTimeobject会话列表、聊天页面的时间格式化回调通知。如果没有提供则使用默认格式化。
recallTimeoutnumber回调超时时间。单位为毫秒。默认 120 秒。
groupobject创建群组选择成员数的最大数目。默认 1000.
conversationDetailConversationDetailType聊天页面的配置集合。详见对应定义。
avatarobject全局头像样式设置。支持边框圆角设置。
inputobject全局输入组件样式设置。支持边框圆角设置。
alertobject全局警告框组件样式设置。支持边框圆角设置。
onInitLanguageSetfunction注册语言包的回调通知。可以自定义语言包。
onInitializedfunction注册初始化完成的回调通知。
onUsersHandlerfunction注册获取用户数据的回调通知。如果用户提供该接口,那么用户的头像和昵称通过该接口获取。如果没有提供则使用默认。
onGroupsHandlerfunction注册获取群组数据的回调通知。如果用户提供该接口,那么群组的头像和昵称通过该接口获取。如果没有提供则使用默认。
onChangeStatusfunction注册 Presence 状态回调通知。如果用户提供该接口,将收到用户的状态变更通知。
enableTranslateboolean是否启用翻译功能。如果启用,还需要后台开启。
enableThreadboolean是否启用话题功能。如果启用,还需要后台开启。
enableReactionboolean是否启用表情回复功能。如果启用,还需要后台开启。
enablePresenceboolean是否启用状态订阅功能。如果启用,还需要后台开启。
enableAVMeetingboolean是否启用音视频通话功能。如果启用,还需要后台开启。 默认开启。

IM SDK 组件

ChatService 组件是对 Chat SDK 的封装,可以简化调用逻辑、返回标准化的结果、触发 UI 变化的事件等。

常用接口包括:登录、登出、添加和删除事件监听、添加和删除 Chat SDK 事件监听等。

核心的常用方法定义如下:

方法类型描述
loginfunction登录。
logoutfunction登出。
autoLoginfunction自动登录。
loginStatefunction当前登录状态。
userIdget获取当前登录用户 ID。
updateDataListfunction主动更新指定数据的头像和昵称。将触发已加载的 UI 组件刷新。

ChatService 提供的方法非常多,详见 GitHubopen in new windowGiteeopen in new window 上的对应定义。

监听器

ChatService 提供常用监听器 EventServiceListenerConnectServiceListener

  • ConnectServiceListener:接收 SDK 与服务器的连接状态变化的通知。
方法描述
onConnected已连接。
onDisconnected断开连接。详见 DisconnectReasonType
  • EventServiceListener:接收指定接口调用的事件变化的通知。
方法描述
onBefore接口调用前的通知。
onFinished接口调用完成的通知。
onError接口调用发生错误的通知。

自定义 hooks

以下介绍常见的 hooks。

修改颜色

通常配合 usePaletteContext 使用,自定义颜色对象,改变组件颜色。

例如:

export function SomeView() {
  const { colors } = usePaletteContext();
  const { getColor } = useColors({
    bg: {
      light: colors.neutral[98],
      dark: colors.neutral[1],
    },
  });
  return (
    <View
      style={{
        backgroundColor: getColor("bg"),
      }}
    />
  );
}

延迟执行任务

如果在超时发生之前再次调用,则继续延迟,直到超时发生在执行任务。

例如:

export function SomeView() {
  const [value, setValue] = React.useState("");
  const { delayExecTask: deferSearch } = useDelayExecTask(
    500,
    React.useCallback((keyword: string) => {
      // 执行搜索
    }, [])
  );
  return (
    <Search
      onCancel={onCancel}
      onChangeText={(v) => {
        setValue(v);
        deferSearch?.(v);
      }}
      value={value}
    />
  );
}

强制刷新 UI

useForceUpdate 提供强制更新。如果组件没有状态,或者需要手动更新,可以使用该 hook。

例如:

export function SomeView() {
  const count = React.useRef(0);
  const { updater } = useForceUpdate();
  return (
    <View style={{ paddingTop: 100, flexGrow: 1 }}>
      <Pressable
        onPress={() => {
          count.current += 1;
          updater();
        }}
      >
        <Text>{"test updater"}</Text>
      </Pressable>
      <Text>{`${count.current}`}</Text>
    </View>
  );
}

获取组件样式属性

useGetStyleProps 提供获取组件样式属性。通常和 getStyleSize 配合使用。

例如:

export function SomeView(props) {
  const { containerStyle } = props;
  const { getStyleSize } = useGetStyleProps();
  const { width: propsWidth } = getStyleSize(containerStyle);
  const { checkType } = useCheckType();
  if (propsWidth) {
    checkType(propsWidth, "number");
  }
  return <View style={{ width: propsWidth }} />;
}

获取键盘高度

useKeyboardHeight 获取用户的键盘高度。

提示

至少需要弹出一次键盘才能获取高度。

获取用户权限

usePermissions 请求 UI 组件库需要的权限。

例如:

export function SomeView() {
  const { getPermission } = usePermissions();
  React.useEffect(() => {
    getPermission({
      onResult: (isSuccess: boolean) => {
        // 获取权限成功。
      },
    });
  }, [getPermission]);
  return <View />;
}

事件通知

目前 Chat UIKit SDK UI 组件库主要提供两种事件通知方式:

  1. SDK 事件通知:Chat SDK 转发的事件,侧重于数据变化。
  • ConnectServiceListener:监听 SDK 和服务器的连接变化的通知。
  • MessageServiceListener:监听消息相关的通知。
  • ConversationListener:监听会话相关的通知。
  • GroupServiceListener:监听群组相关的通知。
  • ContactServiceListener:监听联系人相关的通知。
  • PresenceServiceListener:监听用户状态订阅的通知。
  • CustomServiceListener:监听自定义的通知。
  • MultiDeviceStateListener:监听多设备相关的通知。
  • EventServiceListener:监听事件通知,例如,添加好友之前的通知、添加好友成功的通知和添加好友失败的通知。
  1. UI 事件通知:应用主动行为触发可能导致列表 item 增加、删除和变更、列表刷新和列表重载。例如,在群详情页面修改了群名称,会话列表组件的页面也会更新。很多情况下,单个界面行可能会导致多个 UI 组件发生的变化。详见 UIListener
  • UIConversationListListener:监听会话列表的变更的通知。
  • UIContactListListener:监听联系人列表的变更的通知。
  • UIGroupListListener:监听群组列表的变更的通知。
  • UIGroupParticipantListListener:监听群成员列表的变更的通知。
  • UINewRequestListListener:监听好友请求列表的变更的通知。

自定义 SDK 数据模型

ChatServiceImpl 类即 SDK 数据模型,对 IM SDK 进行封装。用户可以继承 ChatServiceImpl 对 IM SDK 中的方法进行自定义,例如,UIKit 示例应用通过自定义 ChatServiceImpl 类,实现了重载添加好友的方法,支持通过搜索手机号获取用户信息。

class ChatServiceDemo extends ChatServiceImpl {
  constructor() {
    super();
  }
// 自定义添加好友方法:实现调用 REST API
  override addNewContact(params: {
    userId: string;
    reason?: string;
    onResult?: ResultCallback<void>;
  }): void {
    const processAsync = async () => {
      const chatUserName = await this.client.getCurrentUsername();
      const userToken = await this.client.getAccessToken();

      // 先请求 app server API,通过手机号获取用户 ID
      RestApi.requestGetUserByPhone({
        phone: params.userId,
        chatUserName: chatUserName ?? '',
        userToken: userToken ?? '',
      })
        .then((result) => {
          if (result.isOk || result.value?.code === 200) {
            // 通过调用 IM SDK API 添加好友
            super.addNewContact(
              params && ({ userId: result.value?.chatUserName } as any)
            );
          } else {
            params.onResult?.({ isOk: false, error: result.error });
          }
        })
        .catch((error) => {
          params.onResult?.({ isOk: false, error });
        });
    };
    processAsync();
  }
}

自定义图片消息预览组件

如果内部提供的图片预览组件样式不能满足需求,UIKit 支持自定义该组件。

type Props = NativeStackScreenProps<RootScreenParamsList>;
export function ImageMessagePreviewScreen(props: Props) {
  // ImagePreviewDemo 为自定义的图片预览组件
  return <ImageMessagePreview imagePreviewComponent={ImagePreviewDemo} />;
}

自定义组件 ImagePreviewDemo 的示例如下:

import React from 'react';
import { Dimensions, StyleSheet } from 'react-native';
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, {
  useAnimatedStyle,
  useSharedValue,
  withSpring,
} from 'react-native-reanimated';

import type { ImagePreviewProps } from 'react-native-chat-uikit';

const { width: SCREEN_WIDTH, height: SCREEN_HEIGHT } = Dimensions.get('window');

export const ImagePreviewDemo: React.FC<ImagePreviewProps> = ({
  source,
  onClicked,
  onDupClicked,
  onLongPress,
}) => {
  const composedGesture = {}; // 自定义手势处理
  const animatedStyle = {}; // 自定义动画样式
  return (
    <GestureDetector gesture={composedGesture}>
      <Animated.View style={[styles.container]}>
        <Animated.Image
          source={source}
          style={[styles.image, animatedStyle]}
          resizeMode="contain"
        />
      </Animated.View>
    </GestureDetector>
  );
};

// 自定义组件样式
const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#000',
  },
  image: {
    width: SCREEN_WIDTH,
    height: SCREEN_HEIGHT,
  },
});

自定义视频消息预览组件

如果内部提供的图片预览组件样式不能满足需求,UIKit 支持自定义该组件。

type Props = NativeStackScreenProps<RootScreenParamsList>;
export function VideoMessagePreviewScreen(props: Props) {
  // VideoPreviewDemo 为自定义的短视频预览组件
  return <VideoMessagePreview videoPreviewComponent={VideoPreviewDemo} />;
}

自定义组件 VideoPreviewDemo 的示例如下:

import React, { forwardRef, useImperativeHandle, useRef } from 'react';
import { Image, ImageStyle, Pressable, ViewStyle } from 'react-native';
import Video, { VideoRef } from 'react-native-video';

export interface VideoPreviewProps {
  source: { uri: string } | number;
  thumbnailUrl?: string;
  videoStyle?: ViewStyle;
  thumbnailStyle?: ImageStyle;
  onClicked?: () => void;
  onLongPress?: () => void;
  onError?: (error: any) => void;
}

export interface VideoPreviewRef {
  seek: (time: number, tolerance?: number) => void;
  resume: () => void;
  pause: () => void;
  presentFullscreenPlayer?: () => void;
}

export const VideoPreviewDemo = forwardRef<VideoPreviewRef, VideoPreviewProps>(
  (props, ref) => {
    const {
      source,
      videoStyle,
      thumbnailStyle,
      thumbnailUrl,
      onClicked,
      onLongPress,
      onError,
    } = props;
    const videoRef = useRef<VideoRef>(null);
    useImperativeHandle(
      ref, // 自定义视频控制器
      () => ({
        seek: (time: number, tolerance?: number) => {
          videoRef.current?.seek(time, tolerance);
        },
        resume: () => {
          videoRef.current?.resume();
        },
        pause: () => {
          videoRef.current?.pause();
        },
        presentFullscreenPlayer: () => {
          videoRef.current?.presentFullscreenPlayer?.();
        },
      }),
      []
    );
    return (
      <Pressable onPress={onClicked} onLongPress={onLongPress}>
        <Video
          ref={videoRef}
          source={source as any}
          resizeMode={'contain'}
          style={videoStyle}
          onError={onError}
        />
        {/* 自定义视频缩略图 */}
        {thumbnailUrl ? (
          <Image
            source={{ uri: thumbnailUrl }}
            style={[{ position: 'absolute' }, thumbnailStyle]}
          />
        ) : null}
      </Pressable>
    );
  }
);
上次编辑于: