Can put solution of first challenge ?
This worked for me: there is a new view model class
fun bbRate(r: Int): Float =
if (0 <= r || r <= 100) 0.5f + ((r.toFloat() / 100.0f ) * 1.5f)
else throw IllegalArgumentException("rate out of bound: $r")
class BeatBoxViewModel(private val beatBox: AtomicReference<BeatBox>): BaseObservable() {
private val TAG: String? = this::class.simpleName
val max = 100
private val default = 33
private var _rate: Int = default
init {
beatBox.get()?.apply { rate = bbRate(default) }
Log.d(TAG, "beatbox default rate is ${bbRate(default)}")
}
@get:Bindable
@set:Bindable
var rate: Int
get() = _rate
set(value) {
_rate = value
beatBox.get()?.apply { rate = bbRate(_rate) }
Log.d(TAG, "beatbox rate is now $_rate")
notifyChange()
}
}
some changes to the xml for the main layout:
<layout xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="viewModel"
type="com.xrpn.bnr.beatbox.viewmodel.BeatBoxViewModel" />
</data>
<LinearLayout
android:id="@+id/sound_view_layout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/recycler_grid_view"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="90">
</androidx.recyclerview.widget.RecyclerView>
<SeekBar
android:id="@+id/seekbar_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="10"
android:max="@{viewModel.max}"
<!-- this is a two-way (read/write) binding -->
android:progress="@={viewModel.rate}">
</SeekBar>
</LinearLayout>
</layout>
and a little new code in MainActivity:
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.viewModel = BeatBoxViewModel(beatBox)
Also, BeatBox has a new attribute:
class BeatBox(private val assets: AssetManager) {
...
private var _rate: Float = 1.0f
var rate: Float
get() = _rate
set(value) {
_rate = when {
value < 0.5f -> 0.5f
2.0f < value -> 2.0f
else -> value
}
}
...
fun play(sound: SoundWav) {
sound.soundId?.let {
// used here vvvv
soundPool.play(it, 1.0f, 1.0f, DEFAULT_SOUND_PRIORITY, NO_SOUND_LOOP, rate)
}
}
thanks to the posters at
this is an attempt to return the favor
Here there is my solution, I smashed my head on the keyboard because I forgot to bind the view model to Main activity (I only bound it to the viewholders as the book did). I tried to figure out the onProgressChange of the SeekBar directly in the view model and I went crazy x2 (that “solution” is still not working for me).
But after I figured out that simple error this challenge is doable, there is my code
<LinearLayout
android:orientation="vertical"
android:id="@+id/bottom_view_group"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:padding="10dp">
<TextView
android:id="@+id/rate_label"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toTopOf="@+id/seek_bar"
tools:text="PlayBack Speed 0%"
android:text="@{`PlayBack Speed `+(viewModel.seekBarValue+50)+`%`}"/>
<SeekBar
android:id="@+id/seek_bar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
android:max="150"
android:progress="@={viewModel.seekBarValue}"/>
</LinearLayout>
The key is the double-way binding on the progress attribute, so you can use it not only to set a default value defining where to start, but you can interact with it.
class SoundViewModel(private val beatBox: BeatBox): BaseObservable() {
var sound: Sound? = null
set(sound) {
field = sound
notifyChange()
}
@set:Bindable
var rate: Int? = 0
set(value) {
field = value
value?.let {
BeatBox.soundRate = it/100f
}
}
@get:Bindable
val title: String?
get() = sound?.name
fun onButtonClicked() {
sound?.let {
beatBox.play(it)
}
}
@Bindable
var seekBarValue: Int = 50
set(value) {
field = value
rate = value+50
notifyPropertyChanged(BR.seekBarValue)
}
here the mainActivity code
beatBox = BeatBox(assets)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(this, R.layout.activity_main)
binding.viewModel = SoundViewModel(beatBox) // here we put the view model for the R.layout.activity_main
and here the BeatBox (I put a static variable that represents the rate)
fun play(sound: Sound) {
sound.soundId?.let {
soundPool.play(it, 1f, 1f, 1, 0, soundRate)
}
}
companion object{
var soundRate = 1f
}