EditText back padding - android

This code helps me to draw text in EditText element.
public class LimitedEditText extends EditText
{
private Paint limit_text_paint;
/**
* Current size of text displayed in the EditView.
*/
private int current_text_size;
/**
* Maximum text size allowed.
*/
private int maximum_text_size;
/**
* Default limit indicator X margin used.
*/
private final int DEFAULT_X_MARGIN= 10;
/**
* Default limit indicator Y margin used.
*/
private final int DEFAULT_Y_MARGIN= 20;
/**
* Flag indicating text size limit is unlimited.
*/
public static final int UNLIMITED= -100;
/**
* LimitedEditText constructor.
* #param context Context that will display this view.
* #param attrs Set of attributes defined for this view.
* #param defStyle Style defined for this view.
*/
public LimitedEditText(Context context, AttributeSet attrs, int defStyle)
{
super(context, attrs, defStyle);
initComponents(attrs);
}
/**
* LimitedEditText constructor.
* #param context Context that will display this view.
* #param attrs Set of attributes defined for this view.
*/
public LimitedEditText(Context context, AttributeSet attrs)
{
super(context, attrs);
initComponents(attrs);
}
/**
* LimitedEditText constructor.
* #param context Context that will display this view.
*/
public LimitedEditText(Context context)
{
super(context);
initComponents(null);
}
/**
* Initialize the UI components.
* #param attrs Set of attributes defined for this view.
*/
private void initComponents(AttributeSet attrs)
{
limit_text_paint= new Paint();
limit_text_paint.setColor(Color.GRAY);
limit_text_paint.setTextSize(20);
maximum_text_size= UNLIMITED;
current_text_size= 0;
if(attrs!=null) //android:maxLength
maximum_text_size= attrs.getAttributeIntValue("android", "maxLength", UNLIMITED);
super.addTextChangedListener(new TextWatcher(){
#Override
public void afterTextChanged(Editable s)
{
if(maximum_text_size != UNLIMITED)
current_text_size= getText().toString().length();
invalidate();
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
#Override
public void onTextChanged(CharSequence s, int start, int before,int count) {}
});
}
/**
* Set maximum text size for given EditText.
* #param max Maximum number of characters allowed.
*/
public void setMaxTextSize(int max)
{
maximum_text_size= max;
if(max== UNLIMITED)
setFilters(null);
else
setFilters(new InputFilter[]{new InputFilter.LengthFilter(max)});
}
/**
* Remove text size limit.
* This will stop drawing the amount of characters left.
*/
public void removeTextSizeLimit()
{
setMaxTextSize(UNLIMITED);
}
/**
* Get the X position of limit indicator.
* #param text Text to be drawn on the limit indicator.
* #return X position of limit indicator.
*/
protected float getLimitIndicatorX(String text)
{
float widths[]= new float1;
limit_text_paint.getTextWidths(text, widths);
float sum= 0;
for(float w: widths)
sum+= w;
return getWidth() + getScrollX() - sum - DEFAULT_X_MARGIN;
}
/**
* Get the Y position of limit indicator.
* #param text Text to be drawn on the limit indicator.
* #return Y position of limit indicator.
*/
protected float getLimitIndicatorY(String text)
{
return DEFAULT_Y_MARGIN + getScrollY();
}
#Override
protected void onDraw(Canvas canvas)
{
super.onDraw(canvas);
if(maximum_text_size== UNLIMITED)
return;
String text= current_text_size+"/"+maximum_text_size;
canvas.drawText(text, getLimitIndicatorX(text), getLimitIndicatorY(text), limit_text_paint);
}
}
my result
I need to draw it under the text(and blue line), but i can't do it , because after blue line is end of edittext. How could i do it? Of course, i can draw line myself, but i think it will be not correct solution.

Related

Implement badge counter for all types of android device (different launchers) [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 6 years ago.
Improve this question
Some home launchers, like Samsung, Sony, HTC, Nova Launcher etc., support badge counters. How can I implement badge counters for all devices that support it?
1st of all badge view is not any launcher or device specific .If you want to implement Badge View in Android 1st you have to create custom class for badge view
public class BadgeView extends TextView {
public static final int POSITION_TOP_LEFT = 1;
public static final int POSITION_TOP_RIGHT = 2;
public static final int POSITION_BOTTOM_LEFT = 3;
public static final int POSITION_BOTTOM_RIGHT = 4;
public static final int POSITION_CENTER = 5;
private static final int DEFAULT_MARGIN_DIP = 5;
private static final int DEFAULT_LR_PADDING_DIP = 5;
private static final int DEFAULT_CORNER_RADIUS_DIP = 8;
private static final int DEFAULT_POSITION = POSITION_TOP_RIGHT;
private static final int DEFAULT_BADGE_COLOR = Color
.parseColor("#CCFF0000"); // Color.RED;
private static final int DEFAULT_TEXT_COLOR = Color.WHITE;
private static Animation fadeIn;
private static Animation fadeOut;
private Context context;
private View target;
private int badgePosition;
private int badgeMarginH;
private int badgeMarginV;
private int badgeColor;
private boolean isShown;
private ShapeDrawable badgeBg;
private int targetTabIndex;
public BadgeView(Context context) {
this(context, (AttributeSet) null, android.R.attr.textViewStyle);
}
public BadgeView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
/**
* Constructor -
*
* create a new BadgeView instance attached to a target
* {#link android.view.View}.
*
* #param context
* context for this view.
* #param target
* the View to attach the badge to.
*/
public BadgeView(Context context, View target) {
this(context, null, android.R.attr.textViewStyle, target, 0);
}
/**
* Constructor -
*
* create a new BadgeView instance attached to a target
* {#link android.widget.TabWidget} tab at a given index.
*
* #param context
* context for this view.
* #param target
* the TabWidget to attach the badge to.
* #param index
* the position of the tab within the target.
*/
public BadgeView(Context context, TabWidget target, int index) {
this(context, null, android.R.attr.textViewStyle, target, index);
}
public BadgeView(Context context, AttributeSet attrs, int defStyle) {
this(context, attrs, defStyle, null, 0);
}
public BadgeView(Context context, AttributeSet attrs, int defStyle,
View target, int tabIndex) {
super(context, attrs, defStyle);
init(context, target, tabIndex);
}
public BadgeView(Context context, SlidingTabStrip target,
int i) {
// TODO Auto-generated constructor stub
this(context, null, android.R.attr.textViewStyle, target, i);
}
private void init(Context context, View target, int tabIndex) {
this.context = context;
this.target = target;
this.targetTabIndex = tabIndex;
// apply defaults
badgePosition = DEFAULT_POSITION;
badgeMarginH = dipToPixels(DEFAULT_MARGIN_DIP);
badgeMarginV = badgeMarginH;
badgeColor = DEFAULT_BADGE_COLOR;
setTypeface(Typeface.DEFAULT_BOLD);
int paddingPixels = dipToPixels(DEFAULT_LR_PADDING_DIP);
setPadding(paddingPixels, 0, paddingPixels, 0);
setTextColor(DEFAULT_TEXT_COLOR);
fadeIn = new AlphaAnimation(0, 1);
fadeIn.setInterpolator(new DecelerateInterpolator());
fadeIn.setDuration(200);
fadeOut = new AlphaAnimation(1, 0);
fadeOut.setInterpolator(new AccelerateInterpolator());
fadeOut.setDuration(200);
isShown = false;
if (this.target != null) {
applyTo(this.target);
} else {
show();
}
}
private void applyTo(View target) {
ViewGroup.LayoutParams lp = target.getLayoutParams();
ViewParent parent = target.getParent();
FrameLayout container = new FrameLayout(context);
if (target instanceof TabWidget) {
// set target to the relevant tab child container
target = ((TabWidget) target).getChildTabViewAt(targetTabIndex);
this.target = target;
((ViewGroup) target).addView(container, new ViewGroup.LayoutParams(
ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT));
this.setVisibility(View.GONE);
container.addView(this);
} else if (target instanceof SlidingTabStrip) {
// set target to the relevant tab child container
View textView = ((SlidingTabStrip) target)
.getChildAt(targetTabIndex);
ViewGroup group = (ViewGroup) target;
// getting index of TexTView from SlidingTabStrip
int index = group.indexOfChild(textView);
Log.e("index", "" + index);
group.removeView(textView);
group.addView(container, index, lp);
container.addView(textView);
this.setVisibility(View.GONE);
container.addView(this);
group.invalidate();
} else {
// TODO verify that parent is indeed a ViewGroup
ViewGroup group = (ViewGroup) parent;
int index = group.indexOfChild(target);
group.removeView(target);
group.addView(container, index, lp);
container.addView(target);
this.setVisibility(View.GONE);
container.addView(this);
group.invalidate();
}
}
/**
* Make the badge visible in the UI.
*
*/
public void show() {
show(false, null);
}
/**
* Make the badge visible in the UI.
*
* #param animate
* flag to apply the default fade-in animation.
*/
public void show(boolean animate) {
show(animate, fadeIn);
}
/**
* Make the badge visible in the UI.
*
* #param anim
* Animation to apply to the view when made visible.
*/
public void show(Animation anim) {
show(true, anim);
}
/**
* Make the badge non-visible in the UI.
*
*/
public void hide() {
hide(false, null);
}
/**
* Make the badge non-visible in the UI.
*
* #param animate
* flag to apply the default fade-out animation.
*/
public void hide(boolean animate) {
hide(animate, fadeOut);
}
/**
* Make the badge non-visible in the UI.
*
* #param anim
* Animation to apply to the view when made non-visible.
*/
public void hide(Animation anim) {
hide(true, anim);
}
/**
* Toggle the badge visibility in the UI.
*
*/
public void toggle() {
toggle(false, null, null);
}
/**
* Toggle the badge visibility in the UI.
*
* #param animate
* flag to apply the default fade-in/out animation.
*/
public void toggle(boolean animate) {
toggle(animate, fadeIn, fadeOut);
}
/**
* Toggle the badge visibility in the UI.
*
* #param animIn
* Animation to apply to the view when made visible.
* #param animOut
* Animation to apply to the view when made non-visible.
*/
public void toggle(Animation animIn, Animation animOut) {
toggle(true, animIn, animOut);
}
private void show(boolean animate, Animation anim) {
if (getBackground() == null) {
if (badgeBg == null) {
badgeBg = getDefaultBackground();
}
setBackgroundDrawable(badgeBg);
}
applyLayoutParams();
if (animate) {
this.startAnimation(anim);
}
this.setVisibility(View.VISIBLE);
isShown = true;
}
private void hide(boolean animate, Animation anim) {
this.setVisibility(View.GONE);
if (animate) {
this.startAnimation(anim);
}
isShown = false;
}
private void toggle(boolean animate, Animation animIn, Animation animOut) {
if (isShown) {
hide(animate && (animOut != null), animOut);
} else {
show(animate && (animIn != null), animIn);
}
}
/**
* Increment the numeric badge label. If the current badge label cannot be
* converted to an integer value, its label will be set to "0".
*
* #param offset
* the increment offset.
*/
public int increment(int offset) {
CharSequence txt = getText();
int i;
if (txt != null) {
try {
i = Integer.parseInt(txt.toString());
} catch (NumberFormatException e) {
i = 0;
}
} else {
i = 0;
}
i = i + offset;
setText(String.valueOf(i));
return i;
}
/**
* Decrement the numeric badge label. If the current badge label cannot be
* converted to an integer value, its label will be set to "0".
*
* #param offset
* the decrement offset.
*/
public int decrement(int offset) {
return increment(-offset);
}
private ShapeDrawable getDefaultBackground() {
int r = dipToPixels(DEFAULT_CORNER_RADIUS_DIP);
float[] outerR = new float[] { r, r, r, r, r, r, r, r };
RoundRectShape rr = new RoundRectShape(outerR, null, null);
ShapeDrawable drawable = new ShapeDrawable(rr);
drawable.getPaint().setColor(badgeColor);
return drawable;
}
private void applyLayoutParams() {
FrameLayout.LayoutParams lp = new FrameLayout.LayoutParams(
ViewGroup.LayoutParams.WRAP_CONTENT, AbsoluteLayout.LayoutParams.WRAP_CONTENT);
switch (badgePosition) {
case POSITION_TOP_LEFT:
lp.gravity = Gravity.LEFT | Gravity.TOP;
lp.setMargins(badgeMarginH, badgeMarginV, 0, 0);
break;
case POSITION_TOP_RIGHT:
lp.gravity = Gravity.RIGHT | Gravity.TOP;
lp.setMargins(0, badgeMarginV, badgeMarginH, 0);
break;
case POSITION_BOTTOM_LEFT:
lp.gravity = Gravity.LEFT | Gravity.BOTTOM;
lp.setMargins(badgeMarginH, 0, 0, badgeMarginV);
break;
case POSITION_BOTTOM_RIGHT:
lp.gravity = Gravity.RIGHT | Gravity.BOTTOM;
lp.setMargins(0, 0, badgeMarginH, badgeMarginV);
break;
case POSITION_CENTER:
lp.gravity = Gravity.CENTER;
lp.setMargins(0, 0, 0, 0);
break;
default:
break;
}
setLayoutParams(lp);
}
/**
* Returns the target View this badge has been attached to.
*
*/
public View getTarget() {
return target;
}
/**
* Is this badge currently visible in the UI?
*
*/
#Override
public boolean isShown() {
return isShown;
}
/**
* Returns the positioning of this badge.
*
* one of POSITION_TOP_LEFT, POSITION_TOP_RIGHT, POSITION_BOTTOM_LEFT,
* POSITION_BOTTOM_RIGHT, POSTION_CENTER.
*
*/
public int getBadgePosition() {
return badgePosition;
}
/**
* Set the positioning of this badge.
*
* #param layoutPosition
* one of POSITION_TOP_LEFT, POSITION_TOP_RIGHT,
* POSITION_BOTTOM_LEFT, POSITION_BOTTOM_RIGHT, POSTION_CENTER.
*
*/
public void setBadgePosition(int layoutPosition) {
this.badgePosition = layoutPosition;
}
/**
* Returns the horizontal margin from the target View that is applied to
* this badge.
*
*/
public int getHorizontalBadgeMargin() {
return badgeMarginH;
}
/**
* Returns the vertical margin from the target View that is applied to this
* badge.
*
*/
public int getVerticalBadgeMargin() {
return badgeMarginV;
}
/**
* Set the horizontal/vertical margin from the target View that is applied
* to this badge.
*
* #param badgeMargin
* the margin in pixels.
*/
public void setBadgeMargin(int badgeMargin) {
this.badgeMarginH = badgeMargin;
this.badgeMarginV = badgeMargin;
}
/**
* Set the horizontal/vertical margin from the target View that is applied
* to this badge.
*
* #param horizontal
* margin in pixels.
* #param vertical
* margin in pixels.
*/
public void setBadgeMargin(int horizontal, int vertical) {
this.badgeMarginH = horizontal;
this.badgeMarginV = vertical;
}
/**
* Returns the color value of the badge background.
*
*/
public int getBadgeBackgroundColor() {
return badgeColor;
}
/**
* Set the color value of the badge background.
*
* #param badgeColor
* the badge background color.
*/
public void setBadgeBackgroundColor(int badgeColor) {
this.badgeColor = badgeColor;
badgeBg = getDefaultBackground();
}
private int dipToPixels(int dip) {
Resources r = getResources();
float px = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
r.getDisplayMetrics());
return (int) px;
}
}
And wherever you want badge view just initialize and use it
BadgeView badge;

How to display dynamic text on a button and auto adjust their size in Android?

I'm working on a project which needs me to display some dynamic texts based on users' selection on a button.
I know how to do the text display part, but I was stuck on how I can display different text properly on a fixed size button.
For example: "Apple" and "I have an Apple". How can I achieve the result that when displaying "Apple", the text size will be bigger and fit the button, and when "I have an Apple" the text will be smaller and may become three lines?
Thank you!
Android 8.0 supports Autosizing TextViews so you just have to specify android:autoSizeTextType="uniform". For older versions, you can use android.support.v7.widget.AppCompatTextView with app:autoSizeTextType="uniform".
By chance, it also works for buttons and for older versions just use android.support.v7.widget.AppCompatButton instead.
Hope this helped.
Take a look at this question Auto Scale TextView Text to Fit within Bounds. The same technique should apply to a button.
(yes, it is much more complicated than it seems like it should be.)
Style.xml:
<style name="Widget.Button.CustomStyle" parent="Widget.MaterialComponents.Button">
<item name="android:minHeight">50dp</item>
<item name="android:maxWidth">300dp</item>
<item name="android:textStyle">bold</item>
<item name="android:textSize">16sp</item>
<item name="backgroundTint">#color/white</item>
<item name="cornerRadius">25dp</item>
<item name="autoSizeTextType">uniform</item>
<item name="autoSizeMinTextSize">10sp</item>
<item name="autoSizeMaxTextSize">16sp</item>
<item name="autoSizeStepGranularity">2sp</item>
<item name="android:maxLines">1</item>
<item name="android:textColor">#color/colorPrimary</item>
<item name="android:insetTop">0dp</item>
<item name="android:insetBottom">0dp</item>
<item name="android:lineSpacingExtra">4sp</item>
<item name="android:gravity">center</item>
</style>
Usage:
<com.google.android.material.button.MaterialButton
android:id="#+id/blah"
style="#style/Widget.Button.CustomStyle"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:text="Your long text, to the infinity and beyond!!! Why not :)" />
Result:
[Source: https://stackoverflow.com/a/59302886/421467 ]
I know this question is a few years old, but I want to add a full solution for future reference.
This code is based on AutoFitTextView and has been adapted for Buttons. Specifically it also considers the text width to avoid word-breaks when resizing.
For all licensing information visit the above link.
You'll need at least to java files:
AutoSizeTextButton.java
public class AutoSizeTextButton extends android.support.v7.widget.AppCompatButton implements AutofitHelper.OnTextSizeChangeListener{
private AutofitHelper mHelper;
public AutoSizeTextButton(Context context) {
this(context, null, 0);
}
public AutoSizeTextButton(Context context, AttributeSet attrs) {
super(context, attrs, 0);
}
public AutoSizeTextButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(attrs, defStyleAttr);
}
private void init(AttributeSet attrs, int defStyleAttr) {
mHelper = AutofitHelper.create(this, attrs, defStyleAttr)
.addOnTextSizeChangeListener(this);
}
// Getters and Setters
/**
* {#inheritDoc}
*/
#Override
public void setTextSize(int unit, float size) {
super.setTextSize(unit, size);
if (mHelper != null) {
mHelper.setTextSize(unit, size);
}
}
/**
* {#inheritDoc}
*/
#Override
public void setLines(int lines) {
super.setLines(lines);
if (mHelper != null) {
mHelper.setMaxLines(lines);
}
}
/**
* {#inheritDoc}
*/
#Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
if (mHelper != null) {
mHelper.setMaxLines(maxLines);
}
}
/**
* Returns the {#link AutofitHelper} for this View.
*/
public AutofitHelper getAutofitHelper() {
return mHelper;
}
/**
* Returns whether or not the text will be automatically re-sized to fit its constraints.
*/
public boolean isSizeToFit() {
return mHelper.isEnabled();
}
/**
* Sets the property of this field (sizeToFit), to automatically resize the text to fit its
* constraints.
*/
public void setSizeToFit() {
setSizeToFit(true);
}
/**
* If true, the text will automatically be re-sized to fit its constraints; if false, it will
* act like a normal View.
*
* #param sizeToFit
*/
public void setSizeToFit(boolean sizeToFit) {
mHelper.setEnabled(sizeToFit);
}
/**
* Returns the maximum size (in pixels) of the text in this View.
*/
public float getMaxTextSize() {
return mHelper.getMaxTextSize();
}
/**
* Set the maximum text size to the given value, interpreted as "scaled pixel" units. This size
* is adjusted based on the current density and user font size preference.
*
* #param size The scaled pixel size.
*
* #attr ref android.R.styleable#TextView_textSize
*/
public void setMaxTextSize(float size) {
mHelper.setMaxTextSize(size);
}
/**
* Set the maximum text size to a given unit and value. See TypedValue for the possible
* dimension units.
*
* #param unit The desired dimension unit.
* #param size The desired size in the given units.
*
* #attr ref android.R.styleable#TextView_textSize
*/
public void setMaxTextSize(int unit, float size) {
mHelper.setMaxTextSize(unit, size);
}
/**
* Returns the minimum size (in pixels) of the text in this View.
*/
public float getMinTextSize() {
return mHelper.getMinTextSize();
}
/**
* Set the minimum text size to the given value, interpreted as "scaled pixel" units. This size
* is adjusted based on the current density and user font size preference.
*
* #param minSize The scaled pixel size.
*
* #attr ref R.styleable#AutofitButton_minTextSize
*/
public void setMinTextSize(int minSize) {
mHelper.setMinTextSize(TypedValue.COMPLEX_UNIT_SP, minSize);
}
/**
* Set the minimum text size to a given unit and value. See TypedValue for the possible
* dimension units.
*
* #param unit The desired dimension unit.
* #param minSize The desired size in the given units.
*
* #attr ref R.styleable#AutofitButton_minTextSize
*/
public void setMinTextSize(int unit, float minSize) {
mHelper.setMinTextSize(unit, minSize);
}
/**
* Returns the amount of precision used to calculate the correct text size to fit within its
* bounds.
*/
public float getPrecision() {
return mHelper.getPrecision();
}
/**
* Set the amount of precision used to calculate the correct text size to fit within its
* bounds. Lower precision is more precise and takes more time.
*
* #param precision The amount of precision.
*/
public void setPrecision(float precision) {
mHelper.setPrecision(precision);
}
#Override
public void onTextSizeChange(float textSize, float oldTextSize) {
// do nothing
}
}
AutofitHelper.java
/**
* A helper class to enable automatically resizing a {#link android.widget.Button}`s {#code textSize} to fit
* within its bounds.
*
* #attr ref R.styleable.AutofitButton_sizeToFit
* #attr ref R.styleable.AutofitButton_minTextSize
* #attr ref R.styleable.AutofitButton_precision
*/
public class AutofitHelper {
private static final String TAG = "AutoFitTextHelper";
private static final boolean SPEW = false;
// Minimum size of the text in pixels
private static final int DEFAULT_MIN_TEXT_SIZE = 8; //sp
// How precise we want to be when reaching the target textWidth size
private static final float DEFAULT_PRECISION = 0.5f;
/**
* Creates a new instance of {#code AutofitHelper} that wraps a {#link android.widget.Button} and enables
* automatically sizing the text to fit.
*/
public static AutofitHelper create(Button view) {
return create(view, null, 0);
}
/**
* Creates a new instance of {#code AutofitHelper} that wraps a {#link android.widget.Button} and enables
* automatically sizing the text to fit.
*/
public static AutofitHelper create(Button view, AttributeSet attrs) {
return create(view, attrs, 0);
}
/**
* Creates a new instance of {#code AutofitHelper} that wraps a {#link android.widget.Button} and enables
* automatically sizing the text to fit.
*/
public static AutofitHelper create(Button view, AttributeSet attrs, int defStyle) {
AutofitHelper helper = new AutofitHelper(view);
boolean sizeToFit = true;
if (attrs != null) {
Context context = view.getContext();
int minTextSize = (int) helper.getMinTextSize();
float precision = helper.getPrecision();
TypedArray ta = context.obtainStyledAttributes(
attrs,
R.styleable.AutofitButton,
defStyle,
0);
sizeToFit = ta.getBoolean(R.styleable.AutofitButton_sizeToFit, sizeToFit);
minTextSize = ta.getDimensionPixelSize(R.styleable.AutofitButton_minTextSize,
minTextSize);
precision = ta.getFloat(R.styleable.AutofitButton_precision, precision);
ta.recycle();
helper.setMinTextSize(TypedValue.COMPLEX_UNIT_PX, minTextSize)
.setPrecision(precision);
}
helper.setEnabled(sizeToFit);
return helper;
}
/**
* Re-sizes the textSize of the TextView so that the text fits within the bounds of the View.
*/
private static void autofit(Button view, TextPaint paint, float minTextSize, float maxTextSize,
int maxLines, float precision) {
if (maxLines <= 0 || maxLines == Integer.MAX_VALUE) {
// Don't auto-size since there's no limit on lines.
return;
}
int targetWidth = view.getWidth() - view.getPaddingLeft() - view.getPaddingRight();
if (targetWidth <= 0) {
return;
}
CharSequence text = view.getText();
TransformationMethod method = view.getTransformationMethod();
if (method != null) {
text = method.getTransformation(text, view);
}
Context context = view.getContext();
Resources r = Resources.getSystem();
DisplayMetrics displayMetrics;
float size = maxTextSize;
float high = size;
float low = 0;
if (context != null) {
r = context.getResources();
}
displayMetrics = r.getDisplayMetrics();
paint.set(view.getPaint());
paint.setTextSize(size);
if ((maxLines == 1 && paint.measureText(text, 0, text.length()) > targetWidth)
|| getLineCount(text, paint, size, targetWidth, displayMetrics) > maxLines
|| getMaxWordWidth(text, paint, size, displayMetrics) > targetWidth) {
size = getAutofitTextSize(text, getMaxWord(text, paint), paint, targetWidth, maxLines, low, high, precision,
displayMetrics);
}
if (size < minTextSize) {
size = minTextSize;
}
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, size);
}
/**
* Recursive binary search to find the best size for the text.
*/
private static float getAutofitTextSize(CharSequence text, String widestWord, TextPaint paint,
float targetWidth, int maxLines, float low, float high, float precision,
DisplayMetrics displayMetrics) {
float mid = (low + high) / 2.0f;
int lineCount = 1;
StaticLayout layout = null;
paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, mid,
displayMetrics));
if (maxLines != 1) {
layout = new StaticLayout(text, paint, (int)targetWidth, Layout.Alignment.ALIGN_NORMAL,
1.0f, 0.0f, true);
lineCount = layout.getLineCount();
}
if (SPEW) Log.d(TAG, "low=" + low + " high=" + high + " mid=" + mid +
" target=" + targetWidth + " maxLines=" + maxLines + " lineCount=" + lineCount);
if (lineCount > maxLines) {
// For the case that `text` has more newline characters than `maxLines`.
if ((high - low) < precision) {
return low;
}
return getAutofitTextSize(text, widestWord, paint, targetWidth, maxLines, low, mid, precision,
displayMetrics);
}
else if (lineCount < maxLines) {
return getAutofitTextSize(text, widestWord, paint, targetWidth, maxLines, mid, high, precision,
displayMetrics);
}
else {
float maxLineWidth = 0;
if (maxLines == 1) {
maxLineWidth = paint.measureText(text, 0, text.length());
} else {
for (int i = 0; i < lineCount; i++) {
if (layout.getLineWidth(i) > maxLineWidth) {
maxLineWidth = layout.getLineWidth(i);
}
}
}
float maxWordWidth = paint.measureText(widestWord, 0, widestWord.length());
if(maxWordWidth > maxLineWidth){
maxLineWidth = maxWordWidth;
}
if ((high - low) < precision) {
return low;
} else if (maxLineWidth > targetWidth) {
return getAutofitTextSize(text, widestWord, paint, targetWidth, maxLines, low, mid, precision,
displayMetrics);
} else if (maxLineWidth < targetWidth) {
return getAutofitTextSize(text, widestWord, paint, targetWidth, maxLines, mid, high, precision,
displayMetrics);
} else {
return mid;
}
}
}
private static int getLineCount(CharSequence text, TextPaint paint, float size, float width,
DisplayMetrics displayMetrics) {
paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size,
displayMetrics));
StaticLayout layout = new StaticLayout(text, paint, (int)width,
Layout.Alignment.ALIGN_NORMAL, 1.0f, 0.0f, true);
return layout.getLineCount();
}
private static int getMaxLines(Button view) {
int maxLines = -1; // No limit (Integer.MAX_VALUE also means no limit)
TransformationMethod method = view.getTransformationMethod();
if (method != null && method instanceof SingleLineTransformationMethod) {
maxLines = 1;
}
else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
// setMaxLines() and getMaxLines() are only available on android-16+
maxLines = view.getMaxLines();
}
return maxLines;
}
private static float getMaxWordWidth(CharSequence text, TextPaint paint, float size,
DisplayMetrics displayMetrics) {
paint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_PX, size,
displayMetrics));
String maxWord = getMaxWord(text, paint);
return paint.measureText(maxWord, 0, maxWord.length());
}
private static String getMaxWord(CharSequence text, TextPaint paint) {
String textStr = text.toString();
textStr = textStr.replace("-", "- ");
String[] words = textStr.split("[ \u00AD\u200B]");
String maxWord = "";
float maxWordWidth = 0;
for (String word : words) {
float wordWidth = paint.measureText(word, 0, word.length());
if (wordWidth > maxWordWidth){
maxWordWidth = wordWidth;
maxWord = word;
}
}
return maxWord;
}
// Attributes
private Button mButton;
private TextPaint mPaint;
/**
* Original textSize of the TextView.
*/
private float mTextSize;
private int mMaxLines;
private float mMinTextSize;
private float mMaxTextSize;
private float mPrecision;
private boolean mEnabled;
private boolean mIsAutofitting;
private ArrayList<OnTextSizeChangeListener> mListeners;
private TextWatcher mTextWatcher = new AutofitTextWatcher();
private View.OnLayoutChangeListener mOnLayoutChangeListener =
new AutofitOnLayoutChangeListener();
private AutofitHelper(Button view) {
final Context context = view.getContext();
float scaledDensity = context.getResources().getDisplayMetrics().scaledDensity;
mButton = view;
mPaint = new TextPaint();
setRawTextSize(view.getTextSize());
mMaxLines = getMaxLines(view);
mMinTextSize = scaledDensity * DEFAULT_MIN_TEXT_SIZE;
mMaxTextSize = mTextSize;
mPrecision = DEFAULT_PRECISION;
}
/**
* Adds an {#link OnTextSizeChangeListener} to the list of those whose methods are called
* whenever the {#link android.widget.Button}'s {#code textSize} changes.
*/
public AutofitHelper addOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
if (mListeners == null) {
mListeners = new ArrayList<OnTextSizeChangeListener>();
}
mListeners.add(listener);
return this;
}
/**
* Removes the specified {#link OnTextSizeChangeListener} from the list of those whose methods
* are called whenever the {#link android.widget.Button}'s {#code textSize} changes.
*/
public AutofitHelper removeOnTextSizeChangeListener(OnTextSizeChangeListener listener) {
if (mListeners != null) {
mListeners.remove(listener);
}
return this;
}
/**
* Returns the amount of precision used to calculate the correct text size to fit within its
* bounds.
*/
public float getPrecision() {
return mPrecision;
}
/**
* Set the amount of precision used to calculate the correct text size to fit within its
* bounds. Lower precision is more precise and takes more time.
*
* #param precision The amount of precision.
*/
public AutofitHelper setPrecision(float precision) {
if (mPrecision != precision) {
mPrecision = precision;
autofit();
}
return this;
}
/**
* Returns the minimum size (in pixels) of the text.
*/
public float getMinTextSize() {
return mMinTextSize;
}
/**
* Set the minimum text size to the given value, interpreted as "scaled pixel" units. This size
* is adjusted based on the current density and user font size preference.
*
* #param size The scaled pixel size.
*
* #attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
*/
public AutofitHelper setMinTextSize(float size) {
return setMinTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
/**
* Set the minimum text size to a given unit and value. See TypedValue for the possible
* dimension units.
*
* #param unit The desired dimension unit.
* #param size The desired size in the given units.
*
* #attr ref me.grantland.R.styleable#AutofitTextView_minTextSize
*/
public AutofitHelper setMinTextSize(int unit, float size) {
Context context = mButton.getContext();
Resources r = Resources.getSystem();
if (context != null) {
r = context.getResources();
}
setRawMinTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
return this;
}
private void setRawMinTextSize(float size) {
if (size != mMinTextSize) {
mMinTextSize = size;
autofit();
}
}
/**
* Returns the maximum size (in pixels) of the text.
*/
public float getMaxTextSize() {
return mMaxTextSize;
}
/**
* Set the maximum text size to the given value, interpreted as "scaled pixel" units. This size
* is adjusted based on the current density and user font size preference.
*
* #param size The scaled pixel size.
*
* #attr ref android.R.styleable#TextView_textSize
*/
public AutofitHelper setMaxTextSize(float size) {
return setMaxTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
/**
* Set the maximum text size to a given unit and value. See TypedValue for the possible
* dimension units.
*
* #param unit The desired dimension unit.
* #param size The desired size in the given units.
*
* #attr ref android.R.styleable#TextView_textSize
*/
public AutofitHelper setMaxTextSize(int unit, float size) {
Context context = mButton.getContext();
Resources r = Resources.getSystem();
if (context != null) {
r = context.getResources();
}
setRawMaxTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
return this;
}
private void setRawMaxTextSize(float size) {
if (size != mMaxTextSize) {
mMaxTextSize = size;
autofit();
}
}
/**
* #see TextView#getMaxLines()
*/
public int getMaxLines() {
return mMaxLines;
}
/**
* #see TextView#setMaxLines(int)
*/
public AutofitHelper setMaxLines(int lines) {
if (mMaxLines != lines) {
mMaxLines = lines;
autofit();
}
return this;
}
/**
* Returns whether or not automatically resizing text is enabled.
*/
public boolean isEnabled() {
return mEnabled;
}
/**
* Set the enabled state of automatically resizing text.
*/
public AutofitHelper setEnabled(boolean enabled) {
if (mEnabled != enabled) {
mEnabled = enabled;
if (enabled) {
mButton.addTextChangedListener(mTextWatcher);
mButton.addOnLayoutChangeListener(mOnLayoutChangeListener);
autofit();
} else {
mButton.removeTextChangedListener(mTextWatcher);
mButton.removeOnLayoutChangeListener(mOnLayoutChangeListener);
mButton.setTextSize(TypedValue.COMPLEX_UNIT_PX, mTextSize);
}
}
return this;
}
/**
* Returns the original text size of the View.
*
* #see TextView#getTextSize()
*/
public float getTextSize() {
return mTextSize;
}
/**
* Set the original text size of the View.
*
* #see TextView#setTextSize(float)
*/
public void setTextSize(float size) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
/**
* Set the original text size of the View.
*
* #see TextView#setTextSize(int, float)
*/
public void setTextSize(int unit, float size) {
if (mIsAutofitting) {
// We don't want to update the TextView's actual textSize while we're autofitting
// since it'd get set to the autofitTextSize
return;
}
Context context = mButton.getContext();
Resources r = Resources.getSystem();
if (context != null) {
r = context.getResources();
}
setRawTextSize(TypedValue.applyDimension(unit, size, r.getDisplayMetrics()));
}
private void setRawTextSize(float size) {
if (mTextSize != size) {
mTextSize = size;
}
}
private void autofit() {
float oldTextSize = mButton.getTextSize();
float textSize;
mIsAutofitting = true;
autofit(mButton, mPaint, mMinTextSize, mMaxTextSize, mMaxLines, mPrecision);
mIsAutofitting = false;
textSize = mButton.getTextSize();
if (textSize != oldTextSize) {
sendTextSizeChange(textSize, oldTextSize);
}
}
private void sendTextSizeChange(float textSize, float oldTextSize) {
if (mListeners == null) {
return;
}
for (OnTextSizeChangeListener listener : mListeners) {
listener.onTextSizeChange(textSize, oldTextSize);
}
}
private class AutofitTextWatcher implements TextWatcher {
#Override
public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
// do nothing
}
#Override
public void onTextChanged(CharSequence charSequence, int start, int before, int count) {
autofit();
}
#Override
public void afterTextChanged(Editable editable) {
// do nothing
}
}
private class AutofitOnLayoutChangeListener implements View.OnLayoutChangeListener {
#Override
public void onLayoutChange(View view, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
autofit();
}
}
/**
* When an object of a type is attached to an {#code AutofitHelper}, its methods will be called
* when the {#code textSize} is changed.
*/
public interface OnTextSizeChangeListener {
/**
* This method is called to notify you that the size of the text has changed to
* {#code textSize} from {#code oldTextSize}.
*/
public void onTextSizeChange(float textSize, float oldTextSize);
}
}
Then add the needed attributes in
values/attrs.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
<declare-styleable name="AutofitButton">
<!-- Minimum size of the text. -->
<attr name="minTextSize" format="dimension" />
<!-- Amount of precision used to calculate the correct text size to fit within its
bounds. Lower precision is more precise and takes more time. -->
<attr name="precision" format="float" />
<!-- Defines whether to automatically resize text to fit to the view's bounds. -->
<attr name="sizeToFit" format="boolean" />
</declare-styleable>
<!-- Your other attributes -->
</resources>
And you're done! You can now use the AutoSizeTextButton class.
<your.package.name.AutoSizeTextButton
android:layout_width="..."
android:layout_height="..."
android:maxLines="2" />
And be sure to add the android:maxLines attribute with a value larger than 0, otherwise it won't do anything!
Additional Notes:
The text is shrunken until the longest word fits into the button without wrapping (or the minimum size is reached). The words have to be seperated by either a normal space, or a hyphen. This algorithm also considers a SOFT HYPHEN or a ZERO WIDTH SPACE a word seperator, however I would strongly advise to test them before using them, because the Android Text Engine used in buttons ignores these characters (at least in API 19), which could lead to weird word-wraps.
It's better you use this library named AutoScaleTextView
https://bitbucket.org/ankri/autoscaletextview
This will definitely help you to achieve your desired task.
There is no built-in way of doing this, you will need to create/use a custom view that adapts the inner text to its bounds. Don't worry, this is not the first time its been asked, see the Custom View provided at Auto Scale TextView Text to Fit within Bounds to get the working code.
if any one is looking on how to disable auto text size, it can be done by
<TextView
app:autoSizeTextType="none" <!-- disabled -->
adding the above line to your text view

