Придаём тексту эффект внутренней тени

Виджет TextView в Android можно кастомизировать самыми разными образами: менять шрифт, размер шрифта, задавать стили и многое другое. В этой статье мы рассмотрим, как можно сделать текст красивее, добавив ему тень.

В данном случае нам нужно добиться эффекта внутренней тени, то есть тени, которая находится внутри текста, а не за его пределами. API Android не даёт так просто это сделать: к сожалению, единственным эффектом, который может быть применен без работы с объектом Paint у TextView, это обычная внешняя тень. Однако можно применить хитрость и использовать для достижения нужного эффекта класс EmbossMaskFilter, предоставляемый Android SDK.

Класс EmbossMaskFilter можно применить к любому объекту Paint, чтобы создать «тиснение» контента, будь то текст или любой другой объект, который можно нарисовать на Canvas. Как следует из названия, эффект тиснения предназначен для того, чтобы содержимое выглядело так, будто оно слегка поднято, выдавлено. Это достигается путем размещения бликов на одной стороне объекта и теней или затемненных участков на противоположной. Хотя это не совсем тот внешний вид, который нам нужен, оказывается, что параметры класса EmbossMaskFilter можно настроить таким образом, что мы можем сбавить блики почти полностью и оставить только внутреннюю тень.

Посмотрим на реализацию работы с классом. Для этого воспользуемся приложением для тестирования, исходный код которого доступен на GitHub.

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

public EmbossMaskFilter(float[] direction, float ambient, float specular, float blurRadius) {
  throw new RuntimeException("Stub!");
}

Здесь есть несколько параметров для настройки:

  • direction — положение источника света относительно объекта. Координата X обозначает положение по горизонтали, Y — положение по вертикали, Z — положение над экраном.
  • ambient — яркость источника света. Его значение меняется от 0.0 до 1.0. Его роль заключается в том, чтобы осветлить или затемнить участки тени.
  • specular — отражение контента. Этот параметр определяет отражение света, т.е. эффект «повышенной освещенности» или блеск материала объекта. Высокие значения этого параметра создают блики, которые видны только при прямых углах обзора.
  • blurRadius — радиус размытия. Этот параметр определяет резкость края у бликов и теней.

На активности приложения есть 6 слайдеров, каждый из которых регулируется соответствующий параметр. Слушатель OnSeekBarChangeListener отлавливает изменение значений слайдеров и в методе onProgressChanged() выводит новые значения на экран и обновляет маску.

@Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
  switch (seekBar.getId()) {
    case R.id.x:
      tv_x.setText(String.valueOf(getProgress(progress)) + "|");
      break;
    case R.id.y:
      tv_y.setText(String.valueOf(getProgress(progress)) + "|");
      break;
    case R.id.z:
      tv_z.setText(String.valueOf(getProgress(progress)) + "|");
      break;
    case R.id.ambient:
      tv_ambient.setText(String.valueOf(getAmbient(progress)) + "|");
      break;
    case R.id.specular:
      tv_specular.setText(String.valueOf((float) progress) + "|");
      break;
    case R.id.blur:
      tv_blur.setText(String.valueOf(getProgress(progress)));
      break;
  }
  applyFilter(textView, new float[] {
          getProgress(x.getProgress()), getProgress(y.getProgress()), getProgress(z.getProgress())
      }, getAmbient(ambient.getProgress()), (float) specular.getProgress(),
      getProgress(blur.getProgress()));
}

private float getProgress(int i) {
  return ((float) i - 10) / 10;
}

private float getAmbient(int i) {
  return ((float) i) / 10;
}

Метод applyFilter() принимает в качестве параметров значения всех слайдеров, чтобы создать экземпляр EmbossMaskFilter и применить новые значения к TextView. Поскольку TextView предоставляет метод доступа к своему объекту Paint, фильтр можно применять непосредственно к нему.

private void applyFilter(TextView textView, float[] direction, float ambient, float specular,
    float blurRadius) {
  textView.setLayerType(View.LAYER_TYPE_SOFTWARE, null);
  EmbossMaskFilter filter = new EmbossMaskFilter(direction, ambient, specular, blurRadius);
  textView.getPaint().setMaskFilter(filter);
}

В зависимости от того, устанавливаете ли вы фильтр на этапе разметки или рисования, вы можете вызвать метод invalidate() у TextView, чтобы обеспечить его перерисовку с помощью вашего нового фильтра.

Понимая теперь, какой параметр за что отвечает, можно попробовать добиться нужного эффекта. Нужно, чтобы источник света был очень близок к поверхности, так как это минимизирует отражение бликов, для этого координату Z лучше всего выставить на 0.5. Две других координаты можно скомбинировать, чтобы определить, где должна находиться тень. Внутренняя тень должна опускаться сверху, поэтому зададим координаты X и Y значения 0.0 и -1.0 соответственно.

Что касается остальных параметров:

  • Хорошим параметром для яркости блика будет 0.6.
  • Отражение должно быть больше 15.0, чтобы устранить блики в эффекте тиснения.
  • Радиус размытия должен быть от 0.5 до 5.0. В нашем случае укажем 1.3.

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

С помощью класса EmbossMaskFilter мы смогли настроить внутреннюю тень для текста. Если вы хотите добиться какого-либо другого эффекта, вы можете смело подбирать параметры, пока не добьётесь нужного результата.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *