NLemay
NLemay

Reputation: 2383

Why Android is truncating my ActionBar title?

In my app, I change the title in the ActionBar from each fragment displayed. When I first start my apps, I got a list of requests, so my title is "My requests (20)".

Then, when you click on an item in that list, it is replacing a fragment in my Activity and it set the title to "Action".

When I come back to the first view (always in the same Activity), I reset the title to "My requests (20)", but android decide to truncate it. So the result is "My request...".

After much tests, it looks like that Android is shrinking my title when I put a smaller text in it. But it doesn't enlarge it when I put a longer text, even if there is plenty of room.

What can I do to solve this? I would like a better solution than adding spaces at the end of my short titles :)

Here is the code I use to change the title of the ActionBar :

getActivity().getActionBar().setTitle(title);   

Upvotes: 14

Views: 12132

Answers (13)

Martin Marconcini
Martin Marconcini

Reputation: 27226

A question from 2013, still being a thing.

Well, for those still shocked to occasionally see this happening even though you're happily using Androidx, all recent artifacts, etc. I found something that fixed it and it doesn't require custom titles, or anything special.

Bear in mind that this may not be your case but it appears to have magically fixed this issue in an instant.

In my case, the Toolbar, was set as supportActionBar as usual, and it was contained in a CoordinatorLayout + AppBar. At first I was going to blame those, because why not, but before doing so I decided to inspect the Toolbar's source code and debug what was happening.

I couldn't tell (but I did reach the code that was causing this ellipsis):

/**
 * Set the title of this toolbar.
 *
 * <p>A title should be used as the anchor for a section of content. It should
 * describe or name the content being viewed.</p>
 *
 * @param title Title to set
 */
public void setTitle(CharSequence title) {
    if (!TextUtils.isEmpty(title)) {
        if (mTitleTextView == null) {
            final Context context = getContext();
            mTitleTextView = new AppCompatTextView(context);
            mTitleTextView.setSingleLine();
            mTitleTextView.setEllipsize(TextUtils.TruncateAt.END);
            if (mTitleTextAppearance != 0) {
                mTitleTextView.setTextAppearance(context, mTitleTextAppearance);
            }
            if (mTitleTextColor != null) {
                mTitleTextView.setTextColor(mTitleTextColor);
            }
        }
        if (!isChildOrHidden(mTitleTextView)) {
            addSystemView(mTitleTextView, true);
        }
    } else if (mTitleTextView != null && isChildOrHidden(mTitleTextView)) {
        removeView(mTitleTextView);
        mHiddenViews.remove(mTitleTextView);
    }
    if (mTitleTextView != null) {
        mTitleTextView.setText(title);
    }
    mTitleText = title;
}

This is straight from the latest Toolbar.java in appCompat 1.1.0 (latest at this time in October 2019).

Nothing special, but you can see that when the TitleTextView is first created, it's set to singleLine and to use an Ellipsize at the END... (or right in LTR languages).

However, I noticed that when I was calling setTitle, the code didn't go thought that, presumably because the titleTextView was created before (and at that time, set to singleLine with Ellipsis); when I called setTitle, all the code was doing was mTitleTextView.setText(title). No amount of invalidate(), forceLayout(), requestLayout() to the toolbar would fix this.

Upon further inspection, I noticed that my layout (the Coordinator layout), was contained in a RelativeLayout.

The view Hierarchy looked like:

<RelativeLayout>
   <CoordinatorLayout>
       <AppBarLayout>
          <androidx.appcompat.widget.Toolbar />
       </AppBarLayout>
       <androidx.viewpager2.widget.ViewPager2 />
       <com.google.android.material.bottomnavigation.BottomNavigationView />
   </CoordinatorLayout>
</RelativeLayout>

I never liked RelativeLayout, I thought it was a horrible design to begin with, but also faced various issues with it since I can remember, and that it was very bad at handling its children.

For fun, I decided: "what if I change this RelativeLayout into a ConstraintLayout?"

