Android 實現(xiàn)仿微博列表九宮圖

先看效果圖

  1. IMG_2494.PNG
  2. IMG_2495.PNG

需求

  1. 類似微博九宮圖一樣违帆,單張圖片顯示比例(此處具體先省略)
  2. 2張圖和4張圖片顯示 格子列數(shù)為4宮格

需要的庫

com.github.bumptech.glide:glide:4.9.0
com.github.CymChad:BaseRecyclerViewAdapterHelper:2.9.44

大概思路

  1. 數(shù)據(jù)請求 省略(我用的酷安的數(shù)據(jù)熱門評論接口)
  2. 根據(jù)評論中圖片數(shù)據(jù)設(shè)置圖片RecyclerViewSpanCount描扯,每一條評論都含有一個展示圖片的RecycleView,再設(shè)置ItemDecoration
  3. 對不同的圖片個數(shù)的ViewHolder設(shè)置不同的type

上代碼

CmsStyleCommentNormalViewHolder:類中

if (!TextUtils.isEmpty(dateItem.pic) && dateItem.picArr != null) {
                val nineImageList = arrayListOf<NineImageBean>()
                var spanCount = 0
                dateItem.picArr?.let { it1 ->
                    spanCount = if (it1.size == 1) {
                        1
                    } else if (it1.size == 2 || it1.size == 4) {
                        2
                    } else {
                        3
                    }
                    it1.forEachIndexed { index, s ->
                        nineImageList.add(NineImageBean().apply {
                            this.isGif = StringUtils.isGifUrl(s)
                            this.url = s
                            this.positionAdapter = index
                            if (spanCount == 1) {
                                this.type = NineImageBean.LargeImageType
                            } else {
                                this.type = NineImageBean.SmallImageType
                            }
                        })
                    }
                }
                val cmsChildImageNineAdapter: CmsChildImageNineAdapter
                val adapterTag = nineImageRv.tag
                if (adapterTag is CmsChildImageNineAdapter) {
                    cmsChildImageNineAdapter = adapterTag
                } else {
                    cmsChildImageNineAdapter = CmsChildImageNineAdapter(ArrayList())
                    nineImageRv.apply {
                        cmsChildImageNineAdapter.bindToRecyclerView(this)
                        this.isNestedScrollingEnabled = false
                        this.setHasFixedSize(true)
                        this.tag = cmsChildImageNineAdapter
                    }
                    cmsChildImageNineAdapter.setOnItemClickListener { adapter, _, position ->
                        val pictureBeanList = ArrayList<PictureBean>()
                        for (item in adapter.data) {
                            if (item is NineImageBean) {
                                pictureBeanList.add(PictureBean().apply {
                                    this.originalUrl = item.url
                                })
                            }
                        }
                        if (pictureBeanList.isNotEmpty()) {
                            LaunchNormalUtils.startPictureActivity(mContext, pictureBeanList, position)
                        }
                    }
                }
                nineImageRv.apply {
                    this.layoutParams.width = if (spanCount == 4 || spanCount == 2) {
                        singleImageWidth.toInt() * 2
                    } else if (spanCount == 1) {
                        ViewGroup.LayoutParams.WRAP_CONTENT
                    } else {
                        ViewGroup.LayoutParams.MATCH_PARENT
                    }
                    for (i in 0 until this.itemDecorationCount) {
                        this.removeItemDecorationAt(i)
                    }
                    this.layoutManager = GridLayoutManager(mContext, spanCount)
                    this.addItemDecoration(RecyclerViewItemDividerHelper.getNineImageAdapterDividerItem(mContext, spanCount, this, 16f))
                }
                cmsChildImageNineAdapter.setNewData(nineImageList)
                nineImageRv.visibility = View.VISIBLE
            } else {
                nineImageRv.visibility = View.GONE
            }
        }
  • 上面代碼位于一個評論ViewHolder類
  • CmsChildImageNineAdapter:
class CmsChildImageNineAdapter(data: MutableList<NineImageBean>?): BaseMultiItemQuickAdapter<NineImageBean, BaseViewHolder>(data) {
    init {
        addItemType(NineImageBean.emptyType, R.layout.item_multi_not_found)
        addItemType(NineImageBean.SmallImageType, R.layout.item_multi_nine_image_small)
        addItemType(NineImageBean.LargeImageType, R.layout.item_multi_nine_image_large)
    }

