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)...
}
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.
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.