В этом уроке:

- рассматриваем PathEffect-объекты

У класса PathEffect есть несколько наследников, которые позволяют влиять на рисуемые нами объекты. Рассмотрим на примерах их использование.

 

Создадим проект:

Project name: P1511_PathEffect
Build Target: Android 2.3.3
Application name: PathEffect
Package name: ru.startandroid.develop.p1511patheffect
Create Activity: MainActivity

 

CornerPathEffect

Эффект CornerPathEffect просто закругляет углы. На вход принимает радиус закругления.

 

Пишем в MainActivity.java:

package ru.startandroid.develop.p1511patheffect;

import android.app.Activity;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.CornerPathEffect;
import android.graphics.Paint;
import android.graphics.Path;
import android.os.Bundle;
import android.view.View;

public class MainActivity extends Activity {

  @Override
  protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(new DrawView(this));
  }

  class DrawView extends View {

    Path path;
    Paint p1;
    Paint p2;
    Paint p3;

    public DrawView(Context context) {
      super(context);
      path = new Path();
      path.rLineTo(100, 300);
      path.rLineTo(100, -100);
      path.rLineTo(100, 300);

      p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
      p1.setStyle(Paint.Style.STROKE);
      p1.setStrokeWidth(3);

      p2 = new Paint(p1);
      p2.setColor(Color.GREEN);
      p2.setPathEffect(new CornerPathEffect(25));

      p3 = new Paint(p1);
      p3.setColor(Color.BLUE);
      p3.setPathEffect(new CornerPathEffect(50));

    }

    @Override
    protected void onDraw(Canvas canvas) {
      canvas.drawARGB(80, 102, 204, 255);

      canvas.translate(100, 100);
      canvas.drawPath(path, p1);

      canvas.translate(250, 0);
      canvas.drawPath(path, p2);

      canvas.translate(250, 0);
      canvas.drawPath(path, p3);

    }

  }

}

Смотрим код. Мы создаем Path, состоящий из трех линий. Создаем три кисти с разными цветами: черную без эффектов, зеленую с закруглением с радиусом в 25, и синюю с закруглением 50. И рисуем фигуру три раза.

 

Результат:

 

 

DiscretePathEffect

DiscretePathEffect позволяет получить ломанную линию из прямой. Полученная ломанная линия будет состоять из фрагментов, а мы можем повлиять на длину этих фрагментов (первый параметр конструктора) и степень излома (второй параметр).

Перепишем класс DrawView:

class DrawView extends View {
      
      Path path;
      Paint p1;
      Paint p2;
      Paint p3;
      
      public DrawView(Context context) {
        super(context);
        path = new Path();
        path.rLineTo(100, 300);
        path.rLineTo(100, -100);
        path.rLineTo(100, 300);
        
        p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        p1.setStyle(Paint.Style.STROKE);
        p1.setStrokeWidth(3);
        
        p2 = new Paint(p1);
        p2.setColor(Color.GREEN);
        p2.setPathEffect(new DiscretePathEffect(10,5));
        
        p3 = new Paint(p1);
        p3.setColor(Color.BLUE);
        p3.setPathEffect(new DiscretePathEffect(10,15));
        
      }
      
      @Override
      protected void onDraw(Canvas canvas) {
        canvas.drawARGB(80, 102, 204, 255);
        
        canvas.translate(100, 100);
        canvas.drawPath(path, p1);
        
        canvas.translate(250, 0);
        canvas.drawPath(path, p2);

        canvas.translate(250, 0);
        canvas.drawPath(path, p3);
        
      }
      
    }

Для зеленой линии используем степень излома – 5, а для синей – 15. Длина фрагментов = 10.

 

Результат:

 

 

DashPathEffect

С помощью DashPathEffect мы из сплошной линии можем получить прерывистую. От нас требуется задать длину участка который будет прорисован и длину участка, который прорисован не будет, т.е. «пусто». Далее эта комбинация будет циклично использована для прорисовки всей линии.

Перепишем класс DrawView:

class DrawView extends View {
      
      Path path;
      Paint p1;
      Paint p2;
      Paint p3;
      
      public DrawView(Context context) {
        super(context);
        path = new Path();
        path.rLineTo(100, 300);
        path.rLineTo(100, -100);
        path.rLineTo(100, 300);
        
        p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        p1.setStyle(Paint.Style.STROKE);
        p1.setStrokeWidth(7);
        
        p2 = new Paint(p1);
        p2.setColor(Color.GREEN);
        p2.setPathEffect(new DashPathEffect(new float[] { 30, 10}, 0));
        
        p3 = new Paint(p1);
        p3.setColor(Color.BLUE);
        p3.setPathEffect(new DashPathEffect(new float[] { 50, 10, 5, 10 }, 25));
        
      }
      
      @Override
      protected void onDraw(Canvas canvas) {
        canvas.drawARGB(80, 102, 204, 255);
        
        canvas.translate(100, 100);
        canvas.drawPath(path, p1);
        
        canvas.translate(250, 0);
        canvas.drawPath(path, p2);

        canvas.translate(250, 0);
        canvas.drawPath(path, p3);
        
      }
      
    }

