RecyclerView之間隔線

1,系統自帶的間割線

系統自帶的間割線實現方法很簡單,看下面的代碼:

     rv_content.layoutManager = LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false)
     rv_content.adapter = rcAdapter
     rv_content.addItemDecoration(DividerItemDecoration(this, DividerItemDecoration.VERTICAL)) 

這樣就實現了間割線的添加,效果如下:

device-2017-06-23-171259.png

上面的效果,是將方向設置為:LinearLayoutManager.VERTICAL,那么將方向設置為LinearLayoutManager.HORIZONTAL,此時效果為:

device-2017-06-23-171206.png

上面的顯示方式為LinearLayoutManager,那么將顯示方式設置為GridLayoutManager時,也就是:

rv_content.layoutManager = GridLayoutManager(this, 3,GridLayoutManager.VERTICAL, false)

此時的效果為:

device-2017-06-23-171637.png

此時,應該察覺到,系統提供的間隔線類DividerItemDecoration只能適用于一些簡單的情形。當布局復雜時,系統的間隔線就不能滿足我們的要求了,此時我們就要自己動手來寫一個自己的間隔線或者使用第三方的間隔線。

2, 自定義間隔線

通過閱讀代碼,我們可以發現系統間割線DividerItemDecoration是繼承RecyclerView.ItemDecoration并實現了onDraw()getItemOffsets()方法 。
那么我們照著DividerItemDecoration來寫自己的間隔線類MyItemDecoration,代碼如下:

class MyItemDecoration(context: Context) : RecyclerView.ItemDecoration(){
    private var mOrientation: Int? = null
    private var dividerLine: Drawable? = null

    init {
        val typedArray = context.obtainStyledAttributes(ATTRS)
        dividerLine = typedArray!!.getDrawable(0)
        typedArray.recycle()
    }

    override fun onDraw(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        drawHorizontalLine(c, parent, state)
        drawVerticalLine(c, parent, state)
    }

    override fun getItemOffsets(outRect: Rect?, view: View?, parent: RecyclerView?, state: RecyclerView.State?) {
        val spanCount = getSpanCount(parent)
        val childCount = parent!!.adapter.itemCount
        val itemPosition = ((view!!.layoutParams)as RecyclerView.LayoutParams).viewLayoutPosition
        if (isLastRow(parent, itemPosition, spanCount, childCount)){
            outRect!!.set(0, 0, dividerLine!!.intrinsicWidth, 0)
        }else if (isLastColum(parent, itemPosition, spanCount, childCount)){
            outRect!!.set(0, 0, 0, dividerLine!!.intrinsicHeight)
        }else{
            outRect!!.set(0, 0, dividerLine!!.intrinsicWidth, dividerLine!!.intrinsicHeight)
        }

    }

    /**
     * 畫豎線
     */
    private fun  drawVerticalLine(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        for (i in 0..(parent!!.childCount - 1)){
            val child: View = parent.getChildAt(i)

            //獲取child布局參數
            val params: RecyclerView.LayoutParams = child.layoutParams as RecyclerView.LayoutParams
            val left = child.right + params.rightMargin
            val right = left + dividerLine!!.intrinsicWidth
            val top = child.top - params.topMargin
            val bottom = child.bottom + params.bottomMargin
            dividerLine!!.setBounds(left, top, right, bottom)
            dividerLine!!.draw(c)
        }
    }

    /**
     * 畫橫線
     */
    private fun  drawHorizontalLine(c: Canvas?, parent: RecyclerView?, state: RecyclerView.State?) {
        for (i in 0..(parent!!.childCount - 1)){
            val child: View = parent.getChildAt(i)

            val params: RecyclerView.LayoutParams = child.layoutParams as RecyclerView.LayoutParams
            val left = child.left - params.leftMargin
            val right = child.right + params.rightMargin+ dividerLine!!.intrinsicWidth
            val top = child.bottom + params.bottomMargin
            val bottom = top + dividerLine!!.intrinsicHeight
            dividerLine!!.setBounds(left, top, right, bottom)
            dividerLine!!.draw(c)
        }
    }

    /**
     * 獲取列數
     */
    private fun getSpanCount(parent: RecyclerView?): Int{
        var spanCount: Int = -1
        val layoutManager: RecyclerView.LayoutManager = parent!!.layoutManager
        if (layoutManager is GridLayoutManager){
            spanCount = layoutManager.spanCount
        }else if(layoutManager is StaggeredGridLayoutManager){
            spanCount = layoutManager.spanCount
        }
        return spanCount
    }

    /**
     * 判定是否為最后一列
     */
    private fun isLastColum(parent: RecyclerView?, pos: Int, spanCount: Int, childCount: Int): Boolean{
        val layoutManager: RecyclerView.LayoutManager = parent!!.layoutManager
        if (layoutManager is GridLayoutManager){
            if ((pos + 1)% spanCount == 0) {//如果是最后一列,則不在繪制右邊
                return true
            }
        }else if(layoutManager is StaggeredGridLayoutManager){
            val orientation = layoutManager.orientation
            if (orientation == StaggeredGridLayoutManager.VERTICAL){
                if ((pos + 1)% spanCount == 0) {//如果是最后一列,則不在繪制右邊
                    return true
                }
            }else{
                val childNum = childCount - childCount % spanCount
                if (pos >= childNum) {
                    return true
                }
            }
        }
        return false
    }

