I was able to complete this challenge, but not using data binding and it’s driving me nuts. In my layout I am using the onProgressChanged attribute inside the seekbar element but when I bind it to a method in my viewmodel or elsewhere I get a “Cannot Resolve Type” error. I’m not sure what I am missing, or how to properly implement the SeekBarBindingAdapter I guess.
Debugging data binding has kind of been a nightmare. I got it to compile and run earlier by using he viewModel:onSeekBarMove syntax but the listener wasn’t working.
C:\Users\pstev\Documents\BeatBox\app\build\generated\source\apt\debug\com\bignerdranch\android\beatbox\databinding\FragmentBeatBoxBinding.java:131: error: cannot find symbol
callbackArg_0.onSeekBarMove(callbackArg_1, callbackArg_2, com.bignerdranch.android.beatbox.SoundViewModel);
symbol: class beatbox
location: package com.bignerdranch.android
Note: C:\Users\pstev\Documents\BeatBox\app\src\main\java\com\bignerdranch\android\beatbox\BeatBox.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
1 error
I am working on this as well. The issue for me, is that I did take a look at the adapter source for seekbar using Navigate->File and saw this: seekbaradapter.png1109×584 20 KB
The problem is that when I am in the layout file and start typing, the only prompt that Android Studio gives me is for the “Progress” item:
seekbarlayout.png1065×733 36.8 KB
The ‘onProgressChanged’ value is not listed, and if I type it in anyway, studio gives me a warning that the attribute is unknown:
Yet I found this example which seems to indicate that this should work:
seekbararticle.png1130×632 50.8 KB
I wonder if there is a different namespace that has to be used or something?
Update: I went ahead and just tried it - at first I got all kinds of errors because I wasn’t matching the expected parameters correctly in the layout XML. However this works:
As long as I have the method in my viewModel class correctly written to accept the same parameters that are defined in the SeekBarBindingAdapter file:
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
Log.d("BeatBox", "Got progress change of: " + progress);
So I took a much simpler route to solve this challenge. I didn’t see any need to create a separate view model class the way we did for the SoundViewModel class for the Button. Why? Because as I understand it, the primary reason for separating these classes out is when we need UI widget manipulation (i.e. View manipulation) that we want to separate out from the controller. In the case of the Button, we wanted to be able to dynamically update the text on the Button as well as manage the Sound associated with the button. But in this case:
a) We aren’t having to do a notify in the case of reacting to the seekbar being changed, the only update is internal to BeatBox itself (updating the playback rate)
b) BeatBox is where the play is done, and it controls the rate at which the sound is played for all buttons, so we don’t need an intermediary file for this - we can just use BeatBox itself.
c) I think if the challenge had been to put a different rate on a per button basis, then a different approach would have been warranted.
So here is my layout file:
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<variable
name="viewModel"
type="com.reddragon.beatbox.BeatBox" />
</data>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- Recyclerview grabs all the screen real estate itself, so define seekbar first even
though it is on the bottom -->
<SeekBar
android:id="@+id/seekBar"
style="@style/Widget.AppCompat.SeekBar.Discrete"
android:layout_alignParentBottom="true"
android:max="10"
android:paddingTop="5dp"
android:paddingBottom="5dp"
android:progress="5"
android:onProgressChanged="@{viewModel.onProgressChanged}"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_alignParentTop="true"
android:layout_above="@id/seekBar"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
</android.support.v7.widget.RecyclerView>
</RelativeLayout>
I simply added one method to BeatBox.java:
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if ( fromUser ) {
// 0-based counting for progress
mSoundRate = (float)(1.0f +( (float)( progress - 5 )/10));
Log.d("BeatBox", "Got progress change of: " + progress + ", changed rate to: " + mSoundRate);
} else {
Log.d("BeatBox", "Got progress change of: " + progress + " but not from user");
As an aside - I had a hard time with various layouts getting the SeekBar to show. I finally settled on the above approach after Googling a bit, but LinearLayout was not working for no matter use of layoutweight, etc. I see some have used FrameLayout - I thought this was now getting a bad rap?
clippership:
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if ( fromUser ) {
// 0-based counting for progress
mSoundRate = (float)(1.0f +( (float)( progress - 5 )/10));
Log.d("BeatBox", "Got progress change of: " + progress + ", changed rate to: " + mSoundRate);
} else {
Log.d("BeatBox", "Got progress change of: " + progress + " but not from user");
Obvious, but for completion sake note that I also updated the play method to utilize an instance variable that gets updated from onProgressChanged:
I don’t think Beatbox should listen the progress changed of SeekBar. BeatBox is just responsible for play, so it should’t know the playback rate is controlled by SeekBar. I think onProgressChanged should be place in BeatBoxFragment, and then pass it to BeatBox. I hope you can understand what i say with my poor English.
SeekBar should be placed in BeatBoxFragment. BeatBox should not know how it’s playback speed is changed