SpannableString not working when using AppCompat theme - android

I'm unable to get SpannableString to work when I set AppTheme to Theme.AppCompat.Light.DarkActionBar.
I have a button and its text is set with a SpannableString. When I use Holo theme the text renders as expected, but when I switch to AppCompat theme the span effects seam to be ignored. How can I get the SpannableString to work using the AppCompat theme?
styles.xml - when switching between those 2 themes I get very different results...
<resources>
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar" />
<!--<style name="AppTheme" parent="#android:style/Theme.Holo.Light" />-->
</resources>
... for my button that uses SpannableString
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container, false);
Button button = (Button) rootView.findViewById(R.id.button);
String detail = "ABC";
String caption = String.format("2 %s", detail);
Spannable span = new SpannableString(caption);
int detailIndex = caption.indexOf(detail);
span.setSpan(new StyleSpan(Typeface.BOLD), 0, detailIndex, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
span.setSpan(new RelativeSizeSpan(0.5f), detailIndex, detailIndex+detail.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
button.setText(span);
return rootView;
}
}

Well, it's not tied to appcompat-v7. If you remove the theme stuff entirely, and just use the default theme, on Android 5.0+ you will get Theme.Material, and the same effect can be seen there.
Part of the Material Design aesthetic is that button captions should be all caps, and however they implemented that is wiping out your spans. appcompat-v7 works with your code on pre-5.0 devices, suggesting that their backported widget effects do not include the app-caps stuff, and that they are delegating to the standard widgets on 5.0+.
Adding android:textAllCaps="false" to your Button in the layout seems to clear up the problem.

Related

Custom Font & Icon on Button in Fragment

I'm trying to get e Menu inside my fragment a little more custom. The Code does some resizing on the Button-Symbol which I created (is there by the way any better solution for this?) the Text & Font of the Button are also customized.
The Code runs in a Standalone Activity, running it in a Fragment doesn't work anymore:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_main, container,true);
final Button events = view.findViewById(R.id.events);
// BUTTON FONT THEME
String etext = "<font color=#FFFFFF>NEXT</font> <font color=#8def00> EVENTS </font>";
if (Build.VERSION.SDK_INT >= 24) {
events.setText(Html.fromHtml(etext, 0)); // for 24 api and more
} else {
events.setText(Html.fromHtml(etext)); // or for older api
}
events.setTypeface(ralewayfont);
Drawable iconevents = ContextCompat.getDrawable(getApplicationContext(),R.drawable.ic_calendar);
int WIconCal = iconevents.getIntrinsicWidth();
int HIconCal = iconevents.getIntrinsicHeight();
iconevents.setBounds(0,0,WIconCal/3,HIconCal/3);
events.setCompoundDrawables(iconevents, null, null, null);
}
It doesn't give me any errors - it doesn't resize the Images and doesn't set the custom font on the Buttons.
You should make more layout files that target specific API versions. In your case, I would make a /res/layout-v24/fragment_main.xml and set your v24-specific button attributes there. No need to do all the stuff your doing in your code snippet. Just inflate the layout like you are doing, connect your button with findViewById(), and Android OS will automatically know to use the layout-v24 version if it detects a phone using v24 or higher. Anything below v24 will use your default /res/layout/fragment_main.xml.
Hope that helps!

How can I get current theme's action bar background color?

I was trying to make my navigation drawer's background always match with the action bar's background color.
So that, every time, if the theme changes both background will change automatically.
I looked into R.attr, but didn't find anything.
The ActionBar API doesn't have a way to retrieve the current background Drawable or color.
However, you could use Resources.getIdentifier to call View.findViewById, retrieve the ActionBarView, then call View.getBackground to retrieve the Drawable. Even so, this still won't give you the color. The only way to do that would be to convert the Drawable into a Bitmap, then use some sort of color analyzer to find the dominant color.
Here's an example of retrieving the ActionBar Drawable.
final int actionBarId = getResources().getIdentifier("action_bar", "id", "android");
final View actionBar = findViewById(actionBarId);
final Drawable actionBarBackground = actionBar.getBackground();
But it seems like the easiest solution would be to create your own attribute and apply it in your themes.
Here's an example of that:
Custom attribute
<attr name="drawerLayoutBackground" format="reference|color" />
Initialize the attribute
<style name="Your.Theme.Dark" parent="#android:style/Theme.Holo">
<item name="drawerLayoutBackground">#color/your_color_dark</item>
</style>
<style name="Your.Theme.Light" parent="#android:style/Theme.Holo.Light">
<item name="drawerLayoutBackground">#color/your_color_light</item>
</style>
Then in the layout that contains your DrawerLayout, apply the android:background attribute like this:
android:background="?attr/drawerLayoutBackground"
Or you can obtain it using a TypedArray
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final TypedArray a = obtainStyledAttributes(new int[] {
R.attr.drawerLayoutBackground
});
try {
final int drawerLayoutBackground = a.getColor(0, 0);
} finally {
a.recycle();
}
}

