-
-
Save Veeyikpong/a376c6128e603b0dee316670a74b345b to your computer and use it in GitHub Desktop.
/* | |
* Copyright 2018 The Android Open Source Project | |
* | |
* Licensed under the Apache License, Version 2.0 (the "License"); | |
* you may not use this file except in compliance with the License. | |
* You may obtain a copy of the License at | |
* | |
* http://www.apache.org/licenses/LICENSE-2.0 | |
* | |
* Unless required by applicable law or agreed to in writing, software | |
* distributed under the License is distributed on an "AS IS" BASIS, | |
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
* See the License for the specific language governing permissions and | |
* limitations under the License. | |
*/ | |
package androidx.recyclerview.widget | |
import android.content.Context | |
import android.graphics.Canvas | |
import android.graphics.Rect | |
import android.graphics.drawable.Drawable | |
import android.util.Log | |
import android.view.View | |
import android.widget.LinearLayout | |
import android.graphics.PorterDuff | |
import android.graphics.PorterDuffColorFilter | |
/** | |
* MiddleDividerItemDecoration is a [RecyclerView.ItemDecoration] that can be used as a divider | |
* between items of a [LinearLayoutManager]. It supports both [.HORIZONTAL] and | |
* [.VERTICAL] orientations. | |
* It can also supports [.ALL], included both the horizontal and vertical. Mainly used for GridLayout. | |
* <pre> | |
* For normal usage with LinearLayout, | |
* val mItemDecoration = MiddleDividerItemDecoration(context!!,DividerItemDecoration.VERTICAL) | |
* For GridLayoutManager with inner decorations, | |
* val mItemDecoration = MiddleDividerItemDecoration(context!!,MiddleDividerItemDecoration.ALL) | |
* recyclerView.addItemDecoration(mItemDecoration); | |
</pre> * | |
*/ | |
class MiddleDividerItemDecoration | |
/** | |
* Creates a divider [RecyclerView.ItemDecoration] that can be used with a | |
* [LinearLayoutManager]. | |
* | |
* @param context Current context, it will be used to access resources. | |
* @param orientation Divider orientation. Should be [.HORIZONTAL] or [.VERTICAL]. | |
*/ | |
(context: Context, orientation: Int) : RecyclerView.ItemDecoration() { | |
private var mDivider: Drawable? = null | |
/** | |
* Current orientation. Either [.HORIZONTAL] or [.VERTICAL]. | |
*/ | |
private var mOrientation: Int = 0 | |
private val mBounds = Rect() | |
init { | |
val a = context.obtainStyledAttributes(ATTRS) | |
mDivider = a.getDrawable(0) | |
if (mDivider == null) { | |
Log.w( | |
TAG, | |
"@android:attr/listDivider was not set in the theme used for this " + "DividerItemDecoration. Please set that attribute all call setDrawable()" | |
) | |
} | |
a.recycle() | |
setOrientation(orientation) | |
} | |
fun setDividerColor(color: Int) { | |
mDivider!!.colorFilter = PorterDuffColorFilter(color, PorterDuff.Mode.SRC_ATOP) | |
} | |
/** | |
* Sets the orientation for this divider. This should be called if | |
* [RecyclerView.LayoutManager] changes orientation. | |
* | |
* @param orientation [.HORIZONTAL] or [.VERTICAL] | |
*/ | |
fun setOrientation(orientation: Int) { | |
if (orientation != HORIZONTAL && orientation != VERTICAL && orientation != ALL) { | |
throw IllegalArgumentException( | |
"Invalid orientation. It should be either HORIZONTAL or VERTICAL" | |
) | |
} | |
mOrientation = orientation | |
} | |
/** | |
* Sets the [Drawable] for this divider. | |
* | |
* @param drawable Drawable that should be used as a divider. | |
*/ | |
fun setDrawable(drawable: Drawable) { | |
if (drawable == null) { | |
throw IllegalArgumentException("Drawable cannot be null.") | |
} | |
mDivider = drawable | |
} | |
override fun onDraw(c: Canvas, parent: RecyclerView, state: RecyclerView.State) { | |
if (parent.layoutManager == null || mDivider == null) { | |
return | |
} | |
when (mOrientation) { | |
ALL -> { | |
drawVertical(c, parent) | |
drawHorizontal(c, parent) | |
} | |
VERTICAL -> drawVertical(c, parent) | |
else -> drawHorizontal(c, parent) | |
} | |
} | |
private fun drawVertical(canvas: Canvas, parent: RecyclerView) { | |
canvas.save() | |
val left: Int | |
val right: Int | |
if (parent.clipToPadding) { | |
left = parent.paddingLeft | |
right = parent.width - parent.paddingRight | |
canvas.clipRect( | |
left, parent.paddingTop, right, | |
parent.height - parent.paddingBottom | |
) | |
} else { | |
left = 0 | |
right = parent.width | |
} | |
var childCount = parent.childCount | |
if (parent.layoutManager is GridLayoutManager) { | |
var leftItems = childCount % (parent.layoutManager as GridLayoutManager).spanCount | |
if (leftItems == 0) { | |
leftItems = (parent.layoutManager as GridLayoutManager).spanCount | |
} | |
//Identify last row, and don't draw border for these items | |
childCount -= leftItems | |
} | |
for (i in 0 until childCount - 1) { | |
val child = parent.getChildAt(i) ?: return | |
parent.getDecoratedBoundsWithMargins(child, mBounds) | |
val bottom = mBounds.bottom + Math.round(child.translationY) | |
val top = bottom - mDivider!!.intrinsicHeight | |
mDivider!!.setBounds(left, top, right, bottom) | |
mDivider!!.draw(canvas) | |
} | |
canvas.restore() | |
} | |
private fun drawHorizontal(canvas: Canvas, parent: RecyclerView) { | |
canvas.save() | |
val top: Int | |
val bottom: Int | |
if (parent.clipToPadding) { | |
top = parent.paddingTop | |
bottom = parent.height - parent.paddingBottom | |
canvas.clipRect( | |
parent.paddingLeft, top, | |
parent.width - parent.paddingRight, bottom | |
) | |
} else { | |
top = 0 | |
bottom = parent.height | |
} | |
var childCount = parent.childCount | |
if (parent.layoutManager is GridLayoutManager) { | |
childCount = (parent.layoutManager as GridLayoutManager).spanCount | |
} | |
for (i in 0 until childCount - 1) { | |
val child = parent.getChildAt(i) ?: return | |
parent.layoutManager!!.getDecoratedBoundsWithMargins(child, mBounds) | |
val right = mBounds.right + Math.round(child.translationX) | |
val left = right - mDivider!!.intrinsicWidth | |
mDivider!!.setBounds(left, top, right, bottom) | |
mDivider!!.draw(canvas) | |
} | |
canvas.restore() | |
} | |
override fun getItemOffsets( | |
outRect: Rect, view: View, parent: RecyclerView, | |
state: RecyclerView.State | |
) { | |
if (mDivider == null) { | |
outRect.set(0, 0, 0, 0) | |
return | |
} | |
if (mOrientation == VERTICAL) { | |
outRect.set(0, 0, 0, mDivider!!.intrinsicHeight) | |
} else { | |
outRect.set(0, 0, mDivider!!.intrinsicWidth, 0) | |
} | |
} | |
companion object { | |
val HORIZONTAL = LinearLayout.HORIZONTAL | |
val VERTICAL = LinearLayout.VERTICAL | |
//mainly used for GridLayoutManager | |
val ALL = 2 | |
private val TAG = "DividerItem" | |
private val ATTRS = intArrayOf(android.R.attr.listDivider) | |
} | |
} |
Hi, It looks good. Please create a Java version as well.
Hi, I had to use your gist code in java.
So I converted your code from kotlin to java and It works well.
How I could share the translation version of your code in java?
I've attached my code below.
https://gist.github.com/nuovothoth/ed8f3952e90a5c4ed263ad1480382522
Hi @nuovothoth thanks for the help to translate.
You can share your gist to Stackoverflow on your own. I will help to include in my medium post also. (https://medium.com/p/6ba48d13bf16)
Thank you.
Hi,
Thanks for your sample of ItemDecoration.
I see a little bug. The ALL case is forgotten in getItemOffsets.
You can replace :
if (mOrientation == VERTICAL) { outRect.set(0, 0, 0, mDivider!!.intrinsicHeight) } else { outRect.set(0, 0, mDivider!!.intrinsicWidth, 0) }
by
when (mOrientation) { ALL -> { outRect.set(0, 0, mDivider!!.intrinsicWidth, mDivider!!.intrinsicHeight) } VERTICAL -> {outRect.set(0, 0, 0, mDivider!!.intrinsicHeight) } else -> { outRect.set(0, 0, mDivider!!.intrinsicWidth, 0) } }
This is a very cool solution. Thanks a lot.
var childCount = parent.childCount
if (parent.layoutManager is GridLayoutManager) {
childCount = (parent.layoutManager as GridLayoutManager).spanCount
}
for (i in 0 until childCount - 1) {
val child = parent.getChildAt(i)
Here will NPE when children count is small than span count, shoud change to
for (i in 0 until (childCount - 1).coerceAtMost(parent.childCount)) {
val child = parent.getChildAt(i)
Sample usage
GridLayoutManager
LInearLayoutManager
You can also set the divider color with setDividerColor