Dec 15 | 3:50 PM |
Mark M. | has entered the room |
Mark M. | turned on guest access |
Dec 15 | 3:55 PM |
Susheel | has entered the room |
Mark M. |
hello, Susheel!
|
Susheel |
Hi Mark how are you?
|
Mark M. |
how can I help you today?
|
Susheel |
I have a question about GridLayoutManager ...
|
Dec 15 | 4:00 PM |
Mark M. |
go ahead
|
Susheel |
So I have a recylerview that is using GridLayoutManager and the spancount is set to 3....In each individual item, there is an imageview with width as match_parent and height as wrap_content....depending on the device width, the imageitem's dimensions also change
|
Steve | has entered the room |
Steve |
Hi
|
Mark M. |
(BTW, hi, Steve, I'll be with you shortly!)
|
Steve |
Thanks, Mark.
|
Susheel |
Now when I launch the app on my device, I am seeing the initial few rows with the images loaded but when I scroll down, the items look like they are being drawn at runtime...and causing a bad UI experience
|
Mark M. |
well, the items *are* being drawn at runtime, as were your first few rows' worth
|
Susheel |
right
|
Mark M. |
what specifically do you mean by "causing a bad UI experience"?
|
Mark M. |
does "being drawn at runtime" mean "slow"?
|
Susheel |
I mean, the height of each row is changing as if from zero to the wrap_content...
|
Mark M. |
when are the images being put in the rows? directly, or asynchronously?
|
Susheel |
using picasso in the onbindviewholder method of my recylerviewadapter
|
Mark M. |
are you using a placeholder drawable with Picasso?
|
Susheel |
yes
|
Mark M. |
hmmm... I was expecting a "no" there... :-)
|
Susheel |
but that does not affect my issue though
|
Dec 15 | 4:05 PM |
Mark M. |
so, if "the height of each row is changing as if from zero to the wrap_content", is that happening as the row is showing the placeholder, or is that as the row is swapping in the real image?
|
Susheel |
when I hardcode the imageview dimensions I don't see this problem but my spancount has to be 3 so I cannot hardcode the image item's width
|
Susheel |
That is happening as the row is showing
|
Susheel |
I have this issue even without the placeholder image set
|
Mark M. |
that didn't answer my question
|
Mark M. |
that's fine, but that still did not answer my question
|
Susheel |
oops
|
Mark M. |
you say the height of the row is changing
|
Mark M. |
that implies the passage of time
|
Mark M. |
you start in one state (short or zero height)
|
Mark M. |
you end in another state (expected height) after some amount of time
|
Mark M. |
I am trying to understand whether that amount of time is tied into the asynchronous loading of the real image or not
|
Susheel |
the height of the row is same for a given device, the height of the image item is changing it seems like...
|
Mark M. |
when "the height of the image item" is done "changing", are you seeing the placeholder, or the final image?
|
Mark M. |
also, are all images (placeholder plus the real ones) the same size?
|
Susheel |
You know what, I am actually not using a placeholder image on this adapter....sorry about that...I just checked and I am not using a placeholder image...
|
Mark M. |
you might try adding one
|
Susheel |
oh
|
Dec 15 | 4:10 PM |
Mark M. |
the ImageView height will be zero if there is no image in it
|
Dec 15 | 4:10 PM |
Susheel |
but will it look consistent on different devices?
|
Susheel |
right
|
Susheel |
just using mdpi, hdpi folders I guess?
|
Mark M. |
typically, yes
|
Susheel |
thank you I will try that
|
Mark M. |
let me take a question from Steve, and I'll be back with you in a bit
|
Susheel |
cool
|
Mark M. |
Steve: sorry for the delay! how can I help you today?
|
Steve |
Thanks, Mark. I have a very basic question about using a Service from a Fragment. I'll try to paste in my question.
|
Steve |
View paste
|
Mark M. |
the register and unregister seem OK
|
Steve |
Ok.
|
Mark M. |
in terms of when to start the service, that's tough to answer in the abstract
|
Steve |
Ok. What further details would help?
|
Mark M. |
what are you doing about configuration changes (e.g., screen rotations)?
|
Mark M. |
onActivityCreated() gets called again after a configuration change IIRC
|
Mark M. |
onCreate() would not be
|
Steve |
That's something I can use some help with. What would you suggest in terms of handling configuration changes?
|
Mark M. |
again, that's tough to answer in the abstract
|
Chandra S. | has entered the room |
Mark M. |
let's back up a step: why is this an IntentService, versus just a plain thread?
|
Mark M. |
(BTW, hello, Chandra -- I will be with you shortly!)
|
Rajeef | has entered the room |
Mark M. |
(BTW, hello, Rajeef -- I will be with you in a bit, after Steve and Chandra!)
|
Dec 15 | 4:15 PM |
Mark M. |
Steve: is this work going to continue even after the activity and fragment are destroyed?
|
Rajeef |
No problem
|
Chandra S. |
OK Mark
|
Mark M. |
Steve: usually, with an IntentService, the idea is that the work might take a while, and you want to be fairly decoupled from the UI layer as a result
|
Steve |
Yes. I'd like to download the information from the network and store it for later use. I don't need to keep downloading it each time the activity/fragment start up.
|
Mark M. |
in that case, you probably only want to call startService() once
|
Mark M. |
and in that case, I would lean towards onCreate() rather than onActivityCreated()
|
Mark M. |
well, actually, it's probably a wash
|
Steve |
Ok. The service makes a network call to get a list of contacts, and the contacts are displayed in a list view. Will that work in onCreate?
|
Mark M. |
both will get called if the fragment is destroyed and recreated
|
Mark M. |
the IntentService neither knows nor cares whether you are calling it from onCreate(), onCreateView(), onActivityCreated(), onViewCreated(), or anything else
|
Steve |
I understand that. But how do I control when I get the results back so I can display them?
|
Steve |
Is that by registering the receiver?
|
Susheel | has left the room |
Mark M. |
well, you don't really "control when [you] get the results back"
|
Mark M. |
that will be some time after you call startService(), and the exact amount of time is indeterminate
|
Dec 15 | 4:20 PM |
Mark M. |
ideally, you register the receiver ahead of, or contemporaneously with, calling startService(), in the off chance the service gets done before your receiver is ready
|
Steve |
Ok. Given that the receiver does things with the UI, don't I have to wait for the UI to be created before registering the receiver?
|
Mark M. |
you'll also need to take into account the possibility that the service completes before your ListView is ready, particularly if the IntentService says "oh, hey, I have this stuff cached already, I'm done!"
|
Steve |
That's what I'm asking now: how do I avoid getting the results back before the ListView is ready?
|
Mark M. |
if the UI is not ready, the receiver needs to cache the result, so you can apply it later on
|
Mark M. |
the safest thing is to not call startService() until onViewCreated() or something
|
Steve |
Great. I'll try that.
|
Mark M. |
and decide how you want to handle configuration changes, so you don't call the service twice unnecessarily (e.g., retained fragment and a boolean isTheServiceStarted flag or something)
|
Steve |
Should I register the receiver in onViewCreated as well, or still do that in inResume?
|
Mark M. |
I'd do it in onResume(), personally
|
Mark M. |
don't register it twice
|
Mark M. |
if you run into race conditions where onResume() is too late, then perhaps register it in onViewCreated(), but keep track of whether it is registered and therefore should be skipped in onResume()
|
Mark M. |
there are a lot of moving parts here, and that's why it's a bit difficult to really give definitive answers in the abstract
|
Mark M. |
let me take questions from the others, and I'll be back with you in a bit
|
Mark M. |
Chandra: your turn! do you have a question?
|
Steve |
Sure, thank you.
|
Chandra S. |
I have an activity with a ViewPager, a TabLayout, and 3 fragments inside that.
|
Chandra S. |
When going from fragment 1 -> fragment 2 -> fragment 1, the fragment 1 is not calling onCreate & onCreateView, but when going from fragment 1 -> fragment 2 -> fragment 3 -> fragment 2 -> fragment 1, it's calling a onCreateView only, without onCreate.
|
Chandra S. |
I saw on the fragment lifecycle docs that it's somehow different, can you clarify?
|
Mark M. |
what PagerAdapter subclass are you using?
|
Dec 15 | 4:25 PM |
Chandra S. |
FragmentStatic
|
Mark M. |
sorry, but I do not recognize that class
|
Chandra S. |
Wait, I'm checking
|
Mark M. |
I was expecting FragmentPagerAdapter or FragmentStatePagerAdapter
|
Mark M. |
or your own custom PagerAdapter-from-scratch implementation, perhaps
|
Chandra S. |
Ah yes the FragmentState
|
Mark M. |
FragmentStatePagerAdapter will release the views associated with fragments that are "far" off screen
|
Mark M. |
that's to save on heap space, so those views can get garbage-collected
|
Mark M. |
when you swipe back towards that fragment, FragmentStatePagerAdapter needs to have the fragment re-create the widgets
|
Chandra S. |
So if I have only 3 tabs, better to have FragmentPagerAdapter?
|
Mark M. |
yes
|
Chandra S. |
Is that the only difference?
|
Mark M. |
FragmentStatePagerAdapter is for when you have lots of pages
|
Mark M. |
it's certainly the primary difference
|
Chandra S. |
But why it's calling only the onCreateView not both of onCreate and onCreateView?
|
Mark M. |
because the fragment is still created and not yet destroyed, presumably
|
Dec 15 | 4:30 PM |
Mark M. |
I have not looked at the implementation of FragmentStatePagerAdapter in quite some time, so I do not recall all the details
|
Mark M. |
let me take a question from Rajeef, and I will be back with you in a bit
|
Mark M. |
Rajeef: sorry about the delay! how can I help you today?
|
Chandra S. |
Sure Mark thanks
|
Rajeef |
Hi Mark, Thanks for your time.
|
Rajeef |
View paste
|
Mark M. |
um, I don't know that I'm going to be able to help you here
|
Mark M. |
can you give me a more specific question?
|
Rajeef |
Ok. In that case, do you have any suggestions/resources for implementing infinite scrolling with recyclervie ?
|
Rajeef |
*recyclerview
|
Mark M. |
I'd look for a library for it on the Android Arsenal
|
Mark M. |
well, rephrase that, I'd avoid infinite scrolling
|
Rajeef |
Oh Why's that ?
|
Mark M. |
IMHO, outside of Twitter timeline-like scenarios, it's a failed UI pattern
|
Rajeef |
View paste
|
Dec 15 | 4:35 PM |
Mark M. |
do something else that allows the user to get to the right content, rather than expecting them to scroll and scroll and scroll and scroll and scroll and scroll and scroll and...
|
Rajeef |
How else do you suggest pagination can be implemented
|
Rajeef |
?
|
Mark M. |
by not implementing pagination
|
Mark M. |
if the content is that huge, focus on search, or browsing by category, or something
|
Mark M. |
all that being said, if I had to implement infinite scrolling, I'd look for a library for it on the Android Arsenal
|
Rajeef |
Uhmm.. the Api is implemented in such a way. There are close to 700-1000 records. And its expects an offset value every time a new network call is made :/
|
Rajeef |
Aah. Ok. Library it is then.
|
Rajeef |
Thanks Mark.
|
Mark M. |
if the solution is "force the user to scroll through 1000 entries", then you have more fundamental problems
|
Mark M. |
let me take questions from others, and I'll be back with you in a bit if there's time
|
Rajeef |
Yes Indeed.
|
Mark M. |
Steve: back to you! do you have another question?
|
Rajeef |
Sure.
|
Steve |
I do have a follow-up question on what I was asking about earlier.
|
Mark M. |
go right ahead
|
Steve |
View paste
|
Steve |
I'm wondering what you would recommend in terms of a design for this scenario.
|
Mark M. |
um, a design for what?
|
Steve |
Is it reasonable to use an IntentService in this case?
|
Dec 15 | 4:40 PM |
Mark M. |
you mean to do the network I/O? sure
|
Mark M. |
I assume that "user adds or deletes a contact" is referring to doing this from your app's UI
|
Steve |
Yes.
|
Mark M. |
in that case, when the user adds/deletes a contact, you update your local data store (if relevant), then do the network I/O
|
Mark M. |
all that could be done in the IntentService
|
Steve |
Ok, great.
|
Mark M. |
where the IntentService uses an event bus (you seem to be using LocalBroadcastManager) to let the UI layer know about results, if the UI layer is still around
|
Mark M. |
perfectly reasonable
|
Steve |
Great. Then regarding when to call the service from the fragment, I should look back at our discussion earlier today?
|
Mark M. |
for add/delete, you call the service when the user adds or deletes
|
Mark M. |
that part should be very straightforward
|
Steve |
Right. But I'm wondering about displaying the list of contacts prior to that.
|
Mark M. |
yes, that's what we talked about earlier in the chat
|
Steve |
Great, thanks. I'll review that.
|
Mark M. |
BTW, the chat transcript will be published at https://commonsware.com/office-hours/ shortly after the chat ends
|
Mark M. |
let me take questions from the others, and I'll be back with you in a bit if there's time
|
Steve |
Thank you. I'll plan on getting that.
|
Mark M. |
Chandra: your turn! do you have another question?
|
Chandra S. |
Yes, let say I'm creating a chat app.
|
Dec 15 | 4:45 PM |
Chandra S. |
Is there any way to have like an 'onInternetReconnected' that will send all pending network related task while when the user still disconnected they will have the message they typed saved in local.
|
Dec 15 | 4:45 PM |
Mark M. |
I'm creating a chat app
|
Chandra S. |
Or should we check regularly with service?
|
Chandra S. |
Ah which chat app, Mark?
|
Mark M. |
sorry, that was a joke
|
Mark M. |
you said "let's say I'm creating a chat app"
|
Chandra S. |
Hahaha OK
|
Mark M. |
so I followed the instructions
|
Mark M. |
:-)
|
Rajeef |
:)
|
Mark M. |
anyway, you can monitor connectivity-change broadcasts (see ConnectivityManager), and kick off an IntentService to flush any outstanding work from a queue
|
Mark M. |
usually, when I think of a chat app, though, a loss of connectivity doesn't queue up messages
|
Mark M. |
otherwise, a lot of stale messages might get posted hours later
|
Mark M. |
but, for other scenarios (e.g., Steve's add/delete contacts), if there's no connectivity now, you can watch for connectivity-change broadcasts and try to do the network I/O then
|
Mark M. |
on Android 5.0+, JobScheduler can simplify this a bit, but that's only Android 5.0+
|
Chandra S. |
And is there any way to detect the GPS coming from the real one, not from the apps like FakeGPS?
|
Mark M. |
off the top of my head, I don't know
|
Mark M. |
I haven't looked into that ever
|
Mark M. |
sorry
|
Mark M. |
let me take questions from the others, and I'll swing back to you if I can for any follow
|
Chandra S. |
OK Mark thanks
|
Dec 15 | 4:50 PM |
Mark M. |
er, any follow-up
|
Mark M. |
Rajeef: back to you! do you have another question?
|
Rajeef |
Nope. That's all for today.Thanks.
|
Mark M. |
OK
|
Mark M. |
Steve: do you have another question?
|
Vasanthi | has entered the room |
Steve |
I do have another question:
|
Steve |
View paste
|
Vasanthi |
Hi Everyone
|
Mark M. |
(BTW, Vasanthi, the chat is nearly ending, but I'll try to take a question from you in a moment)
|
Mark M. |
Steve: replace "IntentService" with "Service" and you'll at least be closer
|
Vasanthi |
Oh, sorry, I thought it will be for another 40min
|
Chandra S. |
A very good session, thanks everyone, see you
|
Rajeef | has left the room |
Chandra S. | has left the room |
Mark M. |
IntentService destroys itself after onHandleIntent() returns
|
Mark M. |
regarding the Bluetooth bits, I've never used them, so I don't know how practical what you're describing is, though it seems reasonable
|
Steve |
Ok. Are there examples in your book that I you would suggest I look at?
|
Steve |
For the Service.
|
Mark M. |
well, they're a bit overkill, but the media projection API samples have services that are closer to what you're doing here than the rest of my samples
|
Steve |
Thank you, Mark. I will look at them.
|
Mark M. |
OK
|
Mark M. |
Vansanthi: your turn! do you have a quick question?
|
Vasanthi |
View paste
|
Dec 15 | 4:55 PM |
Mark M. |
I can't really answer 1, 3, and 4, as I don't know your app at all
|
Mark M. |
with regards to 2, AndroidTestCase is for the old JUnit3 instrumentation tests
|
Mark M. |
you're probably wanting to use JUnit4 instrumentation tests or possibly true unit tests
|
Mark M. |
in either case, you don't inherit from any particular class
|
Vasanthi |
So, for JUnit4, which class should I derive frm
|
Mark M. |
tests are handled via annotations with JUnit4
|
Mark M. |
so, you can derive from Object for all the testing stuff cares
|
Vasanthi |
OK, so I don't need to use any of the Android specific test classes?
|
Mark M. |
you might create your own base class for common functionality across your tests, and inherit from that, but that's purely your decision
|
Mark M. |
correct
|
Vasanthi |
Thank you - regarding question 1), my app uses the mainAdapter, which is derived from the baseAdapter. For the unit test, do I need to test both adapters separately?
|
Mark M. |
I can't really answer that
|
Mark M. |
you need to test enough that you think it workse
|
Mark M. |
er, works
|
Mark M. |
if you're aiming for 100% code coverage, presumably you need to test both adapters
|
Mark M. |
whether they can be tested 'separately' depends on a lot on their implentation
|
Vasanthi |
OK. Thank you very much. I will try with JUnit4 and if I have any problems, will attend your tomorrow's session. Thanks a lot
|
Mark M. |
sorry I couldn't be more useful
|
Steve |
Thank you for the session, Mark. Are the chats always one hour long?
|
Vasanthi |
Could you please let me know about the Android training?
|
Mark M. |
almost always
|
Mark M. |
Vasanthi: I am not certain what you mean, exactly
|
Mark M. |
I offer private training to organizations: https://commonsware.com/training/
|
Dec 15 | 5:00 PM |
Mark M. |
if you have questions about that, email me at training@commonsware.com
|
Vasanthi |
Thank you very much. Will mail you. Bye
|
Mark M. |
that's a wrap for today's chat
|
Mark M. |
the transcript will be posted on https://commonsware.com/office-hours/ shortly
|
Steve | has left the room |
Vasanthi | has left the room |
Mark M. |
the next chat is tomorrow at 7:30pm US Eastern
|
Mark M. | turned off guest access |