Create ButtonStateView with some custom attributes

This commit is contained in:
Benoit Marty 2019-05-27 12:08:18 +02:00
parent 390c6a1977
commit fe6e27fd6a
5 changed files with 167 additions and 58 deletions

View File

@ -0,0 +1,85 @@
/*
* Copyright 2019 New Vector Ltd
*
* 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 im.vector.riotredesign.core.platform
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import im.vector.riotredesign.R
import kotlinx.android.synthetic.main.view_button_state.view.*
class ButtonStateView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyle: Int = 0)
: FrameLayout(context, attrs, defStyle) {
sealed class State {
object Button : State()
object Loading : State()
object Loaded : State()
object Error : State()
}
var callback: Callback? = null
interface Callback {
fun onButtonClicked()
fun onRetryClicked()
}
init {
View.inflate(context, R.layout.view_button_state, this)
layoutParams = LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
buttonStateButton.setOnClickListener {
callback?.onButtonClicked()
}
buttonStateRetry.setOnClickListener {
callback?.onRetryClicked()
}
// Read attributes
context.theme.obtainStyledAttributes(
attrs,
R.styleable.ButtonStateView,
0, 0)
.apply {
try {
buttonStateButton.text = getString(R.styleable.ButtonStateView_bsv_button_text)
buttonStateLoaded.setImageDrawable(getDrawable(R.styleable.ButtonStateView_bsv_loaded_image_src))
} finally {
recycle()
}
}
}
fun render(newState: State) {
if (newState == State.Button) {
buttonStateButton.isVisible = true
} else {
// We use isInvisible because we want to keep button space in the layout
buttonStateButton.isInvisible = true
}
buttonStateLoading.isVisible = newState == State.Loading
buttonStateLoaded.isVisible = newState == State.Loaded
buttonStateRetry.isVisible = newState == State.Error
}
}

View File

@ -16,17 +16,15 @@
package im.vector.riotredesign.features.roomdirectory package im.vector.riotredesign.features.roomdirectory
import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.ImageView import android.widget.ImageView
import android.widget.TextView import android.widget.TextView
import androidx.core.view.isInvisible
import androidx.core.view.isVisible
import com.airbnb.epoxy.EpoxyAttribute import com.airbnb.epoxy.EpoxyAttribute
import com.airbnb.epoxy.EpoxyModelClass import com.airbnb.epoxy.EpoxyModelClass
import im.vector.riotredesign.R import im.vector.riotredesign.R
import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder import im.vector.riotredesign.core.epoxy.VectorEpoxyHolder
import im.vector.riotredesign.core.epoxy.VectorEpoxyModel import im.vector.riotredesign.core.epoxy.VectorEpoxyModel
import im.vector.riotredesign.core.platform.ButtonStateView
import im.vector.riotredesign.features.home.AvatarRenderer import im.vector.riotredesign.features.home.AvatarRenderer
@EpoxyModelClass(layout = R.layout.item_public_room) @EpoxyModelClass(layout = R.layout.item_public_room)
@ -68,21 +66,27 @@ abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
// TODO Use formatter for big numbers? // TODO Use formatter for big numbers?
holder.counterView.text = nbOfMembers.toString() holder.counterView.text = nbOfMembers.toString()
if (joinState == JoinState.NOT_JOINED) { holder.buttonState.render(
holder.joinButton.isVisible = true when (joinState) {
} else { JoinState.NOT_JOINED -> ButtonStateView.State.Button
// We use isInvisible because we want to keep button space in the layout JoinState.JOINING -> ButtonStateView.State.Loading
holder.joinButton.isInvisible = true JoinState.JOINED -> ButtonStateView.State.Loaded
JoinState.JOINING_ERROR -> ButtonStateView.State.Error
}
)
holder.buttonState.callback = object : ButtonStateView.Callback {
override fun onButtonClicked() {
joinListener?.invoke()
}
override fun onRetryClicked() {
// Same action
onButtonClicked()
}
} }
holder.joiningView.isVisible = joinState == JoinState.JOINING
holder.retryButton.isVisible = joinState == JoinState.JOINING_ERROR
holder.joinedView.isVisible = joinState == JoinState.JOINED
holder.joinButton.setOnClickListener { joinListener?.invoke() }
holder.retryButton.setOnClickListener { joinListener?.invoke() }
} }
class Holder : VectorEpoxyHolder() { class Holder : VectorEpoxyHolder() {
val rootView by bind<ViewGroup>(R.id.itemPublicRoomLayout) val rootView by bind<ViewGroup>(R.id.itemPublicRoomLayout)
@ -90,11 +94,7 @@ abstract class PublicRoomItem : VectorEpoxyModel<PublicRoomItem.Holder>() {
val nameView by bind<TextView>(R.id.itemPublicRoomName) val nameView by bind<TextView>(R.id.itemPublicRoomName)
val counterView by bind<TextView>(R.id.itemPublicRoomMembersCount) val counterView by bind<TextView>(R.id.itemPublicRoomMembersCount)
val joinedView by bind<View>(R.id.itemPublicRoomJoined) val buttonState by bind<ButtonStateView>(R.id.itemPublicRoomButtonState)
val joinButton by bind<View>(R.id.itemPublicRoomJoin)
val joiningView by bind<View>(R.id.itemPublicRoomJoining)
val retryButton by bind<View>(R.id.itemPublicRoomRetry)
} }
} }

View File

@ -55,56 +55,23 @@
android:textColor="#7E899C" android:textColor="#7E899C"
android:textSize="15sp" android:textSize="15sp"
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator" app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
app:layout_constraintEnd_toStartOf="@id/itemPublicRoomJoin" app:layout_constraintEnd_toStartOf="@id/itemPublicRoomButtonState"
app:layout_constraintStart_toEndOf="@id/itemPublicRoomName" app:layout_constraintStart_toEndOf="@id/itemPublicRoomName"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
tools:text="148" /> tools:text="148" />
<Button <im.vector.riotredesign.core.platform.ButtonStateView
android:id="@+id/itemPublicRoomJoin" android:id="@+id/itemPublicRoomButtonState"
style="@style/VectorButtonStyleFlat"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginEnd="8dp" android:layout_marginEnd="8dp"
android:layout_marginRight="8dp" android:layout_marginRight="8dp"
android:text="@string/join" app:bsv_button_text="@string/join"
app:bsv_loaded_image_src="@drawable/ic_tick"
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator" app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
app:layout_constraintEnd_toEndOf="parent" app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:id="@+id/itemPublicRoomJoined"
android:layout_width="0dp"
android:layout_height="0dp"
android:scaleType="center"
android:src="@drawable/ic_tick"
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
app:layout_constraintEnd_toEndOf="@+id/itemPublicRoomJoin"
app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin"
app:layout_constraintTop_toTopOf="parent" />
<ProgressBar
android:id="@+id/itemPublicRoomJoining"
android:layout_width="0dp"
android:layout_height="24dp"
android:scaleType="center"
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
app:layout_constraintEnd_toEndOf="@+id/itemPublicRoomJoin"
app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin"
app:layout_constraintTop_toTopOf="parent" />
<Button
android:id="@+id/itemPublicRoomRetry"
style="@style/VectorButtonStyleFlat"
android:layout_width="0dp"
android:layout_height="0dp"
android:text="@string/global_retry"
android:textColor="@color/vector_warning_color"
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
app:layout_constraintEnd_toEndOf="@+id/itemPublicRoomJoin"
app:layout_constraintStart_toStartOf="@+id/itemPublicRoomJoin"
app:layout_constraintTop_toTopOf="parent" />
<View <View
android:id="@+id/itemPublicRoomBottomSeparator" android:id="@+id/itemPublicRoomBottomSeparator"
android:layout_width="0dp" android:layout_width="0dp"

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
tools:parentTag="android.widget.FrameLayout">
<Button
android:id="@+id/buttonStateButton"
style="@style/VectorButtonStyleFlat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginEnd="8dp"
android:layout_marginRight="8dp"
app:layout_constraintBottom_toTopOf="@+id/itemPublicRoomBottomSeparator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:text="@string/join" />
<ProgressBar
android:id="@+id/buttonStateLoading"
android:layout_width="32dp"
android:layout_height="32dp"
android:layout_gravity="center"
android:scaleType="center" />
<ImageView
android:id="@+id/buttonStateLoaded"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
tools:src="@drawable/ic_tick" />
<Button
android:id="@+id/buttonStateRetry"
style="@style/VectorButtonStyleFlat"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="@string/global_retry"
android:textColor="@color/vector_warning_color" />
</merge>

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="ButtonStateView">
<attr name="bsv_loaded_image_src" format="reference" />
<attr name="bsv_button_text" format="reference|string" />
</declare-styleable>
</resources>