I'm working on an Android App, and I had the following code:
int digit = 0;
imageButton = (ImageView)findViewById(R.id.btn1);
numberedImage = (ImageView)findViewById(R.id.Digit1);
imageButton.setOnClickListener(new OnClickListener()
{
public void onClick(View v)
{
digit++;
numberedImage.setImageResource(R.drawable.number + ((digit) + 1));
}
});
and some images on a drawable folder called number1, number2, and so on...
This code was working perfectly, and then, suddenly, it's not working anymore.
I want the ImageView to change the images each click, and what made me completely confused, is the fact that this code was working completely fine (even though it might be wrong some way, I'm not a programmer), and all of the sudden, it's not anymore...
After that I also tried using arrays and loops, but I encountered the same error:
"number cannot be resolved or is not a field"
which wasn't a problem before.
You have some misunderstanding. But may be fortunately(or unfortunately) your code gave you some successful result for which your misunderstanding was more firmed.
ImageView.setImageResource takes a resource id as parameter. not a image name. so when you are adding a digit to the resource id it is setting the image of next id. as your images' names are sequential so the ids were also sequential that's why you got successful result. But it is not guranteed that you will always be getting that done.
I guess previously it was working because there was an image named number in your resource. But now it is removed.
Proposed solution
Resources res = getResources();
String mDrawableName = "number" + digit + 1; // be sure those exist
int resID = res.getIdentifier(mDrawableName , "drawable", getPackageName()); numberedImage.setImageResource(resId);
please kindly use
for(int i=0;i<imageCount;i++){
int imageResId = getResources().getIdentifier("image" + i, "id", YourActivity.this.getPackageName());
numberedImage.setImageResource(imageResId);
}
in order to access image by resource name.
Related
In my app, I want a counter from 0 to 8 to decide the number of players in a game.
Below there are 8 possible fields to write a name inside, which are all set to invisible. If the players-counter is set to 3 players, there should be the first 3 fields visible. Depending on the actual number of the counter, the visibility of the fields changes (1player = first field, 5 players = first 5 fields).
When the +1 (player) button is clicked, a certain method is activated. I tried to run a for-loop everytime the button is clicked. In this for-loop from 0 to "whatever amount" (max. 8 players) the actual fields should be found with "findById" and set to visible.
I tried it with a string resource (.xml) and I can get the text of the resource but with my thought process, I have to update the string resource to every number of the field (if 3 players: "field_" + "1", "field_" + "2", "field_" + "3").
How can I get and (most importantly) set/update a string resource for this specific purpose?
(Switch is too inefficient and I can't use a string with the findViewBy Id()-method by updating the String (not string resource) like mentioned before.
Please help, and accept the fact that I'm new to Android Studio for one week!)
You can use "getIdentifier" which takes a String parameter. So you can set the type as "id" in the second parameter of this method. This method returns the id of the view you want, but beware, it will throw a "FATAL EXCEPTION" if the id of the View doesn't exist. With this id, you can use findViewById to fetch the TextView and change its visibility. The "getIdentifier" method can be called from the "getResources()" method.
Below you can see what it would be like to make visible a TextView that has the id "textView1":
int id = getResources().getIdentifier("textView1", "id", getPackageName());
TextView textView = findViewById(id);
textView.setVisibility(View.VISIBLE);
Below you can see how you would make 8 TextView with id 1 to 8 visible:
TextView textView;
for (int i = 1; i <= 8; i++) {
int id = getResources().getIdentifier("textView" + i, "id", getPackageName());
textView = findViewById(id);
textView.setVisibility(View.VISIBLE);
}
So, just put the limit at i <= x , with x being the limit of players who will play:
TextView textView;
for (int i = 1; i <= totalPlayers; i++) {
int id = getResources().getIdentifier("textView" + i, "id", getPackageName());
textView = findViewById(id);
textView.setVisibility(View.VISIBLE);
}
Do you just want to make some EditTexts visible and others not? Personally I'd keep it simple, do the lookups once (in onCreate or wherever) and store the references in a list. Then when you need to display n fields, you can just iterate over the list and set the first n to VISIBLE and the rest to INVISIBLE.
I feel like it's fine to just list all the EditText IDs (R.id.field_1 etc) and generate your list of actual Views from that, but if that repetition bothers you, there's a few things you could do. Like:
set a tag attribute on each field in the XML, and use findViewWithTag to look them up, generating the lookup strings programmatically, like "field_" + i
do a similar thing with the resource ID, like in #Moises's answer
lookup their containing layout, use getChildCount and [getChildAt] to iterate over the views in that layout, and use isInstance to collect all the EditTexts in order3
create and add the EditTexts in code - you probably don't want to do this, but you could!
I'm not really sure what you mean about the string resource or what you're trying to do - I'd honestly just make a list of R.id.field_1 etc, iterate over that to do findViewById on each and store those in a new list, and you're done. Also my Java's a bit rusty so sorry no example code!
I am developing an app and have begun learning Java a few weeks ago. At this point, there's something that I'm trying to get to work, but I can't figure out what's going wrong.
So the problem is as follows. I have a FlipperView in which I want to dynamically add TextViews between which I should be able to navigate. These TextViews have the same content and layout, the only difference between them is their "ScrollY" attribute. I want the first TextView to have a ScrollY of 0 so that the text can be seen from its beginning. Then, I want the next TextView to have a ScrollY of 0 + x, where x is the height of the TextView (which is originally defined as match_parent). The next one should have a ScrollY of 2x, etc.
I've tried many things to get this work. First I managed to dynamically get the height of the TextView using a .post. For now, I can change the ScrollY of the first TextView. For additional views, I'm using a for a loop. At this point, TextViews are properly created and I can navigate between them with the FlipperView. However, the SetScrollY function doesn't work inside the for loop. I've tried to feed it with an arbitrary integer instead of the dynamic height of the TextView without success, so I guess the function is not working at all in this case. Here's my code (the number of loops is fixed for now):
public int formatText(Context context, ViewFlipper textFlipper) {
int nbOfViews = 1;
final int[] viewMaxHeight = {0};
final int[] lineSpacing = {0};
textContent = context.getResources().getString(R.string.dummyText);
final TextView flipperView = new TextView(context, null, R.style.flipperText);
flipperView.setText(textContent);
textFlipper.addView(flipperView);
flipperView.post(new Runnable() {
#Override
public void run() {
viewMaxHeight[0] = flipperView.getHeight();
lineSpacing[0] = Math.round(flipperView.getLineSpacingExtra());
}
});
for(int i = 0; i < 2; i++) {
TextView flipperViewExtra = new TextView(context, null, R.style.flipperText);
flipperViewExtra.setText(textContent);
flipperViewExtra.setScrollY(viewMaxHeight[0] * nbOfViews + lineSpacing[0]);
textFlipper.addView(flipperViewExtra);
nbOfViews++;
}
return nbOfViews;
}
Any help is appreciated, thanks!
You are working with THREAD and thread tends to optimize the performance by caching the using variables. Hence any chance OUTSIDE the thread it won't "see" the change in memory. To coerce the thread to look at the change in memory you have to declare the variables to volatile. Example
final volatile int[] viewMaxHeight = {0};
final volatile int[] lineSpacing = {0};
I am using a Seekbar library so when I drag the seeker, I expect the textview to be updated with the value from the seeker but unfortunately my app crashes. I get an error that says, "resource not found on the TextView".
The code is below:
RangeSeekBar seekBar1;
seekBar1 = (RangeSeekBar)rootView.findViewById(R.id.seekBar);
seekBar1.setValue(10);
seekBar1.setOnRangeChangedListener(new RangeSeekBar.OnRangeChangedListener() {
#Override
public void onRangeChanged(RangeSeekBar view, float min, float max, boolean isFromUser) {
seekBar1.setProgressDescription((int)min+"%");
TextView txtAmount;
txtAmount = (TextView)rootView.findViewById(R.id.txtAmount);
txtAmount.setText((int) min);
}
});
Solution: You can't set int to the TextView like that, try this instead:
txtAmount.setText(Float.toString(min));
The overload you were using would look for a string resource identifier, that doesn't exist in this case. Here's the correct one that takes a CharSequence as an argument (string is a CharSequence).
Nice to know: If you're now left wondering how an int can be an argument to setText, it's quite simple. In your app, you can have a strings.xml file that defines a set of resources strings to be used in the application:
<resources>
<string name="test">This is a test</string>
</resources>
With that defined, you can show the text on your TextView like this:
txtAmount.setText(R.string.test);
If you pass an integer into setText, android expects the value to be a resource. The system is trying to find a resource who's id is equal to min. You need to convert min to a string.
So to get it working change txtAmount.setText((int) min); to txtAmount.setText(String.valueOf(min));
New Android/Java coder. Trying to replicate in Android app a project I built in MS-Access.
I have a layout with similar named TextViews, like text10, text12, etc. In MS-Access I can dynamically build those names with collection referencing:
For X = 10 To 15
Me.Controls("text" & X) = Null
Next
There is no array required. So looking for structure in java that can accomplish the same functionality.
I want to dynamically set background color of multiple TextView based on two inputs. One is to build TextView reference and the other is a state indicator that will determine color.
Here is one procedure calling setSubColor:
public void Clear(MenuItem mi) {
puz.setText("");
sol.setText("");
for (int i=0; i<26; i++) {
setSubColor(aryA[i].charAt(0), 0);
What I have so far for setSubColor:
public void setSubColor (char c, int i) {
TextView v = (TextView) >>>dynamically reference v using name built with ("tv" + c)
if (i == 0) {v.setBackgroundColor(Color.TRANSPARENT);}
else {v.setBackgroundColor(Color.YELLOW);}
You can get the res id from the res name at runtime. So if your textview had name "text1", you could get the integer id by using:
int id = getResources().getIdentifier("text1", "string", getPackageName());
TextView view = findViewById(id);
But do so only as a last resort, it's error prone, slow and somewhat of an anti pattern.
EDIT by OP: No matter what the name argument is always returns 0 but marked as answer because it led to the following code that works exactly as I want, anti-pattern or not.
TextView v = (TextView) findViewById(getResources().getIdentifier("tv" + c, "id", getPackageName()));
Instead of the TextView Id field use its Tag field.
String tag = (String)textView.getTag() and textView.setTag(Object tag) with tag instanceof String
then you can find the TextView by Tag
Inside my drawabale folder I have these images: levelone, leveltwo, level three.
i need to set the ImageView according to the inputted string as follows:
levelindicatorImageView = (ImageView) findViewById(R.id.levelindicatorImageView);
String tempo="R.drawable.level"+LevelReached;
Drawable replacer = getResources().getDrawable(tempo);
int sdk = android.os.Build.VERSION.SDK_INT;
if(sdk < android.os.Build.VERSION_CODES.JELLY_BEAN) {
levelindicatorImageView.setBackgroundDrawable(replacer);
} else {
levelindicatorImageView.setBackground(replacer);
}
levelindicatorImageView.invalidate();
now inside LevelReached variable i have the needed level (one, two, three)
I need to set the Image R.drawable.leveltwo etc...
How is that possible please?
as getDrawable doesn't work with tempo (needs int)
thanks for the help!
You have 2 options:
Option 1:
Simply use a switch-case to get the correct drawable, then set it as a background image as you normally would
// Get the level drawable resource id
int imageRes = R.drawable.default_image;
switch (levelReached) {
case LEVEL_ONE: // 1
imageRes = R.drawable.level_one;
break;
case LEVEL_TWO: // 2
imageRes = R.drawable.level_two;
break;
...
}
// Set the drawable as a background
levelIndicatorImageView.setBackgroundResource(imageRes);
Where LEVEL_ONE == 1; LEVEL_TWO == 2; as const or enum.
Option 2:
You can find a drawable resource by name (as you wanted), but it is less recommended since it is more error prune. But if you must, use following example:
// Get the level drawable resource id
int imageRes = getResources().getIdentifier("level"+levelReached, "drawable", getPackageName());
// Set the drawable as a background
levelIndicatorImageView.setBackgroundResource(imageRes);
Note:
You don't need to explicitly invalidate the view if you set the background since setting a new background will trigger invalidation by itself. So, this line:
levelindicatorImageView.invalidate();
is not needed.
You cant append input string to image name, but what you can do is,
select the image based on input string and then If you have 3 different images, try with ;
if(input.equalsIgnoreCase("input one"))
{
int id = R.drawable.<img one>;
}
else
{
//so on
}
levelindicatorImageView.setBackground(getResources().getDrawable( id) );
You need to do something like this:
public int getResourecId(String level) {
if(level.equalsIgnoreCase("one"))
return R.drawable.levelone;
if(level.equalsIgnoreCase("two"))
return R.drawable.leveltwo;
if(level.equalsIgnoreCase("three"))
return R.drawable.levelthree;
return 0;
}
and then:
Drawable replacer = getResources().getDrawable(getResourceId(LevelReached));
You need to associate the levels to a resource, in this case, an int. An idiomatic way of doing this would be to store the levels as an enum, say
enum Level { ONE, TWO, THREE }
Using an enum in this way allows you some flexibility with your level conventions. For example, if you later wanted to add the level TWO_THIRDS, there is nothing stoping you. Then, associate each level with a resource in an EnumMap, for example:
Map<Level, Integer> levelResources = new EnumMap<Level, Integer>(Level.class);
levelResources.put(ONE, R.drawable.levelOne);
levelResources.put(TWO, R.drawable.levelTwo);
levelResources.put(THREE, R.drawable.levelThree);
Now, instead of passing around a string which describes your current level, you can pass around an enum, which you can use to determine the correct resource without concatenating strings. So, suppose that levelReached is of type Level, you could write:
Integer tempo = levelResources.get(levelReached);
Drawable replacer = getResources().getDrawable(tempo);