Android SDK: Рисование с помощью узоров

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

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

Вот скриншоты приложения для рисования, которое мы разработаем в этом уроке:

1. Создание приложения

Шаг 1

Создайте новый проект в Android Studio, выбрав минимальный уровень API 14. Назовём его, допустим, Pattern Fill.

Выберите Empty Activity, чтобы студия создала пустую активность с разметкой, на которой мы будем работать.

Добавьте новый класс в проект для создания пользовательской вьюхи, в которой будет происходить рисование. Назовите класс DrawingView и наследуйте его от класса View, как показано ниже:

public class DrawingView extends View

Вам понадобятся следующие импорты в этом классе:

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.Shader;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;

И следующие переменные:

// путь для рисования
private Path drawPath;
// Paint для рисования и для холста
private Paint drawPaint, canvasPaint;
// начальный цвет
private int paintColor = 0xFF000000;
// холст
private Canvas drawCanvas;
// битмап холста
private Bitmap canvasBitmap;

Создайте в классе конструктор, в котором мы будем вызывать метод, работа с которым будет далее:

public DrawingView(Context context, AttributeSet attrs){
  super(context, attrs);
  setupDrawing();
}

Затем добавьте сам метод setupDrawing().

private void setupDrawing(){
  drawPath = new Path();
  drawPaint = new Paint();
  drawPaint.setColor(paintColor);
  drawPaint.setAntiAlias(true);
  drawPaint.setStrokeWidth(50);
  drawPaint.setStyle(Paint.Style.STROKE);
  drawPaint.setStrokeJoin(Paint.Join.ROUND);
  drawPaint.setStrokeCap(Paint.Cap.ROUND);
  canvasPaint = new Paint(Paint.DITHER_FLAG);
}

Метод настраивает класс для рисования. Переопределите метод onSizeChanged(), в котором вьюхе задаётся размер, следующим образом:

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
  super.onSizeChanged(w, h, oldw, oldh);
  canvasBitmap = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
  drawCanvas = new Canvas(canvasBitmap);
}

Теперь переопределите метод onDraw() для отображения рисунка, который нарисован пользователем.

@Override
protected void onDraw(Canvas canvas) {
  canvas.drawBitmap(canvasBitmap, 0, 0, canvasPaint);
  canvas.drawPath(drawPath, drawPaint);
}

Наконец, добавьте следующий метод для реализации того, что пользователь рисует через касания на экране.

@Override
public boolean onTouchEvent(MotionEvent event) {
  float touchX = event.getX();
  float touchY = event.getY();
  // реакция на события "палец на экране", "движение по экрану" и "палец поднят"
  switch (event.getAction()) {
    case MotionEvent.ACTION_DOWN:
      drawPath.moveTo(touchX, touchY);
      break;
    case MotionEvent.ACTION_MOVE:
      drawPath.lineTo(touchX, touchY);
      break;
    case MotionEvent.ACTION_UP:
      drawPath.lineTo(touchX, touchY);
      drawCanvas.drawPath(drawPath, drawPaint);
      drawPath.reset();
      break;
    default:
      return false;
  }
  // перерисовать
  invalidate();
  return true;
}

Шаг 2

Теперь давайте отобразим созданную втюху на экране приложения. Откройте файл разметки (по умолчанию, res/layout/activity_main.xml) и замените код в нём на следующие строки:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="#FFCCCCCC"
    android:orientation="vertical"
    tools:context=".MainActivity" >

</LinearLayout>

Внутри LinearLayout добавьте экземпляр созданного вами DrawingView, используя префикс пакета вашего приложения.

<your.package.name.DrawingView
    android:id="@+id/drawing"
    android:layout_width="match_parent"
    android:layout_height="0dp"
    android:layout_marginBottom="3dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:layout_marginTop="3dp"
    android:layout_weight="1"
    android:background="#FFFFFFFF" />

Мы хотим, чтобы область холста заполняла пространство отдельно от кнопок палитры, которые мы добавим далее.

2. Кнопки узоров

