I would like to see the maxLength of an EditText at run time to be able to make a text display decision.
Is that possible?
Here is a description of what I wan't to do.
I have a ListView with many rows and each row have an EditText and a TextView.
I've made a subclass of ArrayAdapter to be able to feed the String that I want to place in the EditText of each row.
I have set android:maxLength="12" in the XML file.
I want to display a number in that EditText field, but if the number I want to display has more than android:maxLength="12" I want to display an "error message" instead.
And I would prefer not to hard code that 12 in my subclass of ArrayAdapter.
There is probably a simple solution, but I haven't found it yet.
(android first time...)
Only limited parameters have their getters, so I don't think you can read it .
So write length (Say 12) in values folder and use it in xml layout and arrayAdapter .
Now its not hard-coded .
1)Create integer.xml in values *
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item type="integer" name="max_length">12</item>
</resources>
2)In layout
<TextView android:id="#+id/tv"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:maxLength="#integer/max_length"
/>
3) in ArrayAdapter :
int maxLength = getResources().getInteger(R.integer.max_length);
This should work:
editText.setFilters(new InputFilter[] { new InputFilter.LengthFilter(12) });
From api 21 you can do it like that:
for (InputFilter filter : mEditText.getFilters()) {
if (filter instanceof InputFilter.LengthFilter) {
((InputFilter.LengthFilter) filter).getMax());
}
}
I hope this helps someone.
extend the edit text and retrieve the value from the attributeset in the constructor.
public class MyEditText extends EditText {
public static final String XML_NAMESPACE_ANDROID = "http://schemas.android.com/apk/res/android";
private int mMaxLength;
public MyEditText(Context context) {
super(context, null);
}
public MyEditText(Context context, AttributeSet attrs) {
super(context, attrs);
mMaxLength = attrs.getAttributeIntValue(XML_NAMESPACE_ANDROID, "maxLength", -1);
}
Kotlin one line solution - returns max length or null if not set
view.filters.filterIsInstance<InputFilter.LengthFilter>().firstOrNull()?.max
As extension:
val TextView.maxLength: Int?
get() = filters.filterIsInstance<InputFilter.LengthFilter>().firstOrNull()?.max
You can get the Field value using the Reflection API.
Why You Shouldn't Do It
Just about everyone would advocate against it (including me) because:
It's slow
It's implementation-dependant
It's not intended to be accessed (obviously)
As of now, looking at the source code (Android API 19), the implementation depends on an
InputFilter.LengthFilter which is set in the constructor as:
if (maxlength >= 0) {
setFilters(new InputFilter[] { new InputFilter.LengthFilter(maxlength) });
} else {
setFilters(NO_FILTERS);
}
where maxLength is the Integer you're interested in finding, parsed from the xml attribute (android:maxLength="#integer/max_length").
This InputFilter.LengthFilter has only one field (private int mMax) and no accessor method.
How It Can Be Done
Declare a static method in a relevant utility class accepting a TextView and returning an int.
Iterate over each InputFilter set on the TextView and find one belonging to the InputFilter.LengthFilter implementation.
Access, get and return the mMax field's value using Reflection.
This would give you something like this:
import java.lang.reflect.Field;
// [...]
public static int getMaxLengthForTextView(TextView textView)
{
int maxLength = -1;
for (InputFilter filter : textView.getFilters()) {
if (filter instanceof InputFilter.LengthFilter) {
try {
Field maxLengthField = filter.getClass().getDeclaredField("mMax");
maxLengthField.setAccessible(true);
if (maxLengthField.isAccessible()) {
maxLength = maxLengthField.getInt(filter);
}
} catch (IllegalAccessException e) {
Log.w(filter.getClass().getName(), e);
} catch (IllegalArgumentException e) {
Log.w(filter.getClass().getName(), e);
} catch (NoSuchFieldException e) {
Log.w(filter.getClass().getName(), e);
} // if an Exception is thrown, Log it and return -1
}
}
return maxLength;
}
As mentioned earlier, this will break if the implementation that sets the maximum length of the TextView changes. You will be notified of this change when the method starts throwing. Even then, the method still returns -1, which you should be handling as unlimited length.
Kind of complicated, but I don't know of any other approach. I hope it works (not tested):
XmlResourceParser parser = getResources().getLayout(R.layout.theLayout);
String namespace = "http://schemas.android.com/apk/res/android";
int maxLength = parser.getAttributeIntValue(namespace, "maxLength", 12);
Related
I am trying to change hint text size programmatically, but I just can't find the right method. I'm using setHintTextAppearance, like it's shown in example, but it works only when input is focused or filled with some data. I tried to set EditText textSize also, but still no luck.
textInputLayout.setHintTextAppearance(Vabaco_TextInputLayout_hint_small);
EditText a = textInputLayout.getEditText();
a.setTextSize(8);
You can change hint text size when it unfocused using reflection like this;
try {
Field filed = TextInputLayout.class.getDeclaredField("mCollapsingTextHelper");
filed.setAccessible(true);
Object helper = filed.get(textInputLayout);
Field f1 = helper.getClass().getDeclaredField("mExpandedTextSize");
f1.setAccessible(true);
f1.set(helper,100);
}
catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
name of mExpandedTextSize may be different according to the dependency version for TextInputLayout. You should check TextInputLayout and CollapsingTextHelper classes for the name of variables.
Hope this helps you.
Reflection solution doesn't work on support:design:28.0.0(mExpandedTextSize-> expandedTextSize). Also, Android Q (and later) doesn't support some non-sdk solutions.
Create your custom layout:
public class CustomTextInputLayout extends TextInputLayout {
public CustomTextInputLayout(Context context) {
super(context);
}
#Override
public void addView(View child, int index, ViewGroup.LayoutParams params) {
if(child instanceof EditText) {
((EditText)child).setTextSize(16);
}
super.addView(child, index, params);
}
}
If setting the text size programmatically is not required you can try like below,I have disabled TextInputLayout hint,
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:hintEnabled="false">
<EditText
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/edittext"
android:hint="yorhint"
android:inputType="text"
android:textColorHint="#color/colorLightBlack"
android:textSize="10sp" />
</android.support.design.widget.TextInputLayout>
If required programmatically you can find edittext by id and set the text size.
I am trying to find a way to override a non-public attribute of an android style, more specifically an atttribute named itemColor of the Widget.FragmentBreadCrumbs style. This style affects the text color of the breadcumb in a PreferenceActivity when a preference fragment is being displayed on the right pane for large screens. It is used by the class FragmentBreadCrumbs.
My application uses a custom theme that extends Theme.Holo.Light and the theme breaks on API 23 so I am trying to find a workaround.
The aforementioned style sets a default value to itemColor of #null which is not overridden in the Holo theme while for example it is set to a valid value for the Material theme. As a result the title of the breadcrumb is not visible (see screenshot for API 19 and screenshot for API 23)
I guess what I am trying to do is to find a way that could change a private value of a theme similar to the way reflection can be used to modify the private field's value of a class. Alternatively the ContextThemeWrapper seems to be promising but I simple don't get how can I use it or even if it is applicable in my situtation.
What I need is that after FragmentBreadCrumbs class executes its constructor below the mTextColor attribute to not be #null (which I am guessing is 0) as is set by the Android theme configuration but to have a valid color value.
Do you think this is possible?
public FragmentBreadCrumbs(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
super(context, attrs, defStyleAttr, defStyleRes);
final TypedArray a = context.obtainStyledAttributes(attrs,
com.android.internal.R.styleable.FragmentBreadCrumbs, defStyleAttr, defStyleRes);
mGravity = a.getInt(com.android.internal.R.styleable.FragmentBreadCrumbs_gravity,
DEFAULT_GRAVITY);
mLayoutResId = a.getResourceId(
com.android.internal.R.styleable.FragmentBreadCrumbs_itemLayout,
com.android.internal.R.layout.fragment_bread_crumb_item);
/* This is the value needed to be overridden */
mTextColor = a.getColor(
com.android.internal.R.styleable.FragmentBreadCrumbs_itemColor,
0);
a.recycle();
}
Unfortunately the toolchain will report an error if you try to use android:itemColor because this does not correspond to a public attribute name, so you cannot even make a style with this attribute.
The only thing I can think of is to change the text color via reflection just after the views have been constructed(/inflated). You would want to do this as early as possible, before the first time updateCrumbs() is run inside of FragmentBreadCrumbs. Perhaps you can override onCreate() of PreferenceActivity or onCreateView() of PreferenceFragment (whichever is applicable here) and do something like this:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentBreadCrumbs fbc = (FragmentBreadCrumbs) findViewById(...);
int color = ...;
FragmentBreadCrumbsUtils.setTextColor(fbc, color);
}
public class FragmentBreadCrumbsUtils {
private static final Field FRAGMENT_BREAD_CRUMBS_TEXT_COLOR = findField();
private static Field findField() {
try {
Field f = FragmentBreadCrumbs.class.getDeclaredField("mTextColor");
f.setAccessible(true);
return f;
} catch (Throwable t) {
// don't fail for any reason, just log it
Log.e("FragmentBreadCrumbsUtils",
"Couldn't find mTextColor field in FragmentBreadCrumbs",
t);
}
return null;
}
public static void setTextColor(FragmentBreadCrumbs fbc, int color) {
if (FRAGMENT_BREAD_CRUMBS_TEXT_COLOR == null) {
return; // can't do anything, we don't have the field
}
try {
FRAGMENT_BREAD_CRUMBS_TEXT_COLOR.set(fbc, color);
} catch (Throwable t) {
// don't fail for any reason, just log it
Log.e("FragmentBreadCrumbsUtils",
"Couldn't set mTextColor field in FragmentBreadCrumbs",
t);
}
}
}
I have made a class that is responsible for monitoring an EditText widget following the Observer pattern. Its sole function is to disable or re-enable auto-correct based on a method call. I am able to successfully achieve this, but the problem is that the new InputType only applies to new text I add to the EditText - old text still retains the red underline to show that it can be auto-corrected.
Is there any way I can force the new InputType to apply to the entire EditText block, and not simply the new content I add? I tried calling editText.invalidate() and editText.refreshDrawableState() in the hope all the text would refresh, but to no avail.
final class SpellCheckerObserver implements EditTextObserver {
public static final int KEY = KeyGenerator.generateUniqueId();
private int defaultInputType;
SpellCheckerObserver(EditTextSubject subject) {
subject.attach(SpellCheckerObserver.KEY, this);
}
#Override
public void activating(EditText editText) {
defaultInputType = editText.getInputType();
editText.setRawInputType(InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
}
#Override
public void deactivating(EditText editText) {
editText.setInputType(defaultInputType);
}
}
I found out the answer whilst looking through the source code for TextView, where I came across the removeSuggestionSpans() method.I wasn't aware that the suggestions were in fact a type of span, (unsurprisingly, the SuggestionSpan)
This meant I was able to remove the red underline with the following code:
SuggestionSpan[] spans = editText.getText().getSpans(
0, editText.length(), SuggestionSpan.class
);
if (spans.length > 0) {
for (SuggestionSpan span : spans) {
editText.getText().removeSpan(span);
}
}
When the numberpicker is loaded, the default value is not appearing on the screen until touched.
Once touched, everything works fine.Any help appreciated.
Also if the formatter is removed, it works fine.
dialog.xml
<NumberPicker
android:id="#+id/number_picker"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
/>
<Button
android:id="#+id/apply_button"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#+id/number_picker"
android:text="#string/ok_string" />
Here is the activity code:
final NumberPicker np = (NumberPicker) d.findViewById(R.id.number_picker);
np.setMaxValue(50);
np.setMinValue(0);
np.setWrapSelectorWheel(true);
np.setOnValueChangedListener(this);
np.setDescendantFocusability(NumberPicker.FOCUS_BLOCK_DESCENDANTS);
np.setFormatter(new NumberPicker.Formatter() {
#Override
public String format(int i) {
if (i == 25)
return "0";
else
return String.valueOf(i - 25);
}
});
np.setValue(25);
Thanks in advance
#Renjith
Thank for the link, but I think you should link the code or even paste it here.
https://code.google.com/p/android/issues/detail?id=35482#c9
Field f = NumberPicker.class.getDeclaredField("mInputText");
f.setAccessible(true);
EditText inputText = f.get(mPicker);
inputText.setFilters(new InputFilter[0]);
The issue seems to a bug in NumberPicker widget.
And I have solved it using method 2 described here.
I found a solution for this bug in NumberPicker that works in APIs 18-26 without using reflection and without using setDisplayedValues() here.
Here's the accepted answer written in Kotlin in a single line:
// NOTE: workaround for a bug that rendered the selected value wrong until user scrolled, see also: https://stackoverflow.com/q/27343772/3451975
(NumberPicker::class.java.getDeclaredField("mInputText").apply { isAccessible = true }.get(this) as EditText).filters = emptyArray()
Note that I recommend to keep the comment line to document why this code is needed.
I had the same problem and I was using NumberPicker with Strings.
My problem was that after the activity was opened with a transition the the default value in the number picker was invisible, even though i was setting the picker values with picker.setDisplayedValues(list.toStringsArray())
So for me the solution was the following:
private void populatePicker(NumberPicker picker, String[] strings, int index) {
picker.setDisplayedValues(null);
picker.setMinValue(0);
picker.setMaxValue(strings.length - 1);
picker.setDisplayedValues(strings);
picker.setValue(index);
try {
Field field = NumberPicker.class.getDeclaredField("mInputText");
field.setAccessible(true);
EditText inputText = (EditText) field.get(picker);
inputText.setVisibility(View.INVISIBLE);
} catch (Exception e) {
e.printStackTrace();
}
}
Try
np.setWrapSelectorWheel(false);
The border displays a default color (that's orange on my Nexus S) while scrolling a ListView to the limit. How to change that color?
I really don't know how to explain it. Just look at this picture:
So, how to change the highlight color when the ListView scrolling to the border? using themes or styles
The solution is to use setOverscrollFooter(null) and setOverscrollHeader(null).
The documentation is here !
You can also set it directly in the XML :
<ListView android:overScrollMode="never" />
Or specify the footer and the header :
<ListView
android:overscrollHeader="#null"
android:overscrollFooter="#null" />
N.B. : There is also a property fadingEdge that may interest you.
"Overscroll" methodes are supported starting API level 9
Finally I found the solution.
setOverscrollFooter(null) and setOverscrollHeader(null) does not work. At least on 2.3.*. Setting attributes from *.xml doesn't help too.
setOverScrollMode(View.OVER_SCROLL_NEVER) causes glitchy scrolling. At least on 2.3.*.
The only solution that really works involves the use of Java Reflection.
It works even with ugly custom Samsung listviews with bounce overscroll effect.
Here is a snippet:
#Override
protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) {
//onOverScrolled method must be overrided, or we will see the background of the listview when overscroll fast.
}
private void removeOverscrollEffect() {
try {
Class<?> superClass = getClass().getSuperclass().getSuperclass();
Field field = superClass.getDeclaredField("mEdgeGlowTop");
field.setAccessible(true);
Object edgeGlowTop = field.get(this);
if (edgeGlowTop != null) {
Class<? extends Object> edgeClass = edgeGlowTop.getClass();
Field edgeDrawable = edgeClass.getDeclaredField("mEdge");
edgeDrawable.setAccessible(true);
edgeDrawable.set(edgeGlowTop, new ColorDrawable(Color.TRANSPARENT));
Field glowDrawable = edgeClass.getDeclaredField("mGlow");
glowDrawable.setAccessible(true);
glowDrawable.set(edgeGlowTop, new ColorDrawable(Color.TRANSPARENT));
field.set(this, edgeGlowTop);
}
Field fieldBottom = superClass.getDeclaredField("mEdgeGlowBottom");
fieldBottom.setAccessible(true);
Object edgeGlowBottom = fieldBottom.get(this);
if (edgeGlowBottom != null) {
Class<? extends Object> edgeClassBottom = edgeGlowBottom.getClass();
Field edgeDrawableBottom = edgeClassBottom.getDeclaredField("mEdge");
edgeDrawableBottom.setAccessible(true);
edgeDrawableBottom.set(edgeGlowBottom, new ColorDrawable(Color.TRANSPARENT));
Field glowDrawableBottom = edgeClassBottom.getDeclaredField("mGlow");
glowDrawableBottom.setAccessible(true);
glowDrawableBottom.set(edgeGlowBottom, new ColorDrawable(Color.TRANSPARENT));
fieldBottom.set(this, edgeGlowBottom);
}
} catch (Exception e) {
Log.e(TAG, e.getMessage());
}
}
I hope this helps.
Here is a nice article on ListView Backgrounds Optimization.
To fix this issue, all you have to do is either disable the cache color hint optimization, if you use a non-solid color background, or set the hint to the appropriate solid color value. You can do this from code (see setCacheColorHint(int)) or preferably from XML, by using the android:cacheColorHint attribute. To disable the optimization, simply use the transparent color #00000000. The following screenshot shows a list with android:cacheColorHint="#00000000"
Use it in XML file--
<ListView ---
android:fadingEdge="none"
---</ListView>
EDITED:
Using fading edges may introduce noticeable performance degradations and should be used only when required by the application's visual design. To request fading edges with API level 14 and above, use the android:requiresFadingEdge attribute instead.
Check this API link
I used kord's answer until it stopped working in Lollipop, so I changed into this:
try {
Class<?> superClass = getClass().getSuperclass().getSuperclass();
Field field = superClass.getDeclaredField("mEdgeGlowTop");
field.setAccessible(true);
field.set(this, new NoEdgeEffect(getContext()));
Field fieldBottom = superClass.getDeclaredField("mEdgeGlowBottom");
fieldBottom.setAccessible(true);
fieldBottom.set(this, new NoEdgeEffect(getContext()));
} catch (Exception e) {
}
class NoEdgeEffect extends EdgeEffect
{
public NoEdgeEffect(Context context) {
super(context);
}
public boolean draw(Canvas canvas) {
// Do nothing
return false;
}
}
you can use android:listSelector="#002234".
In above value can be any color code that you can find on internet easily.