I "right clicked" in the RelativeLayout in the Android Studio visual editor -> convert to ConstraintLayout, next, finish (Didn't touch the defaults).

Now it looks like this:

<ConstraintLayout>
   <CoordinatorLayout>
       <AppBarLayout>
          <androidx.appcompat.widget.Toolbar />
       </AppBarLayout>
       <androidx.viewpager2.widget.ViewPager2 />
       <com.google.android.material.bottomnavigation.BottomNavigationView />
   </CoordinatorLayout>
</ConstraintLayout>

Same, but the ROOT is no longer a relative layout.

I made two manual changes to the generated XML:

This is how the CoordinatorLayout looked after the conversion:

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_constraintTop_toTopOf="parent"
        app:layout_constraintStart_toStartOf="parent">

It looked right in the preview, but it caught my attention because one _should not use match_parent in a child of ConstraintLayout... so... I changed it to:

    <androidx.coordinatorlayout.widget.CoordinatorLayout
        android:id="@+id/coordinatorLayout"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent">

Which is exactly the same I had before (the coordinator layout taking up the whole screen).

This is thanks to the new ConstraintLayout having these:

    android:layout_width="match_parent"
    android:layout_height="match_parent"

And this magically solved my truncated titles once and for all.

tl;dr

Do not use RelativeLayouts anymore in 2019-onward, they are bad from a lot of points of view. If your toolbar is contained in a relative layout, consider migrating to a ConstraintLayout or any other ViewGroup.

Upvotes: 1

d.aemon
d.aemon

Reputation: 789

If anyone is still interested in a proper answer after more than 2 years I encountered this problem as well recently and realized the reason is that I updated the title from background thread. In Kotlin using supportActionBar?.title = <abc>.
For some reason the app didn't crash as it usually does with an exception mentioning that you are touching views outside of UI thread. Instead it sets new text, but skips layout pass resulting in wrapping the text.
So either use Activity.runOnUiThread or View.post or, when using coroutines as I did, use withContext(Dispatchers.Main) to trigger the code that updates the title.

Upvotes: 0

wklbeta
wklbeta

Reputation: 737

Because ListView / GridView / RecyclerView is called notifyDataSetChanged() at the same time. For some reason, this would cause layout update, which I guess result in the chaos of the action bar title's layout update.

Basically, you can postDelay() to update title to fix this problem, but I don't like this way.

I decide to override the BaseAdapter.notifyDateSetChanged() and update item view by hand. It works:

public abstract class BaseListAdapter<BEAN extends Unique, VH extends BaseListViewHolder<BEAN>> extends BaseAdapter {
    // data
    private List<BEAN> mList;
    // view holder
    private List<VH> mViewHolderList = new ArrayList<>();

    protected abstract void onBindViewHolder(VH holder, int position);

    protected abstract VH onCreateViewHolder(ViewGroup parent, int viewType);

    ...

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        VH viewHolder;
        if (convertView == null) {
            viewHolder = onCreateViewHolder(parent, getItemViewType(position));
            convertView = viewHolder.itemView;
            convertView.setTag(viewHolder);
            mViewHolderList.add(viewHolder);
        } else {
            //noinspection unchecked
            viewHolder = (VH) convertView.getTag();
        }

        viewHolder.setData(getItem(position));
        viewHolder.setPosition(position);
        onBindViewHolder(viewHolder, position);

        return convertView;
    }

    @Override
    public void notifyDataSetChanged() {
        if (mViewHolderList.size() == 0) {
            super.notifyDataSetChanged();
        } else {
            for (VH vh : mViewHolderList) {
                onBindViewHolder(vh, vh.getPosition());
            }
        }
    }

}

public class BaseListViewHolder<T> {
    public View itemView;
    private T data;

    private int position;

    public BaseListViewHolder(View itemView) {
        this.itemView = itemView;
    }

    @Override
    public T getData() {
        return data;
    }

    @Override
    public void setData(T data) {
        this.data = data;
    }

    public int getPosition() {
        return position;
    }

    public void setPosition(int position) {
        this.position = position;
    }
}

Sorry for my bad english.

Upvotes: 1

Steve Bergamini
Steve Bergamini

Reputation: 14600

In my particular case, I'm developing a hybrid app with a complex native menu structure. I'd see this issue intermittently when calling a deeplink from the hybrid content that would update the selected menu option and set the title.

I tried several of the suggested fixes with not luck. But setting a custom view produced a strange result that gave me the feeling that I was dealing with a race condition.

This proved true. I found that simply overriding the activity's setTitle function and wrapping the call to super in a postDelay runnable fixed this for me:

