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.
Related
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.
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.
Let's say a list has 4 items, how can I get a view from each menu item of a list by position?
Unfortunately the items that are in the ListView are generally only those that are visible. You should iterate on the ListAdapter instead.
For example, in some of my code, I have this:
SimpleCursorAdapter adapter = (SimpleCursorAdapter) this.getListAdapter();
int iNum = adapter.getCount();
for(int i=0; i<iNum; i++)
{
Cursor c = (Cursor) adapter.getItem(i);
// Now you can pull data from the cursor object,
// if that's what you used to create the adapter to start with
}
EDIT:
In response to jeffamaphone's comments, here's something else... if you are trying to work with each UI element then getChildAt is certainly more appropriate as it returns the View for the sub-item, but in general you can still only work with those that are visible at the time. If that's all you care about, then fine - just make sure you check for null when the call returns.
If you are trying to implement something like I was - a "Select All / Select None / Invert Selection" type of feature for a list that might exceed the screen, then you are much better off to make the changes in the Adapter, or have an external array (if as in my case, there was nowhere in the adapter to make the chagne), and then call notifyDataSetChanged() on the List Adapter. For example, my "Invert" feature has code like this:
case R.id.selectInvertLedgerItems:
for(int i=0; i<ItemChecked.length; i++)
{
ItemChecked[i] = !ItemChecked[i];
}
la.notifyDataSetChanged();
RecalculateTotalSelected();
break;
Note that in my case, I am also using a custom ListView sub-item, using adapter.setViewBinder(this); and a custom setViewValue(...) function.
Furthermore if I recall correctly, I don't think that the "position" in the list is necessarily the same as the "position" in the adapter... it is again based more on the position in the list. Thus, even though you are wanting the "50th" item on the list, if it is the first visible, getChildAt(50) won't return what you are expecting. I think you can use ListView.getFirstVisiblePosition() to account and adjust.
See here, this question answers the similar problem you mentioned here
In an android ListView, how can I iterate/manipulte all the child views, not just the visible ones?
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.