Say like LiveData<List<Item> is received from remote and few of the Item's properties are calculated in the device based on some conditions.
What would be the best practices to do so. Please suggest.
Say e.g., Item class as below:
class Item{
int id
String name
float location
float distance
}
in which the id, name and location of each Item are received from remote but the distance to be calculated from location locally. How to calculate the distance and return a LiveData which can be supplied back to the View?
Appreciate your time and input.
You cant manipulate LiveData, it is an immutable data type. If you want to work with mutable live data you must use the MutableLiveData.
This document told everything about it. https://developer.android.com/topic/libraries/architecture/livedata
Related
I am new to Epoxy and I'm currently trying some use-cases to check if it's a good fit for my project. I understand that the data that are set to a Controller should be immutable.
In my case I have a View with several toggles and checkboxes and I want to keep track of the user's interactions, because based on those interactions I need to create my network request, later on. I have searched a lot in the documentation and sample projects of Epoxy but haven't found an example with the proper way to do such a thing.
What is the correct way for the user's interaction to change the data model that my controller has.
After a lot of searching, it turns out that you should copy (shallow not deep) the list past to the controller, and change the data (interacted by the user) to the new one, and the pass it again to the controller. I just put this answer here for anyone that stumbles upon this post
val newList = ArrayList(originalList.map { it.copy() })
newList.find { it.id == event.data.id }?.isMainToggleOn = !event.data.isMainToggleOn
controller.setData(newList)
originalList = newList
I want to sort a list of elements by comparing the distance between their variables to a fixed variable that I have.
My variable is the current coordinates of my device. Something like that:
class Location(var lat: Double, var Long:Double)
myLocation = Location(-33.07216, -36.70315)
val locations = mutableListOf(Location(-23.23018, -48.50247),Location(8.3334, 49.04748),Location(61.82096, 50.45373))
And I have a list containing several of these Classes.
I want to sort the list based on the Classes that are closest to my current location
How can I do this? I'm pretty lost with Comparator, SortBy and SortWith
sortWith is for using a Comparator class. You will typically use that only when you're setting up complex relations, like sorting by one thing followed by another thing in case of a tie on the first thing.
sortBy lets you write a simple lambda that selects a value that it will use to compare the items. In this case, you want to sort by distance to a location, so your lambda should return that distance.
locations.sortBy { it.distanceTo(myLocation) }
If you want farthest distances first, use sortByDescending instead.
Context
So, I don't know if any of you has ever gone through the same situation but I've recently taken over an app and we have this RecyclerView on the main screen - because of an NDA I'll change a few things - that shows a list of apartments that you can rent - picture the AirBnB app - and if you tap on one of these apartment items you go to the apartment detail, where you have a bit more of functionality and features.
The thing is that we have way too many moving parts on the apartment list. For example, on each apartment ViewHolder you can:
Use a checkmark to specify if you are going to bring any pets with you.
A few UI items to specify how long are you going to stay.
An EditText to set how may people are going to come.
A Rent button that turns itself into a spinner and sends an API call.
A More Options button that expands the ViewHolder, showing a LinearLayout with yet more UI.
Picture something like this
This is actually a simpler example of what I really have. Let me tell you that it looks as if each ViewHolder could be a Fragment because of all the functionality that we have on each.
Now what's the problem here?
Recycling issues. If you scroll off, and scroll back to the same position you are supposed to keep the same state that you had on that ViewHolder, right? If you had checked a CheckButton that's supposed to be check. If you had written something on an EditText, that's supposed to be there. If you had expanded the More Options section, that's supposed to be expanded. You see where I'm going at?
What am I asking here?
Well, about feedback for a possible solution or improvement. I know what most of you would tell me here - because it is the same thing I thought at first - just move all that functionality into the apartment detail, keep that list as simple as possible. But it is not as simple, we have a large user base who is already used to this UI. Changing things so abruptly is not an option.
What do I have right now?
In my RecyclerView adapter I keep a collection of "State" objects which I use to save/restore the ViewHolder states, but it is getting way too big and way too complex. This may sound crazy, but it is there such thing as having a RecyclerList of Fragments? I just don't want to worry/bother about keeping the states of these ViewHolder anymore.
Notes
Sorry I haven't provided any code, but there's not much to show actually, as you may imagine the onBindViewHolder is just a humongous piece of code that sets the views with the data I fetch from the API plus the data that I store in these "State" objects. I save these "State" objects via the onViewDetachedFromWindows() hook from the adapter class that gets triggered when a ViewHolder scrolls off from screen. I wipe out these "State" objects when I fetch a new API response.
Any feedback is appreciated,
Thanks!🙇
Your post is vague in it's high-level description but I'll try to comment in a similar manner that may guide you towards solutions.
First, as was already mentioned Epoxy is a thing. As is adapter delegates. You may find those useful. However, you don't need a library to solve you problem - you need separation of concerns and architecture.
The thing is that we have way too many moving parts on the apartment list.
OK, so first suggestion is to stop having too many moving parts in the list. Each thing you listed could / should be it's own (custom) view that is driven by it's own ViewModel. A recycler view / view holder / adapter should be as stupid as possible. All those things should be doing is filling in boilerplate that Android requires. Actual logic should exist elsewhere.
If you scroll off, and scroll back to the same position you are supposed to keep the same state that you had on that ViewHolder, right?
No. Your ViewHolder should not maintain state. A ViewHolder holds views so Android doesn't have to re-inflate stuff over and over. It should not keep track of its state - it should be told what its current state is.
You should have a list of data objects (view models) that represent the current state of each item in the list. When you scroll off and back to the same position, you are supposed to re-bind the item that should be at that position to the view that represents it. Saving and clearing "state" objects should not be necessary - you should always have the current state on hand because it's the underlying data model driving your whole UI.
In my RecyclerView adapter I keep a collection of "State" objects which I use to save/restore the ViewHolder states, but it is getting way too big and way too complex
If something is too big and complex, break it down. Instead of having one giant-ass state object for each item, use composition. Make this item state have properties that represent pieces of the UI - PetModel, DateRangeModel, etc.
This may sound crazy, but it is there such thing as having a RecyclerList of Fragments? I just don't want to worry/bother about keeping the states of these ViewHolder anymore.
That does sound crazy because not only would this not solve your problem, you would probably actually make it significantly worse. You don't want to manage the state of a bunch of ViewHolders but you want to manage the states of a bunch of Fragments!? Bruh.
as you may imagine the onBindViewHolder is just a humongous piece of code that sets the views with the data I fetch from the API plus the data that I store in these "State" objects.
Again, break that up. You should not be slapping "data I fetched from the API" directly onto views. Invariably you will need to massage and transform raw data from an API before you display it. This should be handled by a dedicated object (again, ViewModel or some other structure). Again, views should be dumb. Tell them their state and that's it - don't do logic at this level.
Please read the Android Architecture Guide.
Also Google around for "Clean Architecture" - that seems to be all the range in Android these days.
And finally - here's some very rough pseudocode of how you could structure this to be more testable and maintainable.
From the bottom up:
ApiClient - responsible for just fetching the raw data from the API
endpoint or reporting an error.
ApiResponseModel - language-specific object representation
of the data you'll get from the API. Has info on the pet, dates,
guest count, etc. May contain submodels.
ItemDomainModel - client side representation of your data after transforming the data you'll get from the API.
Repository - uses the ApiClient to fetch the data as ApiResponseModel and transforms it into a ItemDomainModel object that makes more sense for your app.
ItemViewModel - Represents the UI state of a single item in the RecyclerView. Takes a ItemDomainModel instance and exposes the state of the UI based on the state of that model. This can be broken down if it's too complex (PetStateViewModel, DateRangeViewModel, GuestCountViewModel, etc)
ListViewModel - The top-level Android ViewModel that represents the state of the screen. Uses the Repository to fetch the data then constructs a list of ItemViewModels to feed into the RecyclerViewAdapter.
If you get those pieces in place, your view binding in the adapter should be stupid dumb:
override fun onBindViewHolder(viewHolder: ViewHolder, position: Int) {
// The adapter list should be a list of view models populated by the
// fragment after the ListViewModel returns a list of them from the fetch
val itemViewModel = itemViewModels[position]
// Populating this item view should just be a one-to-one mapping of the view model
// state - NO LOGIC. Dumb. Stupid. Tonto.
viewHolder.bringingPets.isChecked = itemViewModel.isBringingPets
viewHolder.guestCount.text = itemViewModel.guestCount
// ... etc, etc (if you use databinding this is a one-liner and even stupider)
// Set up your event listeners so interacting with this specific item in the list
// updates the state of the underlying data model
viewHolder.bringingPets.setOnCheckChanged { itemViewModel.isBringingPets = it.isChecked }
viewHolder.rentButton.onClickListener { itemViewModel.rentThis() }
// ... etc, etc
}
The goal is to do as little as possible here. Just update the state and wire up your callbacks that just delegate back to the ViewModel. Then, those UI states are driven by the logic in the view model. This is where you do business logic that determines how the UI should look.
class ItemViewModel(private val dataModel: ItemDomainModel) {
var isBringingPets: Boolean
get() = /* some business logic that determines if the checkbox is checked */
set(value) /* update underlying state and notify of changes */
// ... etc, etc, for guest count and other properties
fun rentThis() {
// Fire an event or update live data or invoke a callback that
// the fragment can use to respond
}
// ... etc, etc, for other functions that respond to UI events
}
In Summary
Refactor your code to break down the huge and complex logic into dedicated components that each have a simpler, specific focus, then compose them together to get the behavior you want. Good luck.
A ParseObject only get's it's ObjectId once it's been saved. If I were offline at the time of creation and couldn't save the object, how can I refer to it whilst still being offline?
For instance how do I set a comment's postId when I don't yet know the
post's Id?
I was considering making a separate attribute called TempId in my extended ParseObject, which I can put an exclusively locally known Id that it can refer to until it has a proper Object Id, but is this the best choice I have?
The following concern me with this:
Double Handling
Maintaining all the callbacks necessary to make sure I update the correct object in the correct order.
Thanks!
Here's my rule of thumb: think objects, not objectIDs.
var post = // ... this is a real object, not an id. it can be new
var comment = // ... this is a real object, not an id. it can be new
// posts might have many comments by keeping an array of pointers called "comments"
post.add("comments", comment);
// or posts might have many comments by keeping a relation called "comments"
post.relation("comments").add(comment);
After doing this, the parent side of the relationship can be saved, and the unsaved children will be saved, too:
post.save().then(function(savedPost) {
// savedPost now has savedPost.id, but who cares?
// if comments is an array of pointers:
// savedPost.get("comments") now has comments with ids, but again, we don't need them.
// if comments is a relation:
return savedPost.get("comments").query.find();
}).then(function(comments) {
// comments is an array of objects with ids, but again, if I find
// myself needing those ids, its a clue that my design is weak
});
I am making a calculator same as windows calculator. I am stuck at the history feature. which would be the best UI control to implement the history feature as seen in Windows Calculator?
I also want onclick events on the history.
I'm not sure how you represent a calculation, but you could have a simple class like this:
enum Operator {PLUS,MINUS,DIV,MULT};
class Calculation {
float operand1,operand2;
Operator operator;
public Calculation(float op1,float op2,Operator operator){
this.operand1=op1;
this.operand1=op2;
this.operator=operator;
}
}
Then when a calculation is done, create an object of this type and add it to an ArrayList:
List<Calculation> history = new ArrayList<Calculation>();// history
history.add(new Calculation(5,5,Operator.PLUS));// add a new `Calculation` to our list
Then access the list with history.get(some_integer), based on your UI.
Could you just use a List containing a number of previously entered calculations? If you knew the maximum possible history size in advance, you could just stick with a normal array, but a List will give you more flexibility.
You need to store all the operations and results with an index here. Increase the index every time when you perform an operation.To retrieve the past operation, manipulate the index and you can get the values.You can use Collection API for storing the operations.