    override fun convert(helper: BaseViewHolder, item: NineImageBean) {
        var associatedObject = helper.associatedObject
        when (item.itemType) {
            NineImageBean.SmallImageType -> {
                if (associatedObject == null) {
                    associatedObject = NineSmallViewHolder(mContext, helper)
                    helper.associatedObject = associatedObject
                }
                if (associatedObject is NineSmallViewHolder) {
                    associatedObject.updateView(item)
                }
            }
            NineImageBean.LargeImageType -> {
                if (associatedObject == null) {
                    associatedObject = NineLargeViewHolder(mContext, helper)
                    helper.associatedObject = associatedObject
                }
                if (associatedObject is NineLargeViewHolder) {
                    associatedObject.updateView(item)
                }
            }
        }
    }
  • NineSmallViewHolder
class NineSmallViewHolder(val mContext: Context, val baseViewHolder: BaseViewHolder) : IBaseViewMultiHolder<NineImageBean>(baseViewHolder.itemView) {
    private val smallImageIv: CustomGifImageView = itemView.findViewById(R.id.small_image_iv)
    private val smallGifFlagRtv: RoundTextView = itemView.findViewById(R.id.small_gif_flag_rtv)
    private var gifHandler: GifHandler? = null

    private inner class ParamTag {
        var isFirstFrame = true
        var isAlwaysPlayerGif = true
    }

    override fun updateView(dateItem: NineImageBean) {
        super.updateView(dateItem)
        smallImageIv.apply {
            if (dateItem.isGif && !this.isGifRunning()) {
                smallGifFlagRtv.visibility = View.VISIBLE
            } else {
                smallGifFlagRtv.visibility = View.GONE
            }
            if (!dateItem.isGif) {
                loadNormalImage(dateItem)
            } else {
                if (!this.isGifRunning()) {
                    loadGifImage(ParamTag().apply {
                        this.isFirstFrame = true
                    })
                }
            }
        }
    }

 private fun loadNormalImage(dateItem: NineImageBean) {
        ImageLoader.Builder(mContext)
                .getRequestManager()
                .asBitmap()
                .load(dateItem.url)
                .apply(ImageLoader.defaultRequestOptions(R.color.placeholder_color))
                .into(smallImageIv)
    }
  • 我粘貼的事項目中代碼涯贞,我也懶得刪除了,所以CustomGifImageView 就用imageview就行,也不用判斷GIF加載什么的贷洲,就gilde直接加載就OK
  • NineLargeViewHolder
class NineLargeViewHolder(val mContext: Context, val baseViewHolder: BaseViewHolder) : IBaseViewMultiHolder<NineImageBean>(baseViewHolder.itemView) {
    private val largeIv: MatrixScaleImageView = itemView.findViewById(R.id.large_iv)
    private val largeFlagRtv: RoundTextView = itemView.findViewById(R.id.large_flag_rtv)
    private val gifFlagRtv: RoundTextView = itemView.findViewById(R.id.gif_flag_rtv)

    override fun updateView(dateItem: NineImageBean) {
        super.updateView(dateItem)
        largeFlagRtv.visibility = View.GONE
        gifFlagRtv.visibility = View.GONE
        dateItem.picImageSize?.apply {
            val matrixScaleHelper = MatrixScaleHelper(this[0].toFloat(), this[1].toFloat())
            largeIv.apply {
                gifFlagRtv.visibility = if (dateItem.isGif && !this.isGifRunning()) {
                    View.VISIBLE
                } else {
                    View.GONE
                }
                largeFlagRtv.visibility = if (matrixScaleHelper.isLongBitmap && !dateItem.isGif) {
                    View.VISIBLE
                } else {
                    View.GONE
                }
                if (!dateItem.isGif) {
                    loadNormalImage()
                } else {
                    if (!this.isGifRunning()) {
                        loadGifImage(true)
                    }
                }
            }
        }
    }

    override fun changeTheme(theme: Theme) {
        super.changeTheme(theme)
        largeFlagRtv.setBackgroundColor(ColorUtils.getThemeColor(mContext, R.attr.colorAccent))
        gifFlagRtv.setBackgroundColor(ColorUtils.getThemeColor(mContext, R.attr.colorAccent))
    }

    fun startPlayerGif() {
        val nineImageBean = getItemTagDate() as? NineImageBean ?: return
        if (nineImageBean.isGif) {
            loadGifImage(false)
        }
    }

