Android表情键盘库EmoticonsBoard

QQ和微信的表情键盘大家应该是熟悉的不能再熟悉了吧。公司的一个项目里就需要一个和微信类似的表情键盘功能,在GitHub上找了一下,发现了XhsEmoticonsKeyboard。使用过一段时间后发现了一些问题。其中最大的一个问题就是:表情数据不能动态刷新。每增加一个表情就需要重新加载所有的数据。针对这个问题,我在XhsEmoticonsKeyboard的基础上做了一些改进,就有了EmoticonsBoard这个项目。
项目地址在这

效果

操作演示

实际看一下效果最直观


overlook.gif

这个效果和XhsEmoticonsKeyboard的没什么区别

表情数据动态刷新

change_data.gif

这里演示了四种刷新方式

  • 增加表情
  • 删除表情
  • 增加表情包
  • 删除表情包
    这里需要说明一下,什么是表情包?
    表情包在代码中对应的类是EmoticonPack。一个EmoticonPack就代表了一个表情集合。比如上图中屏幕最下方的黄色微笑小图标就代表了一个表情包。

整体结构

screenshot的副本.jpg

EmoticonsBoard就是整个键盘库的核心,和表情展示相关的主要有三个部分:EmoticonsFuncView,EmoticonsIndicator和EmoticonsToolBar。

  • EmoticonsFuncView:展示每一页表情,继承自ViewPager
  • EmoticonsIndicator:表情页指示器
  • EmoticonsToolBar:展示表情包图标

core.jpg

这里我们只看一下表情相关的几个类,如果对其它的类有兴趣,可以下载代码自行研究。

刚才我们说了,展示表情的EmoticonsFuncView继承自ViewPager,既然是ViewPager,那么就应该有Adapter,它就是EmoticonPacksAdapter。
EmoticonPacksAdapter保存的是一个EmoticonPack列表。EmoticonPack刚才已经介绍过了,是表情包。EmoticonPack中有一个Emoticon列表。Emoticon就代表一个表情。EmoticonPack中还有一个属性:iconUri,它就是这个表情包图片的uri。每个EmoticonPack的图标最终会设置到EmoticonsToolBar里。而Emoticon列表就通过EmoticonPacksAdapter以页面的方式展示。
EmoticonsToolBar用来展示表情包图标,同时和可以在首尾增加两个View,比如增加和删除两个按钮。

类图中还有一个重要的接口:pageFactory,它的作用是生成ViewPager中每一页的View。通过它就能实现不同的页面展示。比如每页的行列数,点击表情时的效果,图标的大小等。

使用方法

Gradle

allprojects {
    repositories {
        jcenter()
    }
}

and:

dependencies { 
    compile 'im.ll:emoticonsboard:1.0.0'
}

XML

<github.ll.emotionboard.EmoticonsBoard xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical">

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:orientation="vertical">
        <ListView
            android:layout_width="match_parent"
            android:layout_height="match_parent"/>
    </LinearLayout>

</github.ll.emotionboard.EmoticonsBoard>

EmoticonsBoard继承自RelativeLayout,所以它里面可以包含子View,但是它的子View只能有一个,多于一个的话会报错。
上面的例子就是包含了一个LinearLayout。

设置 Adapter

Java code:

EmoticonsBoard ekBar;

List<EmoticonPack> packs = new ArrayList<EmoticonPack>;
// init packs...

EmoticonPacksAdapter adapter = new EmoticonPacksAdapter(packs);
ekBar.setAdapter(adapter);

这里很简单,重点是如何构建EmoticonPack

构建

kotlin code

fun getEmoji(context: Context): EmoticonPack<Emoticon> {
        val emojiArray = mutableListOf<Emoticon>()

       
        DefEmoticons.sEmojiArray.take(30).mapTo(emojiArray) {
            val emoticon = Emoticon()
            emoticon.code = it.emoji
            emoticon.uri = context.getResourceUri(it.icon)
            return@mapTo emoticon
        }

        val pack = EmoticonPack<Emoticon>()
        pack.emoticons = emojiArray

        pack.iconUri = context.getResourceUri(R.mipmap.icon_emoji)

        val factory = DeleteBtnPageFactory<Emoticon>()
        factory.deleteIconUri = context.getResourceUri(R.mipmap.icon_del)
        factory.line = 3
        factory.row = 7

        pack.pageFactory = factory

        return pack
    }

EmoticonPack有三个属性:emoticons就是表情列表。iconUri是这个表情包的图标,上面提到过。pageFactory是View工厂,上面也讲过。
这段代码首先是构造了一个Emoticon列表,Emoticon只有两个属性:code和uri。code是表情代码,uri是图片uri。
然后设置了EmoticonPack的PageFactory,在这用到的是DeleteBtnPageFactory,DeleteBtnPageFactory的功能就是能展示一个删除按钮。
EmoticonPack的创建很简单。这里有一点要注意一下,Emoticon我只提供了uri一个属性统一表示表情图片的地址。不管这个图片是资源,文件,还是网络图片,用一个uri就够了。这里的uri可以用android定义的也可以自己定义,只要能唯一确定图片的位置就行了。这里我写了一个工具类来获取各种uri:
kotlin code

enum class UriType {
    ASSETS,
    DRAWABLE,
    FILE,
    OTHER;
}

object UriUtils {
    fun getUriType(uri: String): UriType {
        if (uri.startsWith("android.resource:")) {
            return UriType.DRAWABLE
        }

        if (uri.startsWith("file:///android_asset/")) {
            return UriType.ASSETS
        }

        if (uri.startsWith("file://") && !uri.contains("android_asset")) {
            return UriType.FILE
        }

        return UriType.OTHER
    }

    fun getResourceID(context: Context, uri: String) = uri.substring("android.resource://${context.packageName}/".length)

    fun getFilePath(uri: String): String? {
        return if (getUriType(uri) != UriType.FILE) {
            null
        } else {
            uri.substring("file://".length)
        }
    }

    fun getAssetsPath(uri: String): String? {
        return if (getUriType(uri) != UriType.ASSETS) {
            null
        } else {
            uri.substring("file:///android_asset/".length)
 

现在主流的图片加载库,比如Glide,都可以通过uri加载图片。如果你觉得还是用资源id方便,那么你也可以自己扩展Emoticon。
那么表情图标具体是在哪个地方加载呢?没错,就是在PageFactory里。

刷新数据

kotlin code

// modify mEmojiPack
mEmojiPack.isDataChanged = true
adapter?.notifyDataSetChanged()

非常简单,mEmojiPack数据改变后首先标记isDataChanged为true,然后调用notifyDataSetChanged方法就可以了。

这里注意,isDataChanged必须要设置为true

总结

以上就是整个项目的简单介绍。如果有兴趣就去下载代码看看吧。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,496评论 6 501
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,407评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,632评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,180评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,198评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,165评论 1 299
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,052评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,910评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,324评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,542评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,711评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,424评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,017评论 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,668评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,823评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,722评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,611评论 2 353