I have a costum built EditText Class, I was wondering if it's possible to change the EditText's Kerning, I have an issue with this, because it will be a password field, so it seams I can't use:
builder.append("\u00A0");
Edit: Here is the code.
public class MyEditText extends EditText {
private float mLetterSpacing = 0;
private CharSequence mOriginalText = "";
public MyEditText(Context context) {
super(context);
}
public MyEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public float getLetterSpacing() {
return mLetterSpacing;
}
public void setLetterSpacing(float letterSpacing) {
mLetterSpacing = letterSpacing;
applyLetterSpacing();
}
#Override
public void setText(CharSequence text, BufferType type) {
mOriginalText = text;
applyLetterSpacing();
}
public void applyLetterSpacing() {
StringBuilder builder = new StringBuilder();
for(int i = 0; i < mOriginalText.length(); i++) {
String c = ""+ mOriginalText.charAt(i);
builder.append(c.toLowerCase());
if(i+1 < mOriginalText.length()) {
builder.append("\u00A0");
}
}
SpannableString finalText = new SpannableString(builder.toString());
if(builder.toString().length() > 1) {
for(int i = 1; i < builder.toString().length(); i+=2) {
finalText.setSpan(new ScaleXSpan((mLetterSpacing+1)/10), i, i+1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
super.setText(finalText, BufferType.SPANNABLE);
}
public static Spannable applyKerning(CharSequence src, float kerning) {
if (src == null) return null;
final int srcLength = src.length();
if (srcLength < 2) return src instanceof Spannable
? (Spannable)src
: new SpannableString(src);
final String nonBreakingSpace = "\u00A0";
final SpannableStringBuilder builder = src instanceof SpannableStringBuilder
? (SpannableStringBuilder)src
: new SpannableStringBuilder(src);
for (int i = src.length() - 1; i >= 1; i--)
{
builder.insert(i, nonBreakingSpace);
builder.setSpan(new ScaleXSpan(kerning), i, i + 1,
Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
}
return builder;
}
}
I tried this, but ofc with no success. I was wondering if it's possible to add the spacing without messing with the "password String".
I think the best (only?) way to do this would be to use a font typeface that has the kerning you want and set that on the EditText. It doesn't look like there is a way to programmatically alter the kerning.
Related
i'm using expandable textview to display some part of the text and when user clicks on this textview then user can see whole String of that text for that i'm using this example but the problem is the trim length of the expandable textview its set to fixed, but i want to set the trim length dynamic based on screen size with only one line, when i use trim_length = 200 the text displayed is of 3 lines, here is my code...
ExpandableTextView.java
public class ExpandableTextView extends TextView {
private static final int DEFAULT_TRIM_LENGTH = 200;
private static final String ELLIPSIS = ".....";
private CharSequence originalText;
private CharSequence trimmedText;
private BufferType bufferType;
private boolean trim = true;
private int trimLength;
public ExpandableTextView(Context context) {
this(context, null);
}
public ExpandableTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView);
this.trimLength = typedArray.getInt(R.styleable.ExpandableTextView_trimLength, DEFAULT_TRIM_LENGTH);
typedArray.recycle();
setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
trim = !trim;
setText();
requestFocusFromTouch();
}
});
}
private void setText() {
super.setText(getDisplayableText(), bufferType);
}
private CharSequence getDisplayableText() {
return trim ? trimmedText : originalText;
}
#Override
public void setText(CharSequence text, BufferType type) {
originalText = text;
trimmedText = getTrimmedText(text);
bufferType = type;
setText();
}
private CharSequence getTrimmedText(CharSequence text) {
if (originalText != null && originalText.length() > trimLength) {
return new SpannableStringBuilder(originalText, 0, trimLength + 1).append(ELLIPSIS);
} else {
return originalText;
}
}
public CharSequence getOriginalText() {
return originalText;
}
public void setTrimLength(int trimLength) {
this.trimLength = trimLength;
trimmedText = getTrimmedText(originalText);
setText();
}
public int getTrimLength() {
return trimLength;
}
}
ExpandableTextView expandableTextView = (ExpandableTextView) findViewById(R.id.lorem_ipsum);
expandableTextView.setText(yourText);
you can do it like this
public class ExpandableTextView extends TextView
{
// copy off TextView.LINES
private static final int MAXMODE_LINES = 1;
// private OnExpandListener onExpandListener;
private final int maxLines;
private boolean expanded;
public ExpandableTextView(final Context context)
{
this(context, null);
}
public ExpandableTextView(final Context context, final AttributeSet attrs)
{
this(context, attrs, 0);
}
public ExpandableTextView(final Context context, final AttributeSet attrs, final int defStyle)
{
super(context, attrs, defStyle);
// read attributes
final TypedArray attributes = context.obtainStyledAttributes(attrs, R.styleable.ExpandableTextView, defStyle, 0);
// this.animationDuration = attributes.getInt(R.styleable.ExpandableTextView_trimLength, 200);
attributes.recycle();
// keep the original value of maxLines
this.maxLines = this.getMaxLines();
}
#Override
public int getMaxLines()
{
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN)
{
return super.getMaxLines();
}
try
{
final Field mMaxMode = TextView.class.getField("mMaxMode");
mMaxMode.setAccessible(true);
final Field mMaximum = TextView.class.getField("mMaximum");
mMaximum.setAccessible(true);
final int mMaxModeValue = (int) mMaxMode.get(this);
final int mMaximumValue = (int) mMaximum.get(this);
return mMaxModeValue == MAXMODE_LINES ? mMaximumValue : -1;
}
catch (final Exception e)
{
return -1;
}
}
public boolean toggle()
{
return this.expanded
? this.collapse()
: this.expand();
}
public boolean expand()
{
if (!this.expanded && this.maxLines >= 0)
{
// set maxLines to MAX Integer, so we can calculate the expanded height
this.setMaxLines(Integer.MAX_VALUE);
// keep track of current status
ExpandableTextView.this.expanded = true;
return true;
}
return false;
}
public boolean collapse()
{
if (this.expanded && this.maxLines >= 0)
{
ExpandableTextView.this.setMaxLines(ExpandableTextView.this.maxLines);
// keep track of current status
ExpandableTextView.this.expanded = false;
return true;
}
return false;
}
}
activity.xml
<com.yourpackagename.ExpandableTextView
android:id="#+id/expandableTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:ellipsize="end"
android:maxLines="1"
/>
activity.java
ExpandableTextView expandableTextView = (ExpandableTextView) this.findViewById(R.id.expandableTextView);
expandableTextView.setOnClickListener(new View.OnClickListener()
{
#SuppressWarnings("ConstantConditions")
#Override
public void onClick(final View v)
{
expandableTextView.toggle();
}
});
Currently I'm developing a pattern lock application and in that application I extend pattern View from Viewgroup, it means it can display pattern lock View and I am using drawable png image to display pattern View.
So, actually I want to change that pattern lock png image dynamically when user selects that image in another GridView and display it on the pattern View.
Basically, I want to change only pattern lock theme.
Main screenshote of lockscreen
Here this is my pattern lock view xml
<com.v1.sensoft.halloweenlock.utils.Lock9View
android:id="#+id/lock_9_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:nodeSrc="#mipmap/ppo"
app:nodeOnSrc="#mipmap/ppn"
android:layout_gravity="center"
app:lineColor="#FFCC00"
app:lineWidth="8dp" />
In that xml app:nodeSrc="#mipmap/ppo" app:nodeOnSrc="#mipmap/ppn"is the image to display on pattern lock.
This is my pattern lock view
public class Lock9View extends ViewGroup {
private Paint paint;
private Bitmap bitmap;
private Canvas canvas;
private List<Pair<NodeView, NodeView>> lineList;
private NodeView currentNode;
private StringBuilder pwdSb;
private CallBack callBack;
private Drawable nodeSrc;
private Drawable nodeOnSrc;
public Lock9View(Context context) {
this(context, null);
}
public Lock9View(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public Lock9View(Context context, AttributeSet attrs, int defStyleAttr) {
this(context, attrs, defStyleAttr, 0);
}
public Lock9View(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr); // TODO api 21
initFromAttributes(attrs, defStyleAttr);
}
private void initFromAttributes(AttributeSet attrs, int defStyleAttr) {
final TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.Lock9View, defStyleAttr, 0);
nodeSrc = a.getDrawable(R.styleable.Lock9View_nodeSrc);
nodeOnSrc = a.getDrawable(R.styleable.Lock9View_nodeOnSrc);
int lineColor = Color.argb(0, 0, 0, 0);
lineColor = a.getColor(R.styleable.Lock9View_lineColor, lineColor);
float lineWidth = 20.0f;
lineWidth = a.getDimension(R.styleable.Lock9View_lineWidth, lineWidth);
a.recycle();
paint = new Paint(Paint.DITHER_FLAG);
paint.setStyle(Style.STROKE);
paint.setStrokeWidth(lineWidth);
paint.setColor(lineColor);
paint.setAntiAlias(true);
DisplayMetrics dm = getResources().getDisplayMetrics();
bitmap = Bitmap.createBitmap(dm.widthPixels, dm.widthPixels, Bitmap.Config.ARGB_8888);
canvas = new Canvas();
canvas.setBitmap(bitmap);
for (int n = 0; n < 9; n++) {
NodeView node = new NodeView(getContext(), n + 1);
addView(node);
}
lineList = new ArrayList<Pair<NodeView,NodeView>>();
pwdSb = new StringBuilder();
setWillNotDraw(false);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(widthMeasureSpec, widthMeasureSpec);
}
#Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
if (!changed) {
return;
}
int width = right - left;
int nodeWidth = width / 3;
int nodePadding = nodeWidth / 6;
for (int n = 0; n < 9; n++) {
NodeView node = (NodeView) getChildAt(n);
int row = n / 3;
int col = n % 3;
int l = col * nodeWidth + nodePadding;
int t = row * nodeWidth + nodePadding;
int r = col * nodeWidth + nodeWidth - nodePadding;
int b = row * nodeWidth + nodeWidth - nodePadding;
node.layout(l, t, r, b);
}
}
#Override
protected void onDraw(Canvas canvas) {
canvas.drawBitmap(bitmap, 0, 0, null);
}
#Override
public boolean onTouchEvent(MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_MOVE:
NodeView nodeAt = getNodeAt(event.getX(), event.getY());
if (nodeAt == null && currentNode == null) {
return true;
} else {
clearScreenAndDrawList();
if (currentNode == null) {
currentNode = nodeAt;
currentNode.setHighLighted(true);
pwdSb.append(currentNode.getNum());
}
else if (nodeAt == null || nodeAt.isHighLighted()) {
canvas.drawLine(currentNode.getCenterX(), currentNode.getCenterY(), event.getX(), event.getY(), paint);
} else {
canvas.drawLine(currentNode.getCenterX(), currentNode.getCenterY(), nodeAt.getCenterX(), nodeAt.getCenterY(), paint);
nodeAt.setHighLighted(true);
Pair<NodeView, NodeView> pair = new Pair<NodeView, NodeView>(currentNode, nodeAt);
lineList.add(pair);
currentNode = nodeAt;
pwdSb.append(currentNode.getNum());
}
invalidate();
}
return true;
case MotionEvent.ACTION_UP:
if (pwdSb.length() <= 0) {
return super.onTouchEvent(event);
}
if (callBack != null) {
callBack.onFinish(pwdSb.toString());
pwdSb.setLength(0);
}
currentNode = null;
lineList.clear();
clearScreenAndDrawList();
for (int n = 0; n < getChildCount(); n++) {
NodeView node = (NodeView) getChildAt(n);
node.setHighLighted(false);
}
invalidate();
return true;
}
return super.onTouchEvent(event);
}
private void clearScreenAndDrawList() {
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
for (Pair<NodeView, NodeView> pair : lineList) {
canvas.drawLine(pair.first.getCenterX(), pair.first.getCenterY(), pair.second.getCenterX(), pair.second.getCenterY(), paint);
}
}
private NodeView getNodeAt(float x, float y) {
for (int n = 0; n < getChildCount(); n++) {
NodeView node = (NodeView) getChildAt(n);
if (!(x >= node.getLeft() && x < node.getRight())) {
continue;
}
if (!(y >= node.getTop() && y < node.getBottom())) {
continue;
}
return node;
}
return null;
}
public void setCallBack(CallBack callBack) {
this.callBack = callBack;
}
public class NodeView extends View {
private int num;
private boolean highLighted;
private NodeView(Context context) {
super(context);
}
public NodeView(Context context, int num) {
this(context);
this.num = num;
highLighted = false;
if (nodeSrc == null) {
setBackgroundResource(0);
} else {
setBackgroundDrawable(nodeSrc);
}
}
public boolean isHighLighted() {
return highLighted;
}
public void setHighLighted(boolean highLighted) {
this.highLighted = highLighted;
if (highLighted) {
if (nodeOnSrc == null) {
setBackgroundResource(0);
} else {
setBackgroundDrawable(nodeOnSrc);
}
} else {
if (nodeSrc == null) {
setBackgroundResource(0);
} else {
setBackgroundDrawable(nodeSrc);
}
}
}
public int getCenterX() {
return (getLeft() + getRight()) / 2;
}
public int getCenterY() {
return (getTop() + getBottom()) / 2;
}
public int getNum() {
return num;
}
public void setNum(int num) {
this.num = num;
}
}
public interface CallBack {
public void onFinish(String password);
}
Activity
prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE);
lock9View = (Lock9View) findViewById(R.id.lock_9_view);
lock9View.setCallBack(new Lock9View.CallBack() {
public int counter = 0;
public int buttCounter = 0;
#Override
public void onFinish(String password) {
PATTERN_KEY = prefs.getString("Pattern", "invalid");
Log.i("Counter","..."+counter);
if (PATTERN_KEY.equals("invalid")) {
Toast.makeText(MainActivity.this, "Options --> Create new Pattern", Toast.LENGTH_LONG).show();
} else {
if (password.equals(PATTERN_KEY)) {
Intent startMain = new Intent(Intent.ACTION_MAIN);
startMain.addCategory(Intent.CATEGORY_HOME);
startMain.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
startActivity(startMain);
} else {
invalidPattern();
Toast.makeText(MainActivity.this, "Please try again..." +failedCount, Toast.LENGTH_LONG).show();
}
}
}
});
}
So,if anyone know please give some idea of that.
Hi, guys! I'm working on app which uses a external sqlite database . I'm using a sample source code and I would like to some excerpts from the entries present bold or italic style or a different color.
For example, if I have an entry like this: Black is my favorite color. I would like my string recognize the HTML tags. The word "black" in my example should be in bold.
I'm a newbie and I need a help in details. This is my code:
WordView.java
public class WordView extends TextView implements OnTouchListener {
private PopupView popup;
private String infoWord;
private ScrollView parent;
private boolean mClickEnabled;
private Typeface transTypeFace, transTypeFaceBold;
private static final Map<String, String[]> FONTS = new HashMap<String, String[]>();
static {
FONTS.put("sans", new String[] { "DejaVuSansCondensed.ttf", "DejaVuSansCondensed-Bold.ttf"});
FONTS.put("serif", new String[] { "DejaVuSerifCondensed.ttf", "DejaVuSerifCondensed-Bold.ttf"});
}
private class Type1Span extends ClickableSpan {
String word;
public Type1Span(String word) {
this.word = word;
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setUnderlineText(touchedInstance == this);
ds.setTypeface(transTypeFace);
}
#Override
public void onClick(View widget) {
if (mClickEnabled) {
Uri uri = Uri.parse("ecidiomas://" + word);
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
intent.addCategory(Intent.CATEGORY_DEFAULT);
widget.getContext().startActivity(intent);
}
}
};
private class Type2Span extends Type1Span {
public Type2Span(String word) {
super(word);
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setTypeface(transTypeFaceBold);
}
}
private class TranscriptionTypeSpan extends ForegroundColorSpan {
public TranscriptionTypeSpan(int color) {
super(color);
}
#Override
public void updateDrawState(TextPaint ds) {
super.updateDrawState(ds);
ds.setTypeface(transTypeFaceBold);
//ds.setTypeface(Typeface.DEFAULT_BOLD);
}
}
private Type1Span touchedInstance;
public WordView(Context context) {
super(context);
init();
}
public WordView(Context context, AttributeSet attrs) {
super(context, attrs);
init();
}
public WordView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init();
}
private void init() {
Context ctx = getContext();
SharedPreferences sp = PreferenceManager.getDefaultSharedPreferences(ctx);
String defaultType = "sans";
String fontType = sp.getString(App.PreferenceKeys.preference_font_idx, defaultType);
if (!FONTS.containsKey(fontType)) fontType = defaultType;
transTypeFace = Typeface.createFromAsset(ctx.getAssets(), FONTS.get(fontType)[0]);
transTypeFaceBold = Typeface.createFromAsset(ctx.getAssets(), FONTS.get(fontType)[1]);
setTypeface(transTypeFaceBold);
setMovementMethod(LinkMovementMethod.getInstance());
setOnTouchListener(this);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
ViewParent p = getParent();
if (p instanceof ScrollView)
parent = (ScrollView) p;
}
#Override
public boolean onTouchEvent(MotionEvent event) {
return event.getAction() == MotionEvent.ACTION_DOWN ?
true : super.onTouchEvent(event);
}
public void setWordInfo(String word, String translation, String text) {
infoWord = word;
translation = translation.trim();
translation = TextUtils.isEmpty(translation) ? "" : (translation + '\n');
setText(infoWord.trim().toUpperCase() + "\n" + translation + "\n"+ text);
}
#Override
public void setText(CharSequence text, BufferType type) {
Integer currentType = LinksFinder.getType(infoWord != null ? infoWord : ""); //XXX
ArrayList<LinksFinder.LinkSpec> links = LinksFinder.getLinks(text.toString());
if (links == null) {
super.setText(text, type);
return;
}
SpannableString ss = new SpannableString(text);
int links_length = links.size();
for (int i = 0; i < links_length; ++i) {
LinksFinder.LinkSpec l = links.get(i);
CharacterStyle span;
if (LinksFinder.TRANSCRIPTION == l.type) {
span = new TranscriptionTypeSpan(0xffc91111);
} else if (currentType == l.type) {
span = new Type2Span(l.url);
} else {
span = new Type1Span(l.url);
}
ss.setSpan(span, l.start, l.end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
super.setText(ss, type);
}
public boolean onTouch(View v, MotionEvent event) {
if (event.getAction() == MotionEvent.ACTION_DOWN
|| event.getAction() == MotionEvent.ACTION_MOVE) {
int x = (int) event.getX();
int y = (int) event.getY();
x -= getTotalPaddingLeft();
y -= getTotalPaddingTop();
int xx = getRealPosX(x);
int yy = getRealPosY(y);
x += getScrollX();
y += getScrollY();
Layout layout = getLayout();
int line = layout.getLineForVertical(y);
int off = layout.getOffsetForHorizontal(line, x);
Type1Span[] candidates = ((Spannable) getText()).getSpans(off, off, Type1Span.class);
if (candidates.length > 0) {
touchedInstance = candidates[0];
} else {
touchedInstance = null;
}
if (mClickEnabled && popup != null && touchedInstance != null)
popup.setPopupText(xx, yy, touchedInstance.word);
} else {
if (popup != null)
popup.clear();
if (touchedInstance != null) {
touchedInstance = null;
}
}
return false;
}
public void setPopup(PopupView popup) {
this.popup = popup;
this.popup.setTypeFace(transTypeFaceBold);
}
private int getRealPosX(int val) {
return parent == null ? val : val - parent.getScrollX();
}
private int getRealPosY(int val) {
return parent == null ? val : val - parent.getScrollY();
}
public void setClickEnabled(boolean enabled) {
mClickEnabled = enabled;
}
}
WordAdapter.java
class WordAdapter extends CursorAdapter implements Filterable, DB {
private String[] QUERY_PROJECTION = new String[] { COLUMN_ID, COLUMN_WORD };
private final int WORD_COLUMN_INDEX;
private SQLiteDatabase db;
public WordAdapter(Context context, Cursor c, SQLiteDatabase db) {
super(context, c);
WORD_COLUMN_INDEX = c.getColumnIndexOrThrow(COLUMN_WORD);
this.db = db;
}
#Override
public View newView(Context context, Cursor cursor, ViewGroup parent) {
final LayoutInflater inflater = LayoutInflater.from(context);
final TextView view = (TextView) inflater.inflate(
R.layout.simple_dropdown_item_1line, parent, false);
view.setText(cursor.getString(WORD_COLUMN_INDEX));
return view;
}
#Override
public void bindView(View view, Context context, Cursor cursor) {
((TextView) view).setText(cursor.getString(WORD_COLUMN_INDEX));
}
#Override
public String convertToString(Cursor cursor) {
return cursor.getString(WORD_COLUMN_INDEX);
}
#Override
public Cursor runQueryOnBackgroundThread(CharSequence s) {
Cursor c = null;
if (s != null)
c = db.query(DB.TABLE_WORDS, QUERY_PROJECTION, getLike(s.toString().toLowerCase()), null, null, null, null);
return c;
}
private String getLike(String s) {
return DB.COLUMN_WORD + ">= '" + s + "' AND " + DB.COLUMN_WORD + "< '" + s + '\u044F' +"'";
}
}
I need make design from photoshop layout. There are some fonts on the layouts.
Designer gave me this fonts. But on layout in photoshop, he use spacing between letters. How i can realize this in android textView? I found one solution:
answer on stackoverflow
answer two
but if i make myTextView extends TextView it work wrong. If i adapt for one devise, on the device with biger display, spacing between letters increase is not proportional.
EDIT
public class MyTextView extends TextView {
private float letterSpacing = 0.0f;
private CharSequence originalText = "";
private Typeface typeface;
public MyTextView(Context context) {
this(context, null);
isInEditMode();
}
public MyTextView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
isInEditMode();
}
public MyTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray attributesArray = getResources().obtainAttributes(attrs, R.styleable.MyTextView);
letterSpacing = attributesArray.getDimension(R.styleable.MyTextView_letterSpacing, 0.0f);
String fontName = attributesArray.getString(R.styleable.MyTextView_fontName);
if(!this.isInEditMode()) {
if (null == fontName) {
typeface = Fonts.getBlockBertholdRegular(context);
} else {
typeface = Fonts.get(context, fontName);
}
super.setTypeface(typeface);
}
originalText = super.getText();
applyLetterSpacing();
this.invalidate();
}
public float getLetterSpacing() {
return letterSpacing;
}
public void setLetterSpacing(float letterSpacing) {
this.letterSpacing = letterSpacing;
applyLetterSpacing();
}
#Override
public void setText(CharSequence text, BufferType type) {
originalText = text;
applyLetterSpacing();
}
#Override
public CharSequence getText() {
return originalText;
}
private void applyLetterSpacing() {
if (this == null || this.originalText == null) return;
StringBuilder builder = new StringBuilder();
for (int i = 0; i < originalText.length(); i++) {
String c = "" + originalText.charAt(i);
builder.append(c.toUpperCase());
if (i + 1 < originalText.length()) {
builder.append("\u00A0");
}
}
SpannableString finalText = new SpannableString(builder.toString());
if (builder.toString().length() > 1) {
for (int i = 1; i < builder.toString().length(); i += 2) {
finalText.setSpan(new ScaleXSpan((letterSpacing + 1) / 10), i, i + 1, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
}
}
super.setText(finalText, BufferType.SPANNABLE);
if(!this.isInEditMode()) {
super.setTypeface(typeface);
}
}
}
Try using the new TextView API method setLetterSpacing.
See here
EDIT
You can also create your own font with spaces inside the font itself and apply it to your TextView.
Since API 21 You can use
setLetterSpacing
Documentation can be found here
I have a weird problem, for some reason the android:ellipsize="end" works, but added the point in the middle of the text == centered vertically instead of being aligned to baseline:
I checked for any "center" properties, but there is none of those:
Update:
This is the XML part:
<com.citylifeapps.cups.customviews.CarmelaTextView
android:id="#+id/venue_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_toLeftOf="#+id/venue_distance"
android:layout_toRightOf="#+id/venue_name"
android:gravity="left"
android:text="#string/placeholder_venue_address"
android:textColor="#color/cups_white"
android:textSize="20sp"
android:textStyle="bold"
android:ellipsize="end"
android:singleLine="true"
android:scrollHorizontally="true"
android:layout_alignBaseline="#+id/venue_name" />
And the custom TextView class:
public class CarmelaTextView extends TextView {
public CarmelaTextView(Context context, AttributeSet attrs) {
super(context, attrs);
setCarmelaTypeface(context);
}
public CarmelaTextView(Context context) {
super(context);
setCarmelaTypeface(context);
}
private void setCarmelaTypeface(Context context) {
if (this.isInEditMode()) return;
Typeface typeface = Typeface.createFromAsset(context.getAssets(), "carmela.ttf");
this.setTypeface(typeface);
}
}
further check shows that if I use a simple TextView the problem disappears,
but there is nothing in the custom TextView that will cause such a behavior.
Does anyone know why this might happen?
Thanks.
It looks like the problem lies within my custom font I'm using for this custom TextView, from the accepted answer here:
Why does TextView in single line elipsized with "end" show boxes?
I'm guessing that I'm facing the same problem but with a different result because the 3 dots (...) U+FEFF glyph for my font is different.
But still if some one found a solution that works for this issue I would be glad if he could share it.
I used this class to resolve this issue
public class EllipsizingTextView extends TextView {
private static final String ELLIPSIS = "...";
public interface EllipsizeListener {
void ellipsizeStateChanged(boolean ellipsized);
}
private final List<EllipsizeListener> ellipsizeListeners = new ArrayList<EllipsizeListener>();
private boolean isEllipsized;
private boolean isStale;
private boolean programmaticChange;
private String fullText;
private int maxLines = -1;
private float lineSpacingMultiplier = 1.0f;
private float lineAdditionalVerticalPadding = 0.0f;
public EllipsizingTextView(Context context) {
super(context);
}
public EllipsizingTextView(Context context, AttributeSet attrs) {
super(context, attrs);
TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.maxLines });
setMaxLines(a.getInt(0, 1));
}
public EllipsizingTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs, new int[] { android.R.attr.maxLines });
setMaxLines(a.getInt(0, 1));
}
public void addEllipsizeListener(EllipsizeListener listener) {
if (listener == null) {
throw new NullPointerException();
}
ellipsizeListeners.add(listener);
}
public void removeEllipsizeListener(EllipsizeListener listener) {
ellipsizeListeners.remove(listener);
}
public boolean isEllipsized() {
return isEllipsized;
}
#Override
public void setMaxLines(int maxLines) {
super.setMaxLines(maxLines);
this.maxLines = maxLines;
isStale = true;
}
public int getMaxLines() {
return maxLines;
}
#Override
public void setLineSpacing(float add, float mult) {
this.lineAdditionalVerticalPadding = add;
this.lineSpacingMultiplier = mult;
super.setLineSpacing(add, mult);
}
#Override
protected void onTextChanged(CharSequence text, int start, int before,
int after) {
super.onTextChanged(text, start, before, after);
if (!programmaticChange) {
fullText = text.toString();
isStale = true;
}
}
#Override
protected void onDraw(Canvas canvas) {
if (isStale) {
super.setEllipsize(null);
resetText();
}
super.onDraw(canvas);
}
private void resetText() {
int maxLines = getMaxLines();
String workingText = fullText;
boolean ellipsized = false;
if (maxLines != -1) {
Layout layout = createWorkingLayout(workingText);
if (layout.getLineCount() > maxLines) {
workingText = fullText.substring(0,
layout.getLineEnd(maxLines - 1)).trim();
while (createWorkingLayout(workingText + ELLIPSIS)
.getLineCount() > maxLines) {
workingText = workingText.substring(0,
workingText.length() - 1 - 1);
}
workingText = workingText + ELLIPSIS;
ellipsized = true;
}
}
if (!workingText.equals(getText())) {
programmaticChange = true;
try {
setText(workingText);
} finally {
programmaticChange = false;
}
}
isStale = false;
if (ellipsized != isEllipsized) {
isEllipsized = ellipsized;
for (EllipsizeListener listener : ellipsizeListeners) {
listener.ellipsizeStateChanged(ellipsized);
}
}
}
private Layout createWorkingLayout(String workingText) {
return new StaticLayout(workingText, getPaint(), getWidth()
- getPaddingLeft() - getPaddingRight(), Alignment.ALIGN_NORMAL,
lineSpacingMultiplier, lineAdditionalVerticalPadding, false);
}
#Override
public void setEllipsize(TruncateAt where) {
// Ellipsize settings are not respected
}
}