Skip to content

Instantly share code, notes, and snippets.

@darylsze
Last active August 5, 2021 01:32
Show Gist options
  • Save darylsze/5b08888207176374d10c33d93ca1a233 to your computer and use it in GitHub Desktop.
Save darylsze/5b08888207176374d10c33d93ca1a233 to your computer and use it in GitHub Desktop.
Converted legacy pain old Java code to Kotlin with builder pattern for rich functional Toolbar setup. Just demo some basic usage, removed a lots of legacy functions internally.
val config = ToolbarConfig(
lefts = listOf(
ToolbarButton(
action = ToolbarAction.ARROW_BACK,
clickHandler = { finish() }
)
),
middle = ToolbarSearchConfig(
hint = System.getLocalString("chat_common_search_message_bar_placeholder"),
onTextChange = { keyword ->
viewModel.keywordChangingObs.postValue(keyword)
if (keyword.isEmpty()) {
navController.navigateUp()
} else if(navController.currentDestination?.id != R.id.chatGroupSearchMainFragment) {
gotoMainPage()
}
},
onFocus = {
if (viewModel.keywordChangingObs.value?.isNotEmpty() == true
&& navController.currentDestination?.id != R.id.chatGroupSearchMainFragment
) {
gotoMainPage()
}
},
onClose = {
navController.navigateUp()
},
onSearch = { keyword ->
onSearchKeyword(keyword)
}
)
)
fun usage() {
val toolbar = MyToolbar(context)
toolbar.setup(toolbarConfig);
}
import android.content.Context
import android.util.AttributeSet
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.annotation.DrawableRes
import androidx.appcompat.widget.Toolbar
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.view.isVisible
import androidx.fragment.app.FragmentActivity
import com.jakewharton.rxbinding3.view.clicks
import kotlinx.android.synthetic.main.toolbar.view.*
import java.util.concurrent.TimeUnit
enum class ToolbarAction(@DrawableRes val drawableRes: Int) {
ARROW_BACK(R.drawable.button_native_back_android),
HAMBURGER(R.drawable.ic_hamburger_menu),
SETTING(R.drawable.button_setting),
NOTIFICATION(R.drawable.button_notification_nonews),
NOTIFICATION_DOT(R.drawable.button_notification_news),
DELETE(R.drawable.button_delete),
CROSS(R.drawable.button_cross_white)
}
data class TextStyle @JvmOverloads constructor(
val color: Int? = null
)
data class ToolbarButton @JvmOverloads constructor(
val action: ToolbarAction? = null,
val text: String? = null,
val textStyle: TextStyle? = null,
val showRadiusBorder: Boolean = false,
val clickHandler: (View) -> Unit
)
interface IToolbarMiddleConfig
enum class ToolbarStyle {
TRANSPARENT, MAIN, BLACK, MAIN_DAYNIGHT, PURPLE
}
data class ToolbarTitleConfig @JvmOverloads constructor(
val title: String = ""
) : IToolbarMiddleConfig
data class ToolbarSearchConfig @JvmOverloads constructor(
val hint: String = "",
val onSearch: ((String) -> Unit)? = null,
val onFocus: (() -> Unit)? = null,
val onClick: (() -> Unit)? = null,
val onClose: (() -> Unit)? = null,
val onTextChange: ((String) -> Unit)? = null
): IToolbarMiddleConfig
data class ToolbarImageConfig @JvmOverloads constructor(
val imageResId: Int,
) : IToolbarMiddleConfig
data class ToolbarConfig @JvmOverloads constructor(
val style: ToolbarStyle = ToolbarStyle.MAIN,
val lefts: List<ToolbarButton>? = null,
val middle: IToolbarMiddleConfig?,
val rights: List<ToolbarButton>? = null
) {
fun changeStyle(style: ToolbarStyle): ToolbarConfig {
return copy(style = style)
}
}
private enum class Orientation {
LEFT, RIGHT
}
class MyToolbar @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyle: Int = 0
) : ConstraintLayout(context, attrs, defStyle) {
private val iconSize: Int
private val borderButtonPadding: Int
init {
LayoutInflater.from(context).inflate(R.layout.toolbar, this, true)
iconSize = context.resources.getDimensionPixelSize(R.dimen._18sdp)
borderButtonPadding = context.resources.getDimensionPixelSize(R.dimen._8sdp)
}
lateinit var config: ToolbarConfig
fun setup(_config: ToolbarConfig) {
// clean up cached config
reset()
if (context is FragmentActivity) {
pushToTop(context as FragmentActivity)
}
config = _config
setupInternal()
}
fun getToolbar(): Toolbar {
return toolBarFrame
}
private fun setupInternal() {
when (config.style) {
ToolbarStyle.TRANSPARENT -> {
toolBarFrame.setBackgroundResource(android.R.color.transparent)
}
ToolbarStyle.BLACK -> {
toolBarFrame.setBackgroundResource(android.R.color.black)
}
ToolbarStyle.MAIN -> {
toolBarFrame.setBackgroundResource(R.drawable.image_bg_navigation_bar_min)
}
ToolbarStyle.MAIN_DAYNIGHT -> {
val bg = context.theme.obtainStyledAttributes(intArrayOf(R.attr.chatGroupToolbarBackground)).getDrawable(0)
toolBarFrame.setBackgroundDrawable(bg)
}
ToolbarStyle.PURPLE -> {
toolBarFrame.setBackgroundResource(R.color.stock_qa_main_color)
}
}
config.lefts?.forEach { button ->
if (button.action != null) {
showAction(button.action, button.clickHandler, Orientation.LEFT)
}
if (button.text.isNullOrEmpty().not()) {
setTextButtonLeft(
text = button.text ?: "",
clickHandler = button.clickHandler
)
}
}
config.rights?.forEach { button ->
if (button.action != null) {
showAction(button.action, button.clickHandler, Orientation.RIGHT)
}
if (button.text.isNullOrEmpty().not()) {
setTextButtonRight(
text = button.text ?: "",
textColor = button.textStyle?.color,
showRadiusBorder = button.showRadiusBorder,
clickHandler = button.clickHandler
)
}
}
config.middle.apply {
when (this) {
is ToolbarTitleConfig -> {
hideSearchBar()
setTitle(this.title)
}
is ToolbarSearchConfig -> {
hideTitle()
showSearchBar()
setSearchHint(this.hint)
setSearchEditTextOnFocusListener { this.onFocus?.invoke() }
setSearchEditTextCloseBtnListener { this.onClose?.invoke() }
setSearchEditTextChangedListener { keyword ->
tool_bar_search_close_button.isVisible = keyword.isNotEmpty()
this.onTextChange?.invoke(keyword)
}
setKeyBoardSearchListener(object: ToolBarSearchListener {
override fun onResult(keyword: String) {
onSearch?.invoke(keyword)
}
})
setSearchEditTextClickListener { this.onClick?.invoke() }
}
is ToolbarImageConfig -> {
setImage(this.imageResId)
}
}
}
}
private fun showAction(action: ToolbarAction, impl: (View) -> Unit, orientation: Orientation) {
val view = ImageView(context).apply {
layoutParams = ViewGroup.LayoutParams(iconSize, iconSize)
setImageDrawable(context.getCompatibleDrawable(action.drawableRes))
clicks()
.doOnNext { impl(this) }
.subscribe()
}
when (orientation) {
Orientation.LEFT -> llLeftButtonContainer.addView(view)
Orientation.RIGHT -> llRightButtonContainer.addView(view)
}
}
private fun hideAllActions() {
llLeftButtonContainer.removeAllViews()
llRightButtonContainer.removeAllViews()
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment