AppCompatEditText.getpParent() inside TextInputLayout returns FrameLayout - android

I am creating simple AppCompatEditText adding OnFocusChangeListener and putting it in the simple TextInputLayout.
When AppCompatEditText loosing focus it's content should be validate by isValidParam method.
It worked till yesterday, when I used rev.23.0.3
But now, when I used rev.24.0.2, it gives error as below on the 1st row of isValidParam method.
java.lang.ClassCastException: android.widget.FrameLayout cannot be
cast to android.support.design.widget.TextInputLayout
I checked in debugging mode. AppCompatEditText.getpParent() really returns Framelayout instead TextInputLayout.
LinearLayout llParams = new LinearLayout(context);
llParams.setOrientation(LinearLayout.VERTICAL);
// Create label for param
final TextInputLayout tilParam = new TextInputLayout(context);
// Add label into layout
llParams.addView(tilParam);
// Create Editor for param
final AppCompatEditText etParam = new AppCompatEditText(context);
edParam.setOnFocusChangeListener(new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus)
if (isValidParam(etParam)) {
do some thing;
} else {
do other thing;
}
}
});
tilParam.addView(etParam);
// validation method
boolean isValidParam(AppCompatEditText editText) {
TextInputLayout til = (TextInputLayout) editText.getParent();
String text = editText.getText().toString().trim();
if (!text.equls("some criteria") {
till.setError("Error text")
return false;
}
return true;
}

Update:
Use the widget TextInputEditText instead of EditText inside a TextInputLayout.
old answer
TextInputLayout textInputLayout = (TextInputLayout) editText.getParent().getParent();
That seems to work as a quick fix. Far from ideal.

getParentForAccessibility() worked for me

You can check if EditText is inside TextInputLayout using following method:
public static <ParentClass> ParentClass getFirstParent(View view, Class<ParentClass> parentClass) {
if (view.getParent() instanceof View) {
if (parentClass.isInstance(view.getParent())) {
return (ParentClass) view.getParent();
} else {
return getFirstParent((View) view.getParent(), parentClass);
}
} else {
return null;
}
}
Example of use:
TextInputLayout textInputLayout = getFirstParent(editText, TextInputLayout.class)

Just extracts from Android official documents:
<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>
Note: The actual view hierarchy present under TextInputLayout is NOT
guaranteed to match the view hierarchy as written in XML. As a result,
calls to getParent() on children of the TextInputLayout -- such as an
TextInputEditText -- may not return the TextInputLayout itself, but
rather an intermediate View. If you need to access a View directly,
set an android:id and use findViewById(int).
Therefore, to resolve the issue you have to turn to findViewById instead of getParent due to an extra layout in between introduced in version 24.

You can check the code of the TextInputLayout v24.x.x.
Now it works with a FrameLayout.
#Override
public void addView(View child, int index, final ViewGroup.LayoutParams params) {
if (child instanceof EditText) {
mInputFrame.addView(child, new FrameLayout.LayoutParams(params));
//...
} else {
// Carry on adding the View...
super.addView(child, index, params);
}
}
where mInputFrame is a FrameLayout.
It is the reason of your issue (the parent is a FrameLayout).
Just pass the tilParam as parameter , instead of using getParent() if you need to use it.

TextInputLayout has a method called getEditText(). This may be an alternate way to solve your problem. Instead of starting from the EditText itself and getting the parent TextInputLayout, you can start with the TextInputLayout and simply get the EditText child view. For xml generated views, the following code is an example:
TextInputLayout someInputLayout = findViewById(R.id.some_input_layout);
EditText someEditText = someInputLayout.getEditText();
String text = someEditText.getText().toString();
This could possibly be a more desired solution as it does not require any external methods, though this would not solve your problem if it is required that you start from EditText for some reason. I know this has been answered a long time ago, but I was using #sylwano's solution, until I found for my particular problem it was better to do as above.

Related

Set default(unfocused) TextInputLayout hint textSize

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.

The base-most Android view that supports gravity

I have a custom android widget that abstracts a forum field, RecordDataField, that is based on a RelativeLayout (basically groups a field and it's label together, and takes custom attributes to determine where the label is relative to the field, to show a drop down button for dropdown list's, ...)
public abstract class RecordDataField : RelativeLayout
{
protected TextView _tvLabel;
protected FieldLayout _rlInput;
//protected EditText _etInput;
protected FieldInput _etInput;
protected Button _btnDrop;
protected abstract FieldInput InstantiateInput();
}
FieldInput is a subclassed EditText, nothing really special.
Any child of RecordDataField implements InstantiateInput similar to as follows (_etInput ultimately ends up with the value returned by InstantiateInput)
protected override FieldInput InstantiateInput()
{
View v = _inflater.Inflate(Resource.Layout.RecordDataFields, null);
_fieldInput = ((ViewGroup)v).FindViewById<FieldInput> Resource.Id.RecordDataFieldInput);
return (FieldInput)_fieldInput;
}
This works great for all my RecordDataField variants, but they are all FieldInput (EditText) based. I now need to go even more general, and when I try changing the abstract method InstantiateInput's return type to View, I run into problems in the base class, b/c it set's gravity and other attributes on _etInput, and apparently View doesn't support gravity
public virtual void InitView()
{
_tvLabel = (TextView)_inflater.Inflate(Resource.Layout.RecordDataFieldLabel, this, false);
_tvLabel.Text = _label;
_tvLabel.Gravity = _labelGravity;
ViewGroup.LayoutParams tvLabelLayout = (ViewGroup.LayoutParams)_tvLabel.LayoutParameters;
tvLabelLayout.Width = _labelWidth;
RelativeLayout.LayoutParams lpLabel = new RelativeLayout.LayoutParams(tvLabelLayout);
lpLabel.AddRule(LayoutRules.AlignParentTop);
lpLabel.AddRule(LayoutRules.AlignParentLeft);
AddView(_tvLabel, lpLabel);
...
}
Any suggestions as to which view type I can return from InstantiateInput so this widget can support the greatest amount of view types (the motivation here is that I want to use a CheckBox in place of the EditText (FieldInput)?
Try TextView (android.widget.TextView) . Both Checkbox and EditText extends TextView which supports the gravity param to support aligning it's content

Binding several views to a Listener with multiple methods in Butterknife

I'm using Butterknife(8.4.0) to instantiate my views in a fragment that has several EditTexts.
I'm using these EditTexts to set a string to a specific Model attribute. I don't want to create loads of bind methods for each Edittext so on #onTextChanged I pass in all the edit texts. I'm only interested in AfterTextChanged() so I've also passed that in. I then use the editText ids to specify which model attribute should be set.
#BindView(R.id.edit_text_one) EditText textFieldOne;
#BindView(R.id.edit_text_two) EditText textFieldTwo;
#OnTextChanged(value = {R.id.edit_text_one, R.id.edit_text_two}, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void setEditTextFields(EditText editText, Editable editable) {
switch (editText.getId()) {
case R.id.edit_text_one:
myModel.setStringOne(editable.toString());
break;
case R.id.edit_text_two:
myModel.setStringTwo(editable.toString());
break;
}
}
However I'm getting a compile error
Error:(117, 10) error: #OnTextChanged methods can have at most 1 parameter(s). (com.skeeno.android.gamecabinet.Fragment.EditorFragment.setEditTextFields)
I've read here that you just pass in the view as the first argument but that doesn't seem to work since AfterTextChanged is only expecting an editable.
Is there a way to do this?
Any help will be greatly appreciated. Thanks.
Passing View with #onTextChanged is not possible currently. I tried too.
However, below code can be used to get the current view,
//Inside a fragment
View view = getActivity().getCurrentFocus();
Hope this helped.
It is a little bit late but if anyone will have this issue, answer can be helpful therefore, here how i make it work. You cannot send two parameter method for AFTER_TEXT_CHANGED therefore; you need to change method void setEditTextFields(EditText editText, Editable editable) to void setEditTextFields(Editable editable). In this case you need to write #OnTextChanged for all EditText you want to listen.
Here is final code should be;
#BindView(R.id.edit_text_one) EditText textFieldOne;
#BindView(R.id.edit_text_two) EditText textFieldTwo;
#OnTextChanged(value =R.id.edit_text_one, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void edit_text_oneChanged(Editable editable) {
myModel.setStringOne(editable.toString());
}
#OnTextChanged(value =R.id.edit_text_two, callback = OnTextChanged.Callback.AFTER_TEXT_CHANGED)
void edit_text_twoChanged(Editable editable) {
myModel.setStringTwo(editable.toString());
}

