Pull to refresh

Custom rounded view

Reading time4 min
Views6.3K
image

Введение


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

Начальный этап

Создадим новый класс, наследник android.widget.ImageView.

public class MyView extends ImageView {

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

Создадим метод init() в котором инициализируем объекты Paint для рисования графических объектов.

public MyView(Context context, AttributeSet attrs) {
        super(context, attrs);
        init();
    }

    private void init() {
        redBorder = new Paint();
        redBorder.setAntiAlias(true);
        redBorder.setColor(Color.RED);
        redBorder.setStrokeWidth(redStrokeWidth);
        redBorder.setStyle(Paint.Style.STROKE);

        imgPaint = new Paint();
        imgPaint.setAntiAlias(true);
        imgPaint.setFilterBitmap(true);
        imgPaint.setDither(true);
    }

Объект Paint redBoard отвечает за отрисовку красной рамки, которая будет отображаться при выделении. Флаг setAntiAlias(true) обеспечивает сглаживание. Затем устанавливаем красный цвет (setColor(Color.RED)), ширину контура setStrokeWidth(redStrokeWidth), redStrokeWidth обычная переменная (private int redStrokeWidth = 10;) и устанавливаем стиль отрисовки setStyle(Paint.Style.STROKE) — рисовать очертания графического примитива.

Объект Paint imgPaint отвечает за скругление аватара пользователя. Флаг setFilterBitmap(true) гласит о том, что фильтрация будет оказывать влияние на растровое изображение при трансформации. Флаг setDither(true) используется для сглаживания цветов, позволяет уменьшить визуальные артефакты.

Преобразование изображения

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

@Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        decrement = (w * decrementFactor)/100;
        bitmapPosition = decrement /2;

        Drawable drawable = getDrawable();
        if (drawable != null) {
            Bitmap bitmap = ((BitmapDrawable)drawable).getBitmap();
            roundBitmap = getRoundedCroppedBitmap(bitmap, getWidth() - decrement);
        } else {
            Bitmap bitmap = BitmapFactory.decodeResource(getResources(), R.mipmap.ic_launcher);
            roundBitmap = getRoundedCroppedBitmap(bitmap, getWidth() - decrement);
        }
    }

Т.к. при выделении нужно отрисовать красную рамку и отступ от изображения, вводим переменную decrementFactor которая, как в данном случаи, будет составлять 15% от всего размера элемента (private int decrementFactor = 15;). Т.е. сам элемент имеет размер w, а изображение внутри него будет на 15% меньше. Так же вычисляем значение для переменной bitmapPosition, которая будет использоваться при позиционировании изображения. Осуществляем проверку на наличие изображения. Если изображение установлено, делаем преобразования над ним (метод getRoundedCroppedBitmap()) иначе, берем логотип.

Метод getRoundedCroppedBitmap() отвечает за скругление изображения.

private Bitmap getRoundedCroppedBitmap(Bitmap bitmap, int radius) {
        Bitmap finalBitmap = bitmap.createScaledBitmap(bitmap, radius , radius, false);
        Bitmap output = Bitmap.createBitmap(finalBitmap.getWidth(), finalBitmap.getHeight(), Bitmap.Config.ARGB_8888);
        Rect rect = new Rect(0, 0, output.getWidth(), output.getHeight());

        Canvas canvas = new Canvas(output);
        canvas.drawCircle(
                finalBitmap.getWidth()  / 2,
                finalBitmap.getHeight() / 2,
                finalBitmap.getWidth() / 2,
                imgPaint);

        imgPaint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        canvas.drawBitmap(finalBitmap, rect, rect, imgPaint);

        return output;
    }

С помощью метода createScaledBitmap() изменяем размер изображения, на основании смаштабированного изображения формируем новое, которое и будет результатом работы метода. Создаем объект Rect rect, на основании которого будем рисовать bitmap. Получаем canvas, русуем круг и для объекта Paint изменяем режим Xfermode который влияет на способ наложения новых цветов поверх уже нарисованных.

Т.е. мы изменили размер изображения на тот, который нам нужен. Нарисовали над этим изображением круг и осуществили наложение круга на изображение, сказав, что будет видно то, что входит в круг.

Нарисуем полученный элемент:

@Override
 protected void onDraw(Canvas canvas) {
     canvas.drawBitmap(roundBitmap, bitmapPosition, bitmapPosition, null);
     if (imgSelected) {
         canvas.drawCircle(getWidth() / 2, getHeight() / 2, getWidth() / 2 - redStrokeWidth, redBorder);
     }
 }

Для того, что бы наш элемент начал реагировать на нажатия, переопределим метод:

@Override
    public boolean onTouchEvent(MotionEvent event) {
        switch (event.getAction()) {
            case MotionEvent.ACTION_DOWN:
                Animation scale = AnimationUtils.loadAnimation(getContext(), R.anim.scale);
                startAnimation(scale);
                break;
            case MotionEvent.ACTION_UP:
                imgSelected = !imgSelected;
                invalidate();
                Animation scale2 = AnimationUtils.loadAnimation(getContext(), R.anim.scale_2);
                startAnimation(scale2);
                break;
        }
        return true;
    }

При нажатии будем немного уменьшать элемент. Загружаем xml-файл в котором описана анимация для уменьшения:

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="1.0"
    android:toXScale="0.9"
    android:fromYScale="1.0"
    android:toYScale="0.9"
    android:duration="200"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fillAfter="true">
</scale>

А когда пользователь отпустит палец, нарисуем красную рамку и немного увеличим элемент:

<scale xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXScale="1.0"
    android:toXScale="1.1"
    android:fromYScale="1.0"
    android:toYScale="1.1"
    android:duration="200"
    android:pivotX="50%"
    android:pivotY="50%"
    android:fillAfter="true">
</scale>

Заключение

В результате получился милый элемент с простой анимацией.

Код проекта доступен на git.

Буду рад комментариям. Спасибо.

image
Tags:
Hubs:
+9
Comments9

Articles

Change theme settings