Use "findViewById" repetitively or store View? - android

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.

Related

How do i call many views at one?

My problem is, i have to make a timetable. For that i made the raw table with the Views i need. For changing the subjects i made Spinners. For just looking at it i use TextViews. In total there are over 100 Views. I don't want to write ervery Id in an Array for calling them. So i want to do that programaticly. How do i call them?
I tried using findViewByTag() but it could't resolve it. I asumed then, that it is not working or there no explanation for it.
Also i have tried to call them in a for loop
for (int i =1; i <100; i++){
String ViewGetter = "R.id.View"+i;
View v = this.findViewById(ViewGetter)
}
logocaly it doestn't work, because findViewById() needs an integer. Is there a way i can call them like this?
Making a loop for getting the Id in an Array doesn't work because the Ids aren't for some reason cronological.
EDIT: as sugestet in the comment, that i could use reflections, i tried it. It hasn't worked. But it may be because i have never worked with reflections before. If this is the anwser. than i would be glad to have an explanation for it.
You can use getChildAt()
for (int i = 0; i < view.getChildCount(); i++) {
view.getChildAt(i)...
}

Exploring the view tree in Android

It's maybe a bit abstract but I'd like to know if some of you have a decent solution to that problem:
I have a complicated layout and I need to find ALL the instances of a certain type of view.
I have few solutions but I find none of them perfect and I'd like to know if there is another way or a way to improve them.
MY FIRST OPTION
We can iterate in the view tree with getChildCount() and getChildAt() and then check with instanceof like in lots of SO answers.
for (int i = 0; i<parentContainer.getChildCount(); i++){
View child = getChildAt(i);
if (child instanceof BlaBla){
// Do something wonderful
}
}
It is highly inefficient because I have these instances in many places and in particular in nested places so I need to make this method recursive.
MY SECOND OPTION
It would be to work with dynamic tags or ids and use findViewById or findViewWithTag. But the issue is that it makes something more to configure and as always it makes the software more complicated.
So my question is: how can I do a complete search in the view tree in
order to find all instances of a component without doing the search
myself (because it would be probably be very inefficient)? Is that
somehow possible?
So, I'm not sure second option is possible as in this case you'll need to create this views in runtime and assign some generated ID with some bit mask to recognize them later. If to create your views from layout you will end up with traversing tree view and assigning these special IDs which is pretty much accends to 1st option.
In my project I also have to dynamically apply colors to some views and I do it without recursion. Pattern is following:
ArrayList<View> views = new ArrayList<>();
views.add(getWindow().getDecorView());
do {
View v = views.remove(0);
if (v instanceof ViewGroup) {
ViewGroup group = (ViewGroup) v;
for (int i = 0; i < group.getChildCount(); i++) {
views.add(group.getChildAt(i));
}
}
if (v instanceof MyCustomView) {
//do whatever you need here
}
} while(!views.isEmpty());
So you get rid of using recursion and replace it with own stack and iteration through it. This solution quite efficient especially if you can skip things like ListView, RecyclerView.

Performance: How to select all ListView elements programmatically in efficient way?

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)... :(

Android access each child control of a parent control

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.

Android dynamic table layout - adding views throws exception IllegalStateException (child already has a parent)

Creating a layout in Java as the number of TableLayouts required is not known as designtime.
I get an IllegalStateException telling me to remove the View (from it's current parent) before assigning it to another parent, when I call createPlayerTables()
The exception is thrown at the first line in this loop, when I try to add an ImageView from the List of ImageViews to the first TableRow:
for (int i = 0; i < 3; i++) {
tableRowsLst.get(0).addView((ImageView) imageViewsLst.get(i));
tableRowsLst.get(1).addView((ImageView) imageViewsLst.get(i+3));
}
The error suggests that the ImageView has already been added to a ViewGroup, but seeing the code below, I create new ImageViews, and I only add them to an ViewGroup at the line that it errors at, so I'm not sure why it's failing.
// List<ImageView> imageViewsLst = new ...
// List<TableRow> tableRowsLst = new ...
/**
* Initialises the TableLayouts, one per player
*/
private TableLayout createPlayerTables(int playerNum) {
...
for (int i = 0; i < 6; i++) {
imageViewsLst.add(new ImageView(this));
...
}
for (int i = 0; i < 3; i++) {
tableRowsLst.add(new TableRow(this));
...
}
for (int i = 0; i < 3; i++) {
tableRowsLst.get(0).addView((ImageView) imageViewsLst.get(i));
tableRowsLst.get(1).addView((ImageView) imageViewsLst.get(i+3));
}
...
}
In this loop:
for (int i = 0; i < 3; i++){
tableRowsLst.add(new TableRow(this));
tableRowsLst.get(i).setLayoutParams(
new TableLayout.LayoutParams(LayoutParams.FILL_PARENT, dipToPixels(55)));
tableRowsLst.get(i).setOrientation(LinearLayout.HORIZONTAL);
}
you just keep adding new TableRows to tableRowsLst, but you always only use the first three elements.
Clear the list before the loop:
tableRowsLst.clear();
Although not the case in this example, another common cause of this problem is not correctly utilizing onCreateDialog() and onPrepareDialog(). The onCreateDialog() is called only once and anything done here will persist. If you are adding dynamic content to a layout (Dialog), you probably want to use onPrepareDialog() which will happen after create but before each display. To quote from the Android documentation:
Before the dialog is displayed, Android also calls the optional callback method onPrepareDialog(int, Dialog). Define this method if you want to change any properties of the dialog each time it is opened. This method is called every time a dialog is opened, whereas onCreateDialog(int) is only called the very first time a dialog is opened. If you don't define onPrepareDialog(), then the dialog will remain the same as it was the previous time it was opened. This method is also passed the dialog's ID, along with the Dialog object you created in onCreateDialog().
Aha! Okay, after a couple false starts, here's the problem.
imageViewsList is a member variable. You're adding 6 views every time you call createPlayerTables, THEN USING THE FIRST 6 each time. First pass (player 0), no problem. Second pass (player 1): boom.
Option 1) Don't save them. The given code doesn't need them, though that doesn't cover all the bases by any stretch. You could dig them out of the table rows and cast them in a pinch.
Option 2) Offset your access to imageViewsList by playerNum * 6 (which will == imageViewsList.size() when createPlayerTables() is first called)
Friendly advice: You could have found the problem in a couple different ways:
Log.d() with the object ID before each call to TableRow.add() would have revealed the same object ID used in the second pass immediately followed by your exception.
Stepping through your code in the Handy Dandy Debugger. Yes, that's a lot of code to step through to figure out what was going on in this case. A couple different break points would have made it easier to see which call to createPlayerTables() was throwing and allowed you to step into cpt() only when it was going to throw.
Asking yourself "what could I have done to catch this" every time you've figured out a bug will improve your debugging skills IMMENSELY.

Categories

Resources