0

I am getting different text size for same screen size, different dpi devices. I want the text to be the exact same size on all devices (like Inshorts app).

enter image description here

As can be seen in these screenshots text size of xxhdpi is slightly bigger than 560 dpi device.

My xml 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"
    tools:context=".MainActivity"
    android:layout_margin="30dp">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s, when an unknown printer took a galley of type and scrambled it to make a type specimen book. It has survived not only five centuries, but also the leap into electronic typesetting, remaining essentially unchanged. It was popularised in the 1960s with the release of Letraset sheets containing Lorem Ipsum passages, and more recently with desktop publishing software like Aldus PageMaker including versions of Lorem Ipsum."
        android:textSize="14sp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

</android.support.constraint.ConstraintLayout>

I have also tried using dp in place of sp without any success. Please help.

Sid
  • 11
  • 3
  • Convert the values in multiple parts,,Like..small,medium,normal,large and extra large..and values inside those files. – Parth Lotia Mar 08 '19 at 10:02
  • use dp instead of sp for text size – underoid Mar 08 '19 at 10:04
  • @ParthLotia I have tried that also but the problem is my both device comes under the large category so there is no change. – Sid Mar 08 '19 at 10:07
  • @underoid As mentioned I have tried dp also without any success. – Sid Mar 08 '19 at 10:07
  • the problem is your textView width is not constant (wrap_content), if you make width of your textView constant in dp text size in dp should work. – underoid Mar 08 '19 at 10:14

5 Answers5

0

If you want your text sizes to be density-independent, you should use dp instead of sp.

With this way, your text sizes also won't be affected by user textsize preferences in phone settings.

Ali Korkmaz
  • 324
  • 2
  • 4
  • As mentioned in my question I have tried dp it but has no effect. Text sizes were different after using dp like in screenshots. – Sid Mar 08 '19 at 10:13
0

Add the follow class (AutoResizeTextView) into your project and then add the follow code into your xml at value android:maxLines="1" put whatever you want, so the auto calculation change the font size

<com.example.youpackage.AutoResizeTextView
                android:id="@+id/textViewTitle"
                android:layout_width="match_parent"
                android:layout_height="0dp"
                android:layout_weight="0.40"
                android:text="@string/title"
                android:textColor="#FFFFFF"
                android:maxLines="1"
                android:paddingBottom="5dp"
                android:layout_marginBottom="5dp"
                android:gravity="center_vertical|center_horizontal"
                android:textSize="50sp" />

here is the AutoResizeTextView class

import android.annotation.TargetApi;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.RectF;
import android.os.Build;
import android.text.Layout.Alignment;
import android.text.StaticLayout;
import android.text.TextPaint;
import android.text.method.TransformationMethod;
import android.util.AttributeSet;
import android.util.SparseIntArray;
import android.util.TypedValue;
import android.widget.TextView;

/**
 * http://stackoverflow.com/questions/16017165/auto-fit-textview-for-android/21851239
 */
public class AutoResizeTextView extends TextView {

    public AutoResizeTextView(Context context) {
        super(context);
        initialize();
    }