Шаг 1

Мы собираемся использовать 8 узоров - вы можете использовать следующие изображения или создать свои собственные, если хотите. Сохраните их в папку /res/drawable - эти файлы включены в исходный код проекта. Мы будем использовать имена файлов для ссылки на изображения в коде приложения.

 

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

Шаг 2

Давайте теперь добавим палитру в наш интерфейс для кнопок шаблонов. Откройте файл разметки. Добавьте следующие строки после нашего пользовательского элемента DrawingView, который был добавлен ранее:

<LinearLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center"
    android:orientation="vertical">
  <!-- верхняя строка -->
  <LinearLayout
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:orientation="horizontal">
  </LinearLayout>
  <!-- нижняя строка -->
  <LinearLayout
      android:layout_width="wrap_content"
      android:layout_height="wrap_content"
      android:orientation="horizontal" >
  </LinearLayout>
</LinearLayout>

Мы добавим по 4 кнопки в каждом ряду. Добавьте первые четыре внутри LinearLayout для верхнего ряда:

<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern1"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern1"
    />
<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern2"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern2"
    />
<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern3"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern3"
    />
<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern4"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern4"
    />

И следующие 4 внутри LinearLayout для нижнего ряда:

<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern5"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern5"
    />
<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern6"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern6"
    />
<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern7"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern7"
    />
<ImageButton
    android:layout_width="30dp"
    android:layout_height="30dp"
    android:layout_margin="2dp"
    android:background="@drawable/pattern8"
    android:contentDescription="pattern"
    android:onClick="paintClicked"
    android:tag="pattern8"
    />

Каждый ImageButton определяет метод для вызова при клике paintClicked - этот метод будет получать доступ к имени узора через атрибут android:tag нажатой кнопки.

3. Рисование узорами

Шаг 1

Теперь мы можем реализовать рисование узорами. В активности вашего приложения добавьте следующий импорт:

import android.view.View;

Перед методом onCreate() активности добавьте следующую переменную, представляющую собой экземпляр созданного нами класса.

private DrawingView drawView;

В методе onCreate() создайте экземпляр DrawingView со ссылкой на вьюху на макете.

drawView = findViewById(R.id.drawing);

Затем добавьте метод с именем, который мы задали в атрибутах android:onClick кнопок палитры.

public void paintClicked(View view){
  // устанавливаем узор  
}

Внутри метода получите тэг из нажатой кнопки.

String pattern = view.getTag().toString();

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

public void setPattern(String newPattern){
  // устанавливаем узор
}

Для начала, внутри метода вызовите invalidate(), чтобы перерисовать вьюху.

invalidate();

Мы собираемся использовать имя узора, чтобы передать его в класс рисования как Bitmap. Получите ссылку на идентификатор ресурса для переданной строки узора:

int patternID =
    getResources().getIdentifier(newPattern, "drawable", BuildConfig.APPLICATION_ID);

Далее в методе декодируйте ресурс как растровое изображение, передавая полученный идентификатор.

Bitmap patternBMP = BitmapFactory.decodeResource(getResources(), patternID);

Вместо того, чтобы рисовать напрямую с помощью растрового изображения, мы будем рисовать сплошным цветом и использовать Bitmap в качестве Shader для него. Создайте объект Shader, используя полученный Bitmap.

BitmapShader patternBMPshader =
    new BitmapShader(patternBMP, Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);

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

drawPaint.setColor(0xFFFFFFFF);
drawPaint.setShader(patternBMPshader);

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

Вернувшись в активность в метод paintClicked(), вы можете теперь вызвать новый метод, передав строку с узором, полученную из тэга кнопки:

drawView.setPattern(pattern);

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

Заключение

В этой статье мы продемонстрировали, как реализовать функции рисования узорами в приложении. Как видно, требуется совсем немного действий для того, чтобы реализовать это. Если у вас возникли сложности с написанием кода, вы можете скачать рабочий проект с исходным кодом, который доступен на GitHub по следующей ссылке.

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

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