Android screen adaptation scheme

Screen adaptation scheme
Reprinted from: https://www.jianshu.com/p/1302ad5a4b04

Foreword
Although an article written last year [a very easy to use Android screen adaptation] included font size adaptation, but that article talked about adapting font sizes according to different screen sizes. Next I want to talk. There are several other scenarios in font size adaptation.

Scenario One
has such a requirement that a title text needs to be displayed on the interface, but the length of the title text is not fixed. It is required that all the text of the title be displayed. It cannot be displayed with ellipsis, and the width and height of the title is fixed. For example, the copy of the title is "This is the title, the name of the title is relatively long, and the product requirements are displayed without wrapping", as shown in the figure below, the first is the title that does not meet the requirements, and the second is the title that meets the requirements.

That is to say, the width and height of the TextView control need to be fixed, and then the font size is dynamically changed according to the length of the title's copy, which is the effect of the second title in the above figure. How is that achieved?

The previous approach is generally to measure the width of the TextView text and the width of the TextView control, dynamically change the font size of the TextView, it is troublesome and performance-consuming to write. But now you don't have to be so troublesome. Android 8.0 has added a new feature Autosizing TextViews, which is used to dynamically change the font size of the TextView. You only need to simply set the properties.

For example, the effect that meets the requirements in the above figure can be written like this:

xml way

<?xml version="1.0" encoding="utf-8"?>

<TextView
    android:layout_width="340dp"
    android:layout_height="50dp"
    android:background="@drawable/shape_bg_008577"
    android:gravity="center_vertical"
    android:maxLines="1"
    android:text="这是标题,该标题的名字比较长,产品要求不换行全部显示出来"
    android:textSize="18sp"
    android:autoSizeTextType="uniform"
    android:autoSizeMaxTextSize="18sp"
    android:autoSizeMinTextSize="10sp"
    android:autoSizeStepGranularity="1sp"/>
You can see that the TextView control has the following attributes:

autoSizeTextType: Set whether the TextView supports automatic font size change, none means not support, uniform means support.
autoSizeMinTextSize: The minimum font size, for example, set to 10sp, which means that the text can only be reduced to 10sp at most.
autoSizeMaxTextSize: The maximum font size, for example, set to 18sp, which means that the text can only be enlarged to 18sp.
autoSizeStepGranularity: The zoom granularity, that is, the value of each font size change. For example, if it is set to 1sp, it means that the value of each reduction or enlargement is 1sp.
The above is only valid for 8.0 devices. If you want to be compatible with devices below 8.0, you need to use AppCompatTextView instead of TextView, and the namespace of the above attributes needs to use the app namespace. as follows:

<?xml version="1.0" encoding="utf-8"?>

<android.support.v7.widget.AppCompatTextView
    android:layout_width="340dp"
    android:layout_height="50dp"
    android:background="@drawable/shape_bg_008577"
    android:gravity="center_vertical"
    android:maxLines="1"
    android:text="这是标题,该标题的名字比较长,产品要求不换行全部显示出来"
    android:textSize="18sp"
    app:autoSizeTextType="uniform"
    app:autoSizeMaxTextSize="18sp"
    app:autoSizeMinTextSize="10sp"
    app:autoSizeStepGranularity="1sp"/>
Certainly many people say, "Why can you not use AppCompatTextView to be compatible with devices below 8.0 when you write it yourself?" That is because the Activity corresponding to your current xml file inherits AppCompatActivity. If it inherits Activity or FragmentActivity, compatibility cannot be achieved. . In fact, the official document Autosizing TextViews did not make it clear, which caused many people to misunderstand. You can verify it yourself.

Dynamic coding method
Use the setAutoSizeTextTypeWithDefaults() method of TextViewCompat to set whether the TextView supports automatic font size change, and the setAutoSizeTextTypeUniformWithConfiguration() method to set the minimum font size, maximum font size and zoom granularity. As follows:

    TextView tvText = findViewById(R.id.tv_text);
    TextViewCompat.setAutoSizeTextTypeWithDefaults(tvText,TextViewCompat.AUTO_SIZE_TEXT_TYPE_UNIFORM);
    TextViewCompat.setAutoSizeTextTypeUniformWithConfiguration(tvText,10,18,1, TypedValue.COMPLEX_UNIT_SP);