    public AutoResizeTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
        initialize();
    }

    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        initialize();
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public AutoResizeTextView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        initialize();
    }

    private interface SizeTester {
        /**
         *
         * @param suggestedSize
         *            Size of text to be tested
         * @param availableSpace
         *            available space in which text must fit
         * @return an integer < 0 if after applying {@code suggestedSize} to
         *         text, it takes less space than {@code availableSpace}, > 0
         *         otherwise
         */
        public int onTestSize(int suggestedSize, RectF availableSpace);
    }

    private RectF mTextRect = new RectF();

    private RectF mAvailableSpaceRect;

    private SparseIntArray mTextCachedSizes;

    private TextPaint mPaint;

    private float mMaxTextSize;

    private float mSpacingMult = 1.0f;

    private float mSpacingAdd = 0.0f;

    private float mMinTextSize = 10;

    private int mWidthLimit;

    private static final int NO_LINE_LIMIT = -1;
    private int mMaxLines;

    private boolean mEnableSizeCache = true;
    private boolean mInitializedDimens;

    private void initialize() {
        mPaint = new TextPaint(getPaint());
        mMaxTextSize = getTextSize();
        mAvailableSpaceRect = new RectF();
        mTextCachedSizes = new SparseIntArray();
        if (mMaxLines == 0) {
            // no value was assigned during construction
            mMaxLines = NO_LINE_LIMIT;
        }
    }

    @Override
    public void setTextSize(float size) {
        mMaxTextSize = size;
        mTextCachedSizes.clear();
        adjustTextSize();
    }

    @Override
    public void setMaxLines(int maxlines) {
        super.setMaxLines(maxlines);
        mMaxLines = maxlines;
        adjustTextSize();
    }

    public int getMaxLines() {
        return mMaxLines;
    }

    @Override
    public void setSingleLine() {
        super.setSingleLine();
        mMaxLines = 1;
        adjustTextSize();
    }

    @Override
    public void setSingleLine(boolean singleLine) {
        super.setSingleLine(singleLine);
        if (singleLine) {
            mMaxLines = 1;
        } else {
            mMaxLines = NO_LINE_LIMIT;
        }
        adjustTextSize();
    }

    @Override
    public void setLines(int lines) {
        super.setLines(lines);
        mMaxLines = lines;
        adjustTextSize();
    }

    @Override
    public void setTextSize(int unit, float size) {
        Context c = getContext();
        Resources r;

        if (c == null)
            r = Resources.getSystem();
        else
            r = c.getResources();
        mMaxTextSize = TypedValue.applyDimension(unit, size,
                r.getDisplayMetrics());
        mTextCachedSizes.clear();
        adjustTextSize();
    }

    @Override
    public void setLineSpacing(float add, float mult) {
        super.setLineSpacing(add, mult);
        mSpacingMult = mult;
        mSpacingAdd = add;
    }

    /**
     * Set the lower text size limit and invalidate the view
     *
     * @param minTextSize
     */
    public void setMinTextSize(float minTextSize) {
        mMinTextSize = minTextSize;
        adjustTextSize();
    }

    private void adjustTextSize() {
        if (!mInitializedDimens) {
            return;
        }
        int startSize = (int) mMinTextSize;
        int heightLimit = getMeasuredHeight() - getCompoundPaddingBottom()
                - getCompoundPaddingTop();
        mWidthLimit = getMeasuredWidth() - getCompoundPaddingLeft()
                - getCompoundPaddingRight();
        mAvailableSpaceRect.right = mWidthLimit;
        mAvailableSpaceRect.bottom = heightLimit;
        super.setTextSize(
                TypedValue.COMPLEX_UNIT_PX,
                efficientTextSizeSearch(startSize, (int) mMaxTextSize,
                        mSizeTester, mAvailableSpaceRect));
    }

    private final SizeTester mSizeTester = new SizeTester() {
        @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
        @Override
        public int onTestSize(int suggestedSize, RectF availableSPace) {
            mPaint.setTextSize(suggestedSize);
            String text = getTransformedText();
            boolean singleline = getMaxLines() == 1;
            if (singleline) {
                mTextRect.bottom = mPaint.getFontSpacing();
                mTextRect.right = mPaint.measureText(text);
            } else {
                StaticLayout layout = new StaticLayout(text, mPaint,
                        mWidthLimit, Alignment.ALIGN_NORMAL, mSpacingMult,
                        mSpacingAdd, true);
                // return early if we have more lines
                if (getMaxLines() != NO_LINE_LIMIT
                        && layout.getLineCount() > getMaxLines()) {
                    return 1;
                }
                mTextRect.bottom = layout.getHeight();
                int maxWidth = -1;
                for (int i = 0; i < layout.getLineCount(); i++) {
                    if (maxWidth < layout.getLineWidth(i)) {
                        maxWidth = (int) layout.getLineWidth(i);
                    }
                }
                mTextRect.right = maxWidth;
            }

            mTextRect.offsetTo(0, 0);
            if (availableSPace.contains(mTextRect)) {
                // may be too small, don't worry we will find the best match
                return -1;
            } else {
                // too big
                return 1;
            }
        }
    };

    /**
     * Enables or disables size caching, enabling it will improve performance
     * where you are animating a value inside TextView. This stores the font
     * size against getText().length() Be careful though while enabling it as 0
     * takes more space than 1 on some fonts and so on.
     *
     * @param enable
     *            enable font size caching
     */
    public void enableSizeCache(boolean enable) {
        mEnableSizeCache = enable;
        mTextCachedSizes.clear();
        adjustTextSize();
    }

    private int efficientTextSizeSearch(int start, int end,
                                        SizeTester sizeTester, RectF availableSpace) {
        if (!mEnableSizeCache) {
            return binarySearch(start, end, sizeTester, availableSpace);
        }
        int key = getText().toString().length();
        int size = mTextCachedSizes.get(key);
        if (size != 0) {
            return size;
        }
        size = binarySearch(start, end, sizeTester, availableSpace);
        mTextCachedSizes.put(key, size);
        return size;
    }

    private static int binarySearch(int start, int end, SizeTester sizeTester,
                                    RectF availableSpace) {
        int lastBest = start;
        int lo = start;
        int hi = end - 1;
        int mid;
        while (lo <= hi) {
            mid = (lo + hi) >>> 1;
            int midValCmp = sizeTester.onTestSize(mid, availableSpace);
            if (midValCmp < 0) {
                lastBest = lo;
                lo = mid + 1;
            } else if (midValCmp > 0) {
                hi = mid - 1;
                lastBest = hi;
            } else {
                return mid;
            }
        }
        // make sure to return last best
        // this is what should always be returned
        return lastBest;

    }

    @Override
    protected void onTextChanged(final CharSequence text, final int start,
                                 final int before, final int after) {
        super.onTextChanged(text, start, before, after);
        adjustTextSize();
    }

    @Override
    protected void onSizeChanged(int width, int height, int oldwidth,
                                 int oldheight) {
        mInitializedDimens = true;
        mTextCachedSizes.clear();
        super.onSizeChanged(width, height, oldwidth, oldheight);
        if (width != oldwidth || height != oldheight) {
            adjustTextSize();
        }
    }

    private String getTransformedText() {
        CharSequence text = getText();
        if (text != null) {
            TransformationMethod transformationMethod = getTransformationMethod();
            if (transformationMethod != null) {
                text = transformationMethod.getTransformation(text, this);
            }
        }
        return text == null ? null : text.toString();
    }
}
  • What's difference between https://developer.android.com/guide/topics/ui/look-and-feel/autosizing-textview and your `AutoResizeTextView`? – Natig Babayev Mar 08 '19 at 10:24
  • @NatigBabayev this class can used to any Android version. Not only Android 8+ as your link says – Christos Themelis Mar 08 '19 at 10:36
  • Link also states: "The Support Library 26.0 provides full support to the autosizing TextView feature on devices running Android versions prior to Android 8.0 (API level 26). The library provides support to Android 4.0 (API level 14) and higher. The android.support.v4.widget package contains the TextViewCompat class to access features in a backward-compatible fashion." So, basically, it works below Android 8 as well. – Natig Babayev Mar 08 '19 at 10:37
  • @ChristosThemelis But As mentioned, I need exact same size of text across all devices and not auto resizing according to available space. – Sid Mar 08 '19 at 11:32
  • @Sid I thought you wanted the same ratio. my mistake. Try to use "px" instead of "sp" – Christos Themelis Mar 08 '19 at 11:40
  • @ChristosThemelis using px doesn't help either. For higher density devices size becomes small and for lower, it becomes large in case of px. I also tried to use (px * getResources().getDisplayMetrics().density) without any success. – Sid Mar 13 '19 at 10:45
  • I will provide you a part of my project that does something like you want, just to know how – Christos Themelis Mar 13 '19 at 11:24
