AndroidDeveloperLB / AutoFitTextView

A TextView that automatically fit its font and line count based on its available size and content
Apache License 2.0
911 stars 156 forks source link

When AutoResizeTextView is used in a custom view for a TabLayout.Tab, the text progressively gets smaller as the tab is selected and reselected #22

Closed afahrenholtzmcgladrey closed 8 years ago

afahrenholtzmcgladrey commented 9 years ago

Layout of custom view for Tab:

<AutoResizeTextView xmlns:android="http://schemas.android.com/apk/res/android"
                                                 xmlns:tools="http://schemas.android.com/tools"
                                                 android:id="@+id/tab_title"
                                                 android:layout_width="wrap_content"
                                                 android:layout_height="wrap_content"
                                                 android:layout_gravity="center"
                                                 android:maxLines="1"
                                                 android:textSize="18sp"
                                                 android:textStyle="bold"/>

Code for setting up TabLayout in Activity:

_tabLayout.setupWithViewPager(_viewPager);
_tabLayout.setTabGravity(TabLayout.GRAVITY_CENTER);
_tabLayout.setOnTabSelectedListener(new TabLayout.ViewPagerOnTabSelectedListener(_viewPager)
        {
            @Override
            public void onTabSelected(TabLayout.Tab tab)
            {
                _viewPager.setCurrentItem(tab.getPosition(), true);

                ViewHolder viewHolder = (ViewHolder) tab.getTag();

                viewHolder.TabTitleView.setTextColor(getResources().getColor(R.color.primaryColor));

                viewHolder.TabTitleView.setText(_pageAdapter.getPage(tab.getPosition()).getSelectedTitle());
            }

            @Override
            public void onTabUnselected(TabLayout.Tab tab)
            {
                ViewHolder viewHolder = (ViewHolder) tab.getTag();

                viewHolder.TabTitleView.setTextColor(getResources().getColor(R.color.tab_title_color));
                viewHolder.TabTitleView.setText(_pageAdapter.getPage(tab.getPosition()).getTitle());
            }
        });

        for(int i = 0; i < _pageAdapter.getCount(); i++)
        {
            TabLayout.Tab tab = _tabLayout.getTabAt(i);
            tab.setCustomView(R.layout.tab_title);

            ViewHolder viewHolder = new ViewHolder(tab.getCustomView(), _tabLayout.getContext());
            viewHolder.TabTitleView.setText(_pageAdapter.getPage(i).getTitle());
            tab.setTag(viewHolder);

            if(i == 0)
                viewHolder.TabTitleView.setTextColor(getResources().getColor(R.color.primaryColor));
        }
AndroidDeveloperLB commented 9 years ago

Sorry if this occurs for you. I never tested it in tabs. However, I think you should consider using what the tabs already provide you. I think it has a way to be scrollable if it takes too much space.

AndroidDeveloperLB commented 9 years ago

Also, do note that the tabs of the support library have issues even if you don't use this special view. I remember it has weird wobbly tab indicator, and that the text there flashes when being selected.

afahrenholtzmcgladrey commented 9 years ago

Unfortunately, having the the tabs scrollable is not desired with the current app. Thankfully this progressive shrinking effect is only noticeable if the tabs are unselected and reselected >3 times.

AndroidDeveloperLB commented 9 years ago

Isn't it incredibly weird? And, do they at least become readable ?

afahrenholtzmcgladrey commented 9 years ago

It does stop shrinking them when it gets to its minimum height which is about 6dp.

AndroidDeveloperLB commented 9 years ago

so it shrinks more and more? Too bad. Maybe you could handle it by telling it to set its size only once, and then stay on it ?

afahrenholtzmcgladrey commented 9 years ago

Yes, that is possible. Although, this won't work with an ExpandableListView as the text view is shrunk and expanded at will so that functionality is needed. May just add a check for ignoring small size changes as that is what is happening when the tab is selected.

