Эксперименты с новыми API Android 12: Render Effect

Смотрим на эффект размытия
3 минуты3057

В Android 12 довольно много нововведений, касающихся разработки. Одно из самых интересных — это Render Effect, с его помощью можно довольно просто делать эффект размытия фотографии или заднего фона, цветовые палитры и другие графические эффекты. Теперь добиться размытия заднего фона можно всего лишь одной строчкой API:

view.setRenderEffect(RenderEffect.createBlurEffect(radiusX, radiusY, SHADER_TILE_MODE))

Это касается не только фотографий, размыть можно любой элемент на экране. Таким образом, например, можно размывать экран при отображении диалогового окна, или скрывать изображение или элементы дизайна до определенного времени или действия пользователя.

Новый API, к сожалению, будет поддерживаться не всеми устройствами, а только теми, которым будет хватать вычислительных мощностей. Все эти дизайнерские изменения всё больше сближают визуальные стили двух мобильных операционных систем: iOS и Android. 

Давайте посмотрим, какие возможности есть у разработчика и что можно с этим сделать. Для наших экспериментов нам понадобится Android Studio, которую можно скачать бесплатно с официального сайта, а также эмулятор со свежей операционной системой на борту. Для этого установим новый эмулятор с API 31, но для начала создадим новый проект через File -> New Project. В меню выберем Empty Activity:

На следующем экране дадим название проекту и нажмём кнопку Finish:

Когда проект загрузится, перейдём в файл Gradle и убедимся, что указан верный SDK:

Нам нужны compileSdkVersion и targerSdkVersion именно 31 — это версия новой ОС. Если стоит версия ниже — исправьте на 31 и синхронизируйте проект:

Убедимся, что в Манифесте нашего приложения указан атрибут exported, иначе приложение просто не запустится. Это касается всех приложений, которые должны устанавливаться на телефоны с новой операционной системой:

<activity
       android:name=".MainActivity"
       android:exported="true">
   <intent-filter>
       <action android:name="android.intent.action.MAIN" />
 
       <category android:name="android.intent.category.LAUNCHER" />
   </intent-filter>
</activity>

 

Пришло время скачать и запустить эмулятор с новейшей операционной системой на борту. Если у вас эмулятор ещё не стоит, откройте Device Manager (вкладка Tools -> AVD Manager) и нажмите в появившемся окне кнопку Create Virtual Device:

Выберите любой смартфон по вкусу:

Далее во вкладке x86 Images выберите API 31 в качестве операционной системы эмулятора:

И, наконец, дайте название устройству. Finish:

Когда эмулятор скачается, его можно открыть и запустить наше приложение. Это будет пустой экран с надписью по центру:

Давайте увеличим шрифт и попробуем размыть надпись с помощью нового API Render Effect. 

Для начала добавим два новых атрибута в макет activity_main.xml: Id — чтобы найти наш макет в коде и textSize — для увеличения размера шрифта:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
   >
 
   <TextView
       android:id="@+id/my_text"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Hello World!"
       android:textSize="42sp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       />
 
</androidx.constraintlayout.widget.ConstraintLayout>

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

Отлично, основа для работы у нас есть. Теперь перейдем в класс MainActivity. Пока он выглядит так:

package com.example.myapplication
 
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
 
class MainActivity : AppCompatActivity() {
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
   }
}

Добавим в него следующий код:

package com.example.myapplication
 
import android.graphics.RenderEffect
import android.graphics.Shader
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.RequiresApi
 
class MainActivity : AppCompatActivity() {
 
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
           makeBlur()
       }
   }
 
   @RequiresApi(31)
   private fun makeBlur() {
       val blurEffect = RenderEffect.createBlurEffect(15f, 0f, Shader.TileMode.MIRROR)
       findViewById<TextView>(R.id.my_text).setRenderEffect(blurEffect)
   }
}

Мы написали метод makeBlur, который создает нужный нам эффект размытия blurEffect, находит надпись Hello World! через findViewById и устанавливает эффект:

Как вы видите, метод createBlurEffect принимает три аргумента:

  • сила размытия по горизонтали (чем выше число, тем сильнее размытие);
  • сила размытия по вертикали (чем выше число, тем сильнее размытие);
  • способ размытия по краям элемента.

Также мы добавили обязательную аннотацию @RequiresApi(31) над методом, потому что этот эффект работает только на смартфонах с операционной системой 12 и выше. Для этого же мы делаем проверку на версию ОС в методе onCreate:

if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
           makeBlur()
       }

Теперь давайте добавим какое-то изображение в проект и попробуем размыть его. Положим в папку res/drawable картинку:

Добавим её в наш макет:

<?xml version="1.0" encoding="utf-8"?><?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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"
   >
 
   <TextView
       android:id="@+id/my_text"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Hello World!"
       android:textSize="42sp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       />
 
   <ImageView
       android:id="@+id/image_view"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:src="@drawable/android"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/my_text"
       />