    fun stopPlayerGif() {
        val nineImageBean = getItemTagDate() as? NineImageBean ?: return
        if (nineImageBean.isGif) {
            loadGifImage(true)
        }
    }

    private fun loadNormalImage() {
        val nineImageBean = getItemTagDate()
        if (nineImageBean is NineImageBean) {
            val picImageSize = nineImageBean.picImageSize ?: return
            val matrixScaleHelper = MatrixScaleHelper(picImageSize[0].toFloat(), picImageSize[1].toFloat())
            ImageLoader.Builder(mContext, nineImageBean.url)
                    .setTransition(ImageLoader.defaultTransitionOptions())
                    .setRequestOptions(ImageLoader.defaultRequestOptions(R.color.placeholder_color)
                            .transform(matrixScaleHelper.getCropTransformation(largeIv)))
                    .build(largeIv)
        }
    }
  • GIF 代碼我就不粘貼了盏筐,其中一部分围俘,這個viewHolder比較特殊,一問微博中單個圖片比較特殊琢融,是有比例顯示的界牡,大長圖顯示上半部分,普通圖片等比例縮放,MatrixScaleHelper類就是一個等比縮放類
  • 粘貼的是部分代碼和思路漾抬,全部代碼整理起來比較麻煩宿亡,實現(xiàn)效果比較多
  • 后面會陸續(xù)貼出來實現(xiàn)微博九宮圖 GIF圖片依次播放,還有單張圖片等比縮放纳令,以及換膚(不重啟應(yīng)用)過程中RecycleView的處理

最后看一樣我實現(xiàn)的效果圖吧

Screenshot_20190225-163519.png
Screenshot_20190225-163534.png
Screenshot_20190225-163557.png
Screenshot_20190225-163649.png
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末挽荠,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子平绩,更是在濱河造成了極大的恐慌圈匆,老刑警劉巖,帶你破解...
    沈念sama閱讀 206,723評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件捏雌,死亡現(xiàn)場離奇詭異跃赚,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)性湿,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,485評論 2 382
  • 文/潘曉璐 我一進(jìn)店門纬傲,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人肤频,你說我怎么就攤上這事叹括。” “怎么了宵荒?”我有些...
    開封第一講書人閱讀 152,998評論 0 344
  • 文/不壞的土叔 我叫張陵汁雷,是天一觀的道長净嘀。 經(jīng)常有香客問我,道長摔竿,這世上最難降的妖魔是什么面粮? 我笑而不...
    開封第一講書人閱讀 55,323評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮继低,結(jié)果婚禮上熬苍,老公的妹妹穿的比我還像新娘。我一直安慰自己袁翁,他們只是感情好柴底,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,355評論 5 374
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著粱胜,像睡著了一般柄驻。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上焙压,一...
    開封第一講書人閱讀 49,079評論 1 285
  • 那天鸿脓,我揣著相機(jī)與錄音,去河邊找鬼涯曲。 笑死野哭,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的幻件。 我是一名探鬼主播拨黔,決...
    沈念sama閱讀 38,389評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼绰沥!你這毒婦竟也來了篱蝇?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 37,019評論 0 259
  • 序言:老撾萬榮一對情侶失蹤徽曲,失蹤者是張志新(化名)和其女友劉穎零截,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體秃臣,經(jīng)...
    沈念sama閱讀 43,519評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡瞻润,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,971評論 2 325
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了甜刻。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 38,100評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡正勒,死狀恐怖得院,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情章贞,我是刑警寧澤祥绞,帶...
    沈念sama閱讀 33,738評論 4 324
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響蜕径,放射性物質(zhì)發(fā)生泄漏两踏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,293評論 3 307
  • 文/蒙蒙 一兜喻、第九天 我趴在偏房一處隱蔽的房頂上張望梦染。 院中可真熱鬧,春花似錦朴皆、人聲如沸帕识。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,289評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽肮疗。三九已至,卻和暖如春扒接,著一層夾襖步出監(jiān)牢的瞬間伪货,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,517評論 1 262
  • 我被黑心中介騙來泰國打工钾怔, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留碱呼,地道東北人。 一個月前我還...
    沈念sama閱讀 45,547評論 2 354
  • 正文 我出身青樓蒂教,卻偏偏與公主長得像巍举,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子凝垛,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,834評論 2 345

推薦閱讀更多精彩內(nèi)容