@Override
public void setTitle(final CharSequence title) {
    toolBar.postDelayed(new Runnable() {
        @Override
        public void run() {
            MainActivity.super.setTitle(title);
        }
    }, 200);
}

I'm using toolbar's postDelayed as a convenience method. But you can certainly use a handler here. Hope this helps.

Upvotes: 4

Yuchao Zhou
Yuchao Zhou

Reputation: 1072

put setTitle() in onCreateOptionsMenu helps me to solve this problem.

In your fragment add setHasOptionsMenu(true); in onCreateView(){}

Then override onCreateOptionsMenu().

Example:

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    super.onCreateOptionsMenu(menu, inflater);
    // put getActivity in onCreateOptionsMenu will not return null
    if (getActivity() != null) {
        getActivity().setTitle(getResources().getString(R.string.Studies));
    }
}

Reference: How to display android actionbar title without truncation occurring

Upvotes: 3

carrizo
carrizo

Reputation: 729

In my case the text was being truncated but not with ellipsis (...), the last character was always cut, like this:

screenshot

I found out that changing the toolbar width from "wrap_content", to "match_parent" solves the issue:

    <android.support.v7.widget.Toolbar
        android:id="@+id/toolbar"
        android:layout_width="match_parent"
        android:layout_height="?attr/actionBarSize"
        android:layout_weight="1"
        android:background="?attr/colorPrimary"
        app:popupTheme="@style/AppTheme.PopupOverlay" />

Upvotes: 2

gilchris
gilchris

Reputation: 1231

I guess this problem is solved by refreshing action bar UI.

So I had solved with below codes.

ActionBar actionBar = getActivity().getActionBar();
actionBar.setTitle(title);

// for refreshing UI
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(true);

Upvotes: 4

Dreamingwhale
Dreamingwhale

Reputation: 433

Your AndroidManifest.xml has your activity definition like so

<activity
        android:name="com.example.DetailsView"
        android:label="Details">
</activity>

If you change android:label="Whatever" to android:label=" ",

It'll work. I assume this happens because android creates a TextView to display label and the width is not re-sized when setTitle is called. So by setting the initial title to have more characters than you're going to set the title with, it will not be truncated.

Upvotes: 1

Simon
Simon

Reputation: 11190

You should find that the question I have posted here is the likey problem and I have also posted the solution.

Why my Android ActionBar doesn't update when it is explictily changed

Upvotes: 1

Poutrathor
Poutrathor

Reputation: 2059

I will add that you can totally use the action bar title on 2 lines to get more space:

//Set the title over 2 lines if needed:
    int titleId = Resources.getSystem()
            .getIdentifier("action_bar_title", "id", "android");
    if (titleId > 0) {
        TextView title = (TextView) findViewById(titleId);
        title.setSingleLine(false);
        title.setMaxLines(2);
        //          title.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
    }

I am using it on my application and I am happy with it :)

Credit to this post

Upvotes: 1

user1549672
user1549672

Reputation: 486

I know this question was posted a long time ago, but I recently ran into this issue and it caused a hell of a headache. I'm working on a Galaxy Note 10.1 and my title was "BidItems" and I only had the action overflow item visible, yet sometimes, "BidItems" became "BidIt..." which is just ridiculous. After much testing, I found that the reason for this truncating behavior in my application is that I was calling a method using ((MyActivity) getApplication()).setTitle(); method from one of my fragments. The setTitle() method in my Activity calls getActionBar().setTitle(). As soon as I called this method, my title was truncated for no reason. But simply calling setTitle() from my activity worked just fine.

I really hope this saves people the headache it caused me.

Upvotes: 5

Philipp E.
Philipp E.

Reputation: 3294

I solved this problem using a custom title as described in this post.

This is the code I use to change the title when a tab changes

((TextView) actionBar.getCustomView().findViewById(R.id.title)).setText(someTitle); 

Note that this solution places the title to the right of the tabs in landscape mode when using actionbar tabs.

Upvotes: 2

Charlie S
Charlie S

Reputation: 4594

I dont think there is much you can do about this. Seems to be the action bar title has a fairly limited width and anything over that gets truncated.

I guess one way round it would be to display: "My reqs (20)" rather than "My requests (20)" but I appreciate that is not ideal.

Upvotes: 0

Related Questions