AndroidDeveloperLB commented 9 years ago

Sorry for that. There should be a better way to handle it.

suarezjulian commented 8 years ago

Had the same issue while using the view on a dynamic view, where the text changes frequently, and the view keeps shrinking size until it settles on the min size

IonutNegru87 commented 8 years ago

I confim this issue is also present when used inside lists, like an RecyclerView.

AndroidDeveloperLB commented 8 years ago

I suspect that you guys don't set the size of the view to be constant (meaning you use wrap_content) , so as it changes the text, it also auto-resizes its font, which changes its size too.

Can you please try to have a constant size for the TextView ? Otherwise it doesn't make sense to use it, as it doesn't need auto-resizing to the size constraints (because it changes its size).

IonutNegru87 commented 8 years ago

The view size isn't dynamic - all the rows have fixed size and their content doesn't change. I suspect this is happening because the views are being recycled and the logic from the binary search doesn't know it found an best case scenario, thus it tries again the next value, thus recreating the effect with the size getting smaller and smaller as the views get redrawn.

IonutNegru87 commented 8 years ago

I've made a quick and dirty workaround for this.

private Integer mFinalTextSize;

private void superSetTextSize(int startSize) {
        int textSize = binarySearch(startSize, (int) mMaxTextSize, mSizeTester, mAvailableSpaceRect);
        if (null == mFinalTextSize) {
            mFinalTextSize= textSize;
        }
        super.setTextSize(TypedValue.COMPLEX_UNIT_PX, mFinalTextSize);
    }

public void forceRefresh() {
        // We invalidate the last text size so that it will be recalculated when the text or text size changes
        mFinalTextSize = null;
    }

And when I need to make sure the size is updated everytime the vie is redrawn I call forceRefresh() on that view. In the views that are recycled I avoid to call the forceRefresh() method so that the size is persisted from the last time it was calculated.

I've also added the ability to save the state of the view by storing the last used size.

@Override
    public Parcelable onSaveInstanceState() {
        Parcelable superState = super.onSaveInstanceState();
        SavedState ss = new SavedState(superState);
        ss.textSize = mFinalTextSize;
        return super.onSaveInstanceState();
    }

    @Override
    public void onRestoreInstanceState(Parcelable state) {
        SavedState ss = (SavedState) state;
        super.onRestoreInstanceState(ss.getSuperState());
        mFinalTextSize = ss.textSize;
    }

static class SavedState extends BaseSavedState {
        int textSize;

        SavedState(Parcelable superState) {
            super(superState);
        }

        private SavedState(Parcel in) {
            super(in);
            textSize = in.readInt();
        }

        @Override
        public void writeToParcel(Parcel out, int flags) {
            super.writeToParcel(out, flags);
            out.writeInt(textSize);
        }

        public static final Parcelable.Creator<SavedState> CREATOR
                = new Parcelable.Creator<SavedState>() {
            public SavedState createFromParcel(Parcel in) {
                return new SavedState(in);
            }

            public SavedState[] newArray(int size) {
                return new SavedState[size];
            }
        };
    }

Maybe this could help others with the same problem and maybe in the future this issue is actually resolved. I know this is not a real solution, but I couldn't come up with one at the moment. I'll invest more time in this in the future.

Anyway, thank you for this great widget.

AndroidDeveloperLB commented 8 years ago

Guys, I can't reproduce this issue on RecyclerView. Maybe it occurs in some cases, but I failed to see any issue with using in RecyclerView. I've updated the sample to include one for RecyclerView (available via the menu). Please, if you can show a sample to show the issue, I could try it out and try to figure what's wrong.

IonutNegru87 commented 8 years ago

I've notice that binarySearch() should return the mid value when midValCmp == 0, but the implementation of the size tester doesn't return 0. I think this is the main reason behind the text size always shrinking until the minimum allowed size.

AndroidDeveloperLB commented 8 years ago

Please provide a minimal sample.