Skip to content

Commit

Permalink
Merge pull request #27 from brookmg/master
Browse files Browse the repository at this point in the history
Add corner radius to the bottom bar
  • Loading branch information
ibrahimsn98 authored Mar 29, 2020
2 parents a4b9124 + da1c2b5 commit 4523114
Show file tree
Hide file tree
Showing 6 changed files with 288 additions and 1 deletion.
1 change: 1 addition & 0 deletions app/src/main/res/layout/activity_main.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
app:backgroundColor="@color/colorPrimary"
app:textColor="@color/colorTextPrimary"
app:textSize="14sp"
app:cornerRadius="10dp"
app:iconSize="24dp"
app:indicatorColor="#2DFFFFFF"
app:indicatorRadius="10dp"
Expand Down
4 changes: 4 additions & 0 deletions lib/src/main/java/me/ibrahimsn/lib/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,12 @@ object Constants {
const val DEFAULT_ICON_MARGIN = 4F
const val DEFAULT_TEXT_SIZE = 11F
const val DEFAULT_CORNER_RADIUS = 20F
const val DEFAULT_BAR_CORNER_RADIUS = 0F

const val OPAQUE = 255
const val TRANSPARENT = 0

const val COS_45 = 0.525321988
const val SHADOW_MULTIPLIER = 1.5f
}

