In my application I use the ListView container with quite a lot of data. In order to provide the possiblity of item management, I use the MultipleChoiceMode to let user choose several items and copy/delete them.
I do it by setting the:
listView.setChoiceMode(GridView.CHOICE_MODE_MULTIPLE_MODAL);
listView.setMultiChoiceModeListener(listener);
Where listener implements a AbsListView.MultiChoiceModeListener.
Since it is a useful feature, I decided to add the possibility to select/deselect all list elements at once.
Currently it is done with following code (I simplified the code to show the main concept):
private void selectAll() {
for(int i = 0; i < listView.getCount(); i++) {
listView.setItemChecked(i, true);
}
}
Unfortunatelly this solution's performance is strictly item amount-dependent.
Could you, please advise me how to do it better/more scallable?
For, let's say 15000 elements, current solution is extremely laggy.
PS. I can't change the container type (e.g. to RecyclerView)... :(
Being the efficiency freak that I am (as I'm sure lots of you all are as well), I've wondered this for a while and just thought of posing this question:
Two scenarios, possibly two different answers.
If I'm looping through a list of items and updating a bunch of EditTexts in a LinearLayout as such, what are the pros/cons of these two methods:
1)
for (int i = 0; i < itemList.size(); i++) {
((TextView)findViewById(itemList.get(i).getId())).setText(itemList.getText());
((TextView)findViewById(itemList.get(i).getId())).setColor(itemList.getColor());
}
2)
TextView tv;
for (int i = 0; i < itemList.size(); i++) {
tv = ((TextView)findViewById(itemList.get(i).getId()));
tv.setText(itemList.getText());
tv.setColor(itemList.getColor());
}
I think the underlying question is how efficient is "findViewById"? This may be picky, I think 2) is the better solution. Thanks!
With your second option you save:
- A call to findViewById()
- A call to itemList.get(i)
- A call to [itemList.get(i)] getId()
Also, note that in a for loop usually going backward is a little bit faster (more optimized) than going forward (because i < value translates to i-value < 0, which is more expensive than i > 0).
This is not picky at all. 2nd option is without doubt the better one.
1
for (int i = 0; i < itemList.size(); i++) {
((TextView)findViewById(itemList.get(i).getId())).setText(itemList.getText());
((TextView)findViewById(itemList.get(i).getId())).setColor(itemList.getColor());
}
Looks clean, but isn't. If you are working with one and the same textview, absolutely do not call findViewById more than once.
2
TextView tv;
for (int i = 0; i < itemList.size(); i++) {
tv = ((TextView)findViewById(itemList.get(i).getId()));
tv.setText(itemList.getText());
tv.setColor(itemList.getColor());
}
This is the better option, because it only calls findViewById once. It's a little less readable, though.
You could also consider a 3rd option
for (int i = 0; i < itemList.size(); i++) {
TextView tv = ((TextView)findViewById(itemList.get(i).getId()));
tv.setText(itemList.getText());
tv.setColor(itemList.getColor());
}
This keeps everything in the loop (easier to read, imo) without notably sacrificing efficiency. I prefer the 3rd, but the 2nd is a good pick as well.
A google employee Dianne Hackborn has answered a very similar question here.
She says that you should avoid using findViewByid repetitevely whenever you can.
I think without doubt the second option is better.
Not only you save the cost of calling findViewById one extra time (ok at the cost of one extra local variable)
but the code is also much more readable.
You should use new RecyclerView if possible now. Combined with LinearLayoutManager it'll allow you achieve the same, but you'll be forced to use ViewHolder pattern.
If you go with your ListView, you should also implement ViewHolder. findViewById is definitely not efficient, so you need to prevent too many calls to it.
Second way is better, because the cost of findViewById() is acceptable in static UI layouts. However, since getView() is called frequently, the usage of findViewById() should be kept to minimum.
The second is for sure less expensive by 50%.
But I would prefer #TimCastelijns 3rd method because he is dumping the view reference at the end of the loop.
In the first method, you use findViewById twice.
In the second method, you use it once and save a reference to it, which saves 50% off of the resource usage.
I preferred #TimCastelijns, because he saves it as a local variable, which will be dumped, therefore saving resources.
I have a TableView that I programmaticly add in rows to using a simple loop like the follows
LayoutInflater inflater = LayoutInflater.from(getActivity());
for (int i = 0; i < dataList.size(); ++i)
{
DataEntry dataEntry = dataList.valueAt(i);
// Add the row
TableRow row = (TableRow)inflater.inflate(R.layout.data_row, _table, false);
_table.addView(row);
row.setTag("dataRow");
// Code that looks up the 'column' views and sets their value to the dataEntry
}
This is working great and populates the list just fine. But when I am changing data sets I want to clear out all the added rows except the header (first) row and then add in a new set of rows (ie run the code above). So I have this little bit of code:
final int childCount = _table.getChildCount();
for (int i = 1; i < childCount; ++i)
{
View view = _table.getChildAt(i);
_table.removeView(view);
}
This is the most simple version of this loop I could do. I was originally checking for null views because they showed up on resuming the task. I would then check to see if the tag for the view was == "dataRow" and only remove the data rows. But this all gives me the same result. It will skip the header (I am starting at 1 instead of 0) and remove the next row and then every other row from there on down. If I run this loop enough times it Will clear the list but adding a while(_table.getChildCount()>1) just seems like I am clearly just doing this wrong. _table.removeAllChildren() will not work as the header is then removed as well (And it likes to rip apart view hierarchies which is also not what I want done).
I am just not seeing what it is I am doing wrong. I am hoping a second (or second hundred) set of eyes may see the flaw in my thinking.
And I figured it out around 4min after I asked this.
I am looping through the children and removing them. The parent is then sliding all of the entries down. Changing the for loop to the following works (and is ironicly turning this for loop into a while (>1) I thought seemed like a bad idea.
for (int i = 1; i < childCount; ++i)
_table.removeView(_table.getChildAt(1));
This makes sense now that I look at it.. I should probably remove from the bottom up for a bit cleaner code and should have remembered this is the proper way to remove items from a linked list which may very well be what the children list is.
Hey I am making an android app that will have ~256 buttons.
Because I dont want to write the very same code for everyone of these I thought it might be possible to realize an easier solution via arrays. My approach in the onCreate to set the listeners was:
1 for (int i=1; i<32; i++)
2 {
3 button[i] = (Button)findViewById(R.id.button[i]);
4 button[i].setOnTouchListener(this);
5 }
I set the Button[] like that: Button[] button=new Button [64];
Now, eclipse tells me in line 3 "button cannot be resolved or is not a field" and it just underlines the word "button", so I think it ignores/just does not recognize the [i] (array)-stuff.
The rest of my code seems to get on with that perfectly because it gets recognized as an object (correct me if I said that wrong) but the findViewById() doesn't get on with it ..
Thanks for the replies, Alex
You can't do what you proposed in your solution. A better way to go about it is to add the buttons dynamically in code. For instance,
View parentView = (LinearLayout) findViewById(R.id.parentView);
// declare button array above
for (int i=1; i<32; i++)
{
Button btn = new Button(context);
// EDIT: adding a background resource
btn.setBackgroundResource(R.layout.button_layout);
btn.setText("This is my text");
btn.setOnTouchListener(this);
button[i] = btn;
}
User "Horschtele" answered it in a perfect way but he deleted his answer on his own (don't know why).
Horschtele, if you read that, I just want to say that this solution is just perfect!
I have to (or at least I think I have to) do this for every tableRow but this saves me an infinite amount of time. Thanks again Horschtele (are you german? :))
My modified version of Horschtele's answer if you already have your buttons in a table:
ViewGroup container = (ViewGroup) findViewById(R.id.tableRow1);
for(int i=0; i<container.getChildCount();i++){
System.out.println(container.getChildCount());
Button button = (Button)container.getChildAt(i);
button.setOnTouchListener(this);
}
(don't wonder about the println, you can easily check if the system correctly recognizes the container you are refering to).
If you did it my way with an array of Button then this is the way to go:
button[i] = (Button)container.getChildAt(i);
button[i].setOnTouchListener(this);
I need to access the children controls of a parent control. The code i am using is:
for (int index = 0; index <= parent.getChildCount() - 1; index++)
{
Log.d("myTag", parent.getChildAt(index).toString());
}
It works fine however i was looking for something like:
foreach(control ctl in parentControl.Children)
{
Log.d("myTag", ctl.toString());
}
Thanks in advance for your valuable time & help.
Since you can only access the children of a view using the method getChildAt(), you won't be able to use it in such a foreach loop.
However, if you really want it, you could make a list of the children, then iterate over it this way:
for(View child : childs)
(Thats the syntaxs of foreach loop in java)
But it's not needed and you will be wasting time and memory doing so. Just use the for loop.