Освой Android играючи
/* Моя кошка замечательно разбирается в программировании. Стоит мне объяснить проблему ей - и все становится ясно. */
John Robbins, Debugging Applications, Microsoft Press, 2000
Фильтр ColorMatrixColorFilter
Инвертирование цветов
Метод setScale()
Метод setSaturation()
Метод setRotate()
Метод setConcat()
Фильтр для изменения цвета, использующий значения 4х5 матрицы.
Матрица может иметь следующий вид: 4 строки, в каждой строке по 5 значений:
rR, rG, rB, rA, rT gR, gG, gB, gA, gT bR, bG, bB, bA, bT aR, aG, aB, aA, aT
Допустим, у нас есть некий цвет ARGB. Применим к нему фильтр. Фильтр возьмёт текущее значение цвета и, используя матрицу, вычислит новые значения. Например, новое значение красного (Rn) он посчитает так:
New Red Value = R * rR + G * rG + B * rB + A * rA + rT
Т.е. значения исходного цвета (R,G,B,A) перемножаем на первые 4 значения (rR, rG, rB, rA) из первой строки матрицы и прибавляем пятое значение (rT) из этой же строки.
[ R, [ 1, 0, 0, 0, 0, [ R, G, * 0, 1, 0, 0, 0, = G, B, 0, 0, 1, 0, 0, B, A ] 0, 0, 0, 1, 0 ] A ]
Самостоятельно считать ничего не нужно, фильтр сам всё рассчитает. Вы должны только эту матрицу предоставить.
Новое значение зелёного получается аналогично, используя исходные значения RGBA и вторую строку матрицы.
New Green Value = R * gR + G * gG + B * gB + A * gA + gT
Синий и прозрачность – третья и четвёртая строки:
New Blue Value = R * bR + G * bG + B * bB + A * bA + bT
New Alpha Value = R * aR + G * aG + B * aB + A * aA + aT
Создать объект ColorMatrix можно, используя конструктор без аргументов или перегруженные версии с аргументами.
ColorMatrix colorMatrix = new ColorMatrix();
Созданный объект применяется к объекту Paint через фильтр ColorMatrixColorFilter:
paint.setColorFilter(new ColorMatrixColorFilter(colorMatrix));
Конструктору без аргументов соответствует матрица:
1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0
Если применить данную матрицу к цвету, то все цвета останутся без изменений.
Для наглядности нарисуем несколько разноцветных квадратиков и один значок, и будем применять к ним фильтры (идея взята с сайта, ссылка на которую приведена в конце статьи).
package ru.alexanderklimov.filter;
import android.app.Activity;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(new DrawView(this));
}
class DrawView extends View {
private Paint mPaint;
private Rect mRect;
private Bitmap mIcon;
private ColorMatrix mColorMatrix;
private ColorFilter mFilter;
// Матрица
float[] cmData = new float[] {
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0 };
public DrawView(Context context) {
super(context);
mRect = new Rect(0, 0, 150, 150);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mIcon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_android_cat);
mColorMatrix = new ColorMatrix(cmData);
mFilter = new ColorMatrixColorFilter(mColorMatrix);
}
@Override
protected void onDraw(Canvas canvas) {
canvas.drawARGB(80, 102, 204, 255);
canvas.translate(100, 100);
// Выводим объекты
drawObjects(canvas);
// Применяем фильтр
mPaint.setColorFilter(mFilter);
// Выводим эти же объекты чуть ниже с применённым фильтром
canvas.translate(0, 300);
drawObjects(canvas);
}
void drawObjects(Canvas canvas) {
canvas.save();
// Рисуем красный квадрат
mPaint.setColor(Color.RED);
canvas.drawRect(mRect, mPaint);
// Рисуем зелёный квадрат
mPaint.setColor(Color.GREEN);
canvas.translate(220, 0);
canvas.drawRect(mRect, mPaint);
// Рисуем синий квадрат
mPaint.setColor(Color.BLUE);
canvas.translate(220, 0);
canvas.drawRect(mRect, mPaint);
// Рисуем белый квадрат
mPaint.setColor(Color.WHITE);
canvas.translate(220, 0);
canvas.drawRect(mRect, mPaint);
// Выводим значок
canvas.translate(220, 0);
canvas.drawBitmap(mIcon, null, mRect, mPaint);
canvas.restore();
}
}
}
Массив cmData содержит значения для матрицы, который мы передаём экземпляру класса ColorMatrix - переменной mColorMatrix.
Далее подготовленную матрицу передаём фильтру ColorMatrixColorFilter. Для применения фильтра используется метод setColorFilter().
В нашем примере фильтр применяется к кисти и мы можем наблюдать за изменениями цветов выводимых фигур.
Сейчас матрица выглядит следующим образом:
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 1, 0
Если мы возьмём RGBA и применим матрицу, получим следующие результаты:
Rn = R * 1 + G * 0 + B * 0 + A * 0 + 0 = R
Gn = R * 0 + G * 1 + B * 0 + A * 0 + 0 = G
Bn = R *0 + G * 0 + B * 1 + A * 0 + 0 = B
An = R * 0 + G * 0 + B * 0 + A * 1 + 0 = A
Как видите, новые значения равны исходным. Матрица составлена таким образом, что RGBA-значения любого цвета не изменяются. Запустив пример, вы увидите, что первый и второй ряд фигур идентичны.
Поменяем матрицу:
float[] cmData = new float[]{
1, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0};
Новый результат.
Изучим, как происходят изменения цвета.
Был красный цвет c RGBA = (255,0,0,255). Применим фильтр:
Rn = 255 * 1 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 255
Gn = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 0
Bn = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 0 + 0 = 0
An = 255 * 0 + 0 * 0 + 0 * 0 + 255 * 1 + 0 = 255
Новые RGBA-значения получились такими (255,0,0,255) и они совпадают с исходным красным цветом. Поэтому красный квадрат остался без изменений.
Возьмём зелёный цвет. Его значение равно (0,255,0,255). Применяем фильтр:
Rn = 0 * 1 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0
Gn = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0
Bn = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 0 + 0 = 0
An = 0 * 0 + 255 * 0 + 0 * 0 + 255 * 1 + 0 = 255
Новые RGBA-значения зелёного = (0,0,0,255), а это чёрный цвет. Вот почему зелёный квадрат стал чёрным.
Такой же результат получается с синим цветом.
А белый цвет (255,255,255,255) после преобразования получит следующие значения:
Rn = 255 * 1 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 255
Gn = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 0
Bn = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 0 + 0 = 0
An = 255 * 0 + 255 * 0 + 255 * 0 + 255 * 1 + 0 = 255
Таким образом, белый цвет (255,255,255,255) трансформируется в красный цвет (255,0,0,255).
Применив данный фильтр, мы для всех цветов «обнулили» значения синего (B) и зелёного (G) составляющих и оставили только красную (R) составляющую. Это видно на значке.
Но тут важно понимать одну вещь. Мы не выкрасили всё в красный цвет. Мы полностью убрали зелёный и синий, а красный оставили в том значении, в каком он был. В красном квадрате значение красного было 255. Таким и осталось. В синем и зелёном квадратах значение красного было 0. Таким и осталось.
Если рассматривать значок, видно, что красный неоднородный, где-то светлее, где-то темнее. Т.е. изначально значок была нарисован разными оттенками, в которых были использованы различные RGB-комбинации. Мы в этих комбинациях убрали G и B, оставили только R. Где R был, например 50, остался 50. Где был 150 – остался 150. А G и B теперь везде равен 0.
Давайте настроим матрицу так, чтобы красный везде стал максимальным, независимо от первоначального значения. А синий и зелёный снова обнулим.
float[] cmData = new float[]{
0, 0, 0, 0, 255,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0};
Мы убрали коэффициент 1 из первого числа первой строки. Теперь новое значение R уже не будет равно старому значение R, умноженному на 1. Теперь оно будет умножаться на 0, но последнее число первой строки равно 255. Оно будет прибавлено к нулю и мы получим полный красный цвет на замену первоначальным оттенкам красного.
Синий и зелёный также станут красными, так как G и B значения мы в них обнулим, а R будет равен 255, т.е. (255,0,0,255).
Результат
Теперь изменим матрицу так, чтобы обнулялся только синий компонент. Красный и зелёный компоненты останутся неизменными.
float[] cmData = new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0};
Получим следующий результат.
Убрав из синего цвета (0,0,255,255) синий компонент, мы получили чёрный цвет (0,0,0,255).
А убрав из белого цвета (255,255,255,255) синий компонент, мы получили жёлтый цвет (255,255,0,255).
Мы меняли компоненты цветов (RGB), теперь давайте попробуем поменять прозрачность (A). Напомню, что если A = 255, то цвет абсолютно непрозрачен. Если A = 0, то цвет совсем не виден.
float[] cmData = new float[]{
1, 0, 0, 0, 0,
0, 1, 0, 0, 0,
0, 0, 1, 0, 0,
0, 0, 0, 0.3f, 0};
Мы поставили коэффициент 0.3 для вычисления нового значения прозрачности (An = A * 0.3). Теперь все цвета станут прозрачными на 30% от текущего уровня.
Можно самостоятельно придумывать собственные комбинации для матрицы. Существуют уже готовые решения. Например, преобразование в чёрно-белый цвет.
float[] cmData = new float[]{
0.3f, 0.59f, 0.11f, 0, 0,
0.3f, 0.59f, 0.11f, 0, 0,
0.3f, 0.59f, 0.11f, 0, 0,
0, 0, 0, 1, 0,};
Вот ещё одна комбинация, дающая серый цвет.
float[] cmData = new float[]{
0.213f, 0.715f, 0.072f, 0.0f, 0.0f,
0.213f, 0.715f, 0.072f, 0.0f, 0.0f,
0.213f, 0.715f, 0.072f, 0.0f, 0.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f,
};
Как видите, можно играться настройками и добиваться нужных результатов.
Для инвертирования цветов используется следующая комбинация.
float[] cmData = new float[]{
-1, 0, 0, 0, 255,
0, -1, 0, 0, 255,
0, 0, -1, 0, 255,
0, 0, 0, 1, 0,};
Аналогичный пример на Kotlin, добавьте на экран ImageView с картинкой и кнопку.
button.setOnClickListener {
val myDrawable = imageView.drawable
val matrixInvert = ColorMatrix().apply {
set(
floatArrayOf(
-1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
)
)
}
val filter = ColorMatrixColorFilter(matrixInvert)
myDrawable.colorFilter = filter
imageView.setImageDrawable(myDrawable)
imageView.invalidate()
}
У ColorMatrix есть специальный метод setScale(), который позволяет указать на какие значения необходимо умножать RGBA значения цвета. В этом случае вам не нужно составлять свою матрицу, а просто указать в параметрах метода коэффициенты для R, G, B и A компонентов. Сама матрица будет иметь вид из первого примера, когда после её применения ничего не изменилось.
Перепишем конструктор
public DrawView(Context context) {
super(context);
mRect = new Rect(0, 0, 150, 150);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStyle(Paint.Style.FILL_AND_STROKE);
mIcon = BitmapFactory.decodeResource(context.getResources(),
R.drawable.ic_android_cat);
mColorMatrix = new ColorMatrix();
mColorMatrix.setScale(1, 0, 0, 1);
mFilter = new ColorMatrixColorFilter(mColorMatrix);
}
Вместо new ColorMatrix(cmData) используем пустой конструктор без указания матрицы new ColorMatrix(). А далее вызываем метод, в котором указываем нужные коэффициенты. Параметры (1, 0, 0, 1) соответствуют матрице из второго примера:
float[] cmData = new float[]{
1, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 0, 0,
0, 0, 0, 1, 0};
И мы видим тот же результат.
Если нужно сделать цвета полупрозрачными для нашего примера, то код был бы:
mColorMatrix.setScale(1, 0, 0, 0.5f);
Проверьте самостоятельно.
Напишем отдельный пример для загрузки изображения из Галереи и применим к нему метод setScale().
Разметка
<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:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/buttonLoadimage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Image" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:background="@drawable/ic_android_cat"
android:scaleType="centerInside" />
<TextView
android:id="@+id/textViewCMScale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="ColorMatrix Scale" />
<SeekBar
android:id="@+id/seekBarRedScale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="200"
android:progress="100" />
<SeekBar
android:id="@+id/seekBarGreenScale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="200"
android:progress="100" />
<SeekBar
android:id="@+id/seekBarBlueScale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="200"
android:progress="100" />
<SeekBar
android:id="@+id/seekBarAlphaScale"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="200"
android:progress="100" />
</LinearLayout>
</ScrollView>
</LinearLayout>
У компонента ImageView задан в качестве фона один из рисунков. При загрузке из Галереи новая картинка закроет фоновый рисунок. Но поменяв прозрачность, вы сможете увидеть фон снова.
Код для активности
package ru.alexanderklimov.test;
import java.io.FileNotFoundException;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class MainActivity extends Activity {
final int RQS_IMAGE = 1;
private Button mLoadImageButton;
private ImageView mGaleryImageView;
private SeekBar mRedSeekbar, mGreenSeekbar, mBlueSeekbar, mAlphaSeekbar;
private TextView mScaleTextView;
private Uri mSource;
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLoadImageButton = findViewById(R.id.buttonLoadimage);
mGaleryImageView = findViewById(R.id.imageView);
mLoadImageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, RQS_IMAGE);
}
});
mScaleTextView = (TextView) findViewById(R.id.textViewCMScale);
mRedSeekbar = (SeekBar) findViewById(R.id.seekBarRedScale);
mGreenSeekbar = (SeekBar) findViewById(R.id.seekBarGreenScale);
mBlueSeekbar = (SeekBar) findViewById(R.id.seekBarBlueScale);
mAlphaSeekbar = (SeekBar) findViewById(R.id.seekBarAlphaScale);
mRedSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
mGreenSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
mBlueSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
mAlphaSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case RQS_IMAGE:
mSource = data.getData();
try {
mBitmap = BitmapFactory
.decodeStream(getContentResolver().openInputStream(
mSource));
mRedSeekbar.setProgress(100);
mGreenSeekbar.setProgress(100);
mBlueSeekbar.setProgress(100);
mAlphaSeekbar.setProgress(100);
loadBitmapScaleColor();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
OnSeekBarChangeListener seekBarChangeListener = new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
loadBitmapScaleColor();
}
};
private void loadBitmapScaleColor() {
if (mBitmap != null) {
int progressScaleRed = mRedSeekbar.getProgress();
int progressScaleGreen = mGreenSeekbar.getProgress();
int progressScaleBlue = mBlueSeekbar.getProgress();
int progressScaleAlpha = mAlphaSeekbar.getProgress();
float scaleRed = (float) progressScaleRed / 100;
float scaleGreen = (float) progressScaleGreen / 100;
float scaleBlue = (float) progressScaleBlue / 100;
float scaleAlpha = (float) progressScaleAlpha / 100;
mScaleTextView.setText("setScale: " + String.valueOf(scaleRed) + ", "
+ String.valueOf(scaleGreen) + ", "
+ String.valueOf(scaleBlue) + ", "
+ String.valueOf(scaleAlpha));
Bitmap bitmapColorScaled = updateScale(mBitmap, scaleRed,
scaleGreen, scaleBlue, scaleAlpha);
mGaleryImageView.setImageBitmap(bitmapColorScaled);
}
}
private Bitmap updateScale(Bitmap src, float rScale, float gScale,
float bScale, float aScale) {
int width = src.getWidth();
int height = src.getHeight();
Bitmap bitmapResult = Bitmap
.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvasResult = new Canvas(bitmapResult);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setScale(rScale, gScale, bScale, aScale);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(filter);
canvasResult.drawBitmap(src, 0, 0, paint);
return bitmapResult;
}
}
Метод setSaturation() позволяет управлять насыщенностью цвета. Принимает на вход значения от 0 до 1. Если задать 0, то получим чёрно-белую картинку.
Разметка
<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:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/buttonLoadimage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Image" />
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:background="@android:color/background_dark"
android:scaleType="centerInside" />
<TextView
android:id="@+id/textViewSaturation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Saturation" />
<SeekBar
android:id="@+id/seekBarSaturation"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="512"
android:progress="256" />
</LinearLayout>
Загружаем из галереи рыжего бандита по кличке Рыжик и превращаем его в серого гангстера по кличке Васька.
package ru.alexanderklimov.test;
import java.io.FileNotFoundException;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class MainActivity extends Activity {
final int RQS_IMAGE = 1;
private Button mLoadImageButton;
private ImageView mGaleryImageView;
private SeekBar mSaturationSeekbar;
private TextView mSaturationTextView;
private Uri mSource;
private Bitmap mBitmap;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLoadImageButton = findViewById(R.id.buttonLoadimage);
mSaturationTextView = findViewById(R.id.textViewSaturation);
mGaleryImageView = findViewById(R.id.imageView);
mLoadImageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, RQS_IMAGE);
}
});
mSaturationSeekbar = (SeekBar) findViewById(R.id.seekBarSaturation);
mSaturationSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case RQS_IMAGE:
mSource = data.getData();
try {
mBitmap = BitmapFactory.decodeStream(getContentResolver()
.openInputStream(mSource));
mSaturationSeekbar.setProgress(256);
loadSaturationBitmap();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
OnSeekBarChangeListener seekBarChangeListener = new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
loadSaturationBitmap();
}
};
private void loadSaturationBitmap() {
if (mBitmap != null) {
int progressSat = mSaturationSeekbar.getProgress();
// Saturation, 0=gray-scale. 1=identity
float saturation = (float) progressSat / 256;
mSaturationTextView.setText("Saturation: "
+ String.valueOf(saturation));
mGaleryImageView.setImageBitmap(updateSaturation(mBitmap,
saturation));
}
}
private Bitmap updateSaturation(Bitmap src, float settingSat) {
int width = src.getWidth();
int height = src.getHeight();
Bitmap bitmapResult = Bitmap
.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvasResult = new Canvas(bitmapResult);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setSaturation(settingSat);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(filter);
canvasResult.drawBitmap(src, 0, 0, paint);
return bitmapResult;
}
}
Упрощённый пример на Kotlin. Добавьте на экран кнопку и ImageView с изображением кота.
// Если этот код работает, его написал Александр Климов,
// а если нет, то не знаю, кто его писал.
package ru.alexanderklimov.filter
import android.graphics.*
import android.os.Bundle
import android.widget.Button
import android.widget.ImageView
import androidx.appcompat.app.AppCompatActivity
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val imageView: ImageView = findViewById(R.id.imageView)
val button: Button = findViewById(R.id.button)
button.setOnClickListener {
val myDrawable = imageView.drawable
val matrix = ColorMatrix().apply {
setSaturation(0f)
}
val filter = ColorMatrixColorFilter(matrix)
myDrawable.colorFilter = filter
imageView.setImageDrawable(myDrawable)
imageView.invalidate()
}
}
}
Пример применения метода setRotate().
Разметка
<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:orientation="vertical"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
tools:context=".MainActivity" >
<Button
android:id="@+id/buttonLoadimage"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Load Image" />
<ScrollView
android:layout_width="match_parent"
android:layout_height="match_parent" >
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical" >
<ImageView
android:id="@+id/imageView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:adjustViewBounds="true"
android:background="@drawable/ic_android_cat"
android:scaleType="centerInside" />
<TextView
android:id="@+id/textViewRotate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Rotate Axis" />
<RadioGroup
android:id="@+id/axisgroup"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<RadioButton
android:id="@+id/radioAxisRed"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:checked="true"
android:text="Red" />
<RadioButton
android:id="@+id/radioAxisGreen"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Green" />
<RadioButton
android:id="@+id/radioAxisBlue"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Blue" />
</RadioGroup>
<SeekBar
android:id="@+id/seekBarRotate"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:max="360"
android:progress="0" />
</LinearLayout>
</ScrollView>
</LinearLayout>
Код
package ru.alexanderklimov.test;
import java.io.FileNotFoundException;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.Paint;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.RadioButton;
import android.widget.RadioGroup;
import android.widget.RadioGroup.OnCheckedChangeListener;
import android.widget.SeekBar;
import android.widget.SeekBar.OnSeekBarChangeListener;
import android.widget.TextView;
public class MainActivity extends Activity {
final int RQS_IMAGE = 1;
private Button mLoadImageButton;
private ImageView mGaleryImageView;
private SeekBar mRotateSeekbar;
private TextView mRotateTextView;
private RadioGroup mAxisRadioGroup;
private RadioButton mAxisRedRadioButton, mAxisGreenRadioButton,
mAxisBlueRadioButton;
private Uri mSource;
private Bitmap mBitmap;
final int RQS_IMAGE1 = 1;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mLoadImageButton = findViewById(R.id.buttonLoadimage);
mGaleryImageView = findViewById(R.id.imageView);
mLoadImageButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(
Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(intent, RQS_IMAGE);
}
});
mRotateTextView = (TextView) findViewById(R.id.textViewRotate);
mRotateSeekbar = (SeekBar) findViewById(R.id.seekBarRotate);
mRotateSeekbar.setOnSeekBarChangeListener(seekBarChangeListener);
mAxisRadioGroup = (RadioGroup) findViewById(R.id.axisgroup);
mAxisRedRadioButton = (RadioButton) findViewById(R.id.radioAxisRed);
mAxisGreenRadioButton = (RadioButton) findViewById(R.id.radioAxisGreen);
mAxisBlueRadioButton = (RadioButton) findViewById(R.id.radioAxisBlue);
mAxisRadioGroup
.setOnCheckedChangeListener(groupOnCheckedChangeListener);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case RQS_IMAGE:
mSource = data.getData();
try {
mBitmap = BitmapFactory.decodeStream(getContentResolver()
.openInputStream(mSource));
mAxisRedRadioButton.setChecked(true);
mRotateSeekbar.setProgress(0);
loadRotatedBitmap();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}
OnSeekBarChangeListener seekBarChangeListener = new OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress,
boolean fromUser) {
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
loadRotatedBitmap();
}
};
OnCheckedChangeListener groupOnCheckedChangeListener = new OnCheckedChangeListener() {
@Override
public void onCheckedChanged(RadioGroup group, int checkedId) {
loadRotatedBitmap();
}
};
private void loadRotatedBitmap() {
if (mBitmap != null) {
int progressRotation = mRotateSeekbar.getProgress();
float rotationDegree = (float) progressRotation;
if (mAxisRedRadioButton.isChecked()) {
mRotateTextView.setText("setRotate: " + "Red: "
+ String.valueOf(rotationDegree));
mGaleryImageView.setImageBitmap(updateRotation(mBitmap, 0,
rotationDegree));
} else if (mAxisGreenRadioButton.isChecked()) {
mRotateTextView.setText("setRotate: " + "Green: "
+ String.valueOf(rotationDegree));
mGaleryImageView.setImageBitmap(updateRotation(mBitmap, 1,
rotationDegree));
} else if (mAxisBlueRadioButton.isChecked()) {
mRotateTextView.setText("setRotate: " + "Blue: "
+ String.valueOf(rotationDegree));
mGaleryImageView.setImageBitmap(updateRotation(mBitmap, 2,
rotationDegree));
}
}
}
private Bitmap updateRotation(Bitmap src, int axis, float degrees) {
int width = src.getWidth();
int height = src.getHeight();
Bitmap bitmapResult = Bitmap.createBitmap(width, height,
Bitmap.Config.ARGB_8888);
Canvas canvasResult = new Canvas(bitmapResult);
Paint paint = new Paint();
ColorMatrix colorMatrix = new ColorMatrix();
colorMatrix.setRotate(axis, degrees);
ColorMatrixColorFilter filter = new ColorMatrixColorFilter(colorMatrix);
paint.setColorFilter(filter);
canvasResult.drawBitmap(src, 0, 0, paint);
return bitmapResult;
}
}
Метод setConcat() использует две матрицы. Пример можно найти здесь.
Create Sepia bitmap using ColorMatrix
Android example code using ColorFilter
Convert ImageView to black and white, and set brightness, using ColorFilter
Android Image Color Change With ColorMatrix (Kotlin)