我的编程空间,编程开发者的网络收藏夹
学习永远不晚

flutter聊天界面-自定义表情键盘实现

短信预约 -IT技能 免费直播动态提醒
省份

北京

  • 北京
  • 上海
  • 天津
  • 重庆
  • 河北
  • 山东
  • 辽宁
  • 黑龙江
  • 吉林
  • 甘肃
  • 青海
  • 河南
  • 江苏
  • 湖北
  • 湖南
  • 江西
  • 浙江
  • 广东
  • 云南
  • 福建
  • 海南
  • 山西
  • 四川
  • 陕西
  • 贵州
  • 安徽
  • 广西
  • 内蒙
  • 西藏
  • 新疆
  • 宁夏
  • 兵团
手机号立即预约

请填写图片验证码后获取短信验证码

看不清楚,换张图片

免费获取短信验证码

flutter聊天界面-自定义表情键盘实现

flutter聊天界面-自定义表情键盘实现
flutter 是 Google推出并开源的移动应用开发框架,主打跨平台、高保真、高性能。开发者可以通过 Dart语言开发 App,一套代码同时运行在 iOS 和 Android平台。

flutter开发基础腾讯IM的聊天应用,使用的是tencent_im_sdk_plugin插件。使用的是自定义表情。

在这里插入图片描述

一、使用的表情

1.1、自定义表情

这里使用自定义表情,表情列表如下

