I can't get a TextView to correctly dynamically display unicode characters, and it's driving me batty. I've stripped it down to the bare minimum, but the TextView populated by setText still shows diamonds with question marks inside them for the unicode characters. The version populated from strings.xml shows the multibyte characters perfectly. Here's the activity:
public class TestMultibyteActivity extends Activity
{
/** Called when the activity is first created. */
#Override
public void onCreate( Bundle savedInstanceState )
{
super.onCreate( savedInstanceState );
setContentView( R.layout.main );
TextView textField = (TextView) findViewById( R.id.text_field );
String str = "Tübingen systemportefølje";
Log.d( "MULTIBYTE", str ); //note that this prints the multibyte chars correctly.
//EDIT: oh crap, no it doesn't. might be onto something here...
textField.setText( str );
}
}
And here's the layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<TextView android:id="#+id/text_field"
android:layout_width="fill_parent"
android:layout_height="wrap_content"/>
<TextView android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/unicodechars"/>
</LinearLayout>
Here's strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">TestMultibyteActivity</string>
<string name="unicodechars">Tübingen systemportefølje</string>
</resources>
I'm building with ant. Here's my default.properties:
target=Google Inc.:Google APIs:8
And here's my AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.mycompany.android.multibyte"
android:versionCode="1"
android:versionName="1.0">
<application android:label="#string/app_name" android:icon="#drawable/icon">
<activity android:name="TestMultibyteActivity"
android:label="#string/app_name">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
I've tinkered with everything I can think of, but it seems like unicode characters are getting split by the CharSequence interface, and I can't figure out why.
Unfortunately, you just can't do it that way from strings.xml AFAIK.
You're left doing one of two things.
Adding the Unicode character within java to the String in the XML file:
String str = "\u00A9" + getContext().getString(R.string.your_string);
Entering the text as HTML in java:
yourTextView.setText(Html.fromHtml("your chars"));
Hope this is useful.
The accepted answer is correct but Html.fromHtml is deprecated now. So you'll need to use:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
textView.setText(Html.fromHtml(user.getInput(), Html.FROM_HTML_MODE_LEGACY));
} else {
textView.setText(Html.fromHtml(user.getInput()));
}
It could be done in such a simple way:
str.replace("uoo26","&");
I had problems trying to display Unicode character from a custom font that were mapped in the Unicode private area. This is a common area where fonts like MaterialDesign and FontAwesome map their icon font characters. Just for clarity I was testing this on an android version 4.4.2 so maybe this is not an issue on later versions (but probably is). I had tried all combinations of "\uF151", , and I also tried the Html.fromHtlm. None of these techniques worked to properly display the characters in the Unicode private area. If I keep myself to the range \u00xx, they all worked fine for the fonts that had Unicode codes in those locations. (The two fonts I've mentioned don't have any symbols mapped to those point locations)
In examining the font using Microsoft's Character Map I noticed that the characters in the Unicode private area space where not showing up when I had the Windows-English character set chosen for the fonts. The characters would show up if I selected the Unicode character set; and in the case of the Material Design font if I selected the Japanese character set.
What I did to get the character to display on the in TextView was to change the Locale to Japanese, call setText on the Textview object passing it the correct private area unicode value and the character then displayed correctly. So here's the code I used to set icon font symbol and then revert back to English:
TextView tv = (TextView)findViewById(R.id.myTextViewObject);
Typeface tf = Typeface.createFromAsset(this.getAssets(), "material_design.ttf");
tv.setTypeface(tf);
String x = "\uE693";
tv.setText(x);
setDefaultLocale(getApplicationContext(),"en");
tv.append("hello");
And here's the code for the setDefaultLocale method:
public static void setDefaultLocale(Context context, String locale) {
Locale locJa = new Locale(locale.trim());
Locale.setDefault(locJa);
Configuration config = new Configuration();
config.locale = locJa;
context.getResources().updateConfiguration(config, context.getResources().getDisplayMetrics());
locJa = null;
config = null;
}
In the case of FontAwesome, you have to select the Tradition Chinese character set to get access to the unicode private area code points.
You might ask, why I didn't select the "Unicode" character set? The character set that android uses is based on the current locale and I couldn't find a way to select unicode as the current locale. In addition, I couldn't find a way to map the private area unicodes into my existing Locale. I also can't tell you whether this is a issue on how these True Type Fonts where constructed or whether this is an Android issue (albeit I think there should have been a why to map the private area code for use in your existing locale) What I have learned is that all Unicode code points are mapped into all language locales for the same font. That's the thing that I think confuses a lot of people about fonts that support unicode. The windows character map was a useful tool for me in determining which character points are mapped into which character set.
Welcome to the world of kotlin,
If someone wants to set a UNICODE programmatically, you can do it the following way
Unicode character for 🤚is U+1F91A
Replace U+ with 0x which makes it 0x1F91A
val unicodeChar = 0x1F91A.toChar() // unicode for 🤚
For the above question user wants to display "Tübingen systemportefølje"
val char1 = 0x00FC.toChar() // unicode for ü is U+00FC
val char2 = 0x00F8.toChar() // unicode for ø is U+00F8
val name = StringBuilder("T")
.append(char1)
.append("bingen systemportef")
.append(char2)
.append("lje")
.toString()
// OUTPUT name = "Tübingen systemportefølje"
Now you can set the string to your Textview.
For dynamic string, you can search for pattern beginning with 0x and replace it by constructing an equivalent Unicode image.
If you want to show text from unicode.
First step, remove "\u" from unicode. Eg: \u00A9 should be "00A9"
Then, convert "00A9" to Integer. Eg:val hexVal =
Integer.parseInt("00A9", 16)
Finally, convert Integer to char. val char = hexVal.toChar().Then
show char.
Source code:
val builder = StringBuilder()
listUnicodes.forEach
{ uniCode ->
val hexVal = Integer.parseInt(uniCode, 16)
val char = hexVal.toChar()
builder.append(char)
}
return builder.toString()
I tried every other answer in here, it couldn't resolved mine. finally i found it :D
StringEscapeUtils.unescapeJava("Your unicode string")
Related
The project supports RTL; Arabic in particular.
The requirement is that all numbers must appear in English format even when language is selected as Arabic Locale, the way we achieve it everywhere is usually the following code in case of TextViews:
TextView t = findViewById(R.id.sampleTV);
t.setText(String.format("123",Locale.US));
Here 123 would be shown as "123" since we formatted it in US locale quite hardcoded-ly not "١٢٣"
I want to achieve the same for the Badge Notifications provided by Android Material Component's Bottom Navigation, here is what I've tried:
if(navigation!=null) {
NumberFormat nf = NumberFormat.getInstance(Locale.US);
BadgeDrawable badgeDrawable = navigation.getOrCreateBadge(R.id.nav_account);
if (badgeDrawable != null) {
if((counterNoti + counter + counterRoom) <= 0) {
badgeDrawable.setVisible(false);
badgeDrawable.clearNumber();
}
else
{
int x = counterNoti + counter + counterRoom;
nf.format(x);
badgeDrawable.setVisible(true);
badgeDrawable.setNumber(x);
}
}
(where navigation is reference to my BottomNavigation!)
Yet even with this the number is shown in Arabic not like setText's case. Is there a way to achieve this or I need an alternative?
Appreciate your input and time! Thanks.
The following worked for me, changing the local for the current JVM instance by calling the following function in onCreate
private fun numbersFormat() {
val locale = Locale.ENGLISH
Locale.setDefault(locale)
}
You can't change badge numeric text locale in a simple way. My decision was to create custom badge via xml:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/layout_badge"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/txt_counter"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="top|right"
android:layout_marginTop="4dp"
android:layout_marginRight="0dp"
android:background="#drawable/bg_badge"
android:gravity="center"
android:minWidth="21dp"
android:minHeight="21dp"
android:textAppearance="#style/Typography.BadgeCounter"
android:textColor="#ffffff"
tools:ignore="RtlHardcoded"
tools:text="999+" />
Inflate layout in your fragment and set TextView's text liek this:
txtCounter.text = NumberFormat.getInstance(Locale.ENGLISH).format(counter)
As you can see here, the phone number here is starting from the right to the left.
However, I want it to display it like this (Image here is edited):
This is the TextView displaying this message:
<TextView
android:text="#string/smsSentTo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/smsSentTo"
android:textColor="#color/mdtp_white"
android:textSize="18sp"
android:layout_above="#+id/chooseUsernameAndPickProfilePicture"
android:layout_centerHorizontal="true"
android:layout_marginBottom="24dp"/>
#string/smsSentTo (Arabic locale): (looks like StackOverflow is displaying the {0} wrong, ignore that.)
<string name="smsSentTo">تم إرسال رسالة SMS إلى {0}</string>
#string/smsSentTo (English locale):
<string name="smsSentTo">An SMS has been sent to {0}</string>
As you can see, I'm formatting the text using MessageFormat:
String smsSentTo = MessageFormat.format(smsSentToTV.getText().toString(), phone);
smsSentToTV.setText(smsSentTo);
How can I get it to display these properly?
There is a solution with a downside
String html = "<p>تم إرسال رسالة SMS إلي</p>" + "+961 01 234 567";
textView.setText(Html.fromHtml(html));
The downside is that the HTML p tag prints a line afterwards separating the number into the next line. I tried to remove it with some HTML attributes or using tags other than p tag, but that didn't work
Hope this satisfies your need, or can give you even a clue.
I want to replace the char ('...') with a string ("...Read more").
Any easy way to do that by extending TextView and overriding a property?
I am expanding the TextView on click by increasing lines from 2 to 500 and vice versa. The TextView is in a ScrollView.
This code is XML File.
<com.google.android.material.textview.MaterialTextView
android:id="#+id/textDescriptions"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="What is Android ? \nBefore learning all topics of android, it is required to know what is android.Android is a software package and linux based operating system for mobile devices such as tablet computers and smartphones." />
This code is Java file.
String readMore = "... Read more";
String shortDescription = textDescriptions.getText().toString().substring(0, 100);
String text = "<font color=#aaaaaa>"+shortDescription+"</font> <font color=#ff669900>"+readMore+"</font>";
textDescriptions.setText(Html.fromHtml(text));
I can use Spannable in TextViews to create spans with different looks, underlines, strikethroughs and such. How can I do the same to alter line wrapping behavior? In particular, I don't want an email address to wrap in the middle, I want it to act like one word.
I tried WrapTogetherSpan, but I couldn't get it to work. It looks like it is only used by DynamicLayout, and I could not force the TextView to use DynamicLayout.
<TextView
android:id="#+id/merchant_email_field"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textSize="#dimen/account_setting_email"
android:gravity="center"
android:bufferType="spannable"
android:maxLines="2"
android:ellipsize="end"
/>
How I'm setting the spannable:
WrapTogetherSpan TOGETHER_SPAN = new WrapTogetherSpan() {};
String collectedString = getString(R.string.email_sentence, userEmail);
int emailOffset = collectedString.indexOf(userEmail);
Spannable emailSpannable = Spannable.Factory.getInstance()
.newSpannable(collectedString);
emailSpannable.setSpan(TOGETHER_SPAN, emailOffset,
emailOffset + userEmail.length(),
Spanned.SPAN_INCLUSIVE_EXCLUSIVE);
textView.setText(emailSpannable)
Don't know if you have found an answer to it but you can use unicode to help you out.
there is a non-break space character, so you will need to replace all the spaces you want to be unbreakable with this character (\u00A0)
for an example
String text = "Hello World";
text.replace(' ', '\u00A0');
textView.setText(text);
by the way I've looked for a span solution and couldn't find one, WrapTogetherSpan is just an interface so it wouldn't work...
but with this method I'm sure you could make a custom unbreakable span if you want.
If you are implementing a more low-level solution (ie, drawing your own text and handling line-wrapping yourself), then see BreakIterator. The BreakIterator.getLineInstance() factory method treats email addresses as a single unit.
String text = "My email is me#example.com.";
BreakIterator boundary = BreakIterator.getLineInstance();
boundary.setText(text);
int start = boundary.first();
for (int end = boundary.next(); end != BreakIterator.DONE; end = boundary.next()) {
System.out.println(start + " " + text.substring(start, end));
start = end;
}
The output shows the boundary start indexes where line breaks would be acceptable.
0 My
3 email
9 is
12 me#example.com.
See also
How does BreakIterator work in Android?
How is StaticLayout used in Android?
Have you tried adding android:singleLine="true" to your XML?
is it possible to add in TextView? Has anyone achieved similar functionality?
I want to have non-breakable space in TextView.
TextView respects the Unicode no-break space character (\u00A0), which would be a simpler/lighter solution than HTML.
It is possible to use to have a readable solution. Including \u00A0 or or / in the text doesn't really convey much information to the reader of the source code (or translator for that matter), unless you remember the hex codes. Here's a way to use the named entity in strings.xml:
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE resources [
<!ENTITY nbsp " "><!-- non-breaking space, U+00A0 -->
]>
<resources>
...
</resources>
This will create the missing declaration. The original HTML declaration can be found in https://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent referenced from the usual XHTML DTDs. All this works, because the XML parser reads these and substitutes while loading the file, so the entity won't be present in the resulting compiled resources.
in Android Text (CharSequence) Resources
<!-- Defined in <resources> -->
<string name="html_text">Don\'t break <b>this name</b></string>
<!-- Used in a layout -->
<TextView
android:layout_width="130dp"
android:layout_height="wrap_content"
android:background="#10000000"
android:text="#string/html_text"
/>
Device and preview (preview doesn't recognize HTML)
in Android String (formatted) Resources
<!-- Defined in <resources> -->
<string name="formatted_text">%1$s is nice</string>
<!-- Used in a layout -->
<TextView
android:layout_width="130dp"
android:layout_height="wrap_content"
android:background="#10000000"
tools:text="#string/formatted_text"
/>
Then in code:
String contents = getString(R.string.formatted_text, "Using an ");
((TextView)view.findViewById(android.R.id.text1)).setText(contents);
Device and preview (preview doesn't recognize entities and Java strings are literal text!)
Further tricks
These are just example uses of DTD entities, use it base on your own preference.
<!ENTITY con "\'"><!-- contraction, otherwise error: "Apostrophe not preceded by \"
Sadly ' cannot be overridden due to XML spec:
https://www.w3.org/TR/xml/#sec-predefined-ent -->
<!ENTITY param1 "%1$s"><!-- format string argument #1 -->
<string name="original">Don\'t wrap %1$s</string>
<string name="with_entities">Don&con;t wrap ¶m1;</string>
Both of them help highlighting:
\u00A0 is a non-breaking space, \u0020 is not a non-breaking space
The TextView should respect the non breaking space
<string name="test">Hello world</string>
or
new TextView("Hello\u00A0world");
One unique situation I ran into was adding a non-breaking space to a string resource that took String.format parameters.
<resources>
<string name="answer_progress" formatted="false">Answered %d of %d</string>
</resources>
I tried to simply copy and past the non-breaking space character into the string and it was replaced with a regular old space after compiling.
Removing the formatted="false", numbering the format args and using the backslash notation worked for me:
<resources>
<string name="answer_progress">Answered %1$d\u00A0of\u00A0%2$d</string>
</resources>
This worked for me:
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.N) {
textview.setText(Html.fromHtml(your string, Html.FROM_HTML_MODE_LEGACY));
} else {
textview.setText(Html.fromHtml(your string);
}
This is an example that using nbsp in a TextView
<string name="text">Example:\u00A0</string>
Strange, but in my case non of these two worked:
\u00A0
The TextView was showing them as text.
So I just inserted non-breaking space as it is: " "
I needed narrow no-break space (nnbs) in my app (its smaller then regular space) but nothing worked for me.
So i made this extension to simulate nnbs
For spannable
fun SpannableString.transparent(symbol: String): SpannableString {
val startIndex = indexOf(symbol)
val endIndex = startIndex + symbol.length
setSpan(ForegroundColorSpan(android.R.color.transparent), startIndex, endIndex, 0)
return this
}
fun SpannableString.fontSize(symbol: String, fontSize: Int): SpannableString {
val startIndex = indexOf(symbol)
val endIndex = startIndex + symbol.length
setSpan(AbsoluteSizeSpan(fontSize), startIndex, endIndex, SPAN_INCLUSIVE_INCLUSIVE)
return this
}
For TextView:
/**
* Font size divider for narrow no-break space
*/
const val NNBS_FONT_SIZE_DIVIDER = 3f
/**
* Unique symbol for simulating narrow no-break space
*/
const val NNBS_SYMBOL = "$"
/**
* For some reasons, Android not supporting narrow no-break space.
* This extension make #param symbol transparent and decrease font size to
* simulate narrow no-break space
*/
fun TextView.narrowNoBreakSpace(symbol: String, originalText: String) {
val nnbsFontSize = (textSize / NNBS_FONT_SIZE_DIVIDER).roundToInt()
text = SpannableString(originalText)
.transparent(symbol)
.fontSize(symbol, nnbsFontSize)
}
Usage:
val text = "Here is >$NNBS_SYMBOL< narrow no-break space."
textView.narrowNoBreakSpace(NNBS_SYMBOL, text)