</androidx.constraintlayout.widget.ConstraintLayout>

Найдем её в коде и добавим эффект размытия:

package com.example.newapi
 
import android.graphics.RenderEffect
import android.graphics.Shader
import android.os.Bundle
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
 
class MainActivity : AppCompatActivity() {
 
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
           makeBlur()
           makeBlurImage()
       }
   }
 
   @RequiresApi(31)
   private fun makeBlurImage() {
       val blurEffect = RenderEffect.createBlurEffect(15f, 15f, Shader.TileMode.MIRROR)
       findViewById<ImageView>(R.id.image_view).setRenderEffect(blurEffect)
   }
 
   @RequiresApi(31)
   private fun makeBlur() {
       val blurEffect = RenderEffect.createBlurEffect(15f, 0f, Shader.TileMode.MIRROR)
       findViewById<TextView>(R.id.my_text).setRenderEffect(blurEffect)
   }
}

Запускаем:

Обратите внимание, что размытие по вертикали и горизонтали может быть разным по силе. Так можно достигать эффекта движения в определённую сторону.

Более того, Render Effect умеет работать не только с отдельными view, но и с иерархией view, то есть этот эффект можно применять на ViewGroup, часть экрана или целый экран: мы можем размыть весь экран, не размывая каждый элемент в отдельности. Для этого немного поменяем макет — добавим id корневому элементу, чтобы найти его в коде:

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.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:id="@+id/root_layout"
   android:layout_width="match_parent"
   android:layout_height="match_parent"
   tools:context=".MainActivity"
   >
 
   <TextView
       android:id="@+id/my_text"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:text="Hello World!"
       android:textSize="42sp"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toTopOf="parent"
       />
 
   <ImageView
       android:id="@+id/image_view"
       android:layout_width="wrap_content"
       android:layout_height="wrap_content"
       android:src="@drawable/android"
       app:layout_constraintBottom_toBottomOf="parent"
       app:layout_constraintLeft_toLeftOf="parent"
       app:layout_constraintRight_toRightOf="parent"
       app:layout_constraintTop_toBottomOf="@+id/my_text"
       />
</androidx.constraintlayout.widget.ConstraintLayout>

Теперь просто найдем контейнер в коде и добавим эффект размытия на весь экран:

package com.example.newapi
 
import android.graphics.RenderEffect
import android.graphics.Shader
import android.os.Bundle
import android.widget.ImageView
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
 
class MainActivity : AppCompatActivity() {
 
   override fun onCreate(savedInstanceState: Bundle?) {
       super.onCreate(savedInstanceState)
       setContentView(R.layout.activity_main)
       if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.S) {
           //makeBlur()
           //makeBlurImage()
           blurAll()
       }
   }
 
   @RequiresApi(31)
   private fun blurAll() {
       val blurEffect = RenderEffect.createBlurEffect(15f, 15f, Shader.TileMode.MIRROR)
       findViewById<ConstraintLayout>(R.id.root_layout).setRenderEffect(blurEffect)
   }
 
   @RequiresApi(31)
   private fun makeBlurImage() {
       val blurEffect = RenderEffect.createBlurEffect(15f, 15f, Shader.TileMode.MIRROR)
       findViewById<ImageView>(R.id.image_view).setRenderEffect(blurEffect)
   }
 
   @RequiresApi(31)
   private fun makeBlur() {
       val blurEffect = RenderEffect.createBlurEffect(15f, 0f, Shader.TileMode.MIRROR)
       findViewById<TextView>(R.id.my_text).setRenderEffect(blurEffect)
   }
}

Обратите внимание, что мы закомментировали предыдущие методы, то есть размытие действительно применяется ко всем элементам на экране. Размытие можно регулировать по интенсивности, сделаем экран менее размытым:

@RequiresApi(31)
private fun blurAll() {
   val blurEffect = RenderEffect.createBlurEffect(5f, 5f, Shader.TileMode.MIRROR)
   findViewById<ConstraintLayout>(R.id.root_layout).setRenderEffect(blurEffect)
}

В итоге

Начиная с API 31 у нас появляется довольно мощный инструмент в лице Render Effect, который позволяет улучшать функционал и внешний вид наших приложений буквально парой строк. 

Код проекта можно скачать тут.

Читайте больше полезных статей для начинающих Android-разработчиков:

А если затянет — приходите на факультет Android-разработки. В время учебы вы разработаете Android-приложение и выложите его в Google Play, даже если никогда не программировали. А также своите языки Java и Kotlin, командную разработку, Material Design и принципы тестирования.

программированиеandroidapiтуториал
Нашли ошибку в тексте? Напишите нам.
Спасибо,
что читаете наш блог!