AppCompatEditText.getpParent() внутри TextInputLayout возвращает FrameLayout

Я создаю простой AppCompatEditText, добавляю OnFocusChangeListener и помещаю его в простой TextInputLayout.

Когда AppCompatEditText теряет фокус, его содержимое должно проверяться методом isValidParam.

Это работало до вчерашнего дня, когда я использовал rev.23.0.3 Но теперь, когда я использовал rev.24.0.2, выдает ошибку, как показано ниже в 1-й строке метода isValidParam.

java.lang.ClassCastException: android.widget.FrameLayout не может быть приведен к android.support.design.widget.TextInputLayout

Я проверил в режиме отладки. AppCompatEditText.getpParent() действительно возвращает Framelayout вместо 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;
}

5 ответов

Обновить:
Используйте виджет TextInputEditText вместо EditText внутри TextInputLayout.

старый ответ

TextInputLayout textInputLayout = (TextInputLayout) editText.getParent().getParent();

Это кажется быстрым решением. Далеко от идеала.

getParentForAccessibility() работал на меня

Вы можете проверить, находится ли EditText внутри TextInputLayout, используя следующий метод:

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;
    }

}

Пример использования:

TextInputLayout textInputLayout = getFirstParent(editText, TextInputLayout.class)

Просто выдержки из официальных документов Android:

 <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>

Примечание. Фактическая иерархия представления, представленная в TextInputLayout, НЕ гарантируется в соответствии с иерархией представления, записанной в XML. В результате вызовы getParent() для дочерних элементов TextInputLayout, таких как TextInputEditText, могут не возвращать сам TextInputLayout, а скорее промежуточное представление. Если вам нужен прямой доступ к View, установите android:id и используйте findViewById(int).

Поэтому, чтобы решить проблему, вы должны обратиться к findViewById вместо getParent из-за дополнительной раскладки между ними, представленной в версии 24.

Вы можете проверить код TextInputLayout v24.xx
Теперь это работает с 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);
    }
}

где mInputFrame это FrameLayout,
Это причина вашей проблемы (родитель FrameLayout).

Просто передайте tilParam в качестве параметра, вместо использования getParent() если вам нужно использовать это.

TextInputLayout имеет метод getEditText(). Это может быть альтернативный способ решения вашей проблемы. Вместо того, чтобы начинать с самого EditText и получать родительский TextInputLayout, вы можете начать с TextInputLayout и просто получить дочернее представление EditText. Для представлений, сгенерированных xml, следующий код является примером:

TextInputLayout someInputLayout = findViewById(R.id.some_input_layout);
EditText someEditText = someInputLayout.getEditText();
String text = someEditText.getText().toString();

Это могло бы быть более желательным решением, поскольку оно не требует каких-либо внешних методов, хотя это не решит вашу проблему, если по какой-то причине требуется, чтобы вы начинали с EditText. Я знаю, что на это давным-давно был дан ответ, но я использовал решение @sylwano, пока не обнаружил, что для моей конкретной проблемы было лучше сделать то, что указано выше.