ActionBar setIcon with Font-Awesome

Is it possible to set an Icon in ActionBar through setIcon method (that accept drawable or Resource id) with a "Text" using AwesomeFont?
Should I create a Drawable or something like that? In Drawable what should I Create?
EDIT
public class AwesomeTextView extends TextView {
private static Typeface fontAwesome;
public AwesomeTextView(Context context, AttributeSet attrs) {
super(context, attrs);
if (fontAwesome == null) {
fontAwesome = Typeface.createFromAsset(context.getAssets(), "fonts/fontawesome-webfont.ttf");
}
setTypeface(fontAwesome);
}
}
So I had created an AwesomeTextView that uses font-awesome, Is there a way to make an AwesomeTextView as drawable?
I made a repo to easily add font awesome icons to your android project
https://github.com/bperin/FontAwesomeAndroid
If you're trying to add these in an action bar you'll probably need to make a custom layout via XML
Yes, you need to create a Drawable and draw the icon. You could take a look at the TextDrawable project on github. You can modify it such that Font-Awesome is used as the typeface.
Make sure that you bundle the font along with the project.
Here's a complete guide on how to set Icon with FontAwesome with setIcon() method.
http://blog.incognitech.in/font-awesome-in-android
UPDATE:
FontManager Class
public class FontAwesomeManager {
public static final String ROOT = "fonts/";
public static final String FONTAWESOME = ROOT + "fontawesome-webfont.ttf";
public static Typeface getTypeface(Context context, String font) {
return Typeface.createFromAsset(context.getAssets(), font);
}
}
TextDrawable class
package in.incognitech.reminder.util;
/**
* Copyright (c) 2012 Wireless Designs, LLC
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Rect;
import android.graphics.Typeface;
import android.graphics.drawable.Drawable;
import android.text.Layout;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.util.TypedValue;
/**
* A Drawable object that draws text.
* A TextDrawable accepts most of the same parameters that can be applied to
* {#link android.widget.TextView} for displaying and formatting text.
*
* Optionally, a {#link Path} may be supplied on which to draw the text.
*
* A TextDrawable has an intrinsic size equal to that required to draw all
* the text it has been supplied, when possible. In cases where a {#link Path}
* has been supplied, the caller must explicitly call
* {#link #setBounds(android.graphics.Rect) setBounds()} to provide the Drawable
* size based on the Path constraints.
*/
public class TextDrawable extends Drawable {
/* Platform XML constants for typeface */
private static final int SANS = 1;
private static final int SERIF = 2;
private static final int MONOSPACE = 3;
/* Resources for scaling values to the given device */
private Resources mResources;
/* Paint to hold most drawing primitives for the text */
private TextPaint mTextPaint;
/* Layout is used to measure and draw the text */
private StaticLayout mTextLayout;
/* Alignment of the text inside its bounds */
private Layout.Alignment mTextAlignment = Layout.Alignment.ALIGN_NORMAL;
/* Optional path on which to draw the text */
private Path mTextPath;
/* Stateful text color list */
private ColorStateList mTextColors;
/* Container for the bounds to be reported to widgets */
private Rect mTextBounds;
/* Text string to draw */
private CharSequence mText = "";
/* Attribute lists to pull default values from the current theme */
private static final int[] themeAttributes = {
android.R.attr.textAppearance
};
private static final int[] appearanceAttributes = {
android.R.attr.textSize,
android.R.attr.typeface,
android.R.attr.textStyle,
android.R.attr.textColor
};
public TextDrawable(Context context) {
super();
//Used to load and scale resource items
mResources = context.getResources();
//Definition of this drawables size
mTextBounds = new Rect();
//Paint to use for the text
mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.density = mResources.getDisplayMetrics().density;
mTextPaint.setDither(true);
int textSize = 15;
ColorStateList textColor = null;
int styleIndex = -1;
int typefaceIndex = -1;
//Set default parameters from the current theme
TypedArray a = context.getTheme().obtainStyledAttributes(themeAttributes);
int appearanceId = a.getResourceId(0, -1);
a.recycle();
TypedArray ap = null;
if (appearanceId != -1) {
ap = context.obtainStyledAttributes(appearanceId, appearanceAttributes);
}
if (ap != null) {
for (int i=0; i < ap.getIndexCount(); i++) {
int attr = ap.getIndex(i);
switch (attr) {
case 0: //Text Size
textSize = a.getDimensionPixelSize(attr, textSize);
break;
case 1: //Typeface
typefaceIndex = a.getInt(attr, typefaceIndex);
break;
case 2: //Text Style
styleIndex = a.getInt(attr, styleIndex);
break;
case 3: //Text Color
textColor = a.getColorStateList(attr);
break;
default:
break;
}
}
ap.recycle();
}
setTextColor(textColor != null ? textColor : ColorStateList.valueOf(0xFF000000));
setRawTextSize(textSize);
Typeface tf = null;
switch (typefaceIndex) {
case SANS:
tf = Typeface.SANS_SERIF;
break;
case SERIF:
tf = Typeface.SERIF;
break;
case MONOSPACE:
tf = Typeface.MONOSPACE;
break;
}
setTypeface(tf, styleIndex);
}
/**
* Set the text that will be displayed
* #param text Text to display
*/
public void setText(CharSequence text) {
if (text == null) text = "";
mText = text;
measureContent();
}
/**
* Return the text currently being displayed
*/
public CharSequence getText() {
return mText;
}
/**
* Return the current text size, in pixels
*/
public float getTextSize() {
return mTextPaint.getTextSize();
}
/**
* Set the text size. The value will be interpreted in "sp" units
* #param size Text size value, in sp
*/
public void setTextSize(float size) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, size);
}
/**
* Set the text size, using the supplied complex units
* #param unit Units for the text size, such as dp or sp
* #param size Text size value
*/
public void setTextSize(int unit, float size) {
float dimension = TypedValue.applyDimension(unit, size,
mResources.getDisplayMetrics());
setRawTextSize(dimension);
}
/*
* Set the text size, in raw pixels
*/
private void setRawTextSize(float size) {
if (size != mTextPaint.getTextSize()) {
mTextPaint.setTextSize(size);
measureContent();
}
}
/**
* Return the horizontal stretch factor of the text
*/
public float getTextScaleX() {
return mTextPaint.getTextScaleX();
}
/**
* Set the horizontal stretch factor of the text
* #param size Text scale factor
*/
public void setTextScaleX(float size) {
if (size != mTextPaint.getTextScaleX()) {
mTextPaint.setTextScaleX(size);
measureContent();
}
}
/**
* Return the current text alignment setting
*/
public Layout.Alignment getTextAlign() {
return mTextAlignment;
}
/**
* Set the text alignment. The alignment itself is based on the text layout direction.
* For LTR text NORMAL is left aligned and OPPOSITE is right aligned.
* For RTL text, those alignments are reversed.
* #param align Text alignment value. Should be set to one of:
*
* {#link Layout.Alignment#ALIGN_NORMAL},
* {#link Layout.Alignment#ALIGN_NORMAL},
* {#link Layout.Alignment#ALIGN_OPPOSITE}.
*/
public void setTextAlign(Layout.Alignment align) {
if (mTextAlignment != align) {
mTextAlignment = align;
measureContent();
}
}
/**
* Sets the typeface and style in which the text should be displayed.
* Note that not all Typeface families actually have bold and italic
* variants, so you may need to use
* {#link #setTypeface(Typeface, int)} to get the appearance
* that you actually want.
*/
public void setTypeface(Typeface tf) {
if (mTextPaint.getTypeface() != tf) {
mTextPaint.setTypeface(tf);
measureContent();
}
}
/**
* Sets the typeface and style in which the text should be displayed,
* and turns on the fake bold and italic bits in the Paint if the
* Typeface that you provided does not have all the bits in the
* style that you specified.
*
*/
public void setTypeface(Typeface tf, int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.setTextSkewX(0);
setTypeface(tf);
}
}
/**
* Return the current typeface and style that the Paint
* using for display.
*/
public Typeface getTypeface() {
return mTextPaint.getTypeface();
}
/**
* Set a single text color for all states
* #param color Color value such as {#link Color#WHITE} or {#link Color#argb(int, int, int, int)}
*/
public void setTextColor(int color) {
setTextColor(ColorStateList.valueOf(color));
}
/**
* Set the text color as a state list
* #param colorStateList ColorStateList of text colors, such as inflated from an R.color resource
*/
public void setTextColor(ColorStateList colorStateList) {
mTextColors = colorStateList;
updateTextColors(getState());
}
/**
* Optional Path object on which to draw the text. If this is set,
* TextDrawable cannot properly measure the bounds this drawable will need.
* You must call {#link #setBounds(int, int, int, int) setBounds()} before
* applying this TextDrawable to any View.
*
* Calling this method with <code>null</code> will remove any Path currently attached.
*/
public void setTextPath(Path path) {
if (mTextPath != path) {
mTextPath = path;
measureContent();
}
}
/**
* Internal method to take measurements of the current contents and apply
* the correct bounds when possible.
*/
private void measureContent() {
//If drawing to a path, we cannot measure intrinsic bounds
//We must resly on setBounds being called externally
if (mTextPath != null) {
//Clear any previous measurement
mTextLayout = null;
mTextBounds.setEmpty();
} else {
//Measure text bounds
double desired = Math.ceil( Layout.getDesiredWidth(mText, mTextPaint) );
mTextLayout = new StaticLayout(mText, mTextPaint, (int)desired,
mTextAlignment, 1.0f, 0.0f, false);
mTextBounds.set(0, 0, mTextLayout.getWidth(), mTextLayout.getHeight());
}
//We may need to be redrawn
invalidateSelf();
}
/**
* Internal method to apply the correct text color based on the drawable's state
*/
private boolean updateTextColors(int[] stateSet) {
int newColor = mTextColors.getColorForState(stateSet, Color.WHITE);
if (mTextPaint.getColor() != newColor) {
mTextPaint.setColor(newColor);
return true;
}
return false;
}
#Override
protected void onBoundsChange(Rect bounds) {
//Update the internal bounds in response to any external requests
mTextBounds.set(bounds);
}
#Override
public boolean isStateful() {
/*
* The drawable's ability to represent state is based on
* the text color list set
*/
return mTextColors.isStateful();
}
#Override
protected boolean onStateChange(int[] state) {
//Upon state changes, grab the correct text color
return updateTextColors(state);
}
#Override
public int getIntrinsicHeight() {
//Return the vertical bounds measured, or -1 if none
if (mTextBounds.isEmpty()) {
return -1;
} else {
return (mTextBounds.bottom - mTextBounds.top);
}
}
#Override
public int getIntrinsicWidth() {
//Return the horizontal bounds measured, or -1 if none
if (mTextBounds.isEmpty()) {
return -1;
} else {
return (mTextBounds.right - mTextBounds.left);
}
}
#Override
public void draw(Canvas canvas) {
final Rect bounds = getBounds();
final int count = canvas.save();
canvas.translate(bounds.left, bounds.top);
if (mTextPath == null) {
//Allow the layout to draw the text
mTextLayout.draw(canvas);
} else {
//Draw directly on the canvas using the supplied path
canvas.drawTextOnPath(mText.toString(), mTextPath, 0, 0, mTextPaint);
}
canvas.restoreToCount(count);
}
#Override
public void setAlpha(int alpha) {
if (mTextPaint.getAlpha() != alpha) {
mTextPaint.setAlpha(alpha);
}
}
#Override
public int getOpacity() {
return mTextPaint.getAlpha();
}
#Override
public void setColorFilter(ColorFilter cf) {
if (mTextPaint.getColorFilter() != cf) {
mTextPaint.setColorFilter(cf);
}
}
}
Use FontManager & TextDrawable class
// ...
TextDrawable faIcon = new TextDrawable(this);
faIcon.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 20);
faIcon.setTextAlign(Layout.Alignment.ALIGN_CENTER);
faIcon.setTypeface(FontAwesomeManager.getTypeface(this, FontAwesomeManager.FONTAWESOME));
faIcon.setText(getResources().getText(R.string.fa_android));
getActionBar().setIcon(faIcon);
// ...

