使用RecyclerView簡單快捷地擼一個直播公屏出來

前言

雖然如今直播已經沒有了當前那麼火爆,可是仍然是不少App盈利收入的一個重要功能,像如今的網易新聞客戶端、抖音短視頻等都有引入直播這個功能,而公屏是直播的一個重要工具,因此咱們瞭解一下公屏的實現也是有點必要的,公屏的實現能夠有挺多作法的,可是就目前來說,我認爲比較快捷的方式就是使用RecyclerView來實現,僅此獻上拙見。git

功能

公屏最簡單的就是經過一個可滑動的列表進行展現用戶發送出來的消息,固然,通常都是經過服務器給客戶端推送單條或者一組數據,而後客戶端再把新來的數據添加到原有的數據源上,再刷新列表。固然,咱們能夠給公屏支持多樣式的消息,使公屏看起來更加豐富;其次,咱們能夠把數據源增長大小的限制,防止數據池過大致使內存暴增;再之,能夠給數據源增長緩存功能,把收集回來的數據放到緩存池中,而後定時把緩存池更新到公屏上,避免對公屏過於頻繁的繪製。github

知足以上功能,基本上就能夠成爲一個及格的公屏了,本文也是基於以上幾個基本的功能點進行分析,而後代碼實現,其中不免會有不合理的地方,但願你們指出。緩存

1.插入數據服務器

支持插入單條或者多條數據,而且插入後在公屏中展現出來。ide

image

2.支持多樣式公屏工具

如上圖所示,目前有四種樣式的公屏,第一種就是系統通知(第一條綠色的直播規則),第二種就是普通的聊天消息,第三種就是送禮消息,第四種就是活動通知(粉紅色的公屏),固然,若是須要更多種,都是能夠繼續增添的。佈局

3.支持公屏大小限制性能

以下圖所示,我限制了公屏數量是100條,當我繼續發送公屏時,數據源的舊數據就會被擦除掉,而後新的數據纔會放到數據池裏面。這樣作能夠避免一些熱門房間的刷公屏致使的內存太高的問題。ui

image

4.緩存池spa

這裏加了緩存池,大概400ms纔會進行數據刷新,因此400ms內進行的插入操做都會先放到緩存池裏面,而後時間到了以後纔會對UI刷新,這樣就能夠避免UI的頻繁繪製致使GPU太高的問題。

實現

首先佈局文件就是一個RecyclerView,以上實現的功能都是對RecyclerView的API進行調用罷了,我就不進行細講,就主要的功能點進行分析。

1.插入數據

插入數據就是在RecyclerView的Adapter對數據源進行增長,而後notify便可。固然,RecyclerView是支持局部更新的,爲了性能問題,咱們在插入數據以後不須要刷新整個數據源,而是隻更新新增的數據便可。

若是隻是插入一條,能夠調用

notifyItemInserted(getItemCount());
複製代碼

若是是插入多條,能夠調用

notifyItemRangeInserted(startPos, addSize);
複製代碼

OK,那插入數據以後,咱們就能夠調用RecyclerView的smoothScrollToPosition(int position)方法,讓數據滾到到你須要的位置,這裏咱們是滾到到最底部,即:

// 獲取底部index
int bottomIndex = mAdapter.getItemCount() - 1;
mChatView.smoothScrollToPosition(bottomIndex);
複製代碼

2.多樣式的公屏

相信你對RecyclerView的getItemType是比較瞭解的,咱們就能夠根據消息的不一樣類型加載不一樣的layout文件,例如:

@Override
public BaseChatViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    switch (viewType) {
        case MyChatMsg.TYPE_NORMAL_TEXT:
            return new NormalChatHolder(LayoutInflater.from(AppUtils.getContext()).inflate(R.layout.layout_normal_text, parent, false));
        case MyChatMsg.TYPE_SYSTEM_NEWS:
            return new SystemNewsHolder(LayoutInflater.from(AppUtils.getContext()).inflate(R.layout.layout_system_news_text, parent, false));
        case MyChatMsg.TYPE_GIFT_MSG:
            return new GiftNewsHolder(LayoutInflater.from(AppUtils.getContext()).inflate(R.layout.layout_gift_text, parent, false));
        default:
            return new HeaderChatHolder(LayoutInflater.from(AppUtils.getContext()).inflate(R.layout.layout_header_text, parent, false));
    }
}
複製代碼

而後,咱們就能夠在不一樣的layout裏面進行不一樣View的定製。因爲咱們上圖中的UI都是比較簡單,因此咱們的layout都是一個TextView便可知足,配合SpannableStringBuilder就能夠作一些富文本的展現。

3.限制公屏大小

這個也是比較簡單,只要在咱們插入數據以後,比較數據源的大小和咱們限制的大小,而後把超出的部分移除掉,而後再更新UI便可,例如:

private void removeOverItems() {
    //獲取數據源大小
    int dataSize = getDataSize();
    //獲取定義的最大限制,如100
    int mMaxChatNum = DEFAULT_MAX_CHAT_NUM;
    if (dataSize > mMaxChatNum) {
        // 計算超出的大小
        int beyondSize = dataSize - mMaxChatNum;
        // 數據源把舊的數據源超出的部分移除
        mDatas.subList(0, beyondSize).clear();
        // 更新UI
        notifyItemRangeRemoved(1, beyondSize);
    }
}
複製代碼

4.緩存池

緩存池其實就是自定義一個Runnable,而後在run()方法內,對Adapter的數據源變動,而後notify一下,這個Runnable是定時重複執行,例如400ms,避免了每插入一條數據就對Adapter進行notify形成的頻繁繪製。詳細代碼請看源碼。

源碼

大概功能介紹完成,若是有興趣深刻了解,能夠移步到SimpleChatView,若是你以爲還OK,能夠給個Star支持一下。

相關文章
相關標籤/搜索