Освой Android играючи

Сайт Александра Климова

Шкодим

/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000

Работаем с графикой. Основы

Цель нашего урока - понять основы графики. Мы напишем простую рисовалку - хотя это слишком громко сказано. Пока мы сами рисовать ничего не будем - за нас это будет делать глупая машина, т.е. Android. Но тем не менее некоторые полезные вещи мы узнаем, а значит повысим свой профессиональный уровень. Продолжить своё обучение можно в разделе Котошоп.

Создадим новый проект SimplePaint. Создадим новый класс Draw2D, который будет наследоваться от View. Именно в этом классе мы и будем проводить графические опыты. Щёлкаем правой кнопкой мыши на имени пакета и выбираем в меню New | Kotlin Class/File или New | Java Class. В открывшемся диалоговом окне устанавливаем имя для класса Draw2D.

Добавляем код.


// Kotlin
package ru.alexanderklimov.simplepaint

import android.content.Context
import android.graphics.Canvas
import android.view.View

class Draw2D(context: Context?) : View(context) {
    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)
    }
}

// Java package ru.alexanderklimov.simplepaint; public class Draw2D extends View{ public Draw2D(Context context) { super(context); } @Override protected void onDraw(Canvas canvas){ super.onDraw(canvas); } }

В данном коде мы наследуемся от класса android.view.View и переопределяем метод класса onDraw().

Далее необходимо загрузить созданный класс при старте программы. Открываем основной файл активности MainActivity и заменяем строчку после super.onCreate(savedInstanceState):


// Kotlin
setContentView(R.layout.activity_main)
val draw2D = Draw2D(this)
setContentView(draw2D)

// Java // эта строчка нам не нужна setContentView(R.layout.activity_main); Draw2D draw2D = new Draw2D(this); setContentView(draw2D);

В нашем случае мы говорим системе, что не нужно загружать разметку в экран активности. Вместо неё мы загрузим свой класс, у которого есть свой холст для рисования.

Подготовительные работы закончены. Перейдём к графике. Весь дальнейший код мы будем писать в классе Draw2D. Совсем коротко о теории рисования. Для графики используется холст Canvas - некая графическая поверхность для рисования. Прежде чем что-то рисовать, нужно определить некоторые параметры - цвет, толщина, фигура. Представьте себе, что вы рисуете на бумаге и в вашем распоряжении есть цветные карандаши, фломастеры, кисть, циркуль, ластик и т.п. Например, вы берёте толстый красный фломастер и рисуете жирную линию, затем берёте циркуль с жёлтым карандашом и рисуете окружность. Улавливаете аналогию? Теория закончена.

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


// Kotlin
private val paint: Paint = Paint()

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)

    paint.apply {
        style = Paint.Style.FILL // стиль Заливка
        color = Color.WHITE // закрашиваем холст белым цветом
    }
    canvas?.drawPaint(paint)
}

// Java private Paint mPaint = new Paint(); @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); // стиль Заливка mPaint.setStyle(Paint.Style.FILL); // закрашиваем холст белым цветом mPaint.setColor(Color.WHITE); canvas.drawPaint(mPaint); }

Итак, холст готов. Далее начинается собственно рисование. Следуя описанному выше принципу мы задаём перед каждым рисованием свои настройки и вызываем нужный метод. Например, для того, чтобы нарисовать жёлтый, круг мы включаем режим сглаживания, устанавливаем жёлтый цвет и вызываем метод drawCircle() с нужными координатами и заливаем окружность выбранным цветом. Получилось симпатичное солнышко.


// Kotlin
paint.apply {
    isAntiAlias = true
    color = Color.YELLOW
}
canvas?.drawCircle(950F, 30F, 25F, paint)

// Java // Рисуем жёлтый круг mPaint.setAntiAlias(true); mPaint.setColor(Color.YELLOW); canvas.drawCircle(950, 30, 25, mPaint);

Всегда соблюдайте очерёдность рисования. Если вы поместите данный код до заливки холста белым цветом, то ничего не увидите. У вас получится, что вы сначала нарисовали на стене солнце, а потом заклеили рисунок обоями.

Для рисования зелёного прямоугольника мы также задаём координаты и цвет. У нас получится красивая лужайка.


// Kotlin
paint.color = Color.GREEN
canvas?.drawRect(20F, 650F, 950F, 680F, paint)

// Java mPaint.setColor(Color.GREEN); canvas.drawRect(20, 650, 950, 680, mPaint);

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


// Kotlin
paint.apply {
    color = Color.BLUE
    style = Paint.Style.FILL
    isAntiAlias = true
    textSize = 32F
}
canvas?.drawText("Лужайка только для котов", 30F, 648F, paint)

// Рисуем текст mPaint.setColor(Color.BLUE); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); mPaint.setTextSize(32); canvas.drawText("Лужайка только для котов", 30, 648, mPaint);

При желании можно вывести текст под углом. Пусть это будет лучик солнца.


// Kotlin
private val rect: Rect = Rect()

val x = 810F
val y = 190F

paint.apply {
    color = Color.GRAY
    style = Paint.Style.FILL
    textSize = 27F
}

val str2rotate = "Лучик солнца!"

canvas?.save()
canvas?.rotate(-45F, x + rect.exactCenterX(), y + rect.exactCenterY())
canvas?.drawText(str2rotate, x, y, paint)

canvas?.restore()