Для зеленой линии мы настраиваем длину выводимого участка = 30, длину пустоты = 10. Мы помещаем эти значение в массив и передаем в DashPathEffect-конструктор первым параметром. Вторым параметром идет отступ, его не используем.

Для синей линии мы задаем чуть более сложную последовательность: 50 выводить, 10 пусто, 5, выводить, 10 пусто. Т.е. принцип наверно уже понятен. Система будет поочередно использовать значения из массива для определения длины рисуемого куска линии и длины следующей за ним пустоты. Отступ используем в 25.

 

Результат:

Зеленая линяя состоит из отрезков длиной 30 и пустоты длиной 10. А синяя из отрезка длиной 50, пустоты 10, отрезка 5, пустоты 10. У синей линии первый отрезок выглядит короче остальных больших. Это сработал отступ в 25. Если вы этот отступ повесите в цикл, то линия оживет и поедет на месте, что выглядит достаточно эффектно.

 

 

PathDashPathEffect

PathDashPathEffect позволяет сделать пунктирную линию, но в качестве пунктира можно использовать свой Path-объект.

Перепишем класс DrawView:

class DrawView extends View {
      
      Path path;
      Path pathStamp;
      Paint p1;
      Paint p2;
      Paint p3;
      Paint p4;
      
      public DrawView(Context context) {
        super(context);
        path = new Path();
        path.addRect(-100, 0, 100, 500, Path.Direction.CW);
        
        pathStamp = new Path();
        pathStamp.lineTo(-10, -10);
        pathStamp.lineTo(10, 0);
        pathStamp.lineTo(-10, 10);
        pathStamp.close();
        
        p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        p1.setStyle(Paint.Style.STROKE);
        p1.setStrokeWidth(20);
        
        p2 = new Paint(p1);
        p2.setColor(Color.GREEN);
        p2.setPathEffect(new PathDashPathEffect(pathStamp, 20, 0, PathDashPathEffect.Style.MORPH));
        
        p3 = new Paint(p1);
        p3.setColor(Color.BLUE);
        p3.setPathEffect(new PathDashPathEffect(pathStamp, 20, 0, PathDashPathEffect.Style.ROTATE));
        
        p4 = new Paint(p1);
        p4.setColor(Color.RED);
        p4.setPathEffect(new PathDashPathEffect(pathStamp, 20, 10, PathDashPathEffect.Style.TRANSLATE));
        
      }
      
      @Override
      protected void onDraw(Canvas canvas) {
        canvas.drawARGB(80, 102, 204, 255);
        
        canvas.translate(120, 100);
        canvas.drawPath(path, p1);
        
        canvas.translate(250, 0);
        canvas.drawPath(path, p2);

        canvas.translate(250, 0);
        canvas.drawPath(path, p3);

        canvas.translate(250, 0);
        canvas.drawPath(path, p4);
      }
      
    }

 

Создаем объект path с прямоугольником и pathStamp в виде стрелки. Далее на кисти вешаем эффект PathDashPathEffect. Его конструктор на вход принимает:

- path-объект, который будет использован в качестве пунктира

- расстояние между пунктирами

- отступ от начала

- стиль эффекта

 

Результат:

 

Линии состоят из стрелок (объект pathStamp). Расстояние между ними = 20. По стилям хелп не дает толковой инфы. По моим наблюдениям могу предположить следующее:

PathDashPathEffect.Style.MORPH – срезает пунктир на углах (видно по зеленой линии)

PathDashPathEffect.Style.ROTATE – корректно работает с углами (видно по синей линии)

PathDashPathEffect.Style.TRANSLATE – не поворачивает pathStamp по направлению основной линии (видно по красной фигуре)

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

 

 

SumPathEffect и ComposePathEffect

Позволяют нам комбинировать два эффекта, которые подаются им на вход.

ComposePathEffect применит сначала один эффект, потом к получившемуся результату – второй и выведет результат. SumPathEffect – применит к искомой фигуре один эффект, выведет результат, затем применит к искомой фигуре второй эффект и выведет результат.

Перепишем класс DrawView:

class DrawView extends View {
      
      Path path;
      Paint p1;
      Paint p2;
      Paint p3;
      Paint p4;
      Paint p5;
      
      public DrawView(Context context) {
        super(context);
        path = new Path();
        path.addRect(-100, 0, 100, 500, Path.Direction.CW);
      
        PathEffect pe1 = new CornerPathEffect(100);
        PathEffect pe2 = new DashPathEffect(new float[] { 20, 5}, 0);
        PathEffect pe3 = new ComposePathEffect(pe2, pe1);
        PathEffect pe4 = new SumPathEffect(pe1, pe2);
        
        p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        p1.setStyle(Paint.Style.STROKE);
        p1.setStrokeWidth(3);
        
        p2 = new Paint(p1);
        p2.setColor(Color.GREEN);
        p2.setPathEffect(pe1);
        
        p3 = new Paint(p1);
        p3.setColor(Color.BLUE);
        p3.setPathEffect(pe2);
        
        p4 = new Paint(p1);
        p4.setColor(Color.RED);
        p4.setPathEffect(pe3);
        
        p5 = new Paint(p1);
        p5.setColor(Color.YELLOW);
        p5.setPathEffect(pe4);
        
      }
      
