I use in my application a custom font called Barlow. However, I need to specify a bottomPadding in each TextView because the font isn't centered vertically.
Here's what I did:
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.Light.NoActionBar">
...
<item name="android:textViewStyle">#style/Widget.Comblat.TextView</item>
<item name="textAppearanceBody1">#style/TextAppearance.Comblat.Body1</item>
<item name="textAppearanceHeadline3">#style/TextAppearance.Comblat.H3</item>
...
</style>
<!-- TextView -->
<style name="Widget.Comblat.TextView" parent="Widget.MaterialComponents.TextView">
<item name="android:paddingBottom">1.6dp</item>
</style>
<!-- CustomFont -->
<style name="TextAppearance.Comblat.Body1" parent="TextAppearance.MaterialComponents.Body1">
<item name="fontFamily">#font/mbarlow_condensed_regular</item>
<item name="android:textStyle">normal</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">16sp</item>
<item name="android:letterSpacing">0</item>
<item name="android:includeFontPadding">false</item>
<item name="android:textColor">#color/colorDarkPrimary60</item>
</style>
<style name="TextAppearance.Comblat.H3" parent="TextAppearance.MaterialComponents.Headline3">
<item name="fontFamily">#font/mbarlow_condensed_semibold</item>
<item name="android:textColor">#color/colorDarkPrimary</item>
<item name="android:textStyle">normal</item>
<item name="android:textAllCaps">false</item>
<item name="android:textSize">48sp</item>
<item name="android:includeFontPadding">false</item>
<item name="android:letterSpacing">-0.023</item>
</style>
Here I set paddingBottom to 1.6dp. But this is working only on the Body1 font (my formula for paddingBottom is 0.1*font_size), for H3 I would like to set 4.8dp.
Is there a way to set a paddingBottom for all TextView but depending on the font size ? I supposed it is possible programmatically, or by Overriding the TextView.
Of course I could set a custom style on each TextView in my layouts, but hey, I'm lazy guys
Thanks!
Well, I created a custom TextView, and replace all TextView with my own.
Here's the code:
public class BarlowTextView extends MaterialTextView {
public static final float RATIO_FONT_PADDING = 0.1f;
private int dpAsPixels = 0;
public BarlowTextView(#NonNull Context context) {
super(context);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
public BarlowTextView(#NonNull Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
public BarlowTextView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
public BarlowTextView(#NonNull Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
float scale = getResources().getDisplayMetrics().density;
dpAsPixels = (int) (getTextSize() * scale * RATIO_FONT_PADDING);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
setPaddingRelative(0 , 0 , 0 , dpAsPixels);
}
}
Related
I have created a MyButton class to set and display a custom
`public class MyButton extends android.support.v7.widget.AppCompatButton {
public MyButton(Context context) {
super(context);
init();
}
public MyButton(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public MyButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
private void init(){
Typeface tf = Typeface.createFromAsset(getContext().getAssets(), "fonts/Filmcryptic.ttf");
setTypeface(tf);
}
}
`
In my styles.xml file, I have set a style like this, where I only want to override the textcolor and the size -
<style name="taWhiteButtonText" parent="#android:style/TextAppearance">
<item name="android:textColor">#FF0000</item>
<item name="android:textSize">20dp</item>
</style>
In my layout file, I am using it so -
<com.xx.yyy.MyButton
android:id="#+id/button_start_quiz"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:background="#drawable/chalkbutton"
android:text="Start Quiz"
style="#style/taWhiteButtonText"
/>
The problem I face is that the font size changes as I update the style, but the font color does not get reflected.
What am I doing wrong?
Change your parent in style.xml file.
<style name="taWhiteButtonText" parent="Widget.AppCompat.Button">
<item name="android:textColor">#FF0000</item>
<item name="android:textSize">20dp</item>
</style>
In my Android app I have two different themes (light and dark).
For example:
<style name="AppThemeDark" parent="Theme.AppCompat">
<item name="colorPrimary">#android:color/black</item>
<item name="colorPrimaryDark">#android:color/black</item>
<item name="colorAccent">#android:color/holo_red_dark</item>
<item name="android:textColor">#android:color/white</item>
<item name="windowActionModeOverlay">true</item>
</style>
<style name="AppThemeLight" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
<item name="windowActionModeOverlay">true</item>
</style>
So, now I can apply, for example, different text colors to a TextView (white for dark theme and black for light):
<item name="android:textViewStyle">#style/TextViewDark</item>
<style name="TextViewDark">
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
But it will apply to all TextViews.
The main question, is it possible to make in XML (not programmatically) next:
Light theme: Half of TextViews text color black, and another half green.
Black theme: TextViews that black in Light theme - red, and another half - blue (which are green in Light theme).
Create 2 classes extends TextView
public class OneTextView extends TextView {
public OneTextView(Context context) {
super(context);
init(context);
}
public OneTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public OneTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
int[] attrs = new int[] { R.attr.myFirstColor};
TypedArray ta = context.obtainStyledAttributes(attrs);
int appColor = ta.getColor(0, 0);
ta.recycle();
// set theme color
setTextColor(appColor);
}
}
public class SecondTextView extends TextView {
public SecondTextView(Context context) {
super(context);
init(context);
}
public SecondTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public SecondTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context){
int[] attrs = new int[] { R.attr.mySecondColor};
TypedArray ta = context.obtainStyledAttributes(attrs);
int appColor = ta.getColor(0, 0);
ta.recycle();
// set theme color
setTextColor(appColor);
}
}
each class you can use in xml like this
<com.route.to.class.OneTextView
android:layout_width="match_parent"
android:layout_height="match_parent" />
OneTextView can have black and red colors
SecondTextView can have green and blue colors
define attr.xml in values
<?xml version="1.0" encoding="utf-8"?>
<resources>
<attr name="myFirstColor" format="color" />
<attr name="mySecondColor" format="color" />
</resources>
Then in your styles.xml, define colors for each theme:
<style name="Theme.MyApp" parent="#style/Theme.Light">
<item name="myFirstColor">#color/black</item>
<item name="mySecondColor">#color/green</item>
</style>
<style name="Theme.MyApp.Dark" parent="#style/Theme.Dark">
<item name="myFirstColor">#color/green</item>
<item name="mySecondColor">#color/blue</item>
</style>
you have defined in styles.xml
<style name="TextViewDark">
<item name="android:textColor">?android:attr/colorAccent</item>
</style>
<style name="TextViewLight">
<item name="android:textColor">#color/green</item>
</style>
then you can use it in main.xml
<LinearLayout>
<TextView
android:id="#+id/light_text_view"
android:text"i´m use light theme"
style="#style/TextViewLight"/>
<TextView
android:id="#+id/dark_text_view"
android:text"i´m use darktheme"
style="#style/TextViewDark"/>
</LinearLayout>
you don´t need to define styles in AppThemes
I'm working on an OpenSource lib as an alternative for the built-in android PopupMenu, in my lib I'd like to make everything customizable such that if you want to use it and change the popup colors/dimensions it becomes easy.
The full source code is here:
https://github.com/shehabic/Droppy/tree/styleable_ui
I'm few issues .. briefly I learned that to make your widget/custom view styleable you have to define 1-Custom View, 2-Styleable attributes, 3-Default values for such styleable attributes.
so I have the following xml files:
res/droppy__attr.xml
<resources>
<declare-styleable name="Droppy">
<attr name="droppyMenuItemTitleStyle" format="reference"/>
</declare-styleable>
<declare-styleable name="DroppyMenuItemTitle">
<attr name="android:textColor"/>
<attr name="android:minWidth"/>
<attr name="android:minHeight"/>
<attr name="android:layout_height"/>
<attr name="android:layout_width"/>
<attr name="android:layout_gravity"/>
<attr name="android:gravity"/>
<attr name="android:layout_weight"/>
</declare-styleable>
</resource>
res/droppy__styles.xml
<resources>
<style name="Theme.DroppyDefaults" parent="android:Theme">
<item name="droppyMenuItemTitleStyle">#style/Droppy.DroppyMenuItemTitle</item>
</style>
<style name="Droppy">
</style>
<style name="Droppy.DroppyMenuItemTitle">
<item name="android:textColor">#color/darkgrey</item>
<item name="android:minWidth">128dp</item>
<item name="android:minHeight">30dp</item>
<item name="android:layout_height">wrap_content</item>
<item name="android:layout_width">0dp</item>
<item name="android:layout_gravity">end|center_vertical</item>
<item name="android:gravity">center_vertical</item>
<item name="android:layout_weight">1</item>
</style>
</resource>
res/droppy__defaults.xml
<resources>
<!-- Menu Item Title -->
<color name="default_menu_item_title_textColor">#color/darkgrey</color>
<dimen name="default_menu_item_title_minWidth">128dp</dimen>
<dimen name="default_menu_item_title_minHeight">30dp</dimen>
<dimen name="default_menu_item_title_layout_width">0dp</dimen>
<!-- There's no easy way to set default values for the following -->
<!--<float name="default_menu_item_title_layout_weight">1</float>-->
<!--<item name="default_menu_item_title_layout_gravity">end|center_vertical</item>-->
<!--<item name="default_menu_item_title_gravity">center_vertical</item>-->
</resources>
In the block above here's the 1st issue,
Now here's the code I use in my custom view:
DroppyMenuItem.java
public class DroppyMenuItemTitle extends TextView {
public DroppyMenuItemTitle(Context context) {
this(context, null);
}
public DroppyMenuItemTitle(Context context, AttributeSet attrs) {
this(context, attrs, R.attr.droppyMenuItemTitleStyle);
}
public DroppyMenuItemTitle(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
final int defaultWidth = ViewGroup.LayoutParams.MATCH_PARENT;
final float defaultWeight = 1;
final int defaultGravity = Gravity.CENTER_VERTICAL;
final int defaultLayoutGravity = Gravity.END | Gravity.CENTER_VERTICAL;
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DroppyMenuItemTitle, defStyleAttr, 0);
int width = (int) a.getDimension(R.styleable.DroppyMenuItemTitle_android_layout_width, defaultWidth);
int height = a.getInt(R.styleable.DroppyMenuItemTitle_android_layout_height, ViewGroup.LayoutParams.WRAP_CONTENT);
setGravity(a.getInt(R.styleable.DroppyMenuItemTitle_android_gravity, defaultGravity));
LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(width, height);
lp.width = width;
lp.height = height;
lp.weight = a.getFloat(R.styleable.DroppyMenuItemTitle_android_layout_weight, defaultWeight);
lp.gravity = a.getInteger(R.styleable.DroppyMenuItemTitle_android_layout_gravity, defaultLayoutGravity);
setLayoutParams(lp);
}
As per the following line:
int width = (int) a.getDimension(R.styleable.DroppyMenuItemTitle_android_layout_width, defaultWidth);
if I use that customView and specify width as wrap_content or match_parent, it works fine as these are translated to -1 and -2.
But if specify it as XXdp (e.g. 20dp) it fails as the number now is actually a float not int
the error I get is as follows:
E/AndroidRuntime(2940): java.lang.NumberFormatException: Invalid int: "20.0dip"
I am trying to implement my own custom progressbar using 9 patch images. I am looking at the Sound ProgressBar example.
http://www.jagsaund.com/blog/2011/11/6/customizing-your-progress-bar-part-one.html
I have two problems that I have been unable to figure out why its happening:
No matter what I do my progressbar is not an instance of
layerdrawable, I am not sure why this is happening so I am unable to
get reference to the layer I want and change it grammatically.
The 9 patch image starts off stretched to the entire width of the container (relativelayout) not sure how to tell it to not
automatically scale so I can control its width grammatically.
Here are the following code snipets:
Drawable progressbar.xml
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
<item android:id="#android:id/background"
android:drawable="#drawable/progressbar_bg" >
</item>
<item android:id="#+id/progress">
<bitmap
android:src="#drawable/progressbar"
android:tileMode="repeat" />
</item>
</layer-list>
Style.xml
<style name="Widget">
</style>
<style name="Widget.ProgressBar">
<item name="android:indeterminateOnly">true</item>
<item name="android:indeterminateBehavior">repeat</item>
<item name="android:indeterminateDuration">3500</item>
<item name="android:minWidth">48dip</item>
<item name="android:maxWidth">48dip</item>
<item name="android:minHeight">48dip</item>
<item name="android:maxHeight">48dip</item>
</style>
<style name="Widget.ProgressBar.RegularProgressBar">
<item name="android:indeterminateOnly">false</item>
<item name="android:progressDrawable">#drawable/progressbar</item>
<item name="android:indeterminateDrawable">#android:drawable/progress_indeterminate_horizontal</item>
<item name="android:minHeight">1dip</item>
<item name="android:maxHeight">10dip</item>
</style>
Custom View inside XML layout file:
<com.custom.ahmad.views.NfcProgressBar
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/timeOutProgress"
android:layout_above="#+id/tvStart"
style="#style/Widget.ProgressBar.RegularProgressBar" />
Finally The custom Progressbar class:
public class NfcProgressBar extends ProgressBar {
private String text = "";
private int textColor = Color.BLACK;
private float textSize = 15;
public NfcProgressBar(Context context) {
super(context);
}
public NfcProgressBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public NfcProgressBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
protected synchronized void onDraw(Canvas canvas) {
Drawable progressDrawable = this.getProgressDrawable();
Log.i("Testing", "progressDrawable : " + ((progressDrawable != null) ? progressDrawable : "null"));
Log.i("Testing", "progressDrawable instanceof LayerDrawable : " + ((progressDrawable instanceof LayerDrawable)? "true" : "false"));
if (progressDrawable != null && progressDrawable instanceof LayerDrawable) {
// Get the layer and do some programming work.
}
}
My question is how to add a shadow to text when TextView is selected or View that TextView is in gets selected. For example I have a CheckedTextView which changes background according to type of selection. I also made a text selector which changes color on differents states. Now I would like to add a shadow when for example View gets selected. So it changes background color, text color and creates a shadow. This is my text selector:
<selector
xmlns:android="http://schemas.android.com/apk/res/android">
<item
android:state_focused="true"
android:state_pressed="false"
android:color="#android:color/white"
style="#style/DarkShadow"/>
<item
android:state_focused="true"
android:state_pressed="true"
android:color="#android:color/white"
style="#style/DarkShadow"/>
<item
android:state_focused="false"
android:state_pressed="true"
android:color="#android:color/white"
style="#style/DarkShadow"/>
<item
android:color="#color/primary_text_light_disable_only"/>
and style:
<style name="DarkShadow">
<item name="android:shadowColor">#BB000000</item>
<item name="android:shadowRadius">2.75</item>
</style>
Now text gets properly highlighted but no shadows appear. Does anyone know how to solve this?
This is a current limitation of the Android SDK.
I extended TextView for it to work, you can use it freely:
CustomTextView.java:
import android.widget.TextView;
import android.util.AttributeSet;
import android.content.res.TypedArray;
import android.content.Context;
import com.client.R;
public class CustomTextView extends TextView
{
private static String TAG = "CustomTextView";
private ColorStateList mShadowColors;
private float mShadowDx;
private float mShadowDy;
private float mShadowRadius;
public CustomTextView(Context context)
{
super(context);
}
public CustomTextView(Context context, AttributeSet attrs)
{
super(context, attrs);
init(context, attrs);
}
public CustomTextView(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
init(context, attrs);
}
/**
* Initialization process
*
* #param context
* #param attrs
* #param defStyle
*/
private void init(Context context, AttributeSet attrs, int defStyle)
{
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView, defStyle, 0);
final int attributeCount = a.getIndexCount();
for (int i = 0; i < attributeCount; i++) {
int curAttr = a.getIndex(i);
switch (curAttr) {
case R.styleable.CustomTextView_shadowColors:
mShadowColors = a.getColorStateList(curAttr);
break;
case R.styleable.CustomTextView_android_shadowDx:
mShadowDx = a.getFloat(curAttr, 0);
break;
case R.styleable.CustomTextView_android_shadowDy:
mShadowDy = a.getFloat(curAttr, 0);
break;
case R.styleable.CustomTextView_android_shadowRadius:
mShadowRadius = a.getFloat(curAttr, 0);
break;
default:
break;
}
}
a.recycle();
updateShadowColor();
}
private void updateShadowColor()
{
if (mShadowColors != null) {
setShadowLayer(mShadowRadius, mShadowDx, mShadowDy, mShadowColors.getColorForState(getDrawableState(), 0));
invalidate();
}
}
#Override
protected void drawableStateChanged()
{
super.drawableStateChanged();
updateShadowColor();
}
}
You also need to add this to your attr.xml (or create one):
attr.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="Theme">
<attr format="reference" name="CustomTextView"/>
</declare-styleable>
<declare-styleable name="CustomTextView">
<attr name="shadowColors" format="color|reference"/>
<attr name="android:shadowDx"/>
<attr name="android:shadowDy"/>
<attr name="android:shadowRadius"/>
</declare-styleable>
</resources>
So finally you'll be able to use it in your xmls, like this:
<com.client.ui.textviews.CustomTextView
xmlns:client="http://schemas.android.com/apk/res/com.client"
android:id="#+id/join_text"
android:shadowDx="1"
android:shadowDy="1"
android:shadowRadius="1"
client:shadowColors="#color/btn_green_shadow_color"/>
Where #color/btn_green_shadow_color points to a selector such a this:
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_enabled="false" android:color="#android:color/white"/>
<item android:state_pressed="true" android:color="#color/BzDarkGray"/>
<item android:color="#android:color/black"/>
</selector>
If you are not familiar with how to use custom attributes (with the custom xml namespace I used), please refer to this good StackOverFlow question.
Yeah, I ran into the same problem, you can change the text color using a selector in xml, but not the shadowcolor.
So in order to solve the problem, you might have to extend CheckedTextView or whatever View you need, and then override onDraw(Canvas canvas) according to the state of the View
Thus, you need to use
public void setShadowLayer (float radius, float dx, float dy, int color) defined in here
for example:
#Override
protected void onDraw(Canvas canvas) {
if(isPressed()){
setShadowLayer(1, 0, 1, Color.RED);
}else{
if(isFocused()){
setShadowLayer(1, 0, 1, Color.WHITE);
}else{
setShadowLayer(1, 0, 1, Color.BLACK);
}
}
super.onDraw(canvas);
}
I hope that works
This is what I ended up doing:
#Override
protected void drawableStateChanged() {
super.drawableStateChanged();
if(isPressed()) {
setShadowLayer(15, 0, 0, getTextColors().getDefaultColor());
} else {
setShadowLayer(0, 0, 0, Color.TRANSPARENT);
}
}