• After 15+ years, we've made a big change: Android Forums is now Early Bird Club. Learn more here.

setText with concatenated elements

Greum

Well-Known Member
I have the following statement

textView9.setText("Sale price: " + String.format("%.0f", salePrice) + " " + saleHold);

but it gives a warning

Do not concatenate text displayed with setText. Use resource string with placeholders.

I understand using a resource string for the "Sale price" part but I'm not sure how a placeholder would help here.

I'm also not sure of the best way to convert a float to a string removing the decimal part in the process. I have read conflicting methods. I would also like to add a thousands separator.
 
Please check the https://developer.android.com/guide/topics/resources/string-resource#formatting-strings
The string resources and the placeholder are very useful and recommended especially when you want to add multilanguage support.
E.g. in my own app https://play.google.com/store/apps/details?id=ro.notnull.bubblesinline , I have defined a string resource
<string name="game_completed_best_score">Best score for %d colors: %d points in %d moves.</string>
and then in the jave code I have:
res.getString(R.string.game_completed_best_score, b.getNumberOfColors(), b.getTotalPoints(), b.getGameMoves().size());
where variable res is of type Resurces.
It make the job of multilanguage support very easy.
But you can also leave with the respective warning, the code will work.
 
Thanks for that. I'll take a look and try as you suggest. The trouble seems to be that I have another similar setText (one for sale price and one for purchase price). The app seems happy with one, but if I have both the app crashes.
 
For sure the crash is not because of that string concatenation. There should be something else. Post here the stack trace to see what is the real reason.
 
@marcelpreda thanks. I confess it didn't seem likely. I'm learning/firefighting on too many fronts at the moment. I have removed the 2nd concat for the moment and will come back to it shortly. Meanwhile I'm trying a simpler placeholder...
 
@marcelpreda I tried it again and the app crashed. The error log has over 3,000 lines, so I'm not sure which are the relevant ones. The log begins:

2019-06-05 16:20:46.066 1601-1606/? E/cutils: Failed to mkdir(/data/system/users/0): No such file or directory
2019-06-05 16:20:46.066 1601-1606/? E/vold: Failed to prepare /data/system/users/0: No such file or directory
2019-06-05 16:20:46.066 1601-1606/? E/vold: Failed to prepare user 0 storage
2019-06-05 16:20:47.674 1601-1676/? E/Cryptfs: Bad magic for real block device /dev/block/vdd

and ends

2019-06-05 16:26:30.393 2393-5028/com.google.android.gms.persistent E/AsyncOperation: serviceID=65, operation=GetPlaceById
OperationException[Status{statusCode=ERROR, resolution=null}]
at bear.b(:com.google.android.gms@16089022@16.0.89 (040700-239467275):1)
at beak.a(:com.google.android.gms@16089022@16.0.89 (040700-239467275):28)
at zgb.run(:com.google.android.gms@16089022@16.0.89 (040700-239467275):27)
at bgot.run(:com.google.android.gms@16089022@16.0.89 (040700-239467275):2)
at rrt.b(:com.google.android.gms@16089022@16.0.89 (040700-239467275):32)
at rrt.run(:com.google.android.gms@16089022@16.0.89 (040700-239467275):21)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1167)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:641)
at rxx.run(Unknown Source:7)
at java.lang.Thread.run(Thread.java:764)
2019-06-05 16:26:31.321 2179-2601/system_process E/memtrack: Couldn't load memtrack module
2019-06-05 16:26:32.515 2179-2197/system_process E/memtrack: Couldn't load memtrack module
2019-06-05 16:26:33.493 1989-2024/? E/storaged: getDiskStats failed with result NOT_SUPPORTED and size 0
2019-06-05 16:26:37.667 2179-2197/system_process E/memtrack: Couldn't load memtrack module

if that helps at all?
 
Hi,
The reason is clear not the string concatenation: Failed to mkdir(/data/system/users/0): No such file or directory.

Not sure what you are doing, or some other class that you are using , but the code is trying to create that directory on the device internal memory/flash. And the respective path does not exist, or it is no write access for your app.

The path where the apps store their data depends by phone vendor, and by the Android version. If you want to access that data, there is an API for that.

https://developer.android.com/guide/topics/data/data-storage
 
Thanks again

I'm running on an AVD (Pixel 2 XL API 28). There is 1 line of code

textView10.setText("Purchase price: " + String.format("%,.f", purchPrice) + " " + purchHold);

which results in the app crashing. If I comment out all but the first part

textView10.setText("Purchase price: "); // + String.format("%,.f", purchPrice) + " " + purchHold);

it works fine (but doesn't give the full result of course).

I have yet to try it on a real device.
 
Okay, so I ran it on a real device and the error log is much more manageable, but no more enlightening to me. The device is an OLD one running Android 4.4.2.

06-05 19:45:50.674 3003-3003/domain.com.conveyancingquoter E/AndroidRuntime: FATAL EXCEPTION: main
Process: domain.com.conveyancingquoter, PID: 3003
java.lang.RuntimeException: Unable to start activity ComponentInfo{domain.com.conveyancingquoter/domain.com.conveyancingquoter.QuoteActivity}: java.util.UnknownFormatConversionException: Conversion: ,.
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2195)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
Caused by: java.util.UnknownFormatConversionException: Conversion: ,.
at java.util.Formatter$FormatSpecifierParser.unknownFormatConversionException(Formatter.java:2306)
at java.util.Formatter$FormatSpecifierParser.parsePrecision(Formatter.java:2374)
at java.util.Formatter$FormatSpecifierParser.parseArgumentIndexAndFlags(Formatter.java:2348)
at java.util.Formatter$FormatSpecifierParser.parseFormatToken(Formatter.java:2283)
at java.util.Formatter.doFormat(Formatter.java:1071)
at java.util.Formatter.format(Formatter.java:1042)
at java.util.Formatter.format(Formatter.java:1011)
at java.lang.String.format(String.java:1999)
at java.lang.String.format(String.java:1973)
at domain.com.conveyancingquoter.QuoteActivity.onCreate(QuoteActivity.java:55)
at android.app.Activity.performCreate(Activity.java:5231)
at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1087)
at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:2159)
at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:2245)
at android.app.ActivityThread.access$800(ActivityThread.java:135)
at android.app.ActivityThread$H.handleMessage(ActivityThread.java:1196)
at android.os.Handler.dispatchMessage(Handler.java:102)
at android.os.Looper.loop(Looper.java:136)
at android.app.ActivityThread.main(ActivityThread.java:5017)
at java.lang.reflect.Method.invokeNative(Native Method)
at java.lang.reflect.Method.invoke(Method.java:515)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:779)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:595)
at dalvik.system.NativeStart.main(Native Method)
 
This error looks different. It is something related to Conversion exception.
Probable the format string is not the right one, try something like String.format("%,.0f", price);
 
Arrggghhh! Such a simple typo and I couldn't see it. Thanks for your patience. I will set my mind to using placeholders in quieter times.
 
Back
Top Bottom