Build a Continous side scrolling TextView in Android

I am trying to build a TextView that scrolls continuously sideways. I found some code on stackoverflow and used it as is. I want it such that beginning and end are wrapped together and not having a gap as it is now.
Here is the code I used:
public class ScrollTextView extends TextView {
// scrolling feature
private Scroller mSlr;
// milliseconds for a round of scrolling
private int mRndDuration = 10000;
// the X offset when paused
private int mXPaused = 0;
// whether it's being paused
private boolean mPaused = true;
/*
* constructor
*/
public ScrollTextView(Context context) {
this(context, null);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/*
* constructor
*/
public ScrollTextView(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/*
* constructor
*/
public ScrollTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
// customize the TextView
setSingleLine();
setEllipsize(null);
setVisibility(INVISIBLE);
}
/**
* begin to scroll the text from the original position
*/
public void startScroll() {
// begin from the very right side
mXPaused = -1 * getWidth();
// assume it's paused
mPaused = true;
resumeScroll();
}
/**
* resume the scroll from the pausing point
*/
public void resumeScroll() {
if (!mPaused)
return;
// Do not know why it would not scroll sometimes
// if setHorizontallyScrolling is called in constructor.
setHorizontallyScrolling(true);
// use LinearInterpolator for steady scrolling
mSlr = new Scroller(this.getContext(), new LinearInterpolator());
setScroller(mSlr);
int scrollingLen = calculateScrollingLen();
// int scrollingLen = 1;
int distance = scrollingLen - (getWidth() + mXPaused);
int duration = (new Double(mRndDuration * distance * 1.00000
/ scrollingLen)).intValue();
setVisibility(VISIBLE);
mSlr.startScroll(mXPaused, 0, distance, 0, duration);
mPaused = false;
}
/**
* calculate the scrolling length of the text in pixel
*
* #return the scrolling length in pixels
*/
private int calculateScrollingLen() {
TextPaint tp = getPaint();
Rect rect = new Rect();
String strTxt = getText().toString();
tp.getTextBounds(strTxt, 0, strTxt.length(), rect);
// int scrollingLen = rect.width() + getWidth();
int scrollingLen = rect.width();
rect = null;
return scrollingLen;
}
/**
* pause scrolling the text
*/
public void pauseScroll() {
if (null == mSlr)
return;
if (mPaused)
return;
mPaused = true;
// abortAnimation sets the current X to be the final X,
// and sets isFinished to be true
// so current position shall be saved
mXPaused = mSlr.getCurrX();
mSlr.abortAnimation();
}
#Override
/*
* override the computeScroll to restart scrolling when finished so as that
* the text is scrolled forever
*/
public void computeScroll() {
super.computeScroll();
if (null == mSlr)
return;
if (mSlr.isFinished() && (!mPaused)) {
this.startScroll();
}
}
public int getRndDuration() {
return mRndDuration;
}
public void setRndDuration(int duration) {
this.mRndDuration = duration;
}
public boolean isPaused() {
return mPaused;
}
}
Thanks in Advance...

Strange problem with modified Android Snake example

this is quite an elaborate problem for me that i've been trying to figure out for a while now. Even explaining it is a little difficult for me but i'll give it a try.
I am using a gutted version of the Snake Android sample. Pretty much I am using the TileView class verbatim and am only trying to display a few tiles on the screen. Instead of the SnakeView class I am using a GameView class in which I only included the code I thought necessary to display a tile on the screen. Here is the class
public class GameView extends TileView {
/**
* Labels for the drawables that will be loaded into the TileView class
*/
private static final int RED_STAR = 1;
private static final int YELLOW_STAR = 2;
private static final int GREEN_STAR = 3;
/**
* mMoveDelay: number of milliseconds between animations.
*/
private long mMoveDelay = 600;
/**
* mLastMove: tracks the absolute time when the last animation happened.
*/
private long mLastMove;
/**
* Create a simple handler that we can use to cause animation to happen. We
* set ourselves as a target and we can use the sleep()
* function to cause an update/invalidate to occur at a later date.
*/
private RefreshHandler mRedrawHandler = new RefreshHandler();
class RefreshHandler extends Handler {
#Override
public void handleMessage(Message msg) {
GameView.this.update();
GameView.this.invalidate();
}
public void sleep(long delayMillis) {
this.removeMessages(0);
sendMessageDelayed(obtainMessage(0), delayMillis);
}
};
/**
* Constructs a GameView based on inflation from XML
*
* #param context
* #param attrs
*/
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
initGameView();
}
public GameView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initGameView();
}
private void initGameView() {
setFocusable(true);
Resources r = this.getContext().getResources();
resetTiles(4);
loadTile(RED_STAR, r.getDrawable(R.drawable.redstar));
loadTile(YELLOW_STAR, r.getDrawable(R.drawable.yellowstar));
loadTile(GREEN_STAR, r.getDrawable(R.drawable.greenstar));
}
public void initNewGame() {
// set the move delay. This tells the update method how often it should
// refresh the screen.
mMoveDelay = 600;
update();
}
public void update() {
long now = System.currentTimeMillis();
if (now - mLastMove > mMoveDelay) {
// clear any tiles on the screen
clearTiles();
updateWalls();
// draws the tiles storred in mCellularArray
//updateCellularArray();
mLastMove = now;
}
mRedrawHandler.sleep(mMoveDelay);
}
private void updateWalls() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, x, 0);
setTile(GREEN_STAR, x, mYTileCount - 1);
}
for (int y = 1; y < mYTileCount - 1; y++) {
setTile(GREEN_STAR, 0, y);
setTile(GREEN_STAR, mXTileCount - 1, y);
}
}
private void updateCellularArray() {
setTile(YELLOW_STAR, 6, 7);
}
}
Now what is happening is that when the updateWalls() method is called, the tiles are placed within the View and comes up like this:
Now when I uncomment out the updateCellularArray() method and comment out the updateWalls() the program force closes and throws a NullPointerException. After some debugging I figured out that when the updateCellular array method is called, the mTileGrid array in the TileView class is not initialized, but it is when updateWalls() is called. I am completely baffled as to why this is happening. It doesn't matter if i replace the for loop with a simple setTile(GREEN_STAR, 3, 3); it still force closes.
Here is the TileView class I am using (again this is the same one in the Snake sample that comes with the Android SDK):
/**
* TileView: a View-variant designed for handling arrays of "icons" or other
* drawables.
*
*/
public class TileView extends View {
/**
* Parameters controlling the size of the tiles and their range within view.
* Width/Height are in pixels, and Drawables will be scaled to fit to these
* dimensions. X/Y Tile Counts are the number of tiles that will be drawn.
*/
protected static int mTileSize;
protected static int mXTileCount;
protected static int mYTileCount;
private static int mXOffset;
private static int mYOffset;
/**
* A hash that maps integer handles specified by the subclasser to the
* drawable that will be used for that reference
*/
private Bitmap[] mTileArray;
/**
* A two-dimensional array of integers in which the number represents the
* index of the tile that should be drawn at that locations
*/
private int[][] mTileGrid;
private final Paint mPaint = new Paint();
public TileView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
public TileView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.TileView);
mTileSize = a.getInt(R.styleable.TileView_tileSize, 12);
a.recycle();
}
/**
* Rests the internal array of Bitmaps used for drawing tiles, and
* sets the maximum index of tiles to be inserted
*
* #param tilecount
*/
public void resetTiles(int tilecount) {
mTileArray = new Bitmap[tilecount];
}
#Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
mXTileCount = (int) Math.floor(w / mTileSize);
mYTileCount = (int) Math.floor(h / mTileSize);
mXOffset = ((w - (mTileSize * mXTileCount)) / 2);
mYOffset = ((h - (mTileSize * mYTileCount)) / 2);
Log.d("Tomek", "TileGrid array dimensions are: " + mXTileCount + "," + mYTileCount);
mTileGrid = new int[mXTileCount][mYTileCount];
clearTiles();
}
/**
* Function to set the specified Drawable as the tile for a particular
* integer key.
*
* #param key
* #param tile
*/
public void loadTile(int key, Drawable tile) {
Bitmap bitmap = Bitmap.createBitmap(mTileSize, mTileSize, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
tile.setBounds(0, 0, mTileSize, mTileSize);
tile.draw(canvas);
mTileArray[key] = bitmap;
}
/**
* Resets all tiles to 0 (empty)
*
*/
public void clearTiles() {
for (int x = 0; x < mXTileCount; x++) {
for (int y = 0; y < mYTileCount; y++) {
setTile(0, x, y);
}
}
}
/**
* Used to indicate that a particular tile (set with loadTile and referenced
* by an integer) should be drawn at the given x/y coordinates during the
* next invalidate/draw cycle.
*
* #param tileindex
* #param x
* #param y
*/
public void setTile(int tileindex, int x, int y) {
//Log.d("Tomek", "Attempting to set tile: " + x + "," + y);
//Log.d("Tomek", "The current value at " + x + "," + y + " is " + mTileGrid[x][y]);
//Log.d("Tomek", "It will be changed to " + tileindex);
mTileGrid[x][y] = tileindex;
}
#Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int x = 0; x < mXTileCount; x += 1) {
for (int y = 0; y < mYTileCount; y += 1) {
if (mTileGrid[x][y] > 0) {
canvas.drawBitmap(mTileArray[mTileGrid[x][y]],
mXOffset + x * mTileSize,
mYOffset + y * mTileSize,
mPaint);
}
}
}
}
}
Sorry for posting all this code. Any help would be greatly appreciated.
Thanks much,
Tomek
EDIT: simplified GameView class
EDIT 2: Alright after modifying the updateCellularArray() method to the following:
private void updateCellularArray() {
for (int x = 0; x < mXTileCount; x++) {
setTile(GREEN_STAR, 12, 12);
}
}
It finally placed the tile where I wanted it to...
I'm starting to wonder if it has something to do with the RefreshHandler. Possibly when the RedrawHandler's sleep method is called in the update method of the GameView class. I'm not quite sure how it works so I am going to try playing around with it and see what I can come up with.
I forgot to instantiate the mLastMove variable. Modifying the declaration to:
private long mLastMove = System.currentTimeMillis();
fixed the problem.
Also it seems that the method used with the RefreshHandler, although clever, is outdated? Found that out with this post:
http://www.mail-archive.com/android-beginners#googlegroups.com/msg07352.html
Tomek

Categories

Resources