237 changes: 237 additions & 0 deletions lib/src/main/java/me/ibrahimsn/lib/RoundRectDrawable.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,237 @@
/*
* Copyright 2020 Brook Mezgebu
* 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 me.ibrahimsn.lib

import android.content.res.ColorStateList
import android.graphics.*
import android.graphics.drawable.Drawable
import androidx.annotation.RequiresApi
import me.ibrahimsn.lib.Constants.COS_45
import me.ibrahimsn.lib.Constants.SHADOW_MULTIPLIER
import kotlin.math.ceil

/**
* Very simple drawable that draws a rounded rectangle background with arbitrary corners and also
* reports proper outline for Lollipop.
*
*
* Simpler and uses less resources compared to GradientDrawable or ShapeDrawable.
*/
@RequiresApi(21)
class RoundRectDrawable( backgroundColor: ColorStateList?, private var mRadius: Float,
private val topLeft: Boolean = false,
private val topRight: Boolean = false,
private val bottomLeft: Boolean = false,
private val bottomRight: Boolean = false
) : Drawable() {

private val mPaint: Paint = Paint(Paint.ANTI_ALIAS_FLAG or Paint.DITHER_FLAG)
private val mBoundsF: RectF
private val mBoundsI: Rect

var padding = 0f
private set

private var mInsetForPadding = false
private var mInsetForRadius = true
private var mBackground: ColorStateList? = null
private var mTintFilter: PorterDuffColorFilter? = null
private var mTint: ColorStateList? = null
private var mTintMode: PorterDuff.Mode? = PorterDuff.Mode.SRC_IN

private fun setBackground(color: ColorStateList?) {
mBackground = color ?: ColorStateList.valueOf(Color.TRANSPARENT)
mPaint.color = mBackground?.getColorForState(state, mBackground?.defaultColor ?: Color.TRANSPARENT) ?: Color.TRANSPARENT
}

fun setPadding( padding: Float, insetForPadding: Boolean, insetForRadius: Boolean) {
if (padding == this.padding && mInsetForPadding == insetForPadding && mInsetForRadius == insetForRadius) {
return
}

this.padding = padding
mInsetForPadding = insetForPadding
mInsetForRadius = insetForRadius
updateBounds(null)
invalidateSelf()
}

override fun draw(canvas: Canvas) {
val paint = mPaint
val clearColorFilter: Boolean
if (mTintFilter != null && paint.colorFilter == null) {
paint.colorFilter = mTintFilter
clearColorFilter = true
} else clearColorFilter = false

canvas.drawPath(
Util.roundedRect(mBoundsF, mRadius, mRadius, topLeft, topRight, bottomRight, bottomLeft),
paint
);

if (clearColorFilter) {
paint.colorFilter = null
}
}

private fun calculateVerticalPadding(maxShadowSize: Float, cornerRadius: Float, addPaddingForCorners: Boolean): Float {
return if (addPaddingForCorners) {
(maxShadowSize * SHADOW_MULTIPLIER + (1 - COS_45) * cornerRadius).toFloat()
} else {
maxShadowSize * SHADOW_MULTIPLIER
}
}

private fun calculateHorizontalPadding(
maxShadowSize: Float, cornerRadius: Float,
addPaddingForCorners: Boolean
): Float {
return if (addPaddingForCorners) {
(maxShadowSize + (1 - COS_45) * cornerRadius).toFloat()
} else {
maxShadowSize
}
}

private fun updateBounds(bounds: Rect?) {
var bounds = bounds

if (bounds == null) bounds = getBounds()

mBoundsF[bounds!!.left.toFloat(), bounds.top.toFloat(), bounds.right.toFloat()] = bounds.bottom.toFloat()
mBoundsI.set(bounds)

if (mInsetForPadding) {
val vInset: Float = calculateVerticalPadding(
padding,
mRadius,
mInsetForRadius
)

val hInset: Float = calculateHorizontalPadding(
padding,
mRadius,
mInsetForRadius
)

mBoundsI.inset(
ceil(hInset.toDouble()).toInt(),
ceil(vInset.toDouble()).toInt()
)

// to make sure they have same bounds.
mBoundsF.set(mBoundsI)
}
}

override fun onBoundsChange(bounds: Rect) {
super.onBoundsChange(bounds)
updateBounds(bounds)
}

override fun getOutline(outline: Outline) {
outline.setConvexPath(
Util.roundedRect(mBoundsF,
mRadius, mRadius,
tl = topLeft, tr = topRight, br = bottomRight, bl = bottomLeft
)
)
}

override fun setAlpha(alpha: Int) {
mPaint.alpha = alpha
}

override fun setColorFilter(cf: ColorFilter) {
mPaint.colorFilter = cf
}

override fun getOpacity(): Int {
return PixelFormat.TRANSLUCENT
}

var radius: Float
get() = mRadius
set(radius) {
if (radius == mRadius) {
return
}
mRadius = radius
updateBounds(null)
invalidateSelf()
}

var color: ColorStateList?
get() = mBackground
set(color) {
setBackground(color)
invalidateSelf()
}

override fun setTintList(tint: ColorStateList) {
mTint = tint
mTintFilter = createTintFilter(mTint, mTintMode)
invalidateSelf()
}

override fun setTintMode(tintMode: PorterDuff.Mode) {
mTintMode = tintMode
mTintFilter = createTintFilter(mTint, mTintMode)
invalidateSelf()
}

override fun onStateChange(stateSet: IntArray): Boolean {
val newColor =
mBackground!!.getColorForState(stateSet, mBackground!!.defaultColor)
val colorChanged = newColor != mPaint.color
if (colorChanged) {
mPaint.color = newColor
}
if (mTint != null && mTintMode != null) {
mTintFilter = createTintFilter(mTint, mTintMode)
return true
}
return colorChanged
}

override fun isStateful(): Boolean {
return (mTint != null && mTint!!.isStateful
|| mBackground != null && mBackground!!.isStateful || super.isStateful())
}

/**
* Ensures the tint filter is consistent with the current tint color and
* mode.
*/
private fun createTintFilter(
tint: ColorStateList?,
tintMode: PorterDuff.Mode?
): PorterDuffColorFilter? {
if (tint == null || tintMode == null) {
return null
}
val color = tint.getColorForState(state, Color.TRANSPARENT)
return PorterDuffColorFilter(color, tintMode)
}

init {
setBackground(backgroundColor)
mBoundsF = RectF()
mBoundsI = Rect()
}
}
9 changes: 8 additions & 1 deletion lib/src/main/java/me/ibrahimsn/lib/SmoothBottomBar.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import android.animation.ArgbEvaluator
import android.animation.ValueAnimator
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.ColorStateList
import android.graphics.Canvas
import android.graphics.Color
import android.graphics.Paint
import android.graphics.RectF
import android.os.Build
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.View
Expand All @@ -16,6 +18,7 @@ import androidx.annotation.FontRes
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import me.ibrahimsn.lib.Constants.DEFAULT_ANIM_DURATION
import me.ibrahimsn.lib.Constants.DEFAULT_BAR_CORNER_RADIUS
import me.ibrahimsn.lib.Constants.DEFAULT_CORNER_RADIUS
import me.ibrahimsn.lib.Constants.DEFAULT_ICON_MARGIN
import me.ibrahimsn.lib.Constants.DEFAULT_ICON_SIZE
Expand All @@ -38,6 +41,7 @@ class SmoothBottomBar : View {
private var barIndicatorColor = Color.parseColor(DEFAULT_INDICATOR_COLOR)
private var barIndicatorRadius = d2p(DEFAULT_CORNER_RADIUS)
private var barSideMargins = d2p(DEFAULT_SIDE_MARGIN)
private var barCornerRadius = d2p(DEFAULT_BAR_CORNER_RADIUS)

private var itemPadding = d2p(DEFAULT_ITEM_PADDING)
private var itemAnimDuration = DEFAULT_ANIM_DURATION
Expand Down Expand Up @@ -94,6 +98,7 @@ class SmoothBottomBar : View {
barIndicatorColor = typedArray.getColor(R.styleable.SmoothBottomBar_indicatorColor, this.barIndicatorColor)
barIndicatorRadius = typedArray.getDimension(R.styleable.SmoothBottomBar_indicatorRadius, this.barIndicatorRadius)
barSideMargins = typedArray.getDimension(R.styleable.SmoothBottomBar_sideMargins, this.barSideMargins)
barCornerRadius = typedArray.getDimension(R.styleable.SmoothBottomBar_cornerRadius, this.barCornerRadius)
itemPadding = typedArray.getDimension(R.styleable.SmoothBottomBar_itemPadding, this.itemPadding)
itemTextColor = typedArray.getColor(R.styleable.SmoothBottomBar_textColor, this.itemTextColor)
itemTextSize = typedArray.getDimension(R.styleable.SmoothBottomBar_textSize, this.itemTextSize)
Expand All @@ -106,7 +111,9 @@ class SmoothBottomBar : View {
items = BottomBarParser(context, typedArray.getResourceId(R.styleable.SmoothBottomBar_menu, 0)).parse()
typedArray.recycle()

setBackgroundColor(barBackgroundColor)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
background = RoundRectDrawable(ColorStateList.valueOf(barBackgroundColor), barCornerRadius, topLeft = true, topRight = true)
} else setBackgroundColor(barBackgroundColor)

// Update default attribute values
paintIndicator.color = barIndicatorColor
Expand Down
37 changes: 37 additions & 0 deletions lib/src/main/java/me/ibrahimsn/lib/Util.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package me.ibrahimsn.lib

import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import android.os.Build
import androidx.annotation.RequiresApi

/**
* Created by BrookMG on 3/29/2020 in me.ibrahimsn.lib
inside the project SmoothBottomBar .
*/
class Util {

companion object {

@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
fun roundedRect(
rect: RectF,
rx: Float, ry: Float,
tl: Boolean, tr: Boolean, br: Boolean, bl: Boolean
): Path {
val path = Path();
val corners: FloatArray = floatArrayOf(
(if (tl) rx else 0f), (if (tl) ry else 0f),
(if (tr) rx else 0f), (if (tr) ry else 0f),
(if (br) rx else 0f), (if (br) ry else 0f),
(if (bl) rx else 0f), (if (bl) ry else 0f)
);

path.addRoundRect(rect, corners, Path.Direction.CW)
return path
}

}

}
1 change: 1 addition & 0 deletions lib/src/main/res/values/attrs.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<attr name="iconSize" format="dimension" />
<attr name="iconTint" format="color"/>
<attr name="iconTintActive" format="color"/>
<attr name="cornerRadius" format="dimension" />
<attr name="activeItem" format="integer" />
<attr name="duration" format="integer" />
</declare-styleable>
Expand Down

0 comments on commit 4523114

Please sign in to comment.