setHint doesnt work with setInputType

I build EditText dynamically. Among other things, I set 2 properties: hint(.setHint) and inputType(.setInputType). My problem: when I invoke setInputType, setHint has no effect: blank edittexts remain blank with no hint. Once I comment out setInputType, I see all hints. I need both input type and hint. What to do? My code:
private EditText buildTextBox(Property property)
{
EditText control = new EditText(this);
control.setInputType(getInputTypeByPropertyInputType(property.getType()));// android.text.InputType.
control.setHint(property.getDisplayName());
return control;
}
private int getInputTypeByPropertyInputType(String type)
{
if (type.equals("integer"))
{
return android.text.InputType.TYPE_CLASS_NUMBER;
}
else
{
return android.text.InputType.TYPE_CLASS_TEXT;
}
}
#Eugene
Ensure you call control.SetHint() just before you call the control.setGravity() and control.setInputType(); and it works for me verrry much!
column1 = new EditText(this);
column1.setId(i);
column1.setHint("Value");
column1.setInputType(InputType.TYPE_CLASS_NUMBER);
column1.setGravity(Gravity.RIGHT);
I agree with Eugene.
Remove the gravity(just don't use CENTER) and the hint texts will come back as normal.
Nice find!

How to remove focus without setting focus to another control?

I like my UIs to be intuitive; each screen should naturally and unobtrusively guide the user on to the next step in the app. Barring that, I strive to make things as confusing and confounding as possible.
Just kidding :-)
I've got three TableRows, each containing a read-only and non-focusable EditText control and then a button to its right. Each button starts the same activity but with a different argument. The user makes a selection there and the sub-activity finishes, populating the appropriate EditText with the user's selection.
It's the classic cascading values mechanism; each selection narrows the available options for the next selection, etc. Thus I'm disabling both controls on each of the next rows until the EditText on the current row contains a value.
I need to do one of two things, in this order of preference:
When a button is clicked, immediately remove focus without setting focus to a different button
Set focus to the first button when the activity starts
The problem manifests after the sub-activity returns; the button that was clicked retains focus.
Re: #1 above - There doesn't appear to be a removeFocus() method, or something similar
Re: #2 above - I can use requestFocus() to set focus to the button on the next row, and that works after the sub-activity returns, but for some reason it doesn't work in the parent activity's onCreate().
I need UI consistency in either direction--either no buttons have focus after the sub-activity finishes or each button receives focus depending on its place in the logic flow, including the very first (and only) active button prior to any selection.
Using clearFocus() didn't seem to be working for me either as you found (saw in comments to another answer), but what worked for me in the end was adding:
<LinearLayout
android:id="#+id/my_layout"
android:focusable="true"
android:focusableInTouchMode="true" ...>
to my very top level Layout View (a linear layout). To remove focus from all Buttons/EditTexts etc, you can then just do
LinearLayout myLayout = (LinearLayout) activity.findViewById(R.id.my_layout);
myLayout.requestFocus();
Requesting focus did nothing unless I set the view to be focusable.
Old question, but I came across it when I had a similar issue and thought I'd share what I ended up doing.
The view that gained focus was different each time so I used the very generic:
View current = getCurrentFocus();
if (current != null) current.clearFocus();
You can use View.clearFocus().
Use View.requestFocus() called from onResume().
android:descendantFocusability="beforeDescendants"
using the following in the activity with some layout options below seemed to work as desired.
getWindow().getDecorView().findViewById(android.R.id.content).clearFocus();
in connection with the following parameters on the root view.
<?xml
android:focusable="true"
android:focusableInTouchMode="true"
android:descendantFocusability="beforeDescendants" />
https://developer.android.com/reference/android/view/ViewGroup#attr_android:descendantFocusability
Answer thanks to:
https://forums.xamarin.com/discussion/1856/how-to-disable-auto-focus-on-edit-text
About windowSoftInputMode
There's yet another point of contention to be aware of. By default,
Android will automatically assign initial focus to the first EditText
or focusable control in your Activity. It naturally follows that the
InputMethod (typically the soft keyboard) will respond to the focus
event by showing itself. The windowSoftInputMode attribute in
AndroidManifest.xml, when set to stateAlwaysHidden, instructs the
keyboard to ignore this automatically-assigned initial focus.
<activity
android:name=".MyActivity"
android:windowSoftInputMode="stateAlwaysHidden"/>
great reference
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/ll_root_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
LinearLayout llRootView = findViewBindId(R.id.ll_root_view);
llRootView.clearFocus();
I use this when already finished update profile info and remove all focus from EditText in my layout
====> Update: In parent layout content my EditText add line:
android:focusableInTouchMode="true"
What about just adding android:windowSoftInputMode="stateHidden" on your activity in the manifest.
Taken from a smart man commenting on this: https://stackoverflow.com/a/2059394/956975
I tried to disable and enable focusability for view and it worked for me (focus was reset):
focusedView.setFocusable(false);
focusedView.setFocusableInTouchMode(false);
focusedView.setFocusable(true);
focusedView.setFocusableInTouchMode(true);
First of all, it will 100% work........
Create onResume() method.
Inside this onResume() find the view which is focusing again and again by findViewById().
Inside this onResume() set requestFocus() to this view.
Inside this onResume() set clearFocus to this view.
Go in xml of same layout and find that top view which you want to be focused and set focusable true and focusableInTuch true.
Inside this onResume() find the above top view by findViewById
Inside this onResume() set requestFocus() to this view at the last.
And now enjoy......
android:focusableInTouchMode="true"
android:focusable="true"
android:clickable="true"
Add them to your ViewGroup that includes your EditTextView.
It works properly to my Constraint Layout. Hope this help
You could try turning off the main Activity's ability to save its state (thus making it forget what control had text and what had focus). You will need to have some other way of remembering what your EditText's have and repopulating them onResume(). Launch your sub-Activities with startActivityForResult() and create an onActivityResult() handler in your main Activity that will update the EditText's correctly. This way you can set the proper button you want focused onResume() at the same time you repopulate the EditText's by using a myButton.post(new Runnable(){ run() { myButton.requestFocus(); } });
The View.post() method is useful for setting focus initially because that runnable will be executed after the window is created and things settle down, allowing the focus mechanism to function properly by that time. Trying to set focus during onCreate/Start/Resume() usually has issues, I've found.
Please note this is pseudo-code and non-tested, but it's a possible direction you could try.
You do not need to clear focus, just add this code where you want to focus
time_statusTV.setFocusable(true);
time_statusTV.requestFocus();
InputMethodManager imm = (InputMethodManager)this.getSystemService(Service.INPUT_METHOD_SERVICE);
imm.showSoftInput( time_statusTV, 0);
Try the following (calling clearAllEditTextFocuses();)
private final boolean clearAllEditTextFocuses() {
View v = getCurrentFocus();
if(v instanceof EditText) {
final FocusedEditTextItems list = new FocusedEditTextItems();
list.addAndClearFocus((EditText) v);
//Focus von allen EditTexten entfernen
boolean repeat = true;
do {
v = getCurrentFocus();
if(v instanceof EditText) {
if(list.containsView(v))
repeat = false;
else list.addAndClearFocus((EditText) v);
} else repeat = false;
} while(repeat);
final boolean result = !(v instanceof EditText);
//Focus wieder setzen
list.reset();
return result;
} else return false;
}
private final static class FocusedEditTextItem {
private final boolean focusable;
private final boolean focusableInTouchMode;
#NonNull
private final EditText editText;
private FocusedEditTextItem(final #NonNull EditText v) {
editText = v;
focusable = v.isFocusable();
focusableInTouchMode = v.isFocusableInTouchMode();
}
private final void clearFocus() {
if(focusable)
editText.setFocusable(false);
if(focusableInTouchMode)
editText.setFocusableInTouchMode(false);
editText.clearFocus();
}
private final void reset() {
if(focusable)
editText.setFocusable(true);
if(focusableInTouchMode)
editText.setFocusableInTouchMode(true);
}
}
private final static class FocusedEditTextItems extends ArrayList<FocusedEditTextItem> {
private final void addAndClearFocus(final #NonNull EditText v) {
final FocusedEditTextItem item = new FocusedEditTextItem(v);
add(item);
item.clearFocus();
}
private final boolean containsView(final #NonNull View v) {
boolean result = false;
for(FocusedEditTextItem item: this) {
if(item.editText == v) {
result = true;
break;
}
}
return result;
}
private final void reset() {
for(FocusedEditTextItem item: this)
item.reset();
}
}

Categories

Resources