My objective is to change the hyperlink from a green color to a blue color in android. Currently the whole textview is green.
I'm using this class to remove the underlining of the hyperlink in android - I found it on SO:
public class TextViewNoUnderline extends AppCompatTextView {
public TextViewNoUnderline(Context context) {
this(context, null);
}
public TextViewNoUnderline(Context context, AttributeSet attrs) {
this(context, attrs, android.R.attr.textViewStyle);
}
public TextViewNoUnderline(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
setSpannableFactory(Factory.getInstance());
}
private static class Factory extends Spannable.Factory {
private final static Factory sInstance = new Factory();
public static Factory getInstance() {
return sInstance;
}
#Override
public Spannable newSpannable(CharSequence source) {
return new SpannableNoUnderline(source);
}
}
private static class SpannableNoUnderline extends SpannableString {
public SpannableNoUnderline(CharSequence source) {
super(source);
}
#Override
public void setSpan(Object what, int start, int end, int flags) {
if (what instanceof URLSpan) {
what = new UrlSpanNoUnderline((URLSpan) what);
}
super.setSpan(what, start, end, flags);
}
}
}
Then in my xml I did this:
<com.myapp.customshapes.TextViewNoUnderline
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/tos"
tools:text="#string/signing_in"/>
Where signing_in is this string:
<string name="signing_in"><body link="blue">By signing in, you agree to our <a href="com.myapp://http://www.myapp.com/Terms.html">Terms of Service</a>
and <a href="com.myapp://http://www.myapp.com/Privacy.html">Privacy Policy</a></string>
I have set this in my java class:
tos.setText(Html.fromHtml(getString(R.string.signing_in)));
As you can see, in my html, I said: <body link="blue">
The whole TOS which includes non-hyperlinked words and hyperlinked words is green as in the pic. Why are the hyperlinks not turning blue?
The UrlSpan just takes the link color from the accent color, so give your TextView a style with a blue accent color.
styles.xml:
<style name="AppThemeBlueAccent" parent="AppTheme">
<item name="colorAccent">#color/blue</item>
</style>
your_layout.xml:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
style="#style/AppThemeBlueAccent"
...
/>
Related
I'm trying to create a custom implementation of TextView that can be used to encapsulate all of the desired attributes of the text to be displayed.
What I've managed to do so far is:
Apply a custom font.
Apply a TextAppearance which includes things like letterSpacing and textColor.
This works as expected but when I try to change the colour of the text in XML the new text colour is completely ignored and the colour of the text remains the default that I've setup in the TextAppearance. It appears that the TextAppearance overrides the properties of the TextView provided in XML.
Question:
How do I create a custom TextView with default properties that can be modified in XML (mainly textColor)? Consider that this custom TextView exists in a separate module from the project it'll be used in.
Code:
CustomTextView
public class MyTextView extends AppCompatTextView {
public MyTextView(Context context) {
super(context);
init(context);
}
public MyTextView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public MyTextView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context);
}
private void init(Context context) {
setTextAppearance(context, R.style.MyText);
setTypeface(FontCache.futuraBookReg(getContext()));
}
}
Style
<style name="MyText" parent="TextAppearance.AppCompat">
<item name="android:textColor">#color/default_text_colour_selector</item>
<item name="android:textSize">28sp</item>
<item name="android:letterSpacing">0.04</item>
<item name="android:lineSpacingExtra">8sp</item>
</style>
FontCache (Included for completeness. Issue persists even without custom fonts)
class FontCache {
private static final String MY_FONT = "font/my_font.ttf";
private static Map<String, Typeface> fontMap = new HashMap<>();
static Typeface myFont(Context context) {
return getFont(context, MY_FONT);
}
private static Typeface getFont(Context context, String fontName) {
if (fontMap.containsKey(fontName)) {
return fontMap.get(fontName);
} else {
Typeface tf = Typeface.createFromAsset(context.getAssets(), fontName);
fontMap.put(fontName, tf);
return tf;
}
}
}
Attempt to colour TextView in XML
<com.example.text.MyText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textColor="#color/new_text_colour"
android:text="Hello" />
I have layout like this -
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.design.widget.TextInputEditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/form_username"/>
</android.support.design.widget.TextInputLayout>
As per our UI design document, we need to have different custom font for Floating label and Edittext.
Any help is appreciated. Thank you.
you can do this by this way,
As of Design Library v23, you can use TextInputLayout#setTypeface().
This will set the typeface on both the expanded and floating hint.
Using a custom span
final SpannableString ss = new SpannableString("Error");
ss.setSpan(new FontSpan(tf), 0, ss.length(), Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
til.setError(ss);
private static final class FontSpan extends MetricAffectingSpan {
private final Typeface mNewFont;
private FontSpan(Typeface newFont) {
mNewFont = newFont;
}
#Override
public void updateDrawState(TextPaint ds) {
ds.setTypeface(mNewFont);
}
#Override
public void updateMeasureState(TextPaint paint) {
paint.setTypeface(mNewFont);
}
}
For custom fonts in EditText you can use following class:
public class CustomEditText extends AppCompatEditText {
public CustomEditText (Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
public CustomEditText (Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public CustomEditText (Context context) {
super(context);
init(null);
}
Typeface myTypeface;
private void init(AttributeSet attrs) {
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs,
R.styleable.CustomTextView);
String fontName = "Orkney Medium.otf";
if (fontName != null && !isInEditMode()) {
myTypeface = Typeface.createFromAsset(getContext().getAssets(),
fontName);
}
setTypeface(myTypeface);
a.recycle();
}
}
}
XML Code :
<com.utils.CustomEditText
android:id="#+id/edt_first_name_register"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#null"
android:hint="#string/hint_first_name"
android:inputType="textCapWords"
android:imeOptions="actionNext"
android:singleLine="true"
android:digits="abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
android:paddingTop="#dimen/15dp"
android:textColor="#color/black"
android:textSize="20sp" />
For TextInputLayout custom classes does not work in all cases, But you can do something like this for optimized solution.
Put this in your Java Class :
public static void setTextInputLayoutTypeFace(Context mContext, TextInputLayout... textInputLayout) {
for (TextInputLayout til : textInputLayout) {
Typeface typeface = Typeface.createFromAsset(mContext.getAssets(), "Orkney Regular.otf");
til.setTypeface(typeface);
}
}
Call the above method using :
setTextInputLayoutTypeFace(mContext, tlFirstNameRegister, tlLastNameRegister,
tlUsernameRegister, tlEmailIdRegister, tlDateOfBirthRegister, tlMobileRegister, tlPasswordRegister,
tlConfPasswordRegister);
Its not the best solution. But it will work.
I'm trying to make a custom EditText for currency which means I need to have a prefix of it for the currency and I have to limit users' input to numbers only.
This is my custom EditText code
public OpenSansEditText(Context context, AttributeSet attrs) {
super(context, attrs);
paint = getPaint();
applyCustomFont(context, attrs);
}
public OpenSansEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
paint = getPaint();
applyCustomFont(context, attrs);
}
private void applyCustomFont(Context context, AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.OpenSansET);
...
// Prefix
String prefix = a.getString(R.styleable.OpenSansET_prefix);
if (prefix != null) {
mPrefix = prefix;
} else {
mPrefix = "";
}
// Prefix Color
int prefixColor = a.getColor(R.styleable.OpenSansET_prefixColor, 0);
if (prefix != null) {
mPrefixColor = prefixColor;
} else {
mPrefixColor = ContextCompat.getColor(context, R.color.miBlack);
}
a.recycle();
}
...
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
if (!mPrefix.equals("")) {
getPaint().getTextBounds(mPrefix, 0, mPrefix.length(), mPrefixRect);
mPrefixRect.right += getPaint().measureText(" "); // add some offset
}
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (!mPrefix.equals("")) {
paint.setColor(mPrefixColor);
canvas.drawText(mPrefix, super.getCompoundPaddingLeft(), getBaseline(), paint);
}
}
#Override
public int getCompoundPaddingLeft() {
return mPrefix.equals("") ? super.getCompoundPaddingLeft()
: super.getCompoundPaddingLeft() + mPrefixRect.width();
}
This is how I use it in xml :
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMinRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/min"
android:textColor="#color/miBlack"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
<com.asta.www.classes.OpenSansEditText
android:id="#+id/shopping_filter_priceMaxRange"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="0.4"
android:gravity="center"
android:hint="#string/max"
android:inputType="number"
android:textColorHint="#color/miGrey"
app:prefix="$"
app:prefixColor="#color/miBlack" />
Which yields :
Only the first one without inputType as number has the currency sign shown, whereas the second ET doesn't have its currency sign shown.
How to achieve currency prefix as text and still keeping inputType to numbers only for user? And I don't want to use two views, namely EditText and TextView to left of it, both inside a ViewGroup to achieve that.
For this type of scenarios I use Compound views. Please see below code for more information.
First create a layout for your custom view like below.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal" android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/txt_prefix"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="$" />
<EditText
android:id="#+id/et_value"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:inputType="number" />
</LinearLayout>
Now create a new class which should extends the LinearLayout. See below code.
public class OpenSansEditText extends LinearLayout {
private TextView txtPrefix;
private EditText etValue;
private String prefix = "$";
private int prefixColor = Color.BLACK;
public OpenSansEditText(Context context) {
super(context);
initializeViews(context, null);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
initializeViews(context, attrs);
}
public OpenSansEditText(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initializeViews(context, attrs);
}
private void initializeViews(Context context, AttributeSet attrs) {
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
inflater.inflate(R.layout.opensansedittext_view, this,true);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.OpenSansEditText);
prefix = a.getString(R.styleable.OpenSansEditText_prefix);
prefixColor = a.getColor(R.styleable.OpenSansEditText_prefixColor, Color.BLACK);
}
}
public CharSequence getValue(){
return etValue.getText();
}
public CharSequence getPrefix(){
return txtPrefix.getText();
}
#Override
protected void onFinishInflate() {
super.onFinishInflate();
txtPrefix = (TextView) findViewById(R.id.txt_prefix);
etValue = (EditText) findViewById(R.id.et_value);
txtPrefix.setText(prefix);
txtPrefix.setTextColor(prefixColor);
}
}
And then add your attributes to attribute xml file Ex: (attrs.xml in my case)
<resources>
<declare-styleable name="OpenSansEditText">
<attr name="prefix" format="string"/>
<attr name="prefixColor" format="color"/>
</declare-styleable>
</resources>
Now you can use it anywhere in the project as below
<com.asta.www.classes.OpenSansEditText
android:layout_width="match_parent"
android:layout_height="match_parent"
app:prefix="$"
app:prefixColor="#f00"/>
Hope this will help you to solve your problem. Thanks...
In the end I found this link https://gist.github.com/kennydude/5407963 which helps me in the right direction. So what it does is I think making the prefix as Drawable using this class :
private class TagDrawable extends Drawable {
public String text = "";
public void setText(String s){
text = s;
// Tell it we need to be as big as we want to be!
setBounds(0,0,getIntrinsicWidth(),getIntrinsicHeight());
invalidateSelf();
}
#Override
public void draw(#NonNull Canvas canvas) {
// I don't know why this y works here, but it does :)
// (aka if you are from Google/are Jake Wharton and I have done it wrong, please tell me!)
canvas.drawText( text, 0, mLine0Baseline + canvas.getClipBounds().top, mTextPaint );
}
#Override public void setAlpha(int i) {}
#Override public void setColorFilter(ColorFilter colorFilter) {}
#Override public int getOpacity() {return PixelFormat.UNKNOWN;}
#Override public int getIntrinsicHeight (){
return (int)mFontHeight;
}
#Override public int getIntrinsicWidth(){
return (int)mTextPaint.measureText( text );
}
}
And draw it to the left of the TextView like
TagDrawable left = new TagDrawable();
left.setText("$");
setCompoundDrawablesRelative(left, null, null, null);
The link I supplied even has suffix support which I haven't tried.
I have a custom widget which is basically an EditText which has a small 'clear' button to the right, which cleans the edittext.
I built it as a subclass of LinearLayout, which adds the EditText and the Button in the constructor.
Everything works the way it should, except that I want the whole LinearLayout to have a Focused style when the EditText has focus; I want the user to see it as an EditText, not as a container with an EditText.
I attempted using addStatesFromChildren, but it does not seem to work. Code attached.
ErasableField.java
public class ErasableField extends LinearLayout {
private EditText editText;
private View button;
public ErasableField(Context context) {
super(context);
init(null);
}
public ErasableField(Context context, AttributeSet attrs) {
super(context, attrs);
init(attrs);
}
public ErasableField(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
private void init(AttributeSet attrs) {
this.setOrientation(HORIZONTAL);
LayoutInflater inflater = LayoutInflater.from(getContext());
editText = (EditText) inflater.
inflate(R.layout.erasable_field_edittext, this, false);
button = inflater.inflate(R.layout.erasable_field_button, this, false);
if (attrs != null) {
TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.ErasableField);
boolean password = a.getBoolean(R.styleable.ErasableField_password, false);
if (password) {
editText.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_VARIATION_PASSWORD);
} else {
editText.setInputType(InputType.TYPE_CLASS_TEXT);
}
}
editText.setFocusable(true);
editText.setClickable(true);
editText.setFocusableInTouchMode(true);
this.setClickable(false);
this.setFocusable(false);
this.setFocusableInTouchMode(false);
this.setGravity(Gravity.CENTER_VERTICAL);
this.setBackgroundResource(android.R.drawable.editbox_background);
addView(editText);
addView(button);
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
editText.getText().clear();
}
});
this.addStatesFromChildren();
}
public Editable getText() {
return editText.getText();
}
public void setText(CharSequence text) {
editText.setText(text);
}
public void setText(int resId) {
editText.setText(resId);
}
public void setText(char[] text, int start, int len) {
editText.setText(text, start, len);
}
}
erasable_field_edittext.xml
<EditText xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="fill_parent"
android:background="#color/white"
android:singleLine="true"
android:paddingLeft="5dp"
android:paddingRight="5dp"
android:textCursorDrawable="#color/grey"
>
</EditText>
erasable_field_button.xml
<ImageButton xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/Button.Delete"
>
</ImageButton>
Nevermind the missing style, it's just a red button with a little cross, it looks like this:
When the user taps the edittext area, the style should change to a focused style.
I.e. (ignore the different button look ):
The style I'm trying to use a built in from Android:
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_focused="true" android:drawable="#drawable/editbox_background_focus_yellow" />
<item android:drawable="#drawable/editbox_background_normal" />
</selector>
Any help?
It's fixed by changing
this.addStatesFromChildren();
to
this.setAddStatesFromChildren(true);
...
It appears that if you have an EditText on android with the
android:inputType="textPassword" or android:password="true
fields on them, right-to-left text does NOT appear right-to-left (stays left-to-right).
However without the password denotations the text does appear RTL.
Is this a known issue or is there a workaround?
For 17+ (4.2.x+) you can use textAlignment
android:textAlignment="viewStart"
The only solution I've found was to set the gravity programatically to LEFT or RIGHT after setting the inputType.
In my case, the problem was simply solved by changing the layout_width to wrap_content.
If you put inputType = textPassword or set a passwordTransformation method on EditText, text direction is taken as LTR. It means RTL for password is discouraged. You need to write custom TextView to override this behaviour.
Code snippet from android source for TextView.
// PasswordTransformationMethod always have LTR text direction heuristics returned by
// getTextDirectionHeuristic, needs reset
mTextDir = getTextDirectionHeuristic();
protected TextDirectionHeuristic getTextDirectionHeuristic() {
if (hasPasswordTransformationMethod()) {
// passwords fields should be LTR
return TextDirectionHeuristics.LTR;
}
In My Case both worked fine.
1) android:textAlignment="viewStart"
And
2)
https://stackoverflow.com/a/38291472/6493661
and the right answer is:
RtlEditText mUserPassword = root.findViewById(R.id.register_fragment_password);
mUserPassword.setTransformationMethod(new AsteriskPasswordTransformationMethod());
creating our own EditText!
it work prefectly only if you replace the dot with astrix by AsteriskPasswordTransformationMethod below this code.
public class RtlEditText extends EditText {
public RtlEditText(Context context) {
super(context);
}
public RtlEditText(Context context, #Nullable AttributeSet attrs) {
super(context, attrs);
}
public RtlEditText(Context context, #Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public RtlEditText(Context context, #Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
}
#Override
public TextDirectionHeuristic getTextDirectionHeuristic() {
// passwords fields should be LTR
return TextDirectionHeuristics.ANYRTL_LTR;
}}
public class AsteriskPasswordTransformationMethod extends PasswordTransformationMethod {
#Override
public CharSequence getTransformation(CharSequence source, View view) {
return new PasswordCharSequence(source);
}
private class PasswordCharSequence implements CharSequence {
private CharSequence mSource;
public PasswordCharSequence(CharSequence source) {
mSource = source; // Store char sequence
}
public char charAt(int index) {
return '*'; // This is the important part
}
public int length() {
return mSource.length(); // Return default
}
public CharSequence subSequence(int start, int end) {
return mSource.subSequence(start, end); // Return default
}
}
}