May 15 | 3:50 PM |
Mark M. | has entered the room |
May 15 | 3:55 PM |
Mark M. | turned on guest access |
May 15 | 4:00 PM |
Anshul V. | has entered the room |
Anshul V. |
Hi Mark, how are you doing today?
|
Mark M. |
hello, Anshul!
|
Mark M. |
how can I help you today?
|
Mark M. |
(and I'm doing well, thanks -- and you?)
|
Anshul V. |
I am doing good, thanks. Let me try to explain a project I am working on currently..
|
May 15 | 4:05 PM |
Anshul V. |
View paste
|
Anshul V. |
I am trying to set an onClickListener() on the AnswerType which would let me get the answerId of the user selected answer, as well as mark the radio button in the answer.
|
Anshul V. |
I am using data binding with all these viewmodels as binding variables but it isn’t clear to me, how do I propagate the clicked answer back to the parent view model?
|
Mark M. |
I guess that I am confused by all of the viewmodels
|
Mark M. |
you can bind a click listener via data binding and android:onClick, and it can call methods on whatever you are binding, such as AViewModel
|
Anshul V. |
I see, well, the child view models (QViewModel) and (AViewModel) are extended from the ParentViewModel, basically I have 1 viewmodel for parent adapter and 2 viewmodels for child recycler adapter
|
Mark M. |
"extended" meaning inheritance?
|
Anshul V. |
yes
|
Mark M. |
OK, that's even more confusing than what I had been thinking which was that this was using composition
|
May 15 | 4:10 PM |
Mark M. |
anyway, not knowing much more about your code, you could always bind more things to the layout for the answers
|
Anshul V. |
Ah, okay, so ParentViewModel receives a List<QuestionAnswerPairs> from the server, as a LiveData, the activity observes that LiveData<> and sets the ParentAdapter, which in turn sets the ChildAdapter in the onBindViewHolder()
|
Mark M. |
that seems reasonable, at least on the surface
|
Mark M. |
so, you could have the answer ViewHolder (or ChildAdapter, depending on your setup) bind both AViewModel and ParentViewModel into the row layout
|
Mark M. |
then, your android:onClick could call a method on ParentViewModel to report the click event
|
Anshul V. |
yeah, and the QViewModel and AViewModel are just trying to set the data to the layouts, and have any click listeners as you just mentioned
|
Anshul V. |
Oh, I see, so technically, I can receive the answerClickEvent directly in the ParentViewModel?
|
Mark M. |
sure, if you wanted
|
Mark M. |
it's just another <variable>
|
Mark M. |
and a tweak to the binding expression
|
Mark M. |
now, there may be architectural issues with doing this, if you want the child stuff to not know about the parent stuff
|
Mark M. |
in that case, you might have the parent supply an OnClickListener, or a lambda expression, that the child can call to forward events to the parent
|
Anshul V. |
Oh, the child view model would implement that listener and pass on the data to the parent view model..
|
May 15 | 4:15 PM |
Mark M. |
yes
|
Mark M. |
so, either have the binding expression call a method on the parent view model directly, or have it call a method on the child view model, which in turn calls something to alert the parent
|
Mark M. |
or, have the binding expression call a method on the child, which uses LiveData to inform the parent
|
Mark M. |
there are a variety of options here
|
Anshul V. |
That makes sense, okay, so where does the activity which hosts the Recyclerviews gets involved in this? Is it just setting the LiveData<List<QuestionAnswer>> to the Recycler view adapter? the rest is communication between VM <-> Adapters?
|
Mark M. |
personally, I tend to have the activity/fragment be the LiveData observer and just pass the data to the adapter
|
Mark M. |
your approach should work; I just haven't done it that way
|
Anshul V. |
okay, that works! Now there's a twist in the tale, whatever number of questionAnswers the server sends us, we only display 1 QuestionAnswer in the RecyclerView in the beginning, once the user answers the first question, we load the second question. I guess that changes how we set the adapter from the LiveData, right?
|
May 15 | 4:20 PM |
Mark M. |
is this simply a display thing? or is the identity of the second question not known until the user answers the first question?
|
Anshul V. |
both! the server sends batches of question, it can be one quesionAnswer batch, or 3. In the prior case, if only 1 question is sent, we display that to user, and once user answers it, we send the metadata back to the server to determine the next question/questionBatch
|
Anshul V. |
in latter, we display 1 question, user answers and we display another question, and then the 3rd question, before adding all the answers metadata to the request and send to server to receive next batch of questions
|
Mark M. |
it would be nice if your view model could hide some of those differences from the UI layer
|
Mark M. |
ideally, the view model would say "this is what you should be showing now" and that's it
|
Mark M. |
the view model, in conjunction with the repository (or wherever your server API calls are happening), would determine what the "should be showing now" actually should be based on user input and server responses
|
May 15 | 4:25 PM |
Anshul V. |
So, Parent view model sends 1 question livedata to activity, which in turn sets the adapter, once the VM receives the first onClickAnswer event back, it stores it, either makes the api call, or append the second question to the livedata to activity, and the adapter gets initialized again, but now with 2 questions?
|
Mark M. |
yes, given your UI setup
|
Mark M. |
off the cuff, I am not a huge fan of the horizontal RecyclerView as the outer container -- I would prefer the questions be individual fragments with traditional navigation
|
Anshul V. |
okay, makes sense! Can I be excused for a few moments?
|
Mark M. |
but, I'm not the designer, and there may be solid reasons for your setup, even if it makes the work more difficult
|
Mark M. |
and you are welcome to do whatever you want!
|
Anshul V. |
okay, be right back
|
Mark M. |
I'm not going anywhere for (...checks watch...) 33 minutes!
|
May 15 | 4:35 PM |
Anshul V. |
Haha.. I am back. Question, how do I make sure that the user can't select multiple answers at once? It should always be only 1 out of 4 answers
|
Mark M. |
well, ideally, the four RadioButtons would be in a RadioGroup
|
Mark M. |
I am not completely certain why the question and answers are implemented as a RecyclerView
|
Mark M. |
but, if you want to stick with that, you would need to update all of the RadioButton objects for the correct checked/unchecked state
|
Anshul V. |
Hehe, I have my doubts about that as well, we don't really know how many answers there might be in a question, may be 4, may be 6..
|
May 15 | 4:40 PM |
Mark M. |
but for something that short, there will not be any actual recycling going on, so I do not know what RecyclerView gets you over a ConstraintLayout wrapped in a ScrollView
|
Mark M. |
and with the ConstraintLayout/ScrollView approach, you could have all N RadioButtons be in a RadioGroup
|
Anshul V. |
Is it possible I can implement this without the inner recyclerview? How would I go about setting the same answerViewHodler multiple times?
|
Mark M. |
just toggle the visibility of unused RadioButtons to View.GONE
|
Mark M. |
there would not be an answerViewHolder, as there would be no ViewHolder -- that is a RecyclerView construct
|
Mark M. |
you would have the outer horizontal RecyclerView, and its ViewHolder would populate the TextView(?) for the question and the RadioButtons for the answers
|
Mark M. |
(where in your case that population would happen via data binding, presumably)
|
Mark M. |
and instead of a layout for the question and a layout for an answer, you would have a single layout for the question and 6 answers, with binding expressions for the text, answer visibility, etc.
|
Shawn W. | has entered the room |
Mark M. |
Anshul: let me take a question from Shawn, and I will be back with you in a bit
|
Mark M. |
Shawn: hi! how can I help you today?
|
May 15 | 4:45 PM |
Shawn W. |
I haven't developed for Android since 2015. Which of your books would you suggest for a review and to get up to speed on new changes.
|
Mark M. |
if you learn by reading, "Elements of Android Jetpack"
|
Mark M. |
if you learn by doing, "Exploring Android"
|
Mark M. |
right now, "Exploring Android" is a bit ahead of "Elements of Android Jetpack", though the latter will catch up in a few weeks
|
Mark M. |
but either will give you an idea of how modern Android app development is being conducted
|
Shawn W. |
Thank you very much. Your book helped me greatly when I started in 2010
|
Mark M. |
great!
|
Mark M. |
note that "Exploring Android" uses Kotlin, while "Elements of Android Jetpack" has examples in both Java and Kotlin
|
Shawn W. |
Awesome. I wanted to start learning Koitlin
|
Mark M. |
"Elements of Kotlin" covers the basics
|
Mark M. |
and, since Google gave the "we will continue to support Java" kiss of death at last week's I|O, learning Kotlin is a really good idea :-)
|
Mark M. |
(technically, they will support Java indefinitely, but it will soon be the second-class language for Android)
|
May 15 | 4:50 PM |
Shawn W. |
Thank you very much! I appreciate the advise. I have no more questions today.
|
Mark M. |
OK -- feel free to join future chats as you start accumulating questions!
|
Mark M. |
in the meantime...
|
Mark M. |
Anshul: back to you! do you have another question?
|
Anshul V. |
That's so interesting, Mark! I didn't know I could do it without inner recycler view. So I can have a List<QuestionAnswer> which comes from the server in the viewmodel, and another LiveData<List<QuestionAnswer>> which mirrors the adapter list in the activity, and we append question by question to the adapter list?
|
Mark M. |
sure
|
Mark M. |
just set up the layout for the question+answers to be what you want an individual "page" to be of your horizontal RecyclerView, and populate it in one shot
|
Anshul V. |
Okay, thanks! Might I ask what should be the data structure for this purpose? the one where I can fill all the <QuestionAnswer> and can get the first question one by one to append to the adapter List<QuestionAnswer>?
|
Mark M. |
um, well, your QuestionAnswer probably has the details of the question and a List of the details of the candidate answers
|
Mark M. |
or, it could have individual properties for the candidate answers
|
Mark M. |
since you have a fixed maximum number of them
|
Mark M. |
and there aren't too many
|
Anshul V. |
yes, the first one, details of the question, and list of details of the candidate answers
|
May 15 | 4:55 PM |
Mark M. |
it may not be much different than the data structure that you already have -- the difference is in what you do with the object in terms of setting up the UI
|
Mark M. |
and I can't really give you specifics, as I do not know your app
|
Anshul V. |
Well, the index of the horizontal recycler view would also come into play, since user can swipe back to see the previously answered question
|
Mark M. |
yes, but that is outside of any individual QuestionAnswer
|
Mark M. |
the horizontal RecyclerView works with a List<QuestionAnswer>
|
Mark M. |
each page of that RecyclerView has widgets to display an individual QuestionAnswer
|
Anshul V. |
Ah, right!
|
Mark M. |
if it helps, ignore the horizontal aspect for a bit, and think of how you would show a vertically scrolling list of to-do items, or contacts, or whatever
|
Mark M. |
in that case, the RecyclerView would have a List<Model> and each row would be rendering an individual Model
|
Mark M. |
your situation is no different, except that you are scrolling horizontally
|
Anshul V. |
Ha, change of perspective. Also, I think I know the answer to this, but to save the state of recyclerview (the checked radio buttons), if the user decides to swipe back, do I use savedInstanceState in the Activity to save recyclerview state?
|
Mark M. |
primarily, it would be your top-level ViewModel, for handling configuration changes
|
Mark M. |
it needs to know the users chosen answers, what the current question is, etc.
|
May 15 | 5:00 PM |
Mark M. |
putting that data in the saved instance state Bundle would be nice for process termination, though
|
Mark M. |
fortunately, Google is adding support for tying the saved instance state Bundle into a ViewModel
|
Anshul V. |
Hmm.. yeah, I heard about that but didn't explrore it. Thank you for all your help Mark! This helps!
|
Mark M. |
happy to be useful!
|
Mark M. |
and that's a wrap for today's chat
|
Mark M. |
as usual, the transcript will be posted to https://commonsware.com/office-hours/ shortly
|
Mark M. |
the next chat is tomorrow at 9am US Eastern
|
Mark M. |
have a pleasant day!
|
Anshul V. |
Have a good day!
|
Anshul V. | has left the room |
Shawn W. | has left the room |
Mark M. | turned off guest access |