I'm trying to create a function that pulls together information that is currently scattered around different parts of my project.
As part of this task, I have a layout file with something like the following content... basically a set of rows, with each row having a label (TextView) and a UI element (e.g. CheckBox, Spinner or EditText) to collect information from the user:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/MyLinearLayout.Section"
android:id="#+id/section_pressure" >
<TableLayout style="#style/MyTableLayout"
android:id="#+id/table_pressure" >
<TableRow style="#style/MyTableRow" >
<TextView
style="#style/MyTextView.Label.WithHelp"
android:tag="label_show"
android:text="#string/label_show" />
<CheckBox
style="#style/MyCheckBox"
android:id="#+id/pressure" />
</TableRow>
<TableRow style="#style/MyTableRow" >
<TextView
style="#style/MyTextView.Label.WithHelp"
android:tag="label_unit"
android:text="#string/label_unit" />
<Spinner
style="#style/MySpinnerStyle"
android:id="#+id/pressureUnit" />
</TableRow>
</TableLayout>
</LinearLayout>
I already have an array of all the android:id values for the UI elements, and from that I want to generate another array of the corresponding android:text labels.
e.g. from R.id.pressureUnit I want to find the associated R.string.label_unit from that TableRow, so that I have a central record of what label is used for each UI element... currently that information is scattered across lots of different layout files.
Is this possible programmatically?
From what I understand you want to find #string resource ID of a sibling view of a given view.
Assuming you have the xml file already inflated, you can do the following:
private int getSiblingStringId(#IdRes int viewId) {
View uiElement = findViewById(viewId);
ViewGroup parent = (ViewGroup) uiElement.getParent();
// iterate through the siblings
for (int i = 0; i < parent.getChildCount(); i++) {
View view = parent.getChildAt(i);
if (view instanceof TextView) {
String value = ((TextView) view).getText().toString();
// found
return getStringIdFromValue(value);
}
}
throw new IllegalArgumentException("no TextView sibling for " + viewId);
}
private int getStringIdFromValue(String value) {
// get all fields using reflection
Field[] fields = R.string.class.getDeclaredFields();
for (Field field : fields) {
int stringResId = getResources().getIdentifier(field.getName(), "string", getPackageName());
String s = getString(stringResId);
if (s.equals(value)) return stringResId;
}
throw new IllegalArgumentException("no matching string for " + value);
}
The above code will work inside an activity. You might need to modify it a bit for other classes. (You will need to call findViewById, getResources and getString)
Well, I would set an ID to each table row. Then, programmatically get every child view under each row by iterating over the row's children. From there, you can easily manipulate every view by simply determining which one are you at by using instanceof. But, let's assume you cannot change this, you can still be doing it but it might not be efficient.
Since I don't really know the structure of your array of ids, I just assume its an array of integers called uiIds. Then, you would have to do something like
TableLayout layout = findViewById(R.id.table_pressure);
TableRow tr = null;
View rowChild = null;
TextView tv = null;
View secondaryView = null;
int tableChildCount = layout.getChildCount();
int rowChildCount = 0;
// Since I don't really know the structure of your array of ids,
// I just assume its an array of integers called uiIds
//This will contain the corresponding text strings of every textview
List<String> textLabels = Arrays.asList(new String[uiIds.length]);
// Go over all the rows on your table
for (int i = 0; i < tableChildCount; i++) {
// I'm assuming that you only have rows as the children of your table
tr = (TableRow) layout.getChildAt(i); //This is the row
rowChildCount = tr.getChildCount();
tv = null;
secondaryView = null;
// Now go over all the children of the current row i
for (int j = 0; j < rowChildCount; j++) {
// Here I'm also assuming you only have two children on every row
rowChild = getChildAt(j);
// At this point, the view v could be any type of the ones you have
// used. Since we are interested in TextView simply do an instanceof
if ( rowChild instanceof TextView ) {
tv = (TextView) v;
} else {
// This is the other view in the row. The ones from which you alreade have its ids
// e.g. the spinner with id pressureUnit
secondaryView = rowChild; //We do not need to cast
}
}
// Now we construct the array
if (tv != null && secondaryView != null) {
// The id we just obtained is one from the ones you have already saved
// in an array called uiIds
int secondaryViewId = secondaryView.getId();
//Now, we need to find the index of this id in uiIds
for (int idx = 0; idx < uiIds.length; idx++) {
if ( uiIds[idx] == secondaryViewId ) {
//We have found a match. Then just add the text of the textview in the corresponding index of our new array
textLabels.set(idx,tv.getText().toString());
}
}
}
}
// At this point, textLabels should contain the text of the text view on every row
// such that the ith element in the array you already have corresponds to
// ith element of the array we just created.
// In other words, if uiIds[0] is equals to R.id.pressureUnit, then,
// textLabels.get(0) is equals to R.string.label_unit
I have edited the answer directly on the text editor so it might have some sintax erros. Hopefully this solves your problem or at least gives you an idea.
To get overview of your label mappings, I suggest to code more systematically, in this case to name the label just like the id.
#+id/pressure => #string/label_pressure
#+id/pressureUnit => #string/label_pressureUnit
I know this is not a direct answer to your question. However I think, if you work in this way, you don't need a central table of your mappings at all and your layouts become more readable. Android Studio makes it really easy to do this kind of changes.
Related
I am new in android. I have an array of images:
private int[] textureArrayWin = {
R.drawable.basketball,
R.drawable.soccer
};
Try to dynamically add them to my view:
for(int i=0; i < textureArrayWin.length; i++){
Log.v("asd", "index=" + i);
ImageView tv = new ImageView(this);
tv.setScaleType(ScaleType.CENTER_CROP);
tv.setImageResource(textureArrayWin[i]);
tv.setAdjustViewBounds(true);
tv.setMaxHeight(20);
tv.setMaxWidth(20);
myLayout.addView(tv);
}
But don't know how:
Fit them by "setMaxHeight", they are still big
How to know index of clicked image in array or to receive some id of image which allow to know the unique id of image in my layout. In javascript, for example, I can do it by adding attr to element and in handler receive attribute by "this.getAttribute(someAttr)"
You can use the Id attribute to identify the views.
Assign a Id to each view by:
myView.setId(<some Integer value here>);
You can later refer to same view by:
View v = findViewById(<same Integer value here>);
Does anyone know how to replace elements using Jsoup. Im trying to replace table elements and their content with buttons but having no success. Code attempt is below. This is for an android project
Elements elements = doc.select("table");
if (elements != null) {
for (Element element : elements) {
Element button = Jsoup.parse("<button type='button'>Click Me!</button>");
element.replaceWith(button);
}
}
I went about this in a bit of a hacky way it seems to work. The replaceWith(button) attribute did'nt do anything. I do actually want to replace the whole table with a button but i also want to add that button along with the results to a string.
for (int i = 0; i < elements.size(); i++) {
Element sibling = siblings.get(i);
if ("table".equals(sibling.tagName())) {
siblings.remove(i);
Element button = Jsoup.parse("<button type='button'>Click Me!</button>");
sibling = button;
sb.append(sibling.toString());
}
else {
sb.append(sibling.toString());
}
}
I have a couple of EditTexts arranged on rows and columns.Those EditTexts contain product name,quantity and price and a TextView that shows the total in real time(calculates it each time you write on one of the EditTexts)
I've setup a a button on each row that when clicked sets visibility of the row(3EditTexts for product name,price and quantity) to GONE.
My problem is that after i set the visibility to GONE,though there are no more EditTexts it still calculates their values from before being GONE.
My question now is,what happens when the EditTexts are set to visibility.GONE ?
My app calculates in real time,so when something happens to an EditText,he calculates again..but it's like the values are still there...Isn't this supposed to be the difference between invisible and gone ?
I'll show you the way i calculate(it is called even after you press the X button to erase the EditTexts,not only when you change values inside EditTexts)
public void calculeaza() {
totaltest = 0;
prod = new String[allprod.size()];
pret = new String[allpret.size()];
cant = new String[allcant.size()];
for (int m = 0; m < allprod.size(); m++) {
prod[m] = allprod.get(m).getText().toString();
if (prod[m].matches("")) {
prod[m] = " - ";
}
}
for (int j = 0; j < allcant.size(); j++) {
cant[j] = allcant.get(j).getText().toString();
if (cant[j].matches("")) {
cant[j] = Float.toString(0);
}
}
for (int k = 0; k < allpret.size(); k++) {
pret[k] = allpret.get(k).getText().toString();
if (pret[k].matches("")) {
pret[k] = Float.toString(0);
}
}
for (int l = 0; l < allpret.size(); l++) {
Float temp = Float.parseFloat(cant[l]) * Float.parseFloat(pret[l]);
totaltest = totaltest + temp;
TextView totalf = (TextView) findViewById(R.id.total);
totalf.setText(String.format("Total: %.2f", totaltest));
}
}
Lines Straight from Android dev site..
View.GONE This view is invisible, and it doesn't take any space for layout purposes.
View.INVISIBLE This view is invisible, but it still takes up space for layout purposes.
i.e it retains EditText object even after Gone..
You can reinitialise edittext if you dont want it to retain its value...or setText = ""
Above quoted is the only difference...
Hope this helps...
I'm not really seeing in the code you posted anything I can use to answer this question, but there does appear to be some confusion as to what setVisibility does:
INVISIBLE elements are not seen on the page, but they still take up space (there's a hole where they would be)
GONE elements have no visible effect on the screen, from the user's perspective they aren't there. However they are still part of the view.
If you want to remove the object from the view, then you need to call removeView() on its parent.
It may still take up memory after it has been removed from the view, in case your code has kept references to it in any variables.
It may still take up memory after there are no further references to it, at least until the garbage collector gets around to it.
I'm hoping the rather generalized statements above help clarify the situation.
I am trying to change the values of several TextView elements but iterating through an array list and adding these values. However I can't seem to find a way to change the R.id value that is used each time. For example:
for (int i=0; i<arrayList.size(); i++)
{
TextView t = (TextView) dialog.findViewById(R.id.value+(i));
t.setText(arrayList.get(i));
}
Where the values are in the format animals_eng1,animals_eng2 etc..
Any help appreciated.
Your best bet is to create an array containing the resource IDs of each text view and looping through them..
ex.
int[] textViewIds = new int[] { R.id.animals_eng1, R.id.animals_eng2, R.id.animals_eng3 };
Then in your activity you can loop through them and set the values like you desire
for (int i=0; i<arrayList.size(); i++)
{
((TextView)findViewById(textViewIds[i])).setText(arrayList.get(i));
}
You'll have to make sure your arrayList size is the same as the number of textview resource IDs you set or you'll end up with an out of bounds exception when looping
I don't know of a way to do exactly what you're asking, but here are two alternatives:
1.Create an array of Integers, and assign each element of the array to a different view id value
int[] ids = new int[arrayList.size()];
ids[0] = R.id.view0;
ids[1] = R.id.view1;
ids[2] = R.id.view2;
//...ids[n] = R.id.viewN; where n goes up to arrayList.size()
for (int i : ids){
((TextView)dialog.findViewById(ids[i])).setText(arrayList.get(i));
}
Note that the above method sorta defeats the point because you have to have a line for every TextView, if you want something more dynamic:
2.Tag your TextViews in your layout xml by adding android:tag="prefix0", for example, to each of your TextViews. Before the loop find the parent View of your layout, and then use the findViewWithTag method of that view within the for loop. From your code I'm guessing you're using a Dialog with a custom layout xml, so you'd first find the parent of that:
ViewGroup parent = dialog.findViewById(R.id.parent); //or whatever the name of your parent LinearLayout/RelativeLayout/whatever is
String commonPrefix = "prefix"; //or whatever you've tagged your views with
for (int i=0; i<arrayList.size(); i++){
TextView t = (TextView) parent.findViewWithTag(commonPrefix+i);
t.setText(arrayList.get(i));
}
One way to solve this would be putting your resource ids in an int array and getting the resource at index i.
TextView t = (TextView) dialog.findViewById(R.id.value+(i))
becomes
TextView t = (TextView) dialog.findViewById(resourceArray[i])
if all of your views are on the same container (and only them) , simply iterate over its children .
if not , you can add an array of ids (in res) and iterate over it .
I have a layout the contains 4 TextViews with ids: "Name1", "Name2", "Name3", "Name4"
I would like to iterate on them with a for loop,
is there any way to do this?
something like
for(int i = 1; i <= 4; i++)
{
findViewById(R.id."Name" + i)
}
I know that this code is far from being real, but any help?
Thank you!
Ron
No, you cannot do it like that because R.id.xyz is referencing a static int of a static class. It's not a string that can be concatenated like that. Also, your code ignores the return value of findViewById so it does nothing (though I realize you mentioned the code is far being real, but still an actual use case might help clarify what you're trying to do). R.id."Name" means nothing and will give you a compiler error.
To loop through you can do something like this:
int[] ids = {R.id.foo, R.id.bar};
then
for(int i = 0; i<ids.length; i++) {
View v = findViewById(ids[i]);
}
Sure you can do that sort of. You cannot access an member of R.id using a string literal you must type out the variable name.
R.id."Test" is no good but R.id.Test is fine.
If you examine the type of R.id.XYZ you will find that it is simply an integer. There is no reason why you cannot perform basic arithmetic on id values.
However it doesn't really make sense to do so. When you build your APK the compiler automatically creates a static class called R that contains references to the various resources and assets contained in your APK, such as layouts, drawables, sounds, etc..
Just because you know that the integer value for R.id.button1 is X there is no guarantee that the integer value for R.id.button2 will be X+1, in fact it could be anything.
If you want to create a list of TextViews to iterate over consider adding them to a List and then iterating over the list. Like so:
ArrayList<TextView> list = new ArrayList<TextView>();
list.add( (TextView) findViewById(R.id.textView1) );
list.add( (TextView) findViewById(R.id.textView2) );
list.add( (TextView) findViewById(R.id.textView3) );
list.add( (TextView) findViewById(R.id.textView4) );
int size = list.getSize();
for (int i=0; i< size; i++)
{
TextView tv = list.get(i);
// Do something with tv like set its label to i
tv.setText(Integer.toString(i));
}