The
parameter 1 of setAutoSizeTextTypeWithDefaults() is the TextView that needs to dynamically change the font size, and the parameter 2 is whether to support the type of automatically changing the font size. AUTO_SIZE_TEXT_TYPE_UNIFORM means support, AUTO_SIZE_TEXT_TYPE_NONE means not.
setAutoSizeTextTypeUniformWithConfiguration()
parameter 1 is the TextView that needs to dynamically change the font size, parameters 2, 3, and 4 are the minimum font size, maximum font size, and scaling granularity respectively, and parameter 5 is the unit of parameters 2, 3, and 4, such as sp, dp, px etc.
Similarly, if you want to be compatible with devices below 8.0, either use AppCompatTextView instead of TextView in xml, or the current Activity inherits AppCompatActivity.

Summary
Autosizing TextViews is a new feature of Android 8.0, which can be used to dynamically change the font size of TextView. If you want to be compatible with devices below 8.0, you need to meet one of the following two conditions.

Use AppCompatTextView instead of TextView in xml, and use app namespace for the namespace of the above attributes.
The current Activity inherits AppCompatActivity instead of Activity or FragmentActivity.
For more properties of Autosizing TextViews, please refer to Autosizing TextViews


Many people must have encountered this situation in the second scene . The test threw a picture over, and then said how to run the test machine after the content below is blocked (the right picture below, the left picture is normal), did you say you did it? Is the screen suitable? Then you take a look at the tested mobile phone, and the extra large font is actually selected in the settings.

Hmm... After looking at it this way, you basically know what the problem is. The reason is that you have written down the height of the control in the xml file, and the font unit of the TextView is sp. In this case, change the font size in the phone settings, then the font size in the interface will change with the system.

So how should we solve this problem? At this time, we can observe the practice of WeChat. After research, we found that the font of WeChat will not change with the change of the system font size, and WeChat itself has the function of changing the font size. After changing the font size in WeChat, not only the font size changes, but also the width and height of the control. So it can be guessed that the font adaptation of WeChat is implemented in the following way:

