Android: обработка размера текста для разных дип-устройств

Я получаю разный размер текста для одного и того же размера экрана, разных устройств с разрешением dpi. Я хочу, чтобы текст был одинакового размера на всех устройствах (например, в приложении Inshorts).

введите здесь описание изображения

Как видно на этих снимках экрана, размер текста xxhdpi немного больше, чем у устройства с разрешением 560 dpi.

Мой макет xml:

<?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>

Я также безуспешно пытался использовать dp вместо sp. Пожалуйста помоги.


person Sid    schedule 08.03.2019    source источник
comment
Преобразуйте значения в несколько частей, например... маленький, средний, нормальный, большой и очень большой... и значения внутри этих файлов.   -  person Parth Lotia    schedule 08.03.2019
comment
используйте dp вместо sp для размера текста   -  person underoid    schedule 08.03.2019
comment
@ParthLotia Я тоже пробовал это, но проблема в том, что оба моих устройства относятся к большой категории, поэтому изменений нет.   -  person Sid    schedule 08.03.2019
comment
@underoid Как уже упоминалось, я безуспешно пробовал dp.   -  person Sid    schedule 08.03.2019
comment
проблема в том, что ширина вашего textView не является постоянной (wrap_content), если вы сделаете ширину вашего textView постоянной в dp, размер текста в dp должен работать.   -  person underoid    schedule 08.03.2019


Ответы (5)


Если вы хотите, чтобы размеры текста не зависели от плотности, вам следует использовать dp вместо sp.

Таким образом, ваши размеры текста также не будут зависеть от пользовательских настроек размера текста в настройках телефона.

person Ali Korkmaz    schedule 08.03.2019
comment
Как уже упоминалось в моем вопросе, я пробовал dp, но безрезультатно. Размеры текста были другими после использования dp, как на скриншотах. - person Sid; 08.03.2019

Добавьте следующий класс (AutoResizeTextView) в свой проект, а затем добавьте следующий код в свой xml со значением android:maxLines="1", поместите все, что хотите, чтобы автоматический расчет изменил размер шрифта.

<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" />

вот класс AutoResizeTextView

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();
    }
}
person Christos Themelis    schedule 08.03.2019
comment
В чем разница между developer.android.com/guide/topics /ui/look-and-feel/ и ваш AutoResizeTextView? - person Natig Babayev; 08.03.2019
comment
@NatigBabayev этот класс можно использовать для любой версии Android. Не только Android 8+, как написано в вашей ссылке - person Christos Themelis; 08.03.2019
comment
Ссылка также гласит: Библиотека поддержки 26.0 обеспечивает полную поддержку функции автоматического изменения размера TextView на устройствах с версиями Android до Android 8.0 (уровень API 26). Библиотека обеспечивает поддержку Android 4.0 (уровень API 14) и выше. Пакет android.support.v4.widget содержит класс TextViewCompat для доступа к функциям с обратной совместимостью. Так что, в основном, он работает и ниже Android 8. - person Natig Babayev; 08.03.2019
comment
@ChristosThemelis Но, как уже упоминалось, мне нужен текст одинакового размера на всех устройствах, а не автоматическое изменение размера в соответствии с доступным пространством. - person Sid; 08.03.2019
comment
@ Сид, я думал, ты хочешь такое же соотношение. виноват. Попробуйте использовать px вместо sp - person Christos Themelis; 08.03.2019
comment
@ChristosThemelis с использованием px тоже не помогает. Для устройств с более высокой плотностью размер становится маленьким, а для более низких - большим в случае px. Я также безуспешно пытался использовать (px * getResources().getDisplayMetrics().density). - person Sid; 13.03.2019
comment
Я предоставлю вам часть моего проекта, которая делает то, что вы хотите, просто чтобы знать, как - person Christos Themelis; 13.03.2019

Вам нужно использовать размеры, чтобы понять, что, пожалуйста, посетите следующие ссылки:

Для получения дополнительной информации посетите Размеры

Также посмотрите ответ на следующий вопрос: Как использовать dimens.xml

person Muhammad Saad Rafique    schedule 08.03.2019
comment
Спасибо за ссылку. Но из этой ссылки я пришел к выводу, что использование dp должно дать мне точно такой же размер текста, а в случае sp размер текста может измениться в зависимости от выбранного размера шрифта в настройках устройства Android. И, как я уже упоминал, я уже использовал dp и sp и получал разный размер текста на разных устройствах. Пожалуйста, поправьте меня, если я ошибаюсь. - person Sid; 08.03.2019

попробуй разные размеры

создать файл dimens.xml(sw-320dp-xhdpi)

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

создать файл dimens.xml(sw-320dp-xxhdpi)

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

отрегулируйте textSize по своему усмотрению

person Vasudev Vyas    schedule 08.03.2019
comment
@Sid Вы пробовали это решение? - person Vasudev Vyas; 08.03.2019

Вот часть моего проекта, которая изменяет размер текста в зависимости от размера экрана. Это то, что вы хотите?

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));
}
person Christos Themelis    schedule 13.03.2019