Setting a spannable string not working on a simple Textview - android

I cannot for the life of me understand why this simple code to set a spannable string is not working on this textview. The method below adds a "Today" marker, which should be in green, before the text displaying the date if the date is the current day.
private void setTimeTextView(String timeString) {
Calendar c = Calendar.getInstance();
String todaysDateString = ApiContentFormattingUtil.getFullDateFormat(c.getTime());
if (timeString.equals(todaysDateString)){
String todayText = getResources().getString(R.string.today_marker);
Spannable timeSpannable = new SpannableString(todayText + timeString);
timeSpannable.setSpan(new ForegroundColorSpan(ContextCompat.getColor(getContext(), R.color.greenish_teal)), 0,
todayText.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
mDateTime.setText(timeSpannable);
} else {
mDateTime.setText(timeString);
}
}
However, the color won't change.
Here is the XML for this view
<TextView
android:id="#+id/newsfeed_date_time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="23dp"
android:textSize="12sp"
android:textColor="#color/white_three"
android:letterSpacing="0.06"
app:fontPath="#string/opensans_bold_path"
tools:text="Monday, January 1st"
android:textAllCaps="true"
tools:ignore="MissingPrefix"
tools:targetApi="lollipop"/>

On versions prior to Oreo, the android:textAllCaps="true" attribute setting will cause the formatting spans to be stripped from your text. You'll need to remove that setting (or set it to false), and handle the conversion to upper case yourself, before creating your SpannableString from it. For example:
String todayText = getResources().getString(R.string.today_marker);
String text = todayText + timeString;
Spannable timeSpannable = new SpannableString(text.toUpperCase());
This is due to a known bug in the platform AllCapsTransformationMethod class, which on versions Nougat 7.1 and below handles the text as a flat String, basically stripping any formatting spans you may have set.
Unfortunately, the support/androidx libraries also use the platform AllCapsTransformationMethod class, so this will happen for their textAllCaps attributes, as well; i.e., app:textAllCaps is broken pre-Oreo, too.
As indicated, this was corrected in Oreo, so this manual fix isn't strictly necessary on those newer versions. However, if you are still supporting pre-Oreo versions, it might be easier to just leave it off and handle the capitalization manually everywhere, rather than having to account for two different setups in your resources and code.

Related

BottomNavigation BadgeDrawable set displayed number in a specific Locale irrespective of selected one

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)

Override TextView ellipsize char('...') with ("... Read more")

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));

Insert drop caps text in android

I am learning Android and the following is part of an assignment.
I need to write some text in an Android layout with the first letter in drop caps, like the following text:
I looked up the web and did not find many answers. Is there a style option or some property that I could use?
At the moment, I am thinking of the following options. Please suggest what is the best way to do such a thing
Use an image for the first letter
Write the first letter separately in a big font.
Any suggestions would be helpful.
You can use a RelativeSizeSpan.
final String someText = "A long time ago in a galaxy far, far away";
final SpannableString ss = new SpannableString(someText);
ss.setSpan(new RelativeSizeSpan(5f), 0, 1, 0);
There's a library written by Novoda in which you can add a DropCap https://github.com/novoda/spikes/tree/master/drop-cap
Here's an image from the demo app:
Please follow GitHub sample app
https://github.com/datanapps/CapTextView
<datanapps.captextview.CapTextView
android:id="#+id/text2"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
android:padding="5dp"
app:layout_constraintTop_toBottomOf="#+id/text1"
app:capsDropNumber="2"
app:capFont="#font/eb_garamond_regular"
app:capTextColor="#color/purple_700"
app:capTextSize="#dimen/cap_text_size"
app:bodyTextColor="#color/purple_700"
app:bodyTextSize="#dimen/body_text_size"
app:bodyTextFont="#font/eb_garamond_regular"
/>
Hope it will help

Altering line wrap behavior

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?

Unicode characters not displayed in TextView.setText

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", &#xF151, 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")

Categories

Resources