Make an array of methods to call - android

I have some classes on my code and based on the click of the listview i want to run the selected class. What I mean is that if a user clicks on position 0 I want to run method GoToTown().
I have more than 40 methods so if I do it with if / elseif it would be really bad. I have a custom adapter for the listview so probably I could use it somehow?
Is there some way I could pass an array to do it?

Instead of using 40 different methods, use only 1 (the one which should get the listview result and call one of the 40 methods). And then you should have a switch inside it:
// "number" is the number if the item clicked in the list
switch (number) {
case 0: // First item was clicked, counting starts from 0
// Put some code here
break;
case 1: // Second item...
// Other code
break;
// Etc.
default: // You did not define a "case" for a number, default gets executed
// Code here
}

Related

RecyclerView and Adapter data updates

This is a question about RecyclerView internal behavior for someone that knows its mechanics or is willing to dig into the source code. I’d like an answer backed up by references to the source.
Original question
(scroll down to ‘In other words’ for a more focused question)
I need to understand how notify* actions (for example, notifyItemInserted()) are enqueued. Imagine I have an adapter backed up by this list:
ArrayList<String> list = Arrays.asList("one", "three", "four");
I want to add the values zero and two, that are missing.
Example 1
list.add(1, "two");
// notify the view
adapter.notifyItemInserted(1);
// Seconds later, I go on with zero
list.add(0, "zero");
// notify the view
adapter.notifyItemInserted(0);
This is pretty straightforward and clear, nothing to tell.
Example 2
But what if the two actions are very close to each other, and there’s no layout pass in between?
list.add(1, "two");
list.add(0, "zero”);
What should I do now?
adapter.notifyItemInserted(1);
adapter.notifyItemInserted(0);
Or maybe
adapter.notifyItemInserted(2);
adapter.notifyItemInserted(0);
? From the adapter perspective, the list immediately switched from one, three, four to zero, one, two, three, four so the second option seems more reasonable.
Example 3
list.add(0, “zero”);
adapter.notifyItemInserted(0);
list.add(2, “two”);
adapter.notifyItemInserted(...)
What about it now? 1 or 2 ? The list was updated immediately after, but I am sure there was no layout pass in between.
Question
You got the main issue, and I want to know how should I behave in these situations. The real case is that I have multiple asynchronous tasks ending up in an insert() method. I can enqueue their operations, but:
I don’t want to do that if there’s already an internal queue, and there surely is
I don’t know what happens if two actions happen without a layout pass in between, see Example 3.
In other words
To update recycler, 4 actions must happen:
I actually alter the data model (e.g. insert something into the backing array)
I call adapter.notify*()
Recycler receives the call
Recycler performs the action (e.g. calls getItem*() and onBind() on the adapter) and lays out the change.
It’s easy to understand this when there’s no concurrency, and they happen in sequence:
1. => 2. => 3. => 4. => (new update) 1. => 2. => 3. => 4. ...
Let’s see what happens between steps.
Between 1. and 2.: I would say it is the developer responsibility to call notify() immediately after having altered the data. That’s OK.
Between 2. and 3.: This happens immediately, no issue here.
Between 3. and 4.: This does not happen immediately! AFAIK. So it perfectly possible that a new update (steps 1 and 2) comes between steps 3 and 4 of the previous update.
I want to understand what happens in this case.
How should we behave?
Should I ensure that step 4 of the previous update did took place before inserting new stuff? If so how?
I thought about similar questions before, and I decided:
If I want to insert more than 1 item directly to end of list and
want to get a animation for all, I should:
list.add("0");
list.add("1");
adapter.notifyItemRangeInserted(5, 2); // Suppose there were 5 items before so "0" has index of 5 and we want to insert 2 items.
If I want to insert more than 1 item directly to end of list, but
want to get separated animation for each inserted item, I should:
list.add("0");
list.add("1");
adapter.notifyItemInserted(0);
mRecyclerView.postDelayed(new Runnable() {
#Override
public void run() {
// before this happens, Be careful to call other notify* methods. Never call notifyDataSetChanged.
adapter.notifyItemInserted(1);
}
}, mRecyclerView.getItemAnimator().getAddDuration());
If I want to insert more than 1 item to different position of list,
similar as 2.
Hope this can help.
So lets start from little intro to RecyclerView works with notify items. And works pretty simple with other list of saved ViewGroup items (ListView for ex.)
RecyclerView has Queue of View Items which already drawn. And doesn't know about any your updates, without calling notify(...) methods. When you added new Items and notify RecyclerView, it starts cycle for checking all Views one by one.
RecyclerView contains and drawn next objects
View view-0 (position 0), view-1 (position 1), View-2 (position 2)
// Here is changes after updating
You added Item View view-new into (position 1) and Notify
RecyclerView starts loop to check changes
RecyclerView received unmodified view-0(position-0) and left them;
RecyclerView found new item view-new(position 1)
RecyclerView removing old item view-1(position 1)
RecyclerView drawing new item view-new(position 1)
// In RecyclerView queue in position-2 was item view-2,
// But now we replacing previous item to this position
RecyclerView found new item view-1 (new position-2)
RecyclerView removing old item view-2(position 2)
RecyclerView drawing new item view-1(position 2)
// And again same behavior
RecyclerView found new item view-3 (new position-3)
RecyclerView drawing new item view-1(position 2)
// And after all changes new RecyclerView would be
RecyclerView contains and drawn next objects
View view-0 (position 0), view-new (position 1) view-1 (position 2), View-2 (position 3)
It's just main flow of working notify functions, but what should know all this actions happens on UI Thread, Main Thread, even you can calling updating from Async Tasks. And answering you 2 Question - You can call Notify to the RecyclerView as much as you want, and make sure, you action would be on the correct Queue.
RecyclerView works correct in any usage, more complicated questions would be to your Adapter work. First of all, you need to synchronize you Adapter action, like adding removing items, and totally refuse of index usage. For example, it's would be better for your Example 3
Item firstItem = new Item(0, “zero”);
list.add(firstItem);
adapter.notifyItemInserted(list.indexOf(firstItem));
//Other action...
Item nextItem = new Item(2, “two”);
list.add(nextItem);
adapter.notifyItemInserted(list.indexOf(nextItem))
//Other actions
UPDATE |
Related to RecyclerView.Adapter Doc, where you can see functions same with notifyDataSetChanged(). And where this RecyclerView.Adapter invokes child items with android.database.Observable extensions, see more About Observable. Access to this Observable Holder is synchronized, until View Element in RecyclerView release usage.
See also RecyclerView from support library version 25.0 Lines 9934 - 9988;
It should not be a problem if you make multiple updates between layout passes. The RecyclerView is designed to handle (and optimize) this case :
RecyclerView introduces an additional level of abstraction between the
RecyclerView.Adapter and RecyclerView.LayoutManager to be able to
detect data set changes in batches during a layout calculation. [...]
There are two types of position related methods in RecyclerView:
layout position: Position of an item in the latest layout calculation. This is the position from the LayoutManager's
perspective.
adapter position: Position of an item in the adapter. This is the position from the Adapter's perspective.
These two positions are the same except the time between dispatching
adapter.notify* events and calculating the updated layout.
In your case the steps are :
You update the data layer
You call adapter.notify*()
The recyclerview record the change (in AdapterHelper.mPendingUpdates if I understand the code correctly). This change will be reflected in ViewHolder.getAdapterPosition(), but not yet in ViewHolder.getLayoutPosition().
At some point the recyclerView apply the recorded changes, basically it reconcile the layout's point of view with the adapter's point of view. It seems that this can happen before the layout pass.
The 1., 2., 3. sequence can happen any number of times as long as 2. immediately follows 1. (and both happen on the main thread).
(1. => 2. => 3.) ... (1. => 2. => 3.) ... 4.
Item firstItem = new Item(0, “zero”);
list.add(firstItem);
adapter.notifyItemInserted(list.indexOf(firstItem));
//Other action...
Item nextItem = new Item(2, “two”);
list.add(nextItem);
adapter.notifyItemInserted(list.indexOf(nextItem))

Change fragments from spinner drop down items?

My activity has a spinner and an empty container where fragments should be added when selecting drop down items from spinner.
My plan was to try to make switch construction inside into override method "public void onItemSelected()", where each case represents one drop down item from spinner, and sets correct fragment into container, like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
switch (itemSelectedFromSpinner) {
case "first item": // First and second item put same fragment into container, but do other methods when used
case "second item": // my code
}
My other taught was to put it in if construction like this:
String itemSelectedFromSpinner = parent.getSelectedItem().toString();
if (itemSelectedFromSpinner.equals("first item") || itemSelectedFromSpinner.equals("second item")){
// my code }
Since I've never done something like this, and I believe you can understand from my question what needs to be done, tell me what is the best practice to do that.
Am I doing it right by putting a String itemSelectedFromSpinner into switch construction? Also if user selects one item and first fragment is loaded, when selecting other item will the first fragment disappear and put second fragment into container automatically? (sorry if this sound little silly to you, I have lack of experience with fragments)
Don't use string like "first item" in code directly - move them to string resources.
For components like spinner use Adapter. The concept of Adapters use widely in Android so it's a good idea to be familiar with it. And also it allow you to compare your data by some integer asigned id's, and not by strings (which is unefficient, slow and ugly - correcting string representation everywhere is hard).
To replace or add Fragments dynamically use FragmentManager. See the simple replace() / add() / commit() code

RecycleView update dataset and adapter from another fragment

I'm on a fragment activity that have 3 fragments. each fragments implement Recycle-view and it's adapter. I wanna delete from a list and then update another fragment lists but I have problem in this.
public void deleteIt(View v) {
ZeroActivity.dao.deleteMessages(serverId); // delete the mesage from DB
switch (TransactionActivity.current_tab) {
case 0:
ZeroActivity.itemData_1.remove(data_list_position); // delete the message from arrayList
ZeroActivity.recycleViewAdapter1.notifyItemRemoved(data_list_position); //remove message from Adapter
break;
case 2:
but always after first deletion and select final item app crashes and get this error:
Invalid index 4, size is 3
It seems that problem is with index of Adapter and Arraylist that wont match!
You are removing two times with same index value thats why you are getting error in second time.. just do this . i hope it will work
ZeroActivity.itemData_1.remove(data_list_position); // delete the message from arrayList
ZeroActivity.recycleViewAdapter1.notifyDataSetChanged();
or call only this line
ZeroActivity.recycleViewAdapter1.notifyItemRemoved(data_list_position)
I finally solve this problem by refresh Recycle-view item counts by adding:
notifyItemRangeChanged(data_list_position, newsize);
it seems that Recycle-view needs to update it's list count after remove or adding new items.
ZeroActivity.itemData_1.remove(data_list_position); // delete the message from arrayList
ZeroActivity.recycleViewAdapter1.notifyItemRemoved(data_list_position); //remove message from Adsapter
ZeroActivity.recycleViewAdapter1.notifyItemRangeChanged(data_list_position, ZeroActivity.itemData_1.size());

How to change button background when button state is changed

Wondering how can I change my button background programmatically by setting onClickListener.
I mean that when I firstly pressed my button it changes its background image and save it even if I release finger from it. And then if i press it the second time it must change background image again. I know that I must check what background is there at the moment but can't understand how to do it.
I've tried use getBackground method but it wasn't helpful for me. I even tried to create an XML file with selector which contains three state of my button, but it worked only until the moment I release finger from button.
You could have a global variable storing the background int:
private int backgroundNumber = 0;
Then, in onClick() you could do something like this:
backgroundNumber++;
switch (backgroundNumber % numberOfBackgrounds) { // numberOfBackgrounds is a constant of how many backgrounds there are
case 1:
button.setBackgroundResource(R.drawable.background1);
break;
// Do cases for all the backgrounds
}
I think that should work.
Try like this.
You know how many states you have. Use an int variable (lest say buttonState) to save button state (ex. states 1,2,3. MAX_STATE = 3).
On click just change state and replace background depending on the current buttonState variable value.
#Click(R.id.button_action)
void onButtonActionClicked() {
buttonState = ++buttonState % BTN_STATE_MAX;
switch (buttonState){
case BTN_SAVE:
button.setBackgroundResource(R.drawable.button_save);
break;
case BTN_LOAD:
button.setBackgroundResource(R.drawable.button_load);
break;
case BTN_DELETE:
button.setBackgroundResource(R.drawable.button_delete);
break;
}
}

ListView.onSaveInstanceState() doesn't work if ListView is not scrolled

I recently got a problem in saving the position of the ListView. I'm using Parcelable state = ListView.onSaveInstanceState() to store the state of the ListView. and using fileView.onRestoreInstanceState(state) to restore the position when needed.
But it stores the value in state only if atleast one item is scrolled in the ListView. So if users don't scroll the ListView and ListView.onSaveInstanceState() is used, it restores nothing on fileView.onRestoreInstanceState(state) and old position remains on the screen.
Is there any particular reason behind it or Am I doing something wrong here ?
Comment from android source of AbsListView.onSaveInstanceState():
// Remember the position of the first child.
// We only do this if we are not currently at the top of
// the list, for two reasons:
// (1) The list may be in the process of becoming empty, in
// which case mItemCount may not be 0, but if we try to
// ask for any information about position 0 we will crash.
// (2) Being "at the top" seems like a special case, anyway,
// and the user wouldn't expect to end up somewhere else when
// they revisit the list even if its content has changed.

Categories

Resources