Pull to refresh

Динамическое изменение размера шрифта во всем приложении на Android с помощью Configuration.fontScale

Reading time5 min
Views17K
Доброго времени суток, уважаемые читатели.

Захотелось мне немного поделиться своими мыслями по поводу android разработки. Возникла у меня задача сделать настройку размера шрифта в приложении, чтобы каждый пользователь сам мог подобрать под себя размер.

Изменение размера шрифта решил делать во всем приложении. Но использовать метод setTextSize в каждом activity не вариант, т.к. в один прекрасный момент появится новое поле и придется на нем вновь прописывать нужный размер. Поэтому, решением было сделать автоматическое изменение во всех местах.

Когда писал данную статью, суть заключалась в следующем: в настройках приложения хранится коэффициент увеличения шрифта. Этот коэффициент применяется на реальный размер шрифта в собственном классе, наследованным от TextView. Но Ganster41 подсказал более хорошее решение. Поэтому сперва будет описание первоначального решение, а в конце будет реализация с помощью Configuration.fontScale.

Первоначальная реализация


Начнем с того, что в нужном месте приложения добавим функционал выбора и сохранения коэффициента:
Код
float new_coef = 1.3f;
//активируем параметры
SharedPreferences settings = getSharedPreferences("MyAppSett", MODE_PRIVATE); 
Editor value_add = settings.edit();
//заносим новый коэффициент увеличения шрифта
value_add.putFloat("size_coef", new_coef); 
value_add.commit();


Естественно new_coef нужно заполнять динамически. Например при выборе значения на бегунке.

Далее этот коэффициент необходимо считать в нужном месте и с помощью него изменять размер шрифта. Каждый раз считывать его из настроек приложения, при активации того или иного TextView, не совсем хорошее решение. Поэтому коэффициент будем считывать один раз при активации приложения и хранить в глобальной переменной. Для этого добавим новый класс приложения:
Код
package com.Example.Test;

import android.app.Application;
import android.content.SharedPreferences;

public final class MyApp extends Application {
  private float size_coef; //коэффициент увеличения шрифта

  //получить коэффициент размера шрифта
  public float getSizeCoef() {
    return size_coef;
  }

  //инициализация приложения
  @Override
  public void onCreate() {
    super.onCreate();
    //считываем коэффициент увеличения шрифта
    SharedPreferences settings = getSharedPreferences("MyAppSett", MODE_PRIVATE);
    size_coef= settings.getFloat("size_coef", 1f);
  }
}


Далее в файле манифеста укажем новый наш класс:
Код
<application
  android:name=".MyApp"
  android:icon="@drawable/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme" >


После этого необходимо создать еще один класс, но уже на основе TextView:
Код
package com.Example.Test;

import android.app.Activity;
import android.content.Context;
import android.support.v7.widget.AppCompatTextView;
import android.util.AttributeSet;
import android.util.TypedValue;

//класс приложения с глобальной переменной
public final class MyTextView extends AppCompatTextView {

  //инициализация касса
  public MyTextView(Context context, AttributeSet attrs) {
    super(context, attrs);
    InitSize(context);
  }

  //установка нужного размера
  private void InitSize(Context context) {
    //получаем коэффициент увеличения шрифта
    Activity activity = (Activity) context;
    float koef = ((MyApp) activity.getApplication()).getSizeCoef();

    float cur_size = getTextSize(); //текущий размер шрифта в пикселях
    //устанавливаем новый размер шрифта в пикселях
    setTextSize(TypedValue.COMPLEX_UNIT_PX, cur_size * koef);
  }
}


Обратите внимание на то, что у метода setTextSize первый параметр по умолчанию = TypedValue.COMPLEX_UNIT_SP. Что означает установку размера шрифта в sp единицах измерения. В нашем же случае используется TypedValue.COMPLEX_UNIT_PX. Этот тип необходимо указывать, чтобы задать размер шрифта в пикселях, т.к. getTextSize возвращает текущий размер в пикселях.

В принципе все подготовительные классы готовы. Осталось в нужном месте разметки вместо TextView указать свой собственный класс MyTextView:
Код
<com.Example.Test.MyTextView
  android:id="@+id/TextValue"
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:textColor="@color/TextColor"
  android:textSize="@dimen/TextSize" />


