Same XML, Different Behaviour

Guy :

This is a problem I've been experiencing for quite some time and have already reached out about — other Android programmers found it intriguing.

Context: I have a custom EditText view that relies on XML for instantiation. I have two methods to create this view: either it is within the Layout file of the current Activity (1) or I dynamically add it to the layout at runtime using an Inflater (2). With both methods, the object is defined exactly the same in XML. However, there is a behavior with one that doesn't show in the other.

Behaviors: When using the first method, I get the correct and expected behavior, which is the following: The user types in the center of the EditText (restricted to one horizontal line). Once the user has typed enough to exceed the bounds of the EditText, the EditText will wrap_content to accommodate. When it changes width, the change is anchored to the middle of the view. Length is thus added to both the right AND the left of the box. When using the second method, I get the incorrect behavior: the width change is anchored to the left! Length is added to the right only.

Issue: I'd like to see the correct behavior with the second method and understand why the behavior could be different even though the XML is the same.

What I've Done: I've already reached out for help on the Android Studio Discord and have tried to tweak different aspects of the shared XML. The last thing I've done is to verify this problem still occurs with regular EditText and that my custom class isn't to blame.

Code Snippets: the shared XML (in activity layout OR "tag.xml")

<com.lab.guy.opener.TagView
    xmlns:app="http://schemas.android.com/apk/res-auto"

    style="@style/TagStyle"

    app:layout_constraintEnd_toEndOf="parent"
    app:layout_constraintStart_toStartOf="parent"
    app:layout_constraintTop_toTopOf="parent"
    app:layout_constraintRight_toRightOf="parent" />

And "TagStyle":

 <style name="TagStyle">
    <item name="android:textSize">14sp</item>
    <item name="android:layout_height">21sp</item>
    <item name="android:layout_width">wrap_content</item>
    <item name="android:gravity">center</item>

    <item name="android:paddingLeft">10dp</item>
    <item name="android:paddingRight">10dp</item>

    <item name="android:focusableInTouchMode">true</item>
    <item name="android:maxLines">1</item>
    <item name="android:lines">1</item>
    <item name="android:singleLine">true</item>
    <item name="android:maxLength">20</item>
    <item name="android:inputType">textNoSuggestions|textVisiblePassword</item>
    <item name="android:imeOptions">actionDone</item>

    <item name="android:hint">@string/tag</item>
    <item name="android:textColor">@color/tag</item>
    <item name="android:textCursorDrawable">@drawable/tag_cursor</item>
    <item name="android:background">@drawable/tag_background</item>
</style>

The main layout is a ConstraintLayout.

If you need any other resources for testing, please do tell!

The only code I'm not willing to share is from TagView.java, since the problem occurs with standard EditTexts too.

Thank you very much from reading this far!

Edit: I've tried out more things to do, such as cross-testing on other devices and trying out other layout types (under Linear or Relative, I get the incorrect behavior regardless of which method I use). Currently, I'm trying to recreate the correct behavior on a default EditText that uses the second instantiation method (this time, trying out different XML).

Guy :

I've at long last found the actual cause of and solution to the original issue!
Not that I fully understand it, but here goes:

private ConstraintSet myConstraintSet = new ConstraintSet();

...

CustomEditText myCustomView = new CustomEditText(new ContextThemeWrapper(this, R.style.CustomStyle), null, 0);
myCustomView.setId(View.generateViewId()); 
myConstraintLayout.addView(tag);

myConstraintSet.clone(myConstraintLayout);

myConstraintSet.connect(myCustomView.getId(), ConstraintSet.START, myConstraintLayout.getId(), ConstraintSet.START);
myConstraintSet.connect(myCustomView.getId(), ConstraintSet.END, myConstraintLayout.getId(), ConstraintSet.END);

myConstraintSet.applyTo(myConstraintLayout);


There are two things to note upfront about this code: firstly, I switched the instantiation method from using an Inflater to using ContextThemeWrapper. Feel free to use whichever works best for you, from what I've seen it doesn't seem to change much. Secondly, View.generateViewId() is a method which won't work for API <= 17. If this is an inconvenience for you as it was for me, there are some easy-to-integrate workarounds.

So, as you might guess, it seems the reason why the behaviors were different lies in whether or not the view we wish to instantiate is correctly constrained in the ConstraintLayout. It also seems that for this to work as we want, we NEED to use a ConstraintLayout. The necessary constraints are left-to-left and right-to-right relative to the parent (perhaps other objects can also work).

This solution may seem obvious, but I was mislead by the thought that setting those constraints in tag.xml would correctly apply them once instantiated. It... doesn't. I'm very surprised I never thought to play with the constraints of the original view, and am happy I did — though by complete accident.

This solution requires no XML other than styles.xml.

My previous solution can now be thrown out of the window completely. There are some inconveniences with this solution, such as having to clone the layout every time we want to set new constraints, the object behaving weirdly if proper IDs aren't given, the higher amount of code overall within the Activity and most egregious of all, the setX command now being relative to the horizontal center of the screen rather than absolute. I'll update this answer if I figure out how to fix that.

If there are other major problems on your end that I didn't experience, my other solution can still be used as a workaround. I'll update this answer if I have any more remarks or problems with it. Be sure to contact me if you have any other questions!



Oh, and lastly, if you have a deeper understanding of why I experience the problem at all when no constraints are set, be sure to post an answer. It would be interesting, insightful and probably be marked as the final correct answer.

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=126953&siteId=1