Custom rounded view

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

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.