android實現(xiàn)圖片橡皮擦和快速染色功能
本文為大家分享了android實現(xiàn)圖片橡皮擦和快速染色的具體代碼,供大家參考,具體內(nèi)容如下
源碼地址:Eraselmg
1.染色
關(guān)于染色部分,可以分別設(shè)置調(diào)整畫筆的大小和畫筆的透明度,畫筆已經(jīng)設(shè)置了模糊效果。畫筆的特效可以調(diào)整下面一行代碼:

2.橡皮擦
橡皮擦的實現(xiàn)用了兩個canvas,一個臨時的,一個是作用在ImageTouchView上顯示的,代碼里面有注釋,這里不再詳細(xì)介紹。
3.功能展示:
原圖:

畫筆設(shè)置界面:

(1)畫筆大小為32,透明度為255(不透明)。如下圖:

(2)畫筆大小為32,透明度為10,如下圖:

融合的效果跟畫筆的透明度有關(guān)系,也跟背景圖片的相應(yīng)區(qū)域顏色有關(guān),所以透明度的值自行調(diào)整得出滿意效果。
(3)擦除
擦除前圖像:

部分擦除后:

4.Bitmap處理相關(guān)的類BitmapUtils:
package com.jiangjie.utils;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
public class BitmapUtils {
/**
* 縮放圖片
*/
public static void bitmapScale(Bitmap baseBitmap, Paint paint, float x, float y) {
// 因為要將圖片放大,所以要根據(jù)放大的尺寸重新創(chuàng)建Bitmap
Bitmap scaleBitmap = Bitmap.createBitmap(
(int) (baseBitmap.getWidth() * x),
(int) (baseBitmap.getHeight() * y), baseBitmap.getConfig());
Canvas canvas = new Canvas(scaleBitmap);
// 初始化Matrix對象
Matrix matrix = new Matrix();
// 根據(jù)傳入的參數(shù)設(shè)置縮放比例
matrix.setScale(x, y);
// 根據(jù)縮放比例,把圖片draw到Canvas上
canvas.drawBitmap(baseBitmap, matrix,paint);
}
/**
* 圖片旋轉(zhuǎn)
*/
public static void bitmapRotate(Bitmap baseBitmap, Paint paint,float degrees) {
// 創(chuàng)建一個和原圖一樣大小的圖片
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth(),
baseBitmap.getHeight(), baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
Matrix matrix = new Matrix();
// 根據(jù)原圖的中心位置旋轉(zhuǎn)
matrix.setRotate(degrees, baseBitmap.getWidth() / 2,
baseBitmap.getHeight() / 2);
canvas.drawBitmap(baseBitmap, matrix, paint);
}
/**
* 圖片移動
*/
public static void bitmapTranslate(Bitmap baseBitmap, Paint paint, float dx, float dy) {
// 需要根據(jù)移動的距離來創(chuàng)建圖片的拷貝圖大小
Bitmap afterBitmap = Bitmap.createBitmap(
(int) (baseBitmap.getWidth() + dx),
(int) (baseBitmap.getHeight() + dy), baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
Matrix matrix = new Matrix();
// 設(shè)置移動的距離
matrix.setTranslate(dx, dy);
canvas.drawBitmap(baseBitmap, matrix, paint);
}
/**
* 傾斜圖片
*/
public static void bitmapSkew(Bitmap baseBitmap, Paint paint, float dx, float dy) {
// 根據(jù)圖片的傾斜比例,計算變換后圖片的大小,
Bitmap afterBitmap = Bitmap.createBitmap(baseBitmap.getWidth()
+ (int) (baseBitmap.getWidth() * dx), baseBitmap.getHeight()
+ (int) (baseBitmap.getHeight() * dy), baseBitmap.getConfig());
Canvas canvas = new Canvas(afterBitmap);
Matrix matrix = new Matrix();
// 設(shè)置圖片傾斜的比例
matrix.setSkew(dx, dy);
canvas.drawBitmap(baseBitmap, matrix, paint);
}
public static Bitmap decodeFromResource(Context context, int id) {
Resources res = context.getResources();
Bitmap bitmap = BitmapFactory.decodeResource(res,id).copy(Bitmap.Config.ARGB_8888, true);
return bitmap;
}
/**
* 保存圖片到SD卡
*/
public static void saveToSdCard(String path, Bitmap bitmap) {
if (null != bitmap && null != path && !path.equalsIgnoreCase("")) {
try {
File file = new File(path);
FileOutputStream outputStream = null;
//創(chuàng)建文件,并寫入內(nèi)容
outputStream = new FileOutputStream(new File(path), true);
bitmap.compress(Bitmap.CompressFormat.PNG, 30, outputStream);
outputStream.flush();
outputStream.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
/**
* 復(fù)制bitmap
*/
public static Bitmap duplicateBitmap(Bitmap bmpSrc, int width, int height) {
if (null == bmpSrc) {
return null;
}
int bmpSrcWidth = bmpSrc.getWidth();
int bmpSrcHeight = bmpSrc.getHeight();
Bitmap bmpDest = Bitmap.createBitmap(width, height, Config.ARGB_8888);
if (null != bmpDest) {
Canvas canvas = new Canvas(bmpDest);
Rect viewRect = new Rect();
final Rect rect = new Rect(0, 0, bmpSrcWidth, bmpSrcHeight);
if (bmpSrcWidth <= width && bmpSrcHeight <= height) {
viewRect.set(rect);
} else if (bmpSrcHeight > height && bmpSrcWidth <= width) {
viewRect.set(0, 0, bmpSrcWidth, height);
} else if (bmpSrcHeight <= height && bmpSrcWidth > width) {
viewRect.set(0, 0, width, bmpSrcWidth);
} else if (bmpSrcHeight > height && bmpSrcWidth > width) {
viewRect.set(0, 0, width, height);
}
canvas.drawBitmap(bmpSrc, rect, viewRect, null);
}
return bmpDest;
}
/**
* 復(fù)制bitmap
*/
public static Bitmap duplicateBitmap(Bitmap bmpSrc) {
if (null == bmpSrc) {
return null;
}
int bmpSrcWidth = bmpSrc.getWidth();
int bmpSrcHeight = bmpSrc.getHeight();
Bitmap bmpDest = Bitmap.createBitmap(bmpSrcWidth, bmpSrcHeight,
Config.ARGB_8888);
if (null != bmpDest) {
Canvas canvas = new Canvas(bmpDest);
final Rect rect = new Rect(0, 0, bmpSrcWidth, bmpSrcHeight);
canvas.drawBitmap(bmpSrc, rect, rect, null);
}
return bmpDest;
}
/**
* bitmap轉(zhuǎn)字節(jié)碼
*/
public static byte[] bitampToByteArray(Bitmap bitmap) {
byte[] array = null;
try {
if (null != bitmap) {
ByteArrayOutputStream os = new ByteArrayOutputStream();
bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);
array = os.toByteArray();
os.close();
}
} catch (IOException e) {
e.printStackTrace();
}
return array;
}
/**
* 字節(jié)碼轉(zhuǎn)bitmap
*/
public static Bitmap byteArrayToBitmap(byte[] array) {
if (null == array) {
return null;
}
return BitmapFactory.decodeByteArray(array, 0, array.length);
}
}
5.圖像旋轉(zhuǎn),縮放,橡皮擦和染色功能如下:
package com.jiangjie.ps;
import com.jiangjie.utils.PaintConstants;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.BlurMaskFilter;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PointF;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.FloatMath;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
public class ImageTouchView extends ImageView{
public Matrix matrix = new Matrix();
Matrix savedMatrix = new Matrix();
/** 屏幕的分辨率*/
private DisplayMetrics dm;
/** 當(dāng)前模式*/
int mode = PaintConstants.MODE.NONE;
/** 存儲float類型的x,y值,就是你點下的坐標(biāo)的X和Y*/
PointF prev = new PointF();
PointF curPosition = new PointF();
PointF mid = new PointF();
float dist = 1f;
float oldRotation = 0;
float oldDistX = 1f;
float oldDistY = 1f;
/**位圖對象*/
private Bitmap bitmap = null;
private Paint paint;
private Context context;
private Path path;
private Path tempPath;
//定義一個內(nèi)存中的圖片,該圖片將作為緩沖區(qū)
Bitmap cacheBitmap = null;
//定義cacheBitmap上的Canvas對象
Canvas cacheCanvas = null;
private Paint cachePaint = null;
private String TAG = "APP";
int x = 0;
int y = 0;
public ImageTouchView(Context context) {
super(context);
}
public ImageTouchView(Context context, AttributeSet attrs) {
super(context, attrs);
this.context = context;
Log.i(TAG, "ImageTouchView(Context context, AttributeSet attrs)=>");
setupView();
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(mode == PaintConstants.MODE.COLORING){
canvas.drawPath(tempPath, paint);
}
}
public void setupView(){
//獲取屏幕分辨率,需要根據(jù)分辨率來使用圖片居中
dm = getContext().getResources().getDisplayMetrics();
//根據(jù)MyImageView來獲取bitmap對象
BitmapDrawable bd = (BitmapDrawable)this.getDrawable();
if(bd != null){
bitmap = bd.getBitmap();
// bitmap = setBitmapAlpha(bitmap, 100);
center(true, true);
}
setCoverBitmap(bitmap);
this.setImageMatrix(matrix);
this.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
Matrix matrixTemp = new Matrix();
matrixTemp.set(matrix);
//view的觸摸坐標(biāo)的轉(zhuǎn)換
matrixTemp.invert(matrixTemp);
Log.i(TAG, "Touch screen.");
switch (event.getAction() & MotionEvent.ACTION_MASK) {
// 主點按下
case MotionEvent.ACTION_DOWN:
savedMatrix.set(matrix);
prev.set(event.getX(), event.getY());
float[] pointPrevInit = new float[]{prev.x, prev.y};
matrixTemp.mapPoints(pointPrevInit);
path.moveTo(pointPrevInit[0], pointPrevInit[1]);
tempPath.moveTo(event.getX(), event.getY());
mode = PaintConstants.MODE.DRAG;
Log.i(TAG, "ACTION_DOWN=>.");
break;
// 副點按下
case MotionEvent.ACTION_POINTER_DOWN:
dist = spacing(event);
oldRotation = rotation(event);
oldDistX = spacingX(event);
oldDistY = spacingY(event);
// 如果連續(xù)兩點距離大于10,則判定為多點模式
if (spacing(event) > 10f) {
savedMatrix.set(matrix);
midPoint(mid, event);
mode = PaintConstants.MODE.ZOOM;
}
break;
case MotionEvent.ACTION_UP:
Log.i(TAG, "ACTION_UP=>.");
if(mode == PaintConstants.MODE.COLORING){
cachePaint.setColor(PaintConstants.PEN_COLOR);
cachePaint.setStrokeWidth(PaintConstants.PEN_SIZE);
cachePaint.setAlpha(PaintConstants.TRANSPARENT);
cachePaint.setMaskFilter(new BlurMaskFilter(5, PaintConstants.BLUR_TYPE));
cacheCanvas.drawPath(path, cachePaint);
path.reset();
tempPath.reset();
}
break;
case MotionEvent.ACTION_POINTER_UP:
mode = PaintConstants.MODE.NONE;
break;
case MotionEvent.ACTION_MOVE:
if(!PaintConstants.SELECTOR.KEEP_IMAGE){
if (mode == PaintConstants.MODE.DRAG) {
matrix.set(savedMatrix);
matrix.postTranslate(event.getX() - prev.x, event.getY() - prev.y);
} else if (mode == PaintConstants.MODE.ZOOM) {
float rotation = (rotation(event) - oldRotation)/2;
float newDistX = spacingX(event);
float newDistY = spacingY(event);
float scaleX = newDistX-oldDistX;
float scaleY = newDistY-oldDistY;
float newDist = spacing(event);
if (newDist > 10f) {
matrix.set(savedMatrix);
float tScale = newDist / dist;
tScale = tScale>1?1+((tScale-1)/2):1-(1-tScale)/2;
if(PaintConstants.SELECTOR.KEEP_SCALE){
matrix.postScale(tScale, tScale, mid.x, mid.y);// 縮放
}else{
if(Math.abs(scaleX)>=Math.abs(scaleY)){
matrix.postScale(tScale, 1, mid.x, mid.y);// 縮放
}else{
matrix.postScale(1, tScale, mid.x, mid.y);// 縮放
}
}
if(PaintConstants.SELECTOR.HAIR_RURN)
matrix.postRotate(rotation, mid.x, mid.y);// 旋轉(zhuǎn)
}
}
}else{
float[] pointPrev = new float[]{prev.x, prev.y};
float[] pointStop= new float[]{event.getX(), event.getY()};
//view的觸摸坐標(biāo)的轉(zhuǎn)換
matrixTemp.mapPoints(pointPrev);
matrixTemp.mapPoints(pointStop);
if(PaintConstants.SELECTOR.COLORING){
//染色功能
mode = PaintConstants.MODE.COLORING;
paint.reset();
paint = new Paint(Paint.DITHER_FLAG);
paint.setColor(Color.RED);
//設(shè)置畫筆風(fēng)格
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(1);
//反鋸齒
paint.setAntiAlias(true);
paint.setDither(true);
paint.setColor(PaintConstants.PEN_COLOR);
paint.setStrokeWidth(PaintConstants.PEN_SIZE);
path.quadTo(pointPrev[0],pointPrev[1],pointStop[0],pointStop[1]);
tempPath.quadTo(prev.x, prev.y,event.getX(), event.getY());
// 更新開始點的位置
prev.set(event.getX(), event.getY());
ImageTouchView.this.setImageBitmap(cacheBitmap);
}else if(PaintConstants.SELECTOR.ERASE){
//橡皮擦功能
mode = PaintConstants.MODE.ERASE;
paint.reset();
paint.setColor(Color.TRANSPARENT);
paint.setAntiAlias(false);
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeWidth(16);
paint.setStrokeJoin(Paint.Join.ROUND);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setAlpha(0);
paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
paint.setStrokeWidth(PaintConstants.ERASE_SIZE);
prev.set(event.getX(), event.getY());
cacheCanvas.drawLine(pointPrev[0],pointPrev[1],pointStop[0],pointStop[1], paint);
ImageTouchView.this.setImageBitmap(cacheBitmap);
}
}
}
ImageTouchView.this.setImageMatrix(matrix);
invalidate();
return true;
}
});
}
/**
* 橫向、縱向居中
*/
protected void center(boolean horizontal, boolean vertical) {
RectF rect = new RectF(0, 0, bitmap.getWidth(), bitmap.getHeight());
float height = rect.height();
float width = rect.width();
float deltaX = 0, deltaY = 0;
if (vertical) {
// 圖片小于屏幕大小,則居中顯示。大于屏幕,上方留空則往上移,下方留空則往下移
int screenHeight = dm.heightPixels;
if (height < screenHeight) {
deltaY = (screenHeight - height) / 2 - rect.top;
} else if (rect.top > 0) {
deltaY = -rect.top;
} else if (rect.bottom < screenHeight) {
deltaY = this.getHeight() - rect.bottom;
}
}
if (horizontal) {
int screenWidth = dm.widthPixels;
if (width < screenWidth) {
deltaX = (screenWidth - width) / 2 - rect.left;
} else if (rect.left > 0) {
deltaX = -rect.left;
} else if (rect.right < screenWidth) {
deltaX = screenWidth - rect.right;
}
}
matrix.postTranslate(deltaX, deltaY);
}
private float spacingX(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
return x;
}
private float spacingY(MotionEvent event) {
float y = event.getY(0) - event.getY(1);
return y;
}
// 取旋轉(zhuǎn)角度
private float rotation(MotionEvent event) {
double delta_x = (event.getX(0) - event.getX(1));
double delta_y = (event.getY(0) - event.getY(1));
double radians = Math.atan2(delta_y, delta_x);
return (float) Math.toDegrees(radians);
}
/**
* 兩點的距離
*/
private float spacing(MotionEvent event) {
float x = event.getX(0) - event.getX(1);
float y = event.getY(0) - event.getY(1);
return FloatMath.sqrt(x * x + y * y);
}
/**
* 兩點的中點
*/
private void midPoint(PointF point, MotionEvent event) {
float x = event.getX(0) + event.getX(1);
float y = event.getY(0) + event.getY(1);
point.set(x / 2, y / 2);
}
/**
*
* @param bm
* @note set cover bitmap , which overlay on background.
*/
private void setCoverBitmap(Bitmap bitmap) {
// setting paint
paint = new Paint();
cacheBitmap = Bitmap.createBitmap(bitmap.getWidth(), bitmap.getHeight(), Config.ARGB_8888);
cacheCanvas = new Canvas();
cacheCanvas.setBitmap(cacheBitmap);
cacheCanvas.drawBitmap( bitmap, 0, 0, null);
path = new Path();
tempPath = new Path();
//設(shè)置畫筆的顏色
cachePaint = new Paint();
//設(shè)置畫筆風(fēng)格
cachePaint.setStyle(Paint.Style.STROKE);
//反鋸齒
cachePaint.setAntiAlias(true);
cachePaint.setStrokeJoin(Paint.Join.ROUND);
cachePaint.setStrokeCap(Paint.Cap.ROUND);
cachePaint.setXfermode(new PorterDuffXfermode(Mode.SRC_ATOP));
//設(shè)置畫筆模糊效果
cachePaint.setMaskFilter(new BlurMaskFilter(5, PaintConstants.BLUR_TYPE));
}
}
以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
Flutter彈性布局Flex水平排列Row垂直排列Column使用示例
這篇文章主要為大家介紹了Flutter彈性布局Flex水平排列Row垂直排列Column使用示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08
Android自定義View系列之Path繪制仿支付寶支付成功動畫
這篇文章主要為大家詳細(xì)介紹了Android自定義View系列之Path繪制仿支付寶支付成功動畫,具有一定的參考價值,感興趣的小伙伴們可以參考一下2016-12-12
Android控件FlowLikeView實現(xiàn)點贊動畫
這篇文章主要為大家詳細(xì)介紹了一個點贊動畫的優(yōu)雅控件FlowLikeView,具有一定的參考價值,感興趣的小伙伴們可以參考一下2018-06-06
利用SpannableString和ImageSpan在textview中插入圖片的方法
這篇文章主要為大家詳細(xì)介紹了利用SpannableString和ImageSpan在textview中插入圖片的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下2017-11-11
flutter?material?widget組件之信息展示組件使用詳解
這篇文章主要為大家詳細(xì)介紹了flutter?material?widget組件之信息展示組件的使用,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-08-08
Android Studio 3.0 原生支持kotlin 例子詳解
這篇文章主要介紹了 Android Studio 3.0 原生支持kotlin 例子詳解,非常具有實用價值,需要的朋友可以參考下2017-05-05
Android 使用Vitamio打造自己的萬能播放器(9)—— 在線播放 (在線電視)
本文主要介紹Android 使用Vitamio開發(fā)播放器,實現(xiàn)在線電視播放,這里提供效果圖和實例代碼以便大家參考,2016-07-07