В итоге при открытии activity у данного текста будет изменен размер шрифта на тот, что выбрал пользователь. С EditText все делается аналогично.

Обновленная реализация


Для себя я решил использовать коэффициент размера шрифта от 0.7f до 1.45f с интервалом 0.15f. Т.е. это 6 шагов. Для выбора конкретного значения использую SeekBar.
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
  xmlns:android="http://schemas.android.com/apk/res/android"
  android:layout_width="fill_parent"
  android:layout_height="fill_parent">
  <SeekBar
    android:id="@+id/seekBar"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:progress="0"
    android:max="5" />
</LinearLayout>

В нужном месте приложения (в методе onCreate) реализуем обработку выбранного значения на SeekBar:
final float start_value = 0.7f; //начальное значение размера шрифта
final float step = 0.15f; //шаг увеличения коэффициента
int size_coef; //выбранный коэффициент
...
SeekBar seekBar = (SeekBar) findViewById(R.id.seekBar);
...
if (seekBar != null) {
  seekBar.setProgress(select_coef);
  seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
    @Override
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
      size_coef = progress; //выбранный коэффициент
    }

    @Override
    public void onStartTrackingTouch(SeekBar seekBar) {
    }

    @Override
    public void onStopTrackingTouch(SeekBar seekBar) {
    }
  });
}

Обработку выбора значения на бегунке сделали. Теперь необходимо сохранить выбранный результат и сразу же изменить размер шрифта (например по кнопке применять):
//активируем параметры
SharedPreferences settings = getSharedPreferences("MyAppSett", MODE_PRIVATE);
SharedPreferences.Editor value_add = settings.edit();
//заносим новый коэффициент увеличения шрифта
value_add.putInt("size_coef", size_coef); 
value_add.apply();

//устанавливаем размер шрифта в приложении
Resources res = getResources();
float  сoef = start_value + size_coef * step; //новый коэффициент увеличения шрифта
Configuration configuration = new Configuration(res.getConfiguration());
configuration.fontScale = сoef;
res.updateConfiguration(configuration, res.getDisplayMetrics());

Теперь при смене activity будет новый размер шрифта во всех местах приложения. Но на данный момент этот размер будет только до перезагрузки приложения. При открытии приложения произойдет установка размера шрифта того, что установлен в настройках android на устройстве. И соответственно, чтобы в приложении был нужным нам размер, необходимо его переназначить. Для этого мы и сохраняем коэффициент в параметры приложения:
package com.Example.Test;

import android.app.Application;
import android.content.SharedPreferences;

public final class MyApp extends Application {
  
  //инициализация приложения
  @Override
  public void onCreate() {
    super.onCreate();    
    int size_coef;
    final float start_value = 0.7f; //начальное значение размера шрифта
    final float step = 0.15f; //шаг увеличения коэффициента
    Resources res = getResources();
    SharedPreferences settings = getSharedPreferences("MyAppSett", MODE_PRIVATE);
    try {
      //считываем коэффициент размера шрифта
      size_coef = settings.getInt("size_coef", 2);
    }
    catch (Exception e) {
      size_coef = 2; //коэффициент по умолчанию
    }

    //новый коэффициент увеличения шрифта
    float new_value = start_value + size_coef * step;
    //устанавливаем размер шрифта в приложении
    Configuration configuration = new Configuration(res.getConfiguration());
    configuration.fontScale = new_value;
    res.updateConfiguration(configuration, res.getDisplayMetrics());
  }
}

По умолчанию используется значение 2, т.е. в моей формуле это коэффициент увеличения шрифта равный 1 (0,7 + 0,15 * 2 = 1). Данный класс необходимо прописать в манифесте:
<application
  android:name=".MyApp"
  android:icon="@drawable/ic_launcher"
  android:label="@string/app_name"
  android:theme="@style/AppTheme" >

В итоге при открытии приложения будет изменен размер шрифта во всех местах. Свою реализацию переделал с первого способа на второй, что позволило не добавлять собственные классы для TextView, EditText и т.п.

Всем спасибо за внимание.
Tags:
Hubs:
+7
Comments22

Articles