android圖片編輯,類似PC端QQ截圖後的編輯效果(畫箭頭,圓形,矩形標註)
最近再做聊天功能,遇到需求傳送圖片前編輯,查了好多沒見一個合適的,就自己寫了一個,希望對看到的人有所幫助(拋磚引玉),因為時間倉促請忽視編碼風格,選擇圖片參考http://www.jianshu.com/p/ae25718a1f05
-箭頭原理圖
-效果圖
-佈局程式碼
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:id="@+id/ll"
android:layout_weight="1"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/iv"
android:scaleType="center"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp">
<Button
android:layout_weight="1"
android:onClick="pics"
android:text="相簿"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="photo"
android:text="拍照"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:background="@android:color/holo_red_dark"
android:onClick="red"
android:text="紅"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:background="@android:color/holo_green_dark"
android:onClick="green"
android:text="綠"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:background="@android:color/holo_blue_dark"
android:onClick="blue"
android:text="藍"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp">
<Button
android:layout_weight="1"
android:onClick="small"
android:text="細"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="zhong"
android:text="中"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="big"
android:text="粗"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="arrow"
android:text="箭頭"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="fang"
android:text="方"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="circle"
android:text="圓"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</LinearLayout>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="40dp">
<Button
android:layout_weight="1"
android:onClick="one"
android:text="單步撤銷"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="all"
android:text="全部撤銷"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
<Button
android:layout_weight="1"
android:onClick="save"
android:text="儲存"
android:layout_width="wrap_content"
android:layout_height="match_parent"/>
</LinearLayout>
</LinearLayout>
-Activity程式碼
import android.Manifest;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentUris;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.database.Cursor;
import android.graphics.*;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.provider.DocumentsContract;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.Toast;
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
public class MainActivity extends Activity {
private static final int CAMERA_CODE = 3022;
private static final int SD_CODE = 3020;
private ArrayList<Shapes> shapes = new ArrayList<Shapes>();
private ImageView iv;//展示圖片
private Bitmap copyPic;//編輯圖片
private Canvas canvas;//畫板
private Paint paint;//畫筆
private Matrix matrix;//矩陣
private Bitmap srcPic;//原圖
private int color = Color.BLACK;//畫筆顏色
private int width = 0;//畫筆大小
private int circle;//形狀
/* 用來標識請求照相功能的activity */
private static final int CAMERA_WITH_DATA = 3023;
/* 用來標識請求gallery的activity */
private static final int PHOTO_PICKED_WITH_DATA = 3021;
private String photoPath, camera_path, tempPhotoPath;
//圖片儲存路徑
public static final String filePath = Environment.getExternalStorageDirectory() + "/PictureTest/";
private int screenWidth;
private File mCurrentPhotoFile;
private LinearLayout ll;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ll = (LinearLayout) findViewById(R.id.ll);
iv = (ImageView) findViewById(R.id.iv);
DisplayMetrics metric = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(metric);
// 螢幕寬度(畫素)
screenWidth = metric.widthPixels;
}
/**
* 畫畫
*/
private void drawPic() {
srcPic = BitmapFactory.decodeFile(camera_path);
copyPic = Bitmap.createBitmap(srcPic.getWidth(), srcPic.getHeight(),
srcPic.getConfig());
iv.setLayoutParams(new LinearLayout.LayoutParams(
srcPic.getWidth(), srcPic.getHeight()));
canvas = new Canvas(copyPic);
paint = new Paint();
paint.setAntiAlias(true);
//繪製原圖
drawOld();
iv.setImageBitmap(copyPic);
//觸控事件
iv.setOnTouchListener(new View.OnTouchListener() {
private float endY;
private float endX;
private float startX;
private float startY;
@Override
public boolean onTouch(View v, MotionEvent event) {
int action = event.getAction();
switch (action) {
case MotionEvent.ACTION_DOWN:// 按下的事件型別
startX = event.getX();
startY = event.getY();
drawGuiji();
break;
case MotionEvent.ACTION_MOVE:// 移動的事件型別
// 得到結束位置的座標點
endX = event.getX();
endY = event.getY();
// 清除之前軌跡
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.CLEAR));
canvas.drawPaint(paint);
drawGuiji();
paint.setStrokeWidth(width);
paint.setColor(color);
if (circle == 1) {
paint.setStyle(Paint.Style.STROKE);//設定邊框
canvas.drawRect(startX, startY, endX, endY, paint);// 正方形
} else if (circle == 0) {
paint.setStyle(Paint.Style.STROKE);//設定邊框
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
canvas.drawOval(startX, startY, endX, endY, paint);
}
} else if (circle == 2) {
paint.setStyle(Paint.Style.FILL);//設定邊框
drawArrow(startX, startY, endX, endY, width, paint);
}
iv.setImageBitmap(copyPic);
break;
case MotionEvent.ACTION_UP:// 移動的事件型別
shapes.add(new Shapes(startX, startY, endX, endY, width, paint.getColor(), circle));//儲存歷史軌跡
break;
}
return true;
}
});
}
private void drawGuiji() {
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC));
drawOld();
for (Shapes sp : shapes) {//畫歷史軌跡
paint.setColor(sp.color);
paint.setStrokeWidth(sp.width);
if (sp.circle == 1) {
paint.setStyle(Paint.Style.STROKE);//設定邊框
canvas.drawRect(sp.startX, sp.startY, sp.endX, sp.endY, paint);// 正方形
} else if (sp.circle == 0) {
paint.setStyle(Paint.Style.STROKE);//設定邊框
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//api21之後的方法
canvas.drawOval(sp.startX, sp.startY, sp.endX, sp.endY, paint);//橢圓
}
} else if (sp.circle == 2) {
paint.setStyle(Paint.Style.FILL);//設定邊框
drawArrow(sp.startX, sp.startY, sp.endX, sp.endY, sp.width, paint);//箭頭
}
}
iv.setImageBitmap(copyPic);
}
/**
* 繪製底圖
*/
private void drawOld() {
// 給畫筆設定預設的顏色,在畫畫的過程中會使用原圖的顏色來畫畫
paint.setColor(Color.BLACK);
// 處理圖形
matrix = new Matrix();
// 5、使用畫筆在畫板上畫畫
// 參看原圖畫畫
// srcPic 原圖
// matrix 表示圖形的矩陣物件,封裝了處理圖形的api
// paint 畫畫時使用的畫筆
canvas.drawBitmap(srcPic, matrix, paint);
}
/**
* 紅色按鈕
*
* @param view
*/
public void red(View view) {
color = Color.RED;
}
/**
* 綠色按鈕
*
* @param view
*/
public void green(View view) {
color = Color.GREEN;
}
/**
* 藍色按鈕
*
* @param view
*/
public void blue(View view) {
color = Color.BLUE;
}
public void small(View view) {
//改變刷子的寬度
width = 1;
}
public void zhong(View view) {
//改變刷子的寬度
width = 5;
}
public void big(View view) {
//改變刷子的寬度
width = 10;
}
/**
* 圓形
*
* @param view
*/
public void circle(View view) {
circle = 0;
}
/**
* 矩形
*
* @param view
*/
public void fang(View view) {
circle = 1;
}
/**
* 矩形
*
* @param view
*/
public void arrow(View view) {
circle = 2;
}
/**
* 相簿
*
* @param view
*/
public void pics(View view) {
//申請照相機許可權
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.WRITE_EXTERNAL_STORAGE)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_EXTERNAL_STORAGE},
SD_CODE);
} else {
getPictureFromPhoto();
}
}else {
getPictureFromPhoto();
}
}
/**
* 拍照
*
* @param view
*/
public void photo(View view) {
//申請照相機許可權
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
if (checkSelfPermission(Manifest.permission.CAMERA)
!= PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[]{Manifest.permission.CAMERA},
CAMERA_CODE);
} else {
getPictureFromCamera();
}
}else {
getPictureFromCamera();
}
}
/**
* 單步撤銷
*
* @param view
*/
public void one(View view) {
int size = shapes.size();
if (size > 0) {
shapes.remove(size - 1);
drawGuiji();
}
}
/**
* 全部撤銷
*
* @param view
*/
public void all(View view) {
shapes.clear();
drawGuiji();
}
/**
* 儲存編輯圖片
*
* @param view
*/
public void save(View view) {
saveBitmap(copyPic, getNewFileName());
}
/**
* 畫箭頭
*
* @param sx
* @param sy
* @param ex
* @param ey
* @param paint
*/
private void drawArrow(float sx, float sy, float ex, float ey, int width, Paint paint) {
int size = 5;
int count = 20;
switch (width) {
case 0:
size = 5;
count = 20;
break;
case 5:
size = 8;
count = 30;
break;
case 10:
size = 11;
count = 40;
break;
}
float x = ex - sx;
float y = ey - sy;
double d = x * x + y * y;
double r = Math.sqrt(d);
float zx = (float) (ex - (count * x / r));
float zy = (float) (ey - (count * y / r));
float xz = zx - sx;
float yz = zy - sy;
double zd = xz * xz + yz * yz;
double zr = Math.sqrt(zd);
Path triangle = new Path();
triangle.moveTo(sx, sy);
triangle.lineTo((float) (zx + size * yz / zr), (float) (zy - size * xz / zr));
triangle.lineTo((float) (zx + size * 2 * yz / zr), (float) (zy - size * 2 * xz / zr));
triangle.lineTo(ex, ey);
triangle.lineTo((float) (zx - size * 2 * yz / zr), (float) (zy + size * 2 * xz / zr));
triangle.lineTo((float) (zx - size * yz / zr), (float) (zy + size * xz / zr));
triangle.close();
canvas.drawPath(triangle, paint);
}
/* 從相簿中獲取照片 */
private void getPictureFromPhoto() {
Intent openphotoIntent = new Intent(Intent.ACTION_PICK,
android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
startActivityForResult(openphotoIntent, PHOTO_PICKED_WITH_DATA);
}
/* 從相機中獲取照片 */
private void getPictureFromCamera() {
Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
tempPhotoPath = filePath + getNewFileName()
+ ".png";
mCurrentPhotoFile = new File(tempPhotoPath);
if (!mCurrentPhotoFile.exists()) {
try {
mCurrentPhotoFile.createNewFile();
} catch (IOException e) {
e.printStackTrace();
}
}
intent.putExtra(MediaStore.EXTRA_OUTPUT,
Uri.fromFile(mCurrentPhotoFile));
startActivityForResult(intent, CAMERA_WITH_DATA);
}
/**
* 根據時間戳生成檔名
*
* @return
*/
public static String getNewFileName() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMddHHmmss");
Date curDate = new Date(System.currentTimeMillis());
return formatter.format(curDate);
}
/**
* 將生成的圖片儲存到記憶體中
*
* @param bitmap
* @param name
* @return
*/
public String saveBitmap(Bitmap bitmap, String name) {
if (Environment.getExternalStorageState().equals(
Environment.MEDIA_MOUNTED)) {
File dir = new File(filePath);
if (!dir.exists())
dir.mkdir();
File file = new File(filePath + name + ".jpg");
FileOutputStream out;
try {
out = new FileOutputStream(file);
if (bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out)) {
out.flush();
out.close();
}
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
}
return null;
}
/**
* 根據路徑獲取圖片並且壓縮,適應view
*
* @param filePath 圖片路徑
* @param contentView 適應的view
* @return Bitmap 壓縮後的圖片
*/
public Bitmap compressionFiller(String filePath, View contentView) {
BitmapFactory.Options opt = new BitmapFactory.Options();
opt.inPreferredConfig = Bitmap.Config.RGB_565;
opt.inPurgeable = true;
opt.inInputShareable = true;
Bitmap bitmap = BitmapFactory.decodeFile(filePath, opt);
int layoutHeight = contentView.getHeight();
float scale = 0f;
int bitmapHeight = bitmap.getHeight();
int bitmapWidth = bitmap.getWidth();
scale = bitmapHeight > bitmapWidth
? layoutHeight / (bitmapHeight * 1f)
: screenWidth / (bitmapWidth * 1f);
Bitmap resizeBmp;
if (scale != 0) {
int bitmapheight = bitmap.getHeight();
int bitmapwidth = bitmap.getWidth();
Matrix matrix = new Matrix();
matrix.postScale(scale, scale); // 長和寬放大縮小的比例
resizeBmp = Bitmap.createBitmap(bitmap, 0, 0, bitmapwidth,
bitmapheight, matrix, true);
} else {
resizeBmp = bitmap;
}
return resizeBmp;
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode != RESULT_OK) {
return;
}
switch (requestCode) {
case CAMERA_WITH_DATA://拍照
photoPath = tempPhotoPath;
break;
case PHOTO_PICKED_WITH_DATA://相簿
Uri selectedImage = data.getData();
photoPath = getImageAbsolutePath(MainActivity.this, selectedImage);
break;
}
shapes.clear();//軌跡清空
Bitmap bitmap = compressionFiller(photoPath, ll);//圖片縮放
camera_path = saveBitmap(bitmap, "saveTemp");
drawPic();
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
if (requestCode == CAMERA_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// 呼叫相機
getPictureFromCamera();
} else {
// 沒有許可權
Toast.makeText(MainActivity.this, "沒有許可權", Toast.LENGTH_SHORT).show();
}
}
if (requestCode == SD_CODE) {
if (grantResults[0] == PackageManager.PERMISSION_GRANTED && grantResults[1] == PackageManager.PERMISSION_GRANTED) {
// 呼叫相簿
getPictureFromPhoto();
} else {
// 沒有許可權
Toast.makeText(MainActivity.this, "沒有許可權", Toast.LENGTH_SHORT).show();
}
}
}
/**
* 根據Uri獲取圖片絕對路徑,解決Android4.4以上版本Uri轉換
*
* @param context
* @param imageUri
* @return
*/
@TargetApi(19)
public String getImageAbsolutePath(Context context, Uri imageUri) {
if (context == null || imageUri == null)
return null;
if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.KITKAT && DocumentsContract.isDocumentUri(context, imageUri)) {
if (isExternalStorageDocument(imageUri)) {
String docId = DocumentsContract.getDocumentId(imageUri);
String[] split = docId.split(":");
String type = split[0];
if ("primary".equalsIgnoreCase(type)) {
return Environment.getExternalStorageDirectory() + "/" + split[1];
}
} else if (isDownloadsDocument(imageUri)) {
String id = DocumentsContract.getDocumentId(imageUri);
Uri contentUri = ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"), Long.valueOf(id));
return getDataColumn(context, contentUri, null, null);
} else if (isMediaDocument(imageUri)) {
String docId = DocumentsContract.getDocumentId(imageUri);
String[] split = docId.split(":");
String type = split[0];
Uri contentUri = null;
if ("image".equals(type)) {
contentUri = MediaStore.Images.Media.EXTERNAL_CONTENT_URI;
} else if ("video".equals(type)) {
contentUri = MediaStore.Video.Media.EXTERNAL_CONTENT_URI;
} else if ("audio".equals(type)) {
contentUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
}
String selection = MediaStore.Images.Media._ID + "=?";
String[] selectionArgs = new String[]{split[1]};
return getDataColumn(context, contentUri, selection, selectionArgs);
}
} else if ("content".equalsIgnoreCase(imageUri.getScheme())) {
if (isGooglePhotosUri(imageUri))
return imageUri.getLastPathSegment();
return getDataColumn(context, imageUri, null, null);
} else if ("file".equalsIgnoreCase(imageUri.getScheme())) {
return imageUri.getPath();
}
return null;
}
/**
* 4.4以前
* @param context
* @param uri
* @param selection
* @param selectionArgs
* @return
*/
public String getDataColumn(Context context, Uri uri, String selection, String[] selectionArgs) {
Cursor cursor = null;
String column = MediaStore.Images.Media.DATA;
String[] projection = {column};
try {
cursor = context.getContentResolver().query(uri, projection, selection, selectionArgs, null);
if (cursor != null && cursor.moveToFirst()) {
int index = cursor.getColumnIndexOrThrow(column);
return cursor.getString(index);
}
} finally {
if (cursor != null)
cursor.close();
}
return null;
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is ExternalStorageProvider.
*/
public boolean isExternalStorageDocument(Uri uri) {
return "com.android.externalstorage.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is DownloadsProvider.
*/
public boolean isDownloadsDocument(Uri uri) {
return "com.android.providers.downloads.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is MediaProvider.
*/
public boolean isMediaDocument(Uri uri) {
return "com.android.providers.media.documents".equals(uri.getAuthority());
}
/**
* @param uri The Uri to check.
* @return Whether the Uri authority is Google Photos.
*/
public boolean isGooglePhotosUri(Uri uri) {
return "com.google.android.apps.photos.content".equals(uri.getAuthority());
}
}
-儲存軌跡的實體類Shapes
class Shapes {
public float startX, startY, endX, endY ;
public int width,color,circle;
public Shapes(float startX, float startY, float endX, float endY, int width, int color , int circle) {
this.startX = startX;
this.startY = startY;
this.endX = endX;
this.endY = endY;
this.width = width;
this.color = color;
this.circle = circle;
}
}
如果這些不是你想要的效果,可以參考大神的專案,這個效果很全,而且做了封裝https://github.com/jarlen/PhotoEditDemo
相關文章
- windows10截圖後怎麼編輯圖片文字_win10在截圖上編輯文字的方法WindowsWin10
- 能夠輕鬆編輯畫報的圖片編輯軟體
- canvas圖形編輯器Canvas
- Acorn for Mac - 圖片編輯Mac
- Qt/C++地圖動態繪製折線多邊形矩形圓形標註點/可編輯拖動調整大小和位置QTC++地圖
- CSS圓形圖片效果CSS
- 極簡圖片編輯工具
- Posterino for Mac圖片編輯器Mac
- Artstudio Pro for Mac(繪畫和圖片編輯工具)Mac
- 向量圖形編輯控制元件控制元件
- Android仿微信圖片編輯——塗鴉框架Doodle(多功能畫板)Android框架
- win10平板怎麼圖片編輯_win10平板如何批次編輯圖片Win10
- win10平板怎麼圖片編輯_win10平板如何批量編輯圖片Win10
- 圖片編輯軟體Posterino for MacMac
- IDEA 修改編輯背景圖片Idea
- css底部帶有三角形箭頭的圓角矩形效果CSS
- 圖片編輯工具:FotoJet Photo Editor更好的處理圖片
- 截圖表格轉可編輯Word文字教程
- 箭頭→箭頭 matlab畫圖Matlab
- Splash n Recolor for Mac(圖片編輯工具)Mac
- zGallery for Mac圖片檢視編輯工具Mac
- Darkroom for Mac(圖片視訊編輯器)OOMMac
- GraphicConverter 11 Mac(圖片編輯器)Mac
- Mac圖片拼貼編輯器:PosterinoMac
- 一個基於canvas的移動端圖片編輯器Canvas
- Boxy SVG for Mac - 向量圖形編輯器SVGMac
- 強大好用的圖片編輯效果實現,支援塗鴉、編輯、馬賽克,可撤銷和恢復
- DrawPad for Mac(圖形編輯軟體) v6.74註冊版Mac
- 在 Linux 下截圖並編輯的最佳工具Linux
- Artstudio Pro Mac,繪圖與圖片編輯工具Mac繪圖
- Artstudio Pro Mac,繪圖和圖片編輯工具Mac繪圖
- 《Android開發卷——設定圓形頭像,Android擷取圓形圖片》Android
- win10自帶圖片編輯器在哪裡_win10如何使用自帶圖片編輯器Win10
- KindEditor編輯器的圖片上傳問題
- Movavi Picverse Photo Editor Mac(圖片編輯器)Mac
- android圓形頭像的選擇和剪下並儲存出圓形圖片Android
- css空心三角形箭頭矩形效果CSS
- Artstudio Pro Mac(繪圖與圖片編輯軟體)Mac繪圖