0

You need to use dimens to understand that please visit following links:

For further details visit Dimens

Also look answer of following question: How to use dimens.xml

Muhammad Saad Rafique
  • 3,158
  • 1
  • 13
  • 21
  • Thanks for the link. But from those link, I concluded that using dp should give me the exact same size of text and in case of sp text size may change according to selected font size from android device setting. And as I mentioned I have already used dp and sp, and getting different text size in different devices. Please correct me if I am wrong. – Sid Mar 08 '19 at 11:47
0

try different dimens

create file dimens.xml(sw-320dp-xhdpi)

<resources>
    <dimen name="textSize">16sp</dimen>
</resources>

create file dimens.xml(sw-320dp-xxhdpi)

 <resources>
         <dimen name="textSize">14sp</dimen>
  </resources>

adjust textSize as you required

Vasudev Vyas
  • 726
  • 1
  • 10
  • 28
0

Here is a part of my project that resize the text based on screen size. Is this what you want?

double height;
double width = parent.getMeasuredWidth();

boolean isLandscape;
if ( getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT ) {
    isLandscape = false;
    height = parent.getMeasuredHeight() / 3;
    width = width + (parent.getPaddingLeft() + parent.getPaddingRight());
} else {
    isLandscape = true;
    height = parent.getMeasuredHeight() / 2;
    width = parent.getMeasuredWidth() / 2;
    width = width + ((parent.getPaddingLeft() + parent.getPaddingRight()) / 2);
}       

height = height + (parent.getPaddingTop() + parent.getPaddingBottom());

view.setMinimumHeight((int) Math.round(height));

double h = height / 100;
double w = width / 100; 

if ( isLandscape ) {
    textViewService.setX((int) Math.round(w * 24));
    textViewService.setTextSize(TypedValue.COMPLEX_UNIT_PX, (int) Math.round(16 * h));
} else {
    textViewService.setX((int) Math.round(w * 23));
    textViewService.setTextSize(TypedValue.COMPLEX_UNIT_PX, (int) Math.round(18 * h));
}