    /**
     * 判定是否為最后一行
     */
    private fun isLastRow(parent: RecyclerView?, pos: Int, spanCount: Int, childCount: Int): Boolean{
        val layoutManager: RecyclerView.LayoutManager = parent!!.layoutManager
        if (layoutManager is GridLayoutManager){
            val childNum = if(childCount % spanCount == 0) childCount - spanCount else childCount - childCount % spanCount
            if (pos >= childNum) {//如果是最后一行,則不在繪制底邊
                return true
            }
        }else if(layoutManager is StaggeredGridLayoutManager){
            val orientation = layoutManager.orientation
            if (orientation == StaggeredGridLayoutManager.VERTICAL){//縱向滾動
                val childNum = childCount - childCount % spanCount
                if (pos >= childNum) {//如果是最后一行,則不在繪制底邊
                    return true
                }
            }else{//橫向滾動
                if ((pos + 1) % spanCount == 0) {
                    return true
                }
            }
        }
        return false
    }

    companion object{
        private var ATTRS = intArrayOf(android.R.attr.listDivider)
    }
}

運行程序,看效果圖為:

device-2017-06-23-182642.png

3,第三方分割線

請參考:
github地址:[Y_DividerItemDecoration]:(https://github.com/yanyusong/Y_DividerItemDecoration)
下面來看看對這個第三方jar包的簡單使用示例:

device-2017-06-24-124008.png

實現這個效果也很簡單,代碼為:

class RecyclerViewNewListDividerActivity: BaseKotlinActivity(){
    private var mAdapter: Y_MultiRecyclerAdapter? = null
    private var y_ItemEntityList: Y_ItemEntityList? = null
    private var context: Context  = this

    override fun getLayoutResoucesId(): Int {
        return R.layout.activity_recycler_view_simple
    }

    override fun init() {
        y_ItemEntityList = Y_ItemEntityList()

        initToolBarScroll()

        y_ItemEntityList!!.addItems(R.layout.item_rc_4st, itemList).addOnBind(R.layout.item_rc_4st
                , object : Y_OnBind<String>{
            override fun onBindChildViewData(holder: GeneralRecyclerViewHolder?, itemData: String?, position: Int) {
                holder!!.setText(R.id.tv, itemData)
            }

        })

        rv_content.layoutManager = GridLayoutManager(this, 2, GridLayoutManager.VERTICAL, false)//設置顯示方式--瀑布流
        mAdapter = Y_MultiRecyclerAdapter(this, y_ItemEntityList)
        rv_content.adapter = mAdapter
        /**
         * 第三方----子項之間的分割線位置
         */
        rv_content.addItemDecoration(MyDividerItemDecoration(this))
        mAdapter!!.setOnItemClickListener { position -> showToast("onClick-->"+ position) }

    }

    private fun initToolBarScroll() {
        val param: AppBarLayout.LayoutParams = tb_toolbar.layoutParams as AppBarLayout.LayoutParams

        param.scrollFlags = AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL or AppBarLayout.LayoutParams.SCROLL_FLAG_EXIT_UNTIL_COLLAPSED
    }

    class MyDividerItemDecoration(val context: Context): Y_DividerItemDecoration(context) {
        override fun getDivider(itemPosition: Int): Y_Divider {
            var divider: Y_Divider? = null
            when(itemPosition) {
                else -> {
                    /**
                     * 設置底部線
                     * setBottomSideLine(boolean isHave, @ColorInt int color, float widthDp, float startPaddingDp, float endPaddingDp)
                     *  isHave:true 顯示線;false 不顯示
                     *  color:線的顏色。@ColorInt int color 表示這里需要的是一個顏色值,而不是顏色id。因此直接使用R.color.XXX是不行的,可以通過ContextCompat.getColor(context, R.color.black)來獲取
                     *  widthDp:線的高度
                     *  startPaddingDp:距離左邊多少dp開始畫線
                     *  endPaddingDp:距離右邊多少dp停止畫線
                     */
                    val count = if(itemList.size%2==0) itemList.size - 2 else itemList.size - itemList.size%2
                    if (itemPosition >= count){
                        if (itemPosition % 2 == 0){
                            divider = Y_DividerBuilder().setRightSideLine(true, ContextCompat.getColor(context, R.color.colorAccent), 2F, 0F, 0F)
                                    .create()
                        }else{
                            divider = Y_DividerBuilder().setBottomSideLine(true, ContextCompat.getColor(context, R.color.trans), 2F, 0F, 0F).create()
                        }
                    }else{
                        if (itemPosition % 2 == 0){
                            divider = Y_DividerBuilder().setRightSideLine(true, ContextCompat.getColor(context, R.color.colorAccent), 2F, 0F, 0F)
                                    .setBottomSideLine(true, ContextCompat.getColor(context, R.color.black), 2F, 0F, 0F).create()
                        }else{
                            divider = Y_DividerBuilder().setBottomSideLine(true, ContextCompat.getColor(context, R.color.black), 2F, 0F, 0F).create()
                        }
                    }
                }
            }
            return divider!!
        }

    }
    companion object{
        //    private val itemList = listOf("a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z", "W", "X", "Y", "Z", "Z")
       private val itemList = listOf("a", "b", "c", "d", "e")
    }
}

上面這個效果的關鍵代碼都在Y_DividerItemDecoration類的getDivider方法中,這個jar包中,給提供了設置上下左右線的方法,使用起來并不復雜。
在看下圖效果:

device-2017-06-24-133854.png

這個效果實現的關鍵就是每行的列數,有1列的,2列的,3列的,4列的幾種,看列數獲取的方法:

/**
         * 這里的“12”總共的列數,因為布局列數不定,有1,2,3,4幾種,這樣只有取得它們的公倍數,才能使計算方便
         */
        val layoutManager = GridLayoutManager(this, 12, GridLayoutManager.VERTICAL, false)//設置顯示方式
        layoutManager.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup(){
            override fun getSpanSize(position: Int): Int {
                if (position == 0 || position == 1) {
                    return 6
                } else if (position == 6 || position == 10) {
                    return 12
                } else if (position in 7..9) {
                    return 4
                } else if (position in 2..5) {
                    return 3
                }
                return 3
            }
        }
        rv_content.layoutManager = layoutManager

getDivider方法的實現:

class MyNewDividerItemDecoration(val context: Context): Y_DividerItemDecoration(context) {
        override fun getDivider(itemPosition: Int): Y_Divider {
            var divider: Y_Divider? = null
            if (itemPosition in 1..6 || itemPosition == 9 || itemPosition == 10) {
                divider = Y_DividerBuilder()
                        .setBottomSideLine(true, ContextCompat.getColor(context, R.color.colorAccent), 2f, 0f, 0f)
                        .create()
            } else if (itemPosition == 0 || itemPosition == 7 || itemPosition == 8) {
                divider = Y_DividerBuilder()
                        .setRightSideLine(true, ContextCompat.getColor(context, R.color.black), 2f, 0f, 0f)
                        .setBottomSideLine(true, ContextCompat.getColor(context, R.color.colorAccent), 2f, 0f, 0f)
                        .create()
            } else if (itemPosition in 11..21) {

                when ((itemPosition - 10) % 4) {
                    1, 2, 3 -> {
                        divider = Y_DividerBuilder()
                                .setRightSideLine(true, ContextCompat.getColor(context, R.color.black), 2f, 0f, 0f)
                                .setBottomSideLine(true, ContextCompat.getColor(context, R.color.colorAccent), 2f, 0f, 0f)
                                .create()
                    }
                    0 -> {
                        divider = Y_DividerBuilder()
                                .setBottomSideLine(true, ContextCompat.getColor(context, R.color.colorAccent), 2f, 0f, 0f)
                                .create()
                    }
                    else -> {
                        divider = Y_DividerBuilder().setBottomSideLine(true, ContextCompat.getColor(context, R.color.trans), 2F, 0F, 0F).create()
                    }
                }
            }
            return divider!!
        }

    }

關于RecyclerView的分割線,下一篇寫RecyclerView的條目動畫

參考文檔

1,[Android RecyclerView 使用完全解析 體驗藝術般的控件]:http://blog.csdn.net/lmj623565791/article/details/45059587
2,間隔線第三方github地址:[Y_DividerItemDecoration]:https://github.com/yanyusong/Y_DividerItemDecoration

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 227,967評論 6 531
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 98,273評論 3 415
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 175,870評論 0 373
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 62,742評論 1 309
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 71,527評論 6 407
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,010評論 1 322
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,108評論 3 440
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 42,250評論 0 288
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 48,769評論 1 333
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 40,656評論 3 354
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 42,853評論 1 369
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 38,371評論 5 358
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,103評論 3 347
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 34,472評論 0 26
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 35,717評論 1 281
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 51,487評論 3 390
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 47,815評論 2 372

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 172,611評論 25 708
  • 1 霓虹初上,喧鬧間歇,漸逝的歲月里是否還像往昔一樣笑容依舊? 本是一個安靜的過客,無奈沾染了紛亂的紅塵喧囂,欲讓...
    筆墨生閱讀 243評論 0 1
  • 書看完好久了,沒時間整理,快忘了,先上圖; 鏡像(image)類似于虛擬機鏡像,是創建容器的基礎;鏡像是只讀的; ...
    小直閱讀 2,498評論 0 51
  • 1、完成晨跑10公里,收聽《五分鐘商學院》。 2、完成份內工作資料內業:完成對主體圍護結構核算、上報施工情況及記錄...
    清風_bd61閱讀 98評論 0 0
  • 工薪族能夠做到買寶馬嗎?好吧,今天我的母親就幫我做到了,為此,我決定寫一篇文來作為紀念。同時,也是對母親的感激。 ...
    墨魚好奇寶寶閱讀 663評論 0 0