// Java // до метода onDraw() private Rect mRect = new Rect(); // Текст под углом int x = 810; int y = 190; mPaint.setColor(Color.GRAY); mPaint.setTextSize(27); String str2rotate = "Лучик солнца!"; canvas.save(); // Создаём ограничивающий прямоугольник для наклонного текста // поворачиваем холст по центру текста canvas.rotate(-45, x + mRect.exactCenterX(), y + mRect.exactCenterY()); // Рисуем текст mPaint.setStyle(Paint.Style.FILL); canvas.drawText(str2rotate, x, y, mPaint); // восстанавливаем холст canvas.restore();

И завершим нашу композицию выводом рисунка из ресурсов.


// Выводим изображение
canvas.drawBitmap(mBitmap, 450, 530, mPaint);

В данном примере я вручную подбирал размеры и координаты фигур для экрана свого телефона. В реальных приложениях необходимо сначала вычислить размеры экрана у пользователя, а потом уже выводить фигуры в соответствии с полученными результатами. Иначе получится так, что некоторые элементы композиции просто не попадут на экран при вращении устройства. Допустим, в альбомном режиме вы установите у точки X значение 800, но в портретном режиме ширина экрана будет, скажем, 480, и точка окажется вне поле зрения. Поэтому следует позаботиться о вычислениях размеров экрана и плясать от этой печки. Ниже представлен немного переделанный вариант для общего понимания.

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

Основы графики

Основы графики

Похоже?

Лужайка только для котов

Исходный код класса


// Kotlin
package ru.alexanderklimov.simplepaint

import android.content.Context
import android.content.res.Resources
import android.graphics.*
import android.view.View
import android.graphics.BitmapFactory

class Draw2D(context: Context?) : View(context) {

    private val paint: Paint = Paint()
    private val rect: Rect = Rect()
    val res: Resources = this.resources
    private var bitmap: Bitmap = BitmapFactory.decodeResource(res, R.drawable.cat)

    override fun onDraw(canvas: Canvas?) {
        super.onDraw(canvas)

        paint.apply {
            style = Paint.Style.FILL // стиль Заливка
            color = Color.WHITE // закрашиваем холст белым цветом
        }
        canvas?.drawPaint(paint)

        // Солнце
        paint.apply {
            isAntiAlias = true
            color = Color.YELLOW
        }
        canvas?.drawCircle(width - 30F, 30F, 25F, paint)

        // Лужайка
        paint.color = Color.GREEN
        canvas?.drawRect(0F, height - 30F, width.toFloat(), height.toFloat(), paint)

        // Текст над лужайкой
        paint.apply {
            color = Color.BLUE
            style = Paint.Style.FILL
            isAntiAlias = true
            textSize = 32F
        }
        canvas?.drawText("Лужайка только для котов", 30F, height - 32F, paint)

        // Лучик солнца
        val x = width - 170F
        val y = 190F

        paint.apply {
            color = Color.GRAY
            style = Paint.Style.FILL
            textSize = 27F
        }

        val beam = "Лучик солнца!"

        canvas?.save()
        canvas?.rotate(-45F, x + rect.exactCenterX(), y + rect.exactCenterY())
        canvas?.drawText(beam, x, y, paint)

        canvas?.restore()

        canvas?.drawBitmap(
            bitmap, (width - bitmap.width).toFloat(), (height - bitmap.height
                    - 10).toFloat(), paint
        )
    }
}

//Java // Если этот код работает, его написал Александр Климов, // а если нет, то не знаю, кто его писал. package ru.alexanderklimov.simplepaint; import android.content.Context; import android.content.res.Resources; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; import android.view.View; public class Draw2D extends View { private Paint mPaint = new Paint(); private Rect mRect = new Rect(); private Bitmap mBitmap; public Draw2D(Context context) { super(context); // Выводим значок из ресурсов Resources res = this.getResources(); mBitmap = BitmapFactory.decodeResource(res, R.drawable.cat_bottom); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); int width = canvas.getWidth(); int height = canvas.getHeight(); // стиль Заливка mPaint.setStyle(Paint.Style.FILL); // закрашиваем холст белым цветом mPaint.setColor(Color.WHITE); canvas.drawPaint(mPaint); // Рисуем жёлтый круг mPaint.setAntiAlias(true); mPaint.setColor(Color.YELLOW); // canvas.drawCircle(950, 30, 25, mPaint); canvas.drawCircle(width - 30, 30, 25, mPaint); // Рисуем зелёный прямоугольник mPaint.setColor(Color.GREEN); // canvas.drawRect(20, 650, 950, 680, mPaint); canvas.drawRect(0, canvas.getHeight() - 30, width, height, mPaint); // Рисуем текст mPaint.setColor(Color.BLUE); mPaint.setStyle(Paint.Style.FILL); mPaint.setAntiAlias(true); mPaint.setTextSize(32); // canvas.drawText("Лужайка только для котов", 30, 648, mPaint); canvas.drawText("Лужайка только для котов", 30, height - 32, mPaint); // Текст под углом // int x = 810; int x = width - 170; int y = 190; mPaint.setColor(Color.GRAY); mPaint.setTextSize(27); String beam = "Лучик солнца!"; canvas.save(); // Создаём ограничивающий прямоугольник для наклонного текста // поворачиваем холст по центру текста canvas.rotate(-45, x + mRect.exactCenterX(), y + mRect.exactCenterY()); // Рисуем текст mPaint.setStyle(Paint.Style.FILL); canvas.drawText(beam, x, y, mPaint); // восстанавливаем холст canvas.restore(); // Выводим изображение // canvas.drawBitmap(mBitmap, 450, 530, mPaint); canvas.drawBitmap(mBitmap, width - mBitmap.getWidth(), height - mBitmap.getHeight() - 10, mPaint); } }

Дополнительное чтение

Обсуждение статьи на форуме.

Реклама