const emojiUrl = 'https://web.sdk.qcloud.com/im/assets/emoji/';const emojiMap = {  '[NO]': 'emoji_0@2x.png',  '[OK]': 'emoji_1@2x.png',  '[下雨]': 'emoji_2@2x.png',  '[么么哒]': 'emoji_3@2x.png',  '[乒乓]': 'emoji_4@2x.png',  '[便便]': 'emoji_5@2x.png',  '[信封]': 'emoji_6@2x.png',  '[偷笑]': 'emoji_7@2x.png',  '[傲慢]': 'emoji_8@2x.png',  '[再见]': 'emoji_9@2x.png',  '[冷汗]': 'emoji_10@2x.png',  '[凋谢]': 'emoji_11@2x.png',  '[刀]': 'emoji_12@2x.png',  '[删除]': 'emoji_13@2x.png',  '[勾引]': 'emoji_14@2x.png',  '[发呆]': 'emoji_15@2x.png',  '[发抖]': 'emoji_16@2x.png',  '[可怜]': 'emoji_17@2x.png',  '[可爱]': 'emoji_18@2x.png',  '[右哼哼]': 'emoji_19@2x.png',  '[右太极]': 'emoji_20@2x.png',  '[右车头]': 'emoji_21@2x.png',  '[吐]': 'emoji_22@2x.png',  '[吓]': 'emoji_23@2x.png',  '[咒骂]': 'emoji_24@2x.png',  '[咖啡]': 'emoji_25@2x.png',  '[啤酒]': 'emoji_26@2x.png',  '[嘘]': 'emoji_27@2x.png',  '[回头]': 'emoji_28@2x.png',  '[困]': 'emoji_29@2x.png',  '[坏笑]': 'emoji_30@2x.png',  '[多云]': 'emoji_31@2x.png',  '[大兵]': 'emoji_32@2x.png',  '[大哭]': 'emoji_33@2x.png',  '[太阳]': 'emoji_34@2x.png',  '[奋斗]': 'emoji_35@2x.png',  '[奶瓶]': 'emoji_36@2x.png',  '[委屈]': 'emoji_37@2x.png',  '[害羞]': 'emoji_38@2x.png',  '[尴尬]': 'emoji_39@2x.png',  '[左哼哼]': 'emoji_40@2x.png',  '[左太极]': 'emoji_41@2x.png',  '[左车头]': 'emoji_42@2x.png',  '[差劲]': 'emoji_43@2x.png',  '[弱]': 'emoji_44@2x.png',  '[强]': 'emoji_45@2x.png',  '[彩带]': 'emoji_46@2x.png',  '[彩球]': 'emoji_47@2x.png',  '[得意]': 'emoji_48@2x.png',  '[微笑]': 'emoji_49@2x.png',  '[心碎了]': 'emoji_50@2x.png',  '[快哭了]': 'emoji_51@2x.png',  '[怄火]': 'emoji_52@2x.png',  '[怒]': 'emoji_53@2x.png',  '[惊恐]': 'emoji_54@2x.png',  '[惊讶]': 'emoji_55@2x.png',  '[憨笑]': 'emoji_56@2x.png',  '[手枪]': 'emoji_57@2x.png',  '[打哈欠]': 'emoji_58@2x.png',  '[抓狂]': 'emoji_59@2x.png',  '[折磨]': 'emoji_60@2x.png',  '[抠鼻]': 'emoji_61@2x.png',  '[抱抱]': 'emoji_62@2x.png',  '[抱拳]': 'emoji_63@2x.png',  '[拳头]': 'emoji_64@2x.png',  '[挥手]': 'emoji_65@2x.png',  '[握手]': 'emoji_66@2x.png',  '[撇嘴]': 'emoji_67@2x.png',  '[擦汗]': 'emoji_68@2x.png',  '[敲打]': 'emoji_69@2x.png',  '[晕]': 'emoji_70@2x.png',  '[月亮]': 'emoji_71@2x.png',  '[棒棒糖]': 'emoji_72@2x.png',  '[汽车]': 'emoji_73@2x.png',  '[沙发]': 'emoji_74@2x.png',  '[流汗]': 'emoji_75@2x.png',  '[流泪]': 'emoji_76@2x.png',  '[激动]': 'emoji_77@2x.png',  '[灯泡]': 'emoji_78@2x.png',  '[炸弹]': 'emoji_79@2x.png',  '[熊猫]': 'emoji_80@2x.png',  '[爆筋]': 'emoji_81@2x.png',  '[爱你]': 'emoji_82@2x.png',  '[爱心]': 'emoji_83@2x.png',  '[爱情]': 'emoji_84@2x.png',  '[猪头]': 'emoji_85@2x.png',  '[猫咪]': 'emoji_86@2x.png',  '[献吻]': 'emoji_87@2x.png',  '[玫瑰]': 'emoji_88@2x.png',  '[瓢虫]': 'emoji_89@2x.png',  '[疑问]': 'emoji_90@2x.png',  '[白眼]': 'emoji_91@2x.png',  '[皮球]': 'emoji_92@2x.png',  '[睡觉]': 'emoji_93@2x.png',  '[磕头]': 'emoji_94@2x.png',  '[示爱]': 'emoji_95@2x.png',  '[礼品袋]': 'emoji_96@2x.png',  '[礼物]': 'emoji_97@2x.png',  '[篮球]': 'emoji_98@2x.png',  '[米饭]': 'emoji_99@2x.png',  '[糗大了]': 'emoji_100@2x.png',  '[红双喜]': 'emoji_101@2x.png',  '[红灯笼]': 'emoji_102@2x.png',  '[纸巾]': 'emoji_103@2x.png',  '[胜利]': 'emoji_104@2x.png',  '[色]': 'emoji_105@2x.png',  '[药]': 'emoji_106@2x.png',  '[菜刀]': 'emoji_107@2x.png',  '[蛋糕]': 'emoji_108@2x.png',  '[蜡烛]': 'emoji_109@2x.png',  '[街舞]': 'emoji_110@2x.png',  '[衰]': 'emoji_111@2x.png',  '[西瓜]': 'emoji_112@2x.png',  '[调皮]': 'emoji_113@2x.png',  '[象棋]': 'emoji_114@2x.png',  '[跳绳]': 'emoji_115@2x.png',  '[跳跳]': 'emoji_116@2x.png',  '[车厢]': 'emoji_117@2x.png',  '[转圈]': 'emoji_118@2x.png',  '[鄙视]': 'emoji_119@2x.png',  '[酷]': 'emoji_120@2x.png',  '[钞票]': 'emoji_121@2x.png',  '[钻戒]': 'emoji_122@2x.png',  '[闪电]': 'emoji_123@2x.png',  '[闭嘴]': 'emoji_124@2x.png',  '[闹钟]': 'emoji_125@2x.png',  '[阴险]': 'emoji_126@2x.png',  '[难过]': 'emoji_127@2x.png',  '[雨伞]': 'emoji_128@2x.png',  '[青蛙]': 'emoji_129@2x.png',  '[面条]': 'emoji_130@2x.png',  '[鞭炮]': 'emoji_131@2x.png',  '[风车]': 'emoji_132@2x.png',  '[飞吻]': 'emoji_133@2x.png',  '[飞机]': 'emoji_134@2x.png',  '[饥饿]': 'emoji_135@2x.png',  '[香蕉]': 'emoji_136@2x.png',  '[骷髅]': 'emoji_137@2x.png',  '[麦克风]': 'emoji_138@2x.png',  '[麻将]': 'emoji_139@2x.png',  '[鼓掌]': 'emoji_140@2x.png',  '[龇牙]': 'emoji_141@2x.png',};const emojiName = [  '[龇牙]',  '[调皮]',  '[流汗]',  '[偷笑]',  '[再见]',  '[敲打]',  '[擦汗]',  '[猪头]',  '[玫瑰]',  '[流泪]',  '[大哭]',  '[嘘]',  '[酷]',  '[抓狂]',  '[委屈]',  '[便便]',  '[炸弹]',  '[菜刀]',  '[可爱]',  '[色]',  '[害羞]',  '[得意]',  '[吐]',  '[微笑]',  '[怒]',  '[尴尬]',  '[惊恐]',  '[冷汗]',  '[爱心]',  '[示爱]',  '[白眼]',  '[傲慢]',  '[难过]',  '[惊讶]',  '[疑问]',  '[困]',  '[么么哒]',  '[憨笑]',  '[爱情]',  '[衰]',  '[撇嘴]',  '[阴险]',  '[奋斗]',  '[发呆]',  '[右哼哼]',  '[抱抱]',  '[坏笑]',  '[飞吻]',  '[鄙视]',  '[晕]',  '[大兵]',  '[可怜]',  '[强]',  '[弱]',  '[握手]',  '[胜利]',  '[抱拳]',  '[凋谢]',  '[米饭]',  '[蛋糕]',  '[西瓜]',  '[啤酒]',  '[瓢虫]',  '[勾引]',  '[OK]',  '[爱你]',  '[咖啡]',  '[月亮]',  '[刀]',  '[发抖]',  '[差劲]',  '[拳头]',  '[心碎了]',  '[太阳]',  '[礼物]',  '[皮球]',  '[骷髅]',  '[挥手]',  '[闪电]',  '[饥饿]',  '[困]',  '[咒骂]',  '[折磨]',  '[抠鼻]',  '[鼓掌]',  '[糗大了]',  '[左哼哼]',  '[打哈欠]',  '[快哭了]',  '[吓]',  '[篮球]',  '[乒乓]',  '[NO]',  '[跳跳]',  '[怄火]',  '[转圈]',  '[磕头]',  '[回头]',  '[跳绳]',  '[激动]',  '[街舞]',  '[献吻]',  '[左太极]',  '[右太极]',  '[闭嘴]',  '[猫咪]',  '[红双喜]',  '[鞭炮]',  '[红灯笼]',  '[麻将]',  '[麦克风]',  '[礼品袋]',  '[信封]',  '[象棋]',  '[彩带]',  '[蜡烛]',  '[爆筋]',  '[棒棒糖]',  '[奶瓶]',  '[面条]',  '[香蕉]',  '[飞机]',  '[左车头]',  '[车厢]',  '[右车头]',  '[多云]',  '[下雨]',  '[钞票]',  '[熊猫]',  '[灯泡]',  '[风车]',  '[闹钟]',  '[雨伞]',  '[彩球]',  '[钻戒]',  '[沙发]',  '[纸巾]',  '[手枪]',  '[青蛙]',];