      @Override
      protected void onDraw(Canvas canvas) {
        canvas.drawARGB(80, 102, 204, 255);
        
        canvas.translate(120, 100);
        canvas.drawPath(path, p1);
        
        canvas.translate(250, 0);
        canvas.drawPath(path, p2);

        canvas.translate(250, 0);
        canvas.drawPath(path, p3);

        canvas.translate(250, 0);
        canvas.drawPath(path, p4);
        
        canvas.translate(250, 0);
        canvas.drawPath(path, p5);        
      }
      
    }

 

Создаем 4 эффекта.

pe1 – закругление

pe2 – прерывистая линяя

pe3 – комбинация, сначала будет применен pe1, затем к получившемуся результату - pe2

pe4 – сумма линия будет нарисована с эффектом pe1 и с эффектом pe2

 

Результат:

Зеленый прямоугольник закруглен (pe1). Синий нарисован прерывистым (pe2). Красный сначала закруглен, затем сделан прерывистым (pe1, затем pe2). Желтый – просто вывод обоих эффектов отдельно (pe1 и pe2).

 

В ComposePathEffect имеет значение порядок эффектов. Немного изменим предыдущий пример

class DrawView extends View {
      
      Path path;
      Paint p1;
      Paint p2;
      Paint p3;
      Paint p4;
      Paint p5;
      
      public DrawView(Context context) {
        super(context);
        path = new Path();
        path.addRect(-100, 0, 100, 500, Path.Direction.CW);
        
        
        PathEffect pe1 = new CornerPathEffect(100);
        PathEffect pe2 = new DiscretePathEffect(15, 10);
        PathEffect pe3 = new ComposePathEffect(pe1, pe2);
        PathEffect pe4 = new ComposePathEffect(pe2, pe1);
        
        
        p1 = new Paint(Paint.ANTI_ALIAS_FLAG);
        p1.setStyle(Paint.Style.STROKE);
        p1.setStrokeWidth(7);
        
        p2 = new Paint(p1);
        p2.setColor(Color.GREEN);
        p2.setPathEffect(pe1);
        
        p3 = new Paint(p1);
        p3.setColor(Color.BLUE);
        p3.setPathEffect(pe2);
        
        p4 = new Paint(p1);
        p4.setColor(Color.RED);
        p4.setPathEffect(pe3);
        
        p5 = new Paint(p1);
        p5.setColor(Color.YELLOW);
        p5.setPathEffect(pe4);
        
      }
      
      @Override
      protected void onDraw(Canvas canvas) {
        canvas.drawARGB(80, 102, 204, 255);
        
        canvas.translate(120, 100);
        canvas.drawPath(path, p1);
        
        canvas.translate(250, 0);
        canvas.drawPath(path, p2);

        canvas.translate(250, 0);
        canvas.drawPath(path, p3);

        canvas.translate(250, 0);
        canvas.drawPath(path, p4);
        
        canvas.translate(250, 0);
        canvas.drawPath(path, p5);        
      }
      
    }

pe1 – закругление

pe2 – излом

pe3 – сначала применен pe2, затем pe1

pe4 – сначала применен pe1, затем pe2

 

Результат:

Красная фигура – это результат применения сначала излома, затем закругления. Т.е. закругление было применено уже к излому и он получился сглаженным.

А желтая фигура – результат применения сначала закругления, затем излома. Т.е. излом был применен к уже закругленному прямоугольнику.

 

Я везде использовал Path, но эти эффекты можно применять и при рисовании объектов канвы, например, Canvas.drawRect или Canvas.drawCircle

 

На следующем уроке:

- работаем с Picture


Присоединяйтесь к нам в Telegram:

- в канале StartAndroid публикуются ссылки на новые статьи с сайта startandroid.ru и интересные материалы с хабра, medium.com и т.п.

- в чатах решаем возникающие вопросы и проблемы по различным темам: Android, Compose, Kotlin, RxJava, Dagger, Тестирование, Performance 

- ну и если просто хочется поговорить с коллегами по разработке, то есть чат Флудильня




Language

Автор сайта

Дмитрий Виноградов

Подробнее можно посмотреть или почитать.

Никакие другие люди не имеют к этому сайту никакого отношения и просто занимаются плагиатом.

Социальные сети

 

В канале я публикую ссылки на интересные и полезные статьи по Android

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



Группа ВКонтакте



Поддержка проекта

Яндекс
410011180491924

WebMoney
R248743991365
Z551306702056

Paypal