thyrlian/AwesomeValidation

UNDERLABEL doesn't work on ConstraintLayout

matiit opened this issue · 8 comments

device-2017-07-09-134358

For given layout:

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context="anddev.mat.pckplace.CreateAccount">

    <EditText
        android:id="@+id/email"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="8dp"
        android:ems="10"
        android:inputType="textEmailAddress"
        android:text="example@domain.com"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <EditText
        android:id="@+id/password"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:ems="10"
        android:inputType="textPassword"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/email" />

    <Button
        android:id="@+id/joinButton"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:text="Join"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toBottomOf="@+id/password" />

    <ProgressBar
        android:id="@+id/progressBar"
        style="?android:attr/progressBarStyle"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintVertical_bias="0.185" />

</android.support.constraint.ConstraintLayout>

Thanks for reporting, will check it later.

Just tried with your layout, the UNDERLABEL validation doesn't work properly indeed. I think the problem is because of ConstraintLayout. I'll try to find out a solution and meanwhile update the demo project with ConstraintLayout. Thanks again.

Apart from copying the LayoutParams, I also tried to copy the ConstraintSet for each view inside the ConstraintLayout (and then later apply them to corresponding new container view). Unfortunately this approach doesn't work. After checking the source code of ConstraintSet, I found the cause:

public class ConstraintSet {
    private HashMap<Integer, ConstraintSet.Constraint> mConstraints = new HashMap();
    ...

    public void clone(ConstraintLayout constraintLayout) {
        int count = constraintLayout.getChildCount();
        this.mConstraints.clear();

        for(int i = 0; i < count; ++i) {
            View view = constraintLayout.getChildAt(i);
            LayoutParams param = (LayoutParams)view.getLayoutParams();
            int id = view.getId();
            if(!this.mConstraints.containsKey(Integer.valueOf(id))) {
                this.mConstraints.put(Integer.valueOf(id), new ConstraintSet.Constraint());
            }
            ...

The relationship HashMap mConstraints holds each view's id as the key, that's why simply copying attributes doesn't work. The constraint is bound with view id, always refer to view id at creation, not transferable.

Tried to hack by setting new container's id to EditText's id, then giving EditText a new id, assuming this would hand over all constraints from EditText to its new container parent. But unfortunately it doesn't work either :(

int idOfEditText = editText.getId();
editText.setId(View.generateViewId());
newContainer.setId(idOfEditText);

The private member mConstraintSet of ConstraintLayout is also null:

Field fieldConstraintSet = ((ConstraintLayout) parent).getClass().getDeclaredField("mConstraintSet");
fieldConstraintSet.setAccessible(true);
ConstraintSet constraintSet = (ConstraintSet) fieldConstraintSet.get(parent);
// constraintSet is null here
// the reason is when init, attr != styleable.ConstraintLayout_Layout_constraintSet

Sorry @matiit, finally I gave up - spent too much time on it but could not find a solution, it's way too complicated.

Created and merged a PR: #50

In this PR, when the application tries to use UNDERLABEL for ConstraintLayout, a UnsupportedLayoutException will be thrown with message:

UnderlabelValidator doesn't support ConstraintLayout, please use TextInputLayoutValidator or other any other validator.

And in the demo project, I changed the TextInputLayout validation style's layout to ConstraintLayout, to show how it works.

I'll keep this issue open, won't fix for now, but will still look for a solution in the future. I'm really sorry for that and thank you very much for reporting this issue.

When will this issue be fixed?

@Sabutori probably not, at least it won't be fixed by me. As mentioned earlier, I just could not find a solution for this back in time, but also get no time to check this for the moment. Sorry for giving such a negative answer.