Search code examples
androidandroid-layoutandroid-viewpageronmeasure

Custom font causing my dynamic ViewPager's OnMeasure to cut off lines of text


So my custom TextView is cutting off the last line of text in some cases. I'm using a custom font. It seems to only cut off the last word. I changed my custom text views to regular ones, which forces my text views to use standard fonts instead of my custom one, and suddenly the sizing works.

Theory on issue: While using the DDMS tool to see the layout sizes around my views and comparing custom font version vs regular font version, it looks like it measures the height of the characters properly--if it didn't it would cut off part of the y's, g's, and p's on my bigger, custom font. But then it doesn't properly guess the amount of lines my bigger font will take up, and measures the amount of lines as if it were the smaller, non-custom font. So when a string ends with a two letter word which is just on the edge of wrapping, it doesn't think it needs to wrap to the next line and cuts it off.

Layout for inside the ViewPager which includes the problem child CustomTextView:

<LinearLayout
    android:id="@+id/header_layout"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingLeft="-12dp"
    android:paddingRight="-12dp"
    android:orientation="vertical">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent">

        <android.support.v7.widget.CardView
            snip>

            <RelativeLayout
                android:layout_width="match_parent"
                android:layout_height="match_parent">

                <FrameLayout
                 snip>
                 <!---v--- this is the problem child--->
                <com.snip.custom.views.CustomTextView 
                    android:id="@+id/label"
                    style="@style/typographyParagraphBody"
                    android:layout_centerHorizontal="true"
                    android:gravity="center"
                    android:layout_marginBottom="20dp"
                    android:layout_marginLeft="30dp"
                    android:layout_marginRight="30dp"
                    android:text="@{carouselItemViewModel.label}"/>

            </RelativeLayout>
        </android.support.v7.widget.CardView>
    </RelativeLayout>
</LinearLayout>

Boring ViewPager:

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    int height = 0;
    for (int i = 0; i < getChildCount(); i++) {
        View child = getChildAt(i);
        child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        int h = child.getMeasuredHeight();
        if (h > height){
            height = h;
        }
    }

    heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

My layout file:

<RelativeLayout
    android:layout_width="fill_parent"
    android:layout_height="wrap_content"
    android:background="@{carouselViewModel.carouselBackgroundColor}"
    android:orientation="vertical">

    <com.snip.custom.views.CustomTextView
        android:id="@+id/text"
        style="@style/irrelevant"
        android:text="@{carouselViewModel.text}"/>

    <android.support.design.widget.TabLayout
        android:id="@+id/tab_layout"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_marginBottom="-17dp"
        android:layout_gravity="top"
        android:layout_below="@id/text"
        app:tabBackground="@drawable/dot_tab_selector"
        app:tabGravity="center"
        app:tabIndicatorHeight="0dp"
        app:tabPaddingEnd="5dp"
        app:tabPaddingStart="5dp"
        app:viewPager="@{{viewPager}}" />

    <com.snip.custom.views.CustomViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:clipToPadding="false"
        android:paddingEnd="20dp"
        android:paddingStart="20dp"
        android:layout_below="@id/tab_layout"
        app:currentItem="@{carouselViewModel.carouselCurrentItemSubject}"
        app:setOffscreenPageLimit="@{carouselViewModel.carouselOffscreenPageLimitSubject}"
        app:itemView="@{carouselViewModel.carouselItemViewSelector}"
        app:items="@{carouselViewModel.carouselItems}"
        app:onAdapterChangeListener="@{carouselViewModel.carouselAdapterChangeSubject}"
        app:onPageChangeListener="@{carouselViewModel.carouselPageChangeSubject}"/>

</RelativeLayout>

Solution

  • Looks like I needed more code to measure the children properly, since they weren't aware of their layout parameters. So I remeasure them by applying the parent widthMeasureSpec.

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        int height = 0;
        for (int i = 0; i < getChildCount(); i++) {
            View child = getChildAt(i);
            child.measure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
            int h = child.getMeasuredHeight();
            if (h > height){
                height = h;
            }
            height = Math.max(child.getMeasuredHeight(), measureViewHeight(child, widthMeasureSpec));
        }
    
        heightMeasureSpec = MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY);
    
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    }
    
    private int measureViewHeight(View view, int widthMeasuredSpec) {
        view.measure(getChildMeasureSpec(widthMeasuredSpec,
                getPaddingLeft() + getPaddingRight(),
                view.getLayoutParams().width),
                MeasureSpec.makeMeasureSpec(0, MeasureSpec.UNSPECIFIED));
        return view.getMeasuredHeight();
    }