The font size does not change
with the system There are two ways to realize the font size does not change with the system:


  1. The font unit of TextView in xml mode does not use sp, but dp. Because the font size of the sp unit will change with the change of the system font size, but the dp unit will not.

  2. Dynamic encoding method
    Whether the font size changes with the system can be controlled by the fontScale variable of the Configuration class. The fontScale variable defaults to 1, which means that the font size does not change with the system font size. Then we only need to ensure that the fontScale is always 1. The specific code is as follows, generally placed in the base class BaseActivity of Activity.

    @Override
    public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); if (newConfig.fontScale != 1) {//fontScale is not 1, and needs to be forced to 1 getResources(); } }




    @Override
    public Resources getResources() { Resources resources = super.getResources(); if (resources.getConfiguration().fontScale != 1) {//fontScale is not 1, and needs to be forced to 1 Configuration newConfig = new Configuration() ; newConfig.setToDefaults();//Set to the default value, that is, fontScale is 1 resources.updateConfiguration(newConfig, resources.getDisplayMetrics()); } return resources; } Although both methods can solve the problem of scenario two, but generally All use dynamic encoding for the following reasons:








If the application needs to add a function similar to WeChat that can change the font size, if the dp unit is used in the xml, then this function will not be realized!
If you need to change the font size to change with the system font size, you only need to delete the code.
The official recommendation is to use sp as the font unit.
The width and height of the control should not be fixed as much as possible. The
reason is that if the application needs to add a function like WeChat to change the font size, if the width and height of the control are fixed, increasing the font size will cause the control to not be displayed, which is not the effect we need.


There is a situation in scene three . When you write a TextView control according to the design drawing, the width and height use wrap_content, and no padding is set, but the TextView running on the mobile phone accounts for a ratio of width to height. The design drawing should be large. As shown in the figure below, there are a lot of white space around the font.

This is because the TextView itself contains inner margins, so is there any attribute of TextView to remove the inner margins? The answer is yes, the attribute is includeFontPadding, set to false means that the font padding is not included, the specific code is as follows:

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary"
    android:text="Hello"
    android:textSize="50sp"
    android:includeFontPadding="false"/>

The running effect is the second "Hello" in the figure below (the first "Hello" is a normal TextView). It seems to be ok, but if you look carefully, you still have a little inner margin.

General applications may not care about the inner margin, but if you are doing TV applications, the requirements are more stringent, because the TV interface generally does not support scrolling up, down, left, and right. If the content on the design drawing just fills the screen, then These inner margins will lead to incomplete display of individual controls. So in this case, it must be solved. Since TextView's own properties cannot be solved, it can only be customized. The specific code is as follows:

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Rect;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;

public class NoPaddingTextView extends AppCompatTextView { private Paint mPaint = getPaint(); private Rect mBounds = new Rect(); private Boolean mRemoveFontPadding = false;//Whether to remove the font inner margin, true: remove false: not remove


public NoPaddingTextView(Context context) {
    super(context);
}

public NoPaddingTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    initAttributes(context, attrs);
}

public NoPaddingTextView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    initAttributes(context, attrs);
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
    if (mRemoveFontPadding) {
        calculateTextParams();
        setMeasuredDimension(mBounds.right - mBounds.left, -mBounds.top + mBounds.bottom);
    }
}

protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
}

protected void onDraw(Canvas canvas) {
    drawText(canvas);
}

/**
 * 初始化属性
 */
private void initAttributes(Context context, AttributeSet attrs) {
    TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.NoPaddingTextView);
    mRemoveFontPadding = typedArray.getBoolean(R.styleable.NoPaddingTextView_removeDefaultPadding, false);
    typedArray.recycle();
}

/**
 * 计算文本参数
 */
private String calculateTextParams() {
    String text = getText().toString();
    int textLength = text.length();
    mPaint.getTextBounds(text, 0, textLength, mBounds);
    if (textLength == 0) {
        mBounds.right = mBounds.left;
    }
    return text;
}

/**
 * 绘制文本
 */
private void drawText(Canvas canvas) {
    String text = calculateTextParams();
    int left = mBounds.left;
    int bottom = mBounds.bottom;
    mBounds.offset(-mBounds.left, -mBounds.top);
    mPaint.setAntiAlias(true);
    mPaint.setColor(getCurrentTextColor());
    canvas.drawText(text, (float) (-left), (float) (mBounds.bottom - bottom), mPaint);
}

}
Define the attributes required by NoPaddingTextView in the attr.xml file, as follows:

<?xml version="1.0" encoding="utf-8"?>
<declare-styleable name="NoPaddingTextView">
    <attr name="removeDefaultPadding" format="boolean"/>
</declare-styleable>
Used in the layout file, as follows: <?xml version="1.0" encoding="utf-8"?>

<com.wildma.fontadaptation.NoPaddingTextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:background="@color/colorPrimary"
    android:text="Hello"
    android:textSize="50sp"
    app:removeDefaultPadding="true"/>
The running effect is the third "Hello" in the figure below (the first is a normal TextView, and the second is a TextView with the includeFontPadding attribute), a perfect solution!

OK! The three most commonly used scenarios in font size adaptation are covered, if there are other scenarios, please add them~

Project address: FontAdaptation

Author: wildma
link: https://www.jianshu.com/p/2fdc97ae74a8
Source: Jane books
are copyrighted by the author. For commercial reprints, please contact the author for authorization, and for non-commercial reprints, please indicate the source.

Guess you like

Origin blog.csdn.net/qq_41915623/article/details/102776517