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
}
}
}
Related
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"
...
/>
I recently used TextInputLayout and it's setError() method. The problem I'm getting is, when I clear the error by calling setError(null) it leaves so much of empty space at the bottom.
Normal:
With error:
After clearing error:
After looking at the source, I found that they are making the view INVISIBLE instead of GONE
.setListener(new ViewPropertyAnimatorListenerAdapter() {
#Override
public void onAnimationEnd(View view) {
view.setVisibility(INVISIBLE); // here it is
updateLabelVisibility(true);
} }).start();
I'm wondering why is it so? How to resolve this to avoid the empty space?
Check out the docs for
public void setErrorEnabled (boolean enabled)
It says
Whether the error functionality is enabled or not in this layout.
Enabling this functionality before setting an error message via
setError(CharSequence), will mean that this layout will not change
size when an error is displayed.
Well based on this, try setting setErrorEnabled(true) before setError(), and, set setErrorEnabled(false) after setError(null).
Method setErrorEnabled(false) will clear the extra space, so call it after setError(null).
Dont use setErrorEnabled(boolean), it just doesnt show up the error from the second time.
public class MyTextInputLayout extends android.support.design.widget.TextInputLayout {
public MyTextInputLayout(Context context) {
super(context);
}
public MyTextInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public MyTextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setError(#Nullable CharSequence error) {
super.setError(error);
View layout = getChildAt(1);
if (layout != null) {
if (error != null && !"".equals(error.toString().trim())) {
layout.setVisibility(VISIBLE);
} else {
layout.setVisibility(GONE);
}
}
}
}
Then just setError(errorMessage); or setError(null);
See this page. Google will release the fix in future support library version. It says,
If you want to fix it now you can extends the TextInputLayout and
override the setErrorEnabled() method, but I cant guarantee the
backward compatibility. Because its some danger to change state in
TextInputLayout.
public class TextInputLayout extends android.support.design.widget.TextInputLayout{
public TextInputLayout(Context context) {
super(context);
}
public TextInputLayout(Context context, AttributeSet attrs) {
super(context, attrs);
}
public TextInputLayout(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setErrorEnabled(boolean enabled) {
super.setErrorEnabled(enabled);
if (enabled) {
return;
}
if (getChildCount() > 1) {
View view = getChildAt(1);
if (view != null) {
view.setVisibility(View.GONE);
}
}
}
}
I create a custom view for avoiding repeated code and override setError method.
public class UserInputView extends TextInputLayout {
public UserInputView(Context context) {
this(context, null);
}
public UserInputView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public UserInputView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void setError(#Nullable CharSequence error) {
boolean isErrorEnabled = error != null;
setErrorEnabled(isErrorEnabled);
super.setError(error);
}
}
The source code of TextInputLayout show the following:
If you need to clear the error, just use
til.setErrorEnabled(false);
This will hide the error text and stretch the bottom space to its standard size.
In case you need to set the error again, just use
til.setError("Your text");
which automatically calls til.setErrorEnabled(true) as it assumes you need the error functionality.
This is extension in kotlin solving problem:
fun TextInputLayout.clearError() {
error = null
isErrorEnabled = false
}
The following code works fine
textInputLatout.getEditText().addTextChangedListener(new TextWatcher() {
#Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
if (s.length() < 1) {
textInputLayout.setErrorEnabled(true);
textInputLayout.setError("Please enter a value");
}
if (s.length() > 0) {
textInputLayout.setError(null);
textInputLayout.setErrorEnabled(false);
}
}
#Override
public void afterTextChanged(Editable s) {
}
});
By using mTextInputLayout.setErrorEnabled(false); i have solved this problem
Then you should override it like so:
#Override
public void onAnimationEnd(View view)
{
view.setVisibility(GONE); // <-- this is where you make it GONE
updateLabelVisibility(true);
}
Or try this i.e. on a button or whatever you are using:
final Button btn = (Button) findViewById(R.id.btn);
btn.setVisibility(View.GONE); //<--- makes the button gone
I was recently working with ImageButtons and I came across this new type of ImageButton 'VisibilityAwareImageButton'. It would be really helpful if someone could tell me the usage of this ImageButton and how is it different from the regular ImageButton? Thanks in advance :)
Here's the full source for VisibilityAwareImageButton.
class VisibilityAwareImageButton extends ImageButton {
private int mUserSetVisibility;
public VisibilityAwareImageButton(Context context) {
this(context, null);
}
public VisibilityAwareImageButton(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public VisibilityAwareImageButton(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mUserSetVisibility = getVisibility();
}
#Override
public void setVisibility(int visibility) {
internalSetVisibility(visibility, true);
}
final void internalSetVisibility(int visibility, boolean fromUser) {
super.setVisibility(visibility);
if (fromUser) {
mUserSetVisibility = visibility;
}
}
final int getUserSetVisibility() {
return mUserSetVisibility;
}
}
It appears to be almost exactly the same as a regular ImageButton, only it keeps track of the last visibility actually set by the user. The only usage I can find is in the FloatingActionButton source. It is used to keep track of what the user wants the visibility of the view to be while it does it's own internal changes and animations. i.e.
if (child.getUserSetVisibility() != VISIBLE) {
// The view isn't set to be visible so skip changing it's visibility
return false;
}
It's in the design support library and has package visibility, so it seems like Google intends on using it internally (and seemingly only for the FAB implementation at this time).
I've got a problem with my CustomTextView. I'm trying to get a custom value from my layout-xml file and use this in my setText() method. Unfortunately the setText() method gets called before the constructor and because of this I can't use the custom value in this method.
Here's my code (broken down to the relevant parts):
CustomTextView.class
public class CustomTextView extends TextView {
private float mHeight;
private final String TAG = "CustomTextView";
private static final Spannable.Factory spannableFactory = Spannable.Factory.getInstance();
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "in CustomTextView constructor");
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
this.mHeight = values.getDimension(R.styleable.CustomTextView_cHeight, 20);
}
#Override
public void setText(CharSequence text, BufferType type) {
Log.d(TAG, "in setText function");
Spannable s = getCustomSpannableString(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
private static Spannable getCustomSpannableString(Context context, CharSequence text) {
Spannable spannable = spannableFactory.newSpannable(text);
doSomeFancyStuff(context, spannable);
return spannable;
}
private static void doSomeFancyStuff(Context context, Spannable spannable) {
/*Here I'm trying to access the mHeight attribute.
Unfortunately it's 0 though I set it to 24 in my layout
and it's correctly set in the constructor*/
}
}
styles.xml
<declare-styleable name="CustomTextView">
<attr name="cHeight" format="dimension"/>
</declare-styleable>
layout.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ctvi="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.mypackage.views.CustomTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/my_fancy_string"
android:textSize="16sp"
ctvi:cHeight="24dp" />
</LinearLayout>
And just as a proof - here's the LogCat output:
30912-30912/com.mypackage.views D/CustomTextView﹕ in setText function
30912-30912/com.mypackage.views D/CustomTextView﹕ in CustomTextView constructor
So as you can see the setText() method is called before the constructor. That's kinda weird and I don't know what I need to change in order to use my custom attribute (cHeight) in the setText-method.
Thanks in advance for any help!
It's the TextView super() constructor that calls your setText() based on the attribute values.
If you really need to access your custom attribute when setting a text value, use a custom attribute for the text as well.
I don't think any of this solutions are good, IMHO. What if you just use a custom method, like setCustomText() instead of overriding the custom TextView.setText(). I think it could be much better in terms of scalability, and hacking / overriding the implementation of the TextView could lead you into future problems.
Cheers!
First of all, remember to always recycle the TypedArray after using it.
TextView calls #setText(CharSequence text, BufferType type) during its construction therefore define a delayed call to setText as so:
private Runnable mDelayedSetter;
private boolean mConstructorCallDone;
public CustomTextView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.d(TAG, "in CustomTextView constructor");
TypedArray values = context.obtainStyledAttributes(attrs, R.styleable.CustomTextView);
this.mHeight = values.getDimension(R.styleable.CustomTextView_cHeight, 20);
mConstructorCallDone = true;
}
Then inside your setText-override:
public void setText(final CharSequence text, final TextView.BufferType type) {
if (!mConstructorCallDone) {
// The original call needs to be made at this point otherwise an exception will be thrown in BoringLayout if text contains \n or some other characters.
super.setText(text, type);
// Postponing setting text via XML until the constructor has finished calling
mDelayedSetter = new Runnable() {
#Override
public void run() {
CustomTextView.this.setText(text, type);
}
};
post(mDelayedSetter);
} else {
removeCallbacks(mDelayedSetter);
Spannable s = getCustomSpannableString(getContext(), text);
super.setText(s, BufferType.SPANNABLE);
}
}
Unfortunately it is a limitation on Java, that requires to call super(..) in constructor before anything else. So, your only workaround is to call setText(..) again after you initialize the custom attributes.
Just remember, as setText called also before you initialize your custom attributes, they may have null value and you can get NullPointerException
Check my example of customTextView which capitalize first letter and adds double dots at the and (I use it in all my activities)
package com.example.myapp_android_box_detector;
import android.content.Context;
import android.content.res.TypedArray;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
public class CapsTextView extends AppCompatTextView {
public Boolean doubleDot;
private Boolean inCustomText = false;
public CapsTextView(Context context){
super(context);
doubleDot = false;
setText(getText());
}
public CapsTextView(Context context, AttributeSet attrs){
super(context, attrs);
initAttrs(context, attrs);
setText(getText());
}
public CapsTextView(Context context, AttributeSet attrs, int defStyle){
super(context, attrs, defStyle);
initAttrs(context, attrs);
setText(getText());
}
public void initAttrs(Context context, AttributeSet attrs){
TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.CapsTextView, 0, 0);
doubleDot = a.getBoolean(R.styleable.CapsTextView_doubleDot, false);
a.recycle();
}
#Override
public void setText(CharSequence text, BufferType type) {
if (text.length() > 0){
text = String.valueOf(text.charAt(0)).toUpperCase() + text.subSequence(1, text.length());
// Adds double dot (:) to the end of the string
if (doubleDot != null && doubleDot){
text = text + ":";
}
}
super.setText(text, type);
}
}
In my application, initially I'm setting opacity of TextView to 60.
After that, when the user presses a button, I want to decrease or increase the opacity of the TextView as per pressing in a button that increases it or a button that decreases it.
I have tried this, but every time when I get the opacity of text view its -3 or -1 which is actually not.
public void decreaseOpacity(View v){
int op=txtView.getBackground().getOpacity();// its alwz -ve value
txtView.getBackground().setAlpha(op-1);
}
try this code
public class AlphaTextView extends TextView {
public AlphaTextView(Context context) {
super(context);
}
public AlphaTextView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlphaTextView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
#Override
public boolean onSetAlpha(int alpha) {
setTextColor(getTextColors().withAlpha(alpha));
setHintTextColor(getHintTextColors().withAlpha(alpha));
setLinkTextColor(getLinkTextColors().withAlpha(alpha));
return true;
}
}
Drawable.getOpacity() dos not take into account changes made by setAlpha(). See the docs:
Note that the returned value does not take into account a custom alpha
or color filter that has been applied by the client through the
setAlpha(int) or setColorFilter(ColorFilter) methods.
You may need to store the alpha value as a variable and not use getOpacity(), on example:
private int mTextViewAlpha = 255;
public void decreaseOpacity(View v){
if ( mTextViewAlpha-- <= 0 ) mTextViewAlpha = 0;
txtView.getBackground().setAlpha(mTextViewAlpha);
txtView.getBackground().invalidateSelf();
}