Change the actionbar homeAsUpIndicator Programmatically

I used the following hack to change the homeAsupIndicator programmatically.
int upId = Resources.getSystem().getIdentifier("up", "id", "android");
if (upId > 0) {
ImageView up = (ImageView) findViewById(upId);
up.setImageResource(R.drawable.ic_action_bar_menu);
up.setPadding(0, 0, 20, 0);
}
But this is not working on most new phones (HTC One, Galaxy S3, etc). Is there a way that can be changed uniformly across devices. I need it to be changed only on home screen. Other screens would have the default one. So cannot use the styles.xml
This is what i did to acheive the behavior. I inherited the base theme and created a new theme to use it as a theme for the specific activity.
<style name="CustomActivityTheme" parent="AppTheme">
<item name="android:homeAsUpIndicator">#drawable/custom_home_as_up_icon</item>
</style>
and in the android manifest i made the activity theme as the above.
<activity
android:name="com.example.CustomActivity"
android:theme="#style/CustomActivityTheme" >
</activity>
works great. Will update again when i check on all devices I have. Thanks #faylon for pointing in the right direction
The question was to change dynamically the Up Home Indicator, although this answer was accepting and it is about Themes and Styles. I found a way to do this programmatically, according to Adneal's answer which gives me the clue and specially the right way to do. I used the below snippet code and it works well on (tested) devices with APIs mentioned here.
For lower APIs, I use R.id.up which is not available on higher API. That's why, I retrieve this id by a little workaround which is getting the parent of home button (android.R.id.home) and its first child (android.R.id.up):
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
// get the parent view of home (app icon) imageview
ViewGroup home = (ViewGroup) findViewById(android.R.id.home).getParent();
// get the first child (up imageview)
( (ImageView) home.getChildAt(0) )
// change the icon according to your needs
.setImageResource(R.drawable.custom_icon_up));
} else {
// get the up imageview directly with R.id.up
( (ImageView) findViewById(R.id.up) )
.setImageResource(R.drawable.custom_icon_up));
}
Note: If you don't use the SDK condition, you will get some NullPointerException.
API 18 has new methods ActionBar.setHomeAsUpIndicator() - unfortunately these aren't supported in the support library at this moment
http://developer.android.com/reference/android/app/ActionBar.html#setHomeAsUpIndicator(android.graphics.drawable.Drawable)
edit: these are now supported by the support library
http://developer.android.com/reference/android/support/v7/app/ActionBar.html#setHomeAsUpIndicator(android.graphics.drawable.Drawable)
All you need to do is to use this line of code:
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
This will change the icon with the up indicator. To disable it later, just call this function again and pass false as the param.
The solution by checking Resources.getSystem() doesn't work on all devices, A better solution to change the homeAsUpIndicator is to set it #null in style and change the logo resource programmatically.
Below is my code from style.xml
<style name="Theme.HomeScreen" parent="AppBaseTheme">
<item name="displayOptions">showHome|useLogo</item>
<item name="homeAsUpIndicator">#null</item>
<item name="android:homeAsUpIndicator">#null</item>
</style>
In code you can change the logo using setLogo() method.
getSupportActionBar().setLogo(R.drawable.abc_ic_ab_back_holo_light); //for ActionBarCompat
getActionBar().setLogo(R.drawable.abc_ic_ab_back_holo_light); //for default actionbar for post 3.0 devices
Also note that the Android API 18 has methods to edit the homeAsUpIndicator programatically, refer documentation.
You can achieve this in an easier way. Try to can change the homeAsUpIndicator attribute of actionBarStyle in your theme.xml and styles.xml.
If you want some padding, just add some white space in your image.
You can try this:
this.getSupportActionBar().setHomeAsUpIndicator( R.drawable.actionbar_indicator ); //for ActionBarCompat
this.getActionBar().setHomeAsUpIndicator( R.drawable.actionbar_indicator ); //for default actionbar for post 3.0 devices
If you need change the position of the icon, you must create a drawable file containing a "layer-list" like this:
actionbar_indicator.xml
<?xml version="1.0" encoding="utf-8"?>
<layer-list
xmlns:android="http://schemas.android.com/apk/res/android" >
<item
android:drawable="#drawable/indicator"
android:right="5dp"
android:left="10dp" />
</layer-list>
use getActionBar().setCustomView(int yourView); because ActionBar haven't method to change homeUp icon!
Adding to Fllo answer Change the actionbar homeAsUpIndicator Programamtically
I was able to use this hack on Android 4+ but could not understand why the up/home indicator was back to the default one when search widget was expanded. Looking at the view hierarchy, turns out that the up/home indicator + icon section of the action bar has 2 implementations and of course the first on is the one for when the search widget is not expanded. So here is the code I used to work around this and get the up/home indicator changed in both cases.
mSearchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
#Override
public boolean onMenuItemActionExpand(MenuItem item) {
// https://stackoverflow.com/questions/17585892/change-the-actionbar-homeasupindicator-programamtically
int actionBarId = getResources().getIdentifier("android:id/action_bar", null, null);
View view = getActivity().getWindow().getDecorView().findViewById(actionBarId);
if (view == null
|| !(view instanceof ViewGroup)) {
return true;
}
final ViewGroup actionBarView = (ViewGroup)view;
// The second home view is only inflated after
// setOnActionExpandListener() is first called
actionBarView.post(new Runnable() {
#Override
public void run() {
//The 2 ActionBarView$HomeView views are always children of the same view group
//However, they are not always children of the ActionBarView itself
//(depends on OS version)
int upId = getResources().getIdentifier("android:id/up", null, null);
View upView = actionBarView.findViewById(upId);
ViewParent viewParent = upView.getParent();
if (viewParent == null) {
return;
}
viewParent = viewParent.getParent();
if (viewParent == null
|| !(viewParent instanceof ViewGroup)) {
return;
}
ViewGroup viewGroup = (ViewGroup) viewParent;
int childCount = viewGroup.getChildCount();
for (int i = 0; i < childCount; i++) {
View childView = viewGroup.getChildAt(i);
if (childView instanceof ViewGroup) {
ViewGroup homeView = (ViewGroup) childView;
upView = homeView.findViewById(upId);
if (upView != null
&& upView instanceof ImageView) {
Drawable upDrawable = getResources().getDrawable(R.drawable.ic_ab_back_holo_dark_am);
upDrawable.setColorFilter(accentColorInt, PorterDuff.Mode.MULTIPLY);
((ImageView) upView).setImageDrawable(upDrawable);
}
}
}
}
});
If someone uses the library support-v7 appcompat, you can directly call this method:
getSupportActionBar().setHomeAsUpIndicator(int redId)
In other case you can use this solution:
https://stackoverflow.com/a/23522910/944630
If you are using DrawerLayout with ActionBarDrawerToggle, then check out this answer.
this.getSupportActionBar().setDisplayUseLogoEnabled(true);
this.getSupportActionBar().setLogo(R.drawable.about_selected);
Also you can define the logo in manifest in attribute android:logo of and tags and set in theme that you want to use logo instead of app icon in the action bar.

Custom font for ActionBarSherlock tabs

I want to set font for the "Video" and "Image" tabs in ActionBarSherlock. I have used the following code to do so. Its showing accurately in ICS but not in the lower version device but I have shown accurate output in the other part of this application by setting TYPE FACE like ...
a.settypeface("ab.tttf");
a.settext("VIDEO");
But how to do I set a TypeFace in the ActionBar in this code:
mTabsAdapter.addTab(bar.newTab().setText("IMAGE"), AFragment.class, null);
mTabsAdapter.addTab(bar.newTab().setText("VIDEO"), BFragment.class, null);
Okay . I found it myself some where on SO.
First make an xml file with this in it: tab_title.xml
<TextView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/action_custom_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="My Custom title"
android:textColor="#fff"
android:textSize="18sp"
android:paddingTop="5dp" />
Then in the class where you in instantiate your ActionBar use this code to set the text on each of the tabs. (This example is using ActionBarSherlock.)
ActionBar bar = getSupportActionBar();
bar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
String[] tabNames = {"Tab 1","Tab 2","Tab 3"};
for(int i = 0; i<bar.getTabCount(); i++){
LayoutInflater inflater = LayoutInflater.from(this);
View customView = inflater.inflate(R.layout.tab_title, null);
TextView titleTV = (TextView) customView.findViewById(R.id.action_custom_title);
titleTV.setText(tabNames[i]);
//Here you can also add any other styling you want.
bar.getTabAt(i).setCustomView(customView);
}
Try this:
String s = "VIDEO";
SpannableString mSS = new SpannableString(s);
mSS.setSpan(new StyleSpan(Typeface.BOLD), 0, s.length(), 0);
mTabsAdapter.addTab(bar.newTab().setText(mSS),
BFragment.class, null);
To solve this you could create a Special Actionbar class.
Simply create a Class myBar extends Sherlockactionbar and put in the settypeface. If you now create that Bar in your view it will have the Typeface as you whish. For example here is an button with a new Typeface.
public class ChangedButton extends Button{
public ChangedButton(Context context, AttributeSet attrs) {
super(context, attrs);
Typeface font = Typeface.createFromAsset(context.getAssets(), "fonts/viking2.TTF");
this.setTypeface(font);
}
}
regards
First create following custom TypefaceSpan class in your project.Bit changed version of Custom TypefaceSpan to enable to use both .otf and .ttf fonts.
import java.util.StringTokenizer;
import android.content.Context;
import android.graphics.Typeface;
import android.support.v4.util.LruCache;
import android.text.TextPaint;
import android.text.style.MetricAffectingSpan;
public class TypefaceSpan extends MetricAffectingSpan{
/*Cache to save loaded fonts*/
private static LruCache<String, Typeface> typeFaceCache= new LruCache<String, Typeface>(12);
private Typeface mTypeface;
public TypefaceSpan(Context context,String typeFaceName)
{
StringTokenizer tokens=new StringTokenizer(typeFaceName,".");
String fontName=tokens.nextToken();
mTypeface=typeFaceCache.get(fontName);
if(mTypeface==null)
{
mTypeface=Constants.getFont(context, typeFaceName);
//cache the loaded font
typeFaceCache.put(fontName, mTypeface);
}
}
#Override
public void updateMeasureState(TextPaint p) {
p.setTypeface(mTypeface);
}
#Override
public void updateDrawState(TextPaint tp) {
tp.setTypeface(mTypeface);
}
}
Now apply code like this:(I used this on one of my Bangla apps successfully)
SpannableString mstKnwTitle=new SpannableString(getString(R.string.e_mustknow_tab));
SpannableString cntctsTitle=new SpannableString(getString(R.string.e_number_tab));
TypefaceSpan span=new TypefaceSpan(this, "solaimanlipi.ttf");
mstKnwTitle.setSpan(span, 0, mstKnwTitle.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
cntctsTitle.setSpan(span, 0, mstKnwTitle.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
Tab tab= actionBar.newTab();
tab.setText(mstKnwTitle);
tab.setTabListener(tabListener);
actionBar.addTab(tab);
tab= actionBar.newTab();
tab.setText(cntctsTitle);
tab.setTabListener(tabListener);
actionBar.addTab(tab);
Original inspiration of my answer was:Styling the Android Action Bar title using a custom typeface
It seems like you are not getting any guidance. I am not sure with my answer's result but yes you can defiantly get idea to make it as you want.
Let me try to help you. I think you have to somewhat play with the in-built default resources of the android sdk.
You have to create custom style:
<style name="MyActionBarTabText" parent="Widget.ActionBar.TabText">
<item name="android:textAppearance">#style/TextAppearance.Holo.Medium</item>
<item name="android:textColor">?android:attr/textColorPrimary</item>
<item name="android:textSize">12sp</item>
<item name="android:textStyle">bold</item>
<item name="android:textAllCaps">true</item>
<item name="android:ellipsize">marquee</item>
<item name="android:maxLines">2</item>
</style>
<style name="Widget.ActionBar.TabText" parent="Widget">
<item name="android:textAppearance">#style/TextAppearance.Widget.TextView.PopupMenu</item>
<item name="android:textColor">?android:attr/textColorPrimaryInverse</item>
<item name="android:textSize">18sp</item>
<item name="android:fontFamily">HERE YOU CAN GIVE YOUR FONT</item>
</style>
You can also refer this SO.
Now just apply that theme to your activity and you will get Font of the TabText as you want.
Comment me if have any query.
Enjoy coding... :)
In EXTRA
Another solution could be to modify the tab image to include the text, with GIMP or photoshop something, and then just set those images in the tabs, instead of images and text. It is a bit of an awkward way of doing it but it would work.
Hope this helps!
You can do it with this:
// Set a custom font on all of our ActionBar Tabs
private boolean setCustomFontToActionBarTab(Object root) {
// Found the container, that holds the Tabs. This is the ScrollContainerView to be specific,
// but checking against that class is not possible, since it's part of the hidden API.
// We will check, if root is an instance of HorizontalScrollView instead,
// since ScrollContainerView extends HorizontalScrollView.
if (root instanceof HorizontalScrollView) {
// The Tabs are all wraped in a LinearLayout
root = ((ViewGroup) root).getChildAt(0);
if (root instanceof LinearLayout) {
// Found the Tabs. Now we can set a custom Font to all of them.
for (int i = 0; i < ((ViewGroup) root).getChildCount(); i++) {
LinearLayout child = ((LinearLayout)((ViewGroup) root).getChildAt(i));
TextView actionBarTitle = (TextView) child.getChildAt(0);
Typeface tf = Typeface.createFromAsset(mContext.getAssets(), "font/font.ttf");
actionBarTitle.setTypeface(tf)
}
return true;
}
}
// Search ActionBar and the Tabs. Exclude the content of the app from this search.
else if (root instanceof ViewGroup) {
ViewGroup group = (ViewGroup) root;
if (group.getId() != android.R.id.content) {
// Found a container that isn't the container holding our screen layout.
// The Tabs have to be in here.
for (int i = 0; i < group.getChildCount(); i++) {
if (setCustomFontToActionBarTab(group.getChildAt(i))) {
// Found and done searching the View tree
return true;
}
}
}
}
// Found nothing
return false;
}
Call it with this:
ViewParent root = findViewById(android.R.id.content).getParent();
setCustomFontToActionBarTab(root);

Creating a customised button with 2 lines of text

I'm new to Android (Visual Studio for 20 years). I need to create a clickable control that features 2 lines of text (1 smaller font at the top of the button for a caption and a larger font line for a value - would post an image but I'm not allowed). The size of the larger font scales so that the value will fit on the control.
I'm pretty sure I need to subclass the button control but not sure how to in this case. All samples I have found don't seem to fit the bill.
Have done this easily with VB.Net but I'm stumped when I try with Android. Any help very much appreciated. Might be a handy control for others too.
Thanks
You could try using Spannable in the code like:
public class Test extends Activity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
Button but = (Button) findViewById(R.id.button1);
String butText= "Line 1\nLine 2";
but.setText(formatString(butText));
}
private Spannable formatString(String str) {
int startSpan = str.indexOf("\n");
int endSpan = str.length();
Spannable spanString = null;
spanString = new SpannableString(str);
spanString.setSpan(new TextAppearanceSpan(this,
R.style.custompoint), startSpan, endSpan,
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return spanString;
}
}
Where you have a style 'custompoint'
<?xml version="1.0" encoding="utf-8"?>
<resources>
<style
name="custompoint">
<item name="android:textSize">24sp</item>
<item name="android:textStyle">bold</item>
</style>
</resources>

Categories

Resources