1.2、定义自定义表情数据类

这里定义自定义表情的数据类 CommonChatEmoji

class CommonChatEmojiItem {  String? emojiName;  String? url;  CommonChatEmojiItem({required this.emojiName, required this.url});}class CommonChatEmoji {  static List<CommonChatEmojiItem> emojiUrlList() {    return emojiName        .map((item) => CommonChatEmojiItem(            emojiName: item, url: emojiUrl + emojiMap[item]!))        .toList();  }  static bool emojiIsContain(String emojiName) {    bool isContain = false;    CommonChatEmojiItem? emojiItem = CommonChatEmoji.findEmojiItem(emojiName);    if (emojiName.contains(emojiName) && emojiItem != null) {      isContain = true;    }    return isContain;  }  static CommonChatEmojiItem? findEmojiItem(String emojiName) {    List<CommonChatEmojiItem> emojiItemList = CommonChatEmoji.emojiUrlList();    CommonChatEmojiItem? emojiItem;    for(CommonChatEmojiItem item in emojiItemList) {      if (emojiName == item.emojiName) {        emojiItem = item;        break;      }    }    return emojiItem;  }}

二、聊天表情键盘

2.1、实现表情排列Panel键盘

排列表情,使用的是GridView.builder,GridView网格布局是一种常见的布局类型,GridView 组件正是实现了网格布局的组件,
SliverGridDelegate是一个抽象类,定义了GridView Layout相关接口,子类需要通过实现它们来实现具体的布局算法。Flutter中提供了两个SliverGridDelegate的子类SliverGridDelegateWithFixedCrossAxisCount和SliverGridDelegateWithMaxCrossAxisExtent,

            GridView.builder(              gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(                crossAxisCount: 7, //每行三列                childAspectRatio: 1.0, //显示区域宽高相等              ),              itemCount: CommonChatEmoji.emojiUrlList().length,              itemBuilder: (context, index) {                CommonChatEmojiItem emojiItem =                    CommonChatEmoji.emojiUrlList()[index];                return ChatInputEmojiButton(                  emojiItem: emojiItem,                  size: itemSize,                  onEmojiLongPressed: widget.onEmojiLongPressed,                  onEmojiTapPressed: widget.onEmojiTapPressed,                );              },              padding: EdgeInsets.only(                bottom: deleteBarHeight,              ),            ),

排列效果如图所示
在这里插入图片描述

聊天界面的表情Panel的布局完整代码

// 表情输入class ChatInputEmojiPanel extends StatefulWidget {  const ChatInputEmojiPanel({    Key? key,    required this.emojiPanelHeight,    required this.chatInputBarController,    required this.onTextFieldDelete,    required this.onEmojiTapPressed,    required this.onEmojiLongPressed,    required this.onTextFieldSend,  }) : super(key: key);  final double emojiPanelHeight;  final ChatInputBarController chatInputBarController;  final Function onTextFieldDelete;  final Function onTextFieldSend;  final Function(CommonChatEmojiItem emojiItem) onEmojiTapPressed;  final Function(CommonChatEmojiItem emojiItem, Offset globalPosition) onEmojiLongPressed;    State<ChatInputEmojiPanel> createState() => _ChatInputEmojiPanelState();}class _ChatInputEmojiPanelState extends State<ChatInputEmojiPanel> {    void initState() {    // TODO: implement initState    super.initState();  }    void dispose() {    // TODO: implement dispose    super.dispose();  }    Widget build(BuildContext context) {    Size screenSize = MediaQuery.of(context).size;    int crossAxisCount = 7;    double itemSize = screenSize.width / crossAxisCount;    EdgeInsets viewPadding = MediaQuery.of(context).viewPadding;    double emojiCateBarHeight = 50.0 + viewPadding.bottom;    double deleteBarHeight = 50.0;    return Container(      width: screenSize.width,      height: widget.emojiPanelHeight,      decoration: BoxDecoration(        color: ColorUtil.hexColor(0xf7f7f7),      ),      child: Column(        mainAxisAlignment: MainAxisAlignment.center,        crossAxisAlignment: CrossAxisAlignment.center,        children: [          Expanded(            child: Stack(              children: [                GridView.builder(                  gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount(                    crossAxisCount: 7, //每行三列                    childAspectRatio: 1.0, //显示区域宽高相等                  ),                  itemCount: CommonChatEmoji.emojiUrlList().length,                  itemBuilder: (context, index) {                    CommonChatEmojiItem emojiItem =                        CommonChatEmoji.emojiUrlList()[index];                    return ChatInputEmojiButton(                      emojiItem: emojiItem,                      size: itemSize,                      onEmojiLongPressed: widget.onEmojiLongPressed,                      onEmojiTapPressed: widget.onEmojiTapPressed,                    );                  },                  padding: EdgeInsets.only(                    bottom: deleteBarHeight,                  ),                ),                Positioned(                  bottom: 0.0,                  right: 0.0,                  child: ChatInputEmojiDeleteBar(                    height: deleteBarHeight,                    onTextFieldDelete: widget.onTextFieldDelete,                  ),                ),              ],            ),          ),          ChatInputEmojiCateBar(            height: emojiCateBarHeight,            onTextFieldSend: widget.onTextFieldSend,          ),        ],      ),    );  }}// 显示表情Emoji图片class ChatInputEmojiButton extends StatelessWidget {  const ChatInputEmojiButton({    Key? key,    required this.emojiItem,    required this.size,    required this.onEmojiTapPressed,    required this.onEmojiLongPressed,  }) : super(key: key);  final CommonChatEmojiItem emojiItem;  final double size;  final Function(CommonChatEmojiItem emojiItem) onEmojiTapPressed;  final Function(CommonChatEmojiItem emojiItem, Offset globalPosition) onEmojiLongPressed;    Widget build(BuildContext context) {    double iconSize = size;    if (iconSize > 36.0) {      iconSize = 36.0;    }    return ButtonWidget(      width: size,      height: size,      onLongPressStart: (LongPressStartDetails details) {        onEmojiLongPressed(emojiItem, details.globalPosition);      },      onPressed: () {        onEmojiTapPressed(emojiItem);      },      child: ImageHelper.imageNetwork(        imageUrl: "${emojiItem.url}",        fit: BoxFit.cover,        width: iconSize,        height: iconSize,      ),    );  }}

2.2、表情Panel的布局代码。

// 底部表情切换bar与发送按钮class ChatInputEmojiCateBar extends StatefulWidget {  const ChatInputEmojiCateBar({    Key? key,    required this.height,    required this.onTextFieldSend,  }) : super(key: key);  final double height;  final Function onTextFieldSend;    State<ChatInputEmojiCateBar> createState() => _ChatInputEmojiCateBarState();}class _ChatInputEmojiCateBarState extends State<ChatInputEmojiCateBar> {    Widget build(BuildContext context) {    EdgeInsets viewPadding = MediaQuery.of(context).viewPadding;    Size screenSize = MediaQuery.of(context).size;    print("ChatInputEmojiCateBar viewPadding bottom:${viewPadding.bottom}");    return Container(      width: screenSize.width,      height: widget.height,      decoration: BoxDecoration(        color: ColorUtil.hexColor(0xf7f7f7),        border: Border(          bottom: BorderSide(width: 0.0, color: ColorUtil.hexColor(0xffffff)),          left: BorderSide(width: 0.0, color: ColorUtil.hexColor(0xffffff)),          right: BorderSide(width: 0.0, color: ColorUtil.hexColor(0xffffff)),          top: BorderSide(width: 1.0, color: ColorUtil.hexColor(0xf0f0f0)),        ),      ),      child: Column(        mainAxisAlignment: MainAxisAlignment.start,        crossAxisAlignment: CrossAxisAlignment.center,        children: [          Expanded(            child: Row(              mainAxisAlignment: MainAxisAlignment.spaceBetween,              crossAxisAlignment: CrossAxisAlignment.start,              children: [                ButtonWidget(                  margin: EdgeInsets.only(left: 10.0),                  width: 36.0,                  onPressed: () {},                  child: ImageHelper.wrapAssetAtImages(                    "icons/ic_custom_emoji_cate.png",                    fit: BoxFit.cover,                    width: 32.0,                    height: 32.0,                  ),                ),                Expanded(                  child: Container(),                ),                ButtonWidget(                  margin: const EdgeInsets.only(left: 10.0),                  width: 70.0,                  bgColor: ColorUtil.hexColor(0xf7f7f7),                  bgHighlightedColor: ColorUtil.hexColor(0x3b93ff, alpha: 0.35),                  onPressed: () {                    widget.onTextFieldSend();                  },                  child: Text(                    "发送",                    textAlign: TextAlign.center,                    maxLines: 1000,                    overflow: TextOverflow.ellipsis,                    softWrap: true,                    style: TextStyle(                      fontSize: 16,                      fontWeight: FontWeight.w500,                      fontStyle: FontStyle.normal,                      color: ColorUtil.hexColor(0x3b93ff),                      decoration: TextDecoration.none,                    ),                  ),                ),              ],            ),          ),          SizedBox(            height: viewPadding.bottom,          ),        ],      ),    );  }}

删除输入的表情的删除按钮

// 表情键盘底部发送及删除按钮class ChatInputEmojiDeleteBar extends StatefulWidget {  const ChatInputEmojiDeleteBar({    Key? key,    required this.height,    required this.onTextFieldDelete,  }) : super(key: key);  final double height;  final Function onTextFieldDelete;    State<ChatInputEmojiDeleteBar> createState() =>      _ChatInputEmojiDeleteBarState();}class _ChatInputEmojiDeleteBarState extends State<ChatInputEmojiDeleteBar> {    Widget build(BuildContext context) {    return Container(      padding: EdgeInsets.only(right: 10.0),      color: Colors.transparent,      height: widget.height,      child: Row(        mainAxisAlignment: MainAxisAlignment.end,        crossAxisAlignment: CrossAxisAlignment.center,        children: [          ButtonWidget(            onPressed: () {              widget.onTextFieldDelete();            },            child: ImageHelper.wrapAssetAtImages(              "icons/ic_backspace.png",              fit: BoxFit.cover,              width: 42.0,              height: 42.0,            ),          ),        ],      ),    );  }}

2.3、长按预览表情

当我们使用常见的聊天工具的时候,表情基本上都有预览功能,这里实现长按预览表情功能。
预览表情效果如下
在这里插入图片描述

具体代码

/// 表情长按预览功能class ChatInputEmojiPreview extends StatefulWidget {  const ChatInputEmojiPreview({    Key? key,    required this.emojiItem,    required this.width,    required this.height,  }) : super(key: key);  final CommonChatEmojiItem emojiItem;  final double width;  final double height;    State<ChatInputEmojiPreview> createState() => _ChatInputEmojiPreviewState();}class _ChatInputEmojiPreviewState extends State<ChatInputEmojiPreview> {    Widget build(BuildContext context) {    return Container(      child: ChatInputEmojiShowEmoji(        emojiItem: widget.emojiItem,        width: widget.width,        height: widget.height,      ),    );  }}// 显示预览的内容class ChatInputEmojiShowEmoji extends StatelessWidget {  const ChatInputEmojiShowEmoji({    Key? key,    required this.emojiItem,    required this.width,    required this.height,  }) : super(key: key);  final CommonChatEmojiItem emojiItem;  final double width;  final double height;    Widget build(BuildContext context) {    return Container(      width: width,      height: height,      child: Stack(        children: [          ImageHelper.wrapAssetAtImages(            "icons/bg_emoji-preview.png",            width: width,            height: height,          ),          Column(            mainAxisAlignment: MainAxisAlignment.start,            crossAxisAlignment: CrossAxisAlignment.center,            children: [              SizedBox(                height: 25.0,              ),              ImageHelper.imageNetwork(                imageUrl: "${emojiItem.url}",                fit: BoxFit.cover,                width: 60,                height: 60,              ),              SizedBox(                height: 3.0,              ),              Text(                "${emojiItem.emojiName}",                textAlign: TextAlign.center,                maxLines: 1,                overflow: TextOverflow.ellipsis,                style: TextStyle(                  fontSize: 16,                  fontWeight: FontWeight.w500,                  fontStyle: FontStyle.normal,                  color: ColorUtil.hexColor(0x555555),                  decoration: TextDecoration.none,                ),              ),              Expanded(                child: Container(),              ),            ],          ),        ],      ),    );  }}

三、小结

flutter聊天界面-自定义表情键盘实现,主要实现GridView布局表情,自定义预览功能,使用GestureDetector长按功能得到LongPressStartDetails details获得长按的位置,展示表情预览、表情的图片和文本富文本展示-Text.rich(TextSpan(children: textSapns));。

学习记录,每天不停进步。

来源地址:https://blog.csdn.net/gloryFlow/article/details/131592703

免责声明:

① 本站未注明“稿件来源”的信息均来自网络整理。其文字、图片和音视频稿件的所属权归原作者所有。本站收集整理出于非商业性的教育和科研之目的,并不意味着本站赞同其观点或证实其内容的真实性。仅作为临时的测试数据,供内部测试之用。本站并未授权任何人以任何方式主动获取本站任何信息。

② 本站未注明“稿件来源”的临时测试数据将在测试完成后最终做删除处理。有问题或投稿请发送至: 邮箱/279061341@qq.com QQ/279061341

flutter聊天界面-自定义表情键盘实现

下载Word文档到电脑,方便收藏和打印~

下载Word文档

编程热搜

  • Android:VolumeShaper
    VolumeShaper(支持版本改一下,minsdkversion:26,android8.0(api26)进一步学习对声音的编辑,可以让音频的声音有变化的播放 VolumeShaper.Configuration的三个参数 durati
    Android:VolumeShaper
  • Android崩溃异常捕获方法
    开发中最让人头疼的是应用突然爆炸,然后跳回到桌面。而且我们常常不知道这种状况会何时出现,在应用调试阶段还好,还可以通过调试工具的日志查看错误出现在哪里。但平时使用的时候给你闹崩溃,那你就欲哭无泪了。 那么今天主要讲一下如何去捕捉系统出现的U
    Android崩溃异常捕获方法
  • android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
    系统的设置–>电池–>使用情况中,统计的能耗的使用情况也是以power_profile.xml的value作为基础参数的1、我的手机中power_profile.xml的内容: HTC t328w代码如下:
    android开发教程之获取power_profile.xml文件的方法(android运行时能耗值)
  • Android SQLite数据库基本操作方法
    程序的最主要的功能在于对数据进行操作,通过对数据进行操作来实现某个功能。而数据库就是很重要的一个方面的,Android中内置了小巧轻便,功能却很强的一个数据库–SQLite数据库。那么就来看一下在Android程序中怎么去操作SQLite数
    Android SQLite数据库基本操作方法
  • ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
    工作的时候为了方便直接打开编辑文件,一些常用的软件或者文件我们会放在桌面,但是在ubuntu20.04下直接直接拖拽文件到桌面根本没有效果,在进入桌面后发现软件列表中的软件只能收藏到面板,无法复制到桌面使用,不知道为什么会这样,似乎并不是很
    ubuntu21.04怎么创建桌面快捷图标?ubuntu软件放到桌面的技巧
  • android获取当前手机号示例程序
    代码如下: public String getLocalNumber() { TelephonyManager tManager =
    android获取当前手机号示例程序
  • Android音视频开发(三)TextureView
    简介 TextureView与SurfaceView类似,可用于显示视频或OpenGL场景。 与SurfaceView的区别 SurfaceView不能使用变换和缩放等操作,不能叠加(Overlay)两个SurfaceView。 Textu
    Android音视频开发(三)TextureView
  • android获取屏幕高度和宽度的实现方法
    本文实例讲述了android获取屏幕高度和宽度的实现方法。分享给大家供大家参考。具体分析如下: 我们需要获取Android手机或Pad的屏幕的物理尺寸,以便于界面的设计或是其他功能的实现。下面就介绍讲一讲如何获取屏幕的物理尺寸 下面的代码即
    android获取屏幕高度和宽度的实现方法
  • Android自定义popupwindow实例代码
    先来看看效果图:一、布局
  • Android第一次实验
    一、实验原理 1.1实验目标 编程实现用户名与密码的存储与调用。 1.2实验要求 设计用户登录界面、登录成功界面、用户注册界面,用户注册时,将其用户名、密码保存到SharedPreference中,登录时输入用户名、密码,读取SharedP
    Android第一次实验

目录