Android View's .onMeasure side effect

133 views Asked by At

I'm having an issue where .measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT) somehow alters the view's state.

I've set up a view which listens to the onClick event and performs the .measure. Note that when I perform this directly in onCreate, it does not occur.

I expect that this code should only re-draw my textView with green color, not change any position.

Simple view:

enter image description here

Same view after clicking it. Notice how the position is off:

enter image description here

Activity:

class Main2Activity : AppCompatActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main2)
        setSupportActionBar(toolbar)

        val root = findViewById<ConstraintLayout>(R.id.foo)
        val tv = root.findViewById<TextView>(R.id.bar)
        tv.setOnClickListener {
            tv.measure(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
            tv.setTextColor(resources.getColor(R.color.green))
        }
    }
}

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"
    app:layout_behavior="@string/appbar_scrolling_view_behavior"
    tools:context="io.flogging.activities.Main2Activity"
    android:id="@+id/foo"
    tools:showIn="@layout/activity_main2">
       <TextView
            android:id="@+id/bar"
            android:background="@color/colorAccent"
            android:layout_width="match_parent"
            android:gravity="center"
            android:padding="10dp"
            android:minLines="2"
            android:text="Please let me be in center"
            android:layout_height="100dp" />

</android.support.constraint.ConstraintLayout>
1

There are 1 answers

1
Ben P. On

You probably don't want to be calling measure() in the first place. The system should take care of that for you. Just go ahead and delete that call.

If you've left something out of the question, and you absolutely must call measure(), then you need to make sure that you pass the correct parameters. The documentation for View.measure() specifies that you must pass "measure specs", not LayoutParams constants. That is, instead of what you have, you would pass something that looked like this:

tv.measure(View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY),
           View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY));

Both View.MeasureSpec.makeMeasureSpec(200, View.MeasureSpec.EXACTLY) and ViewGroup.LayoutParams.MATCH_PARENT are of type int, which is why the compiler doesn't complain and the app doesn't crash. But you can't just pass any old int to this method.

Note, however, that the explicit purpose of this call is to change the layout of the view. So if you call measure, then you're going to change the view's layout, because that is what it does.

Note that when I perform this directly in onCreate, it does not occur.

I don't know for sure, but I suspect that this is because your manual call to measure() in onCreate() comes before a system-intiated call to measure() during view layout, so your modified structure gets thrown out. When you do it in an OnClickListener instead, then your manual call comes second, and overrides the system-initiated call.