自定义可以拖动的view
package huiwuhan.com.self_headpicture.view;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.Region;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
/**
* 底图缩放,浮层不变
* @author yanglonghui
*
*/
public class CropImageView extends View {
//单点触摸的时候
private float oldX=0;
private float oldY=0;
//多点触摸的时候
private float oldx_0=0;
private float oldy_0=0;
private float oldx_1=0;
private float oldy_1=0;
//状态
private final int STATUS_Touch_SINGLE=1;//单点
private final int STATUS_TOUCH_MULTI_START=2;//多点开始
private final int STATUS_TOUCH_MULTI_TOUCHING=3;//多点拖拽中
private int mStatus=STATUS_Touch_SINGLE;
//默认的裁剪图片宽度与高度
private final int defaultCropWidth=300;
private final int defaultCropHeight=300;
private int cropWidth=defaultCropWidth;
private int cropHeight=defaultCropHeight;
protected float oriRationWH=0;//原始宽高比率
protected final float maxZoomOut=5.0f;//最大扩大到多少倍
protected final float minZoomIn=0.333333f;//最小缩小到多少倍
protected Drawable mDrawable;//原图
protected FloatDrawable mFloatDrawable;//浮层
protected Rect mDrawableSrc = new Rect();
protected Rect mDrawableDst = new Rect();
protected Rect mDrawableFloat = new Rect();//浮层选择框,就是头像选择框
protected boolean isFrist=true;
protected Context mContext;
public CropImageView(Context context) {
super(context);
init(context);
}
public CropImageView(Context context, AttributeSet attrs) {
super(context, attrs);
init(context);
}
public CropImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(context);
}
private void init(Context context)
{
this.mContext=context;
try {
if(android.os.Build.VERSION.SDK_INT>=11)
{
this.setLayerType(LAYER_TYPE_SOFTWARE, null);
}
} catch (Exception e) {
e.printStackTrace();
}
mFloatDrawable=new FloatDrawable(context);//头像选择框
}
public void setDrawable(Drawable mDrawable, int cropWidth, int cropHeight)
{
this.mDrawable=mDrawable;
this.cropWidth=cropWidth;
this.cropHeight=cropHeight;
this.isFrist=true;
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if(event.getPointerCount()>1)
{
if(mStatus==STATUS_Touch_SINGLE)
{
mStatus=STATUS_TOUCH_MULTI_START;
oldx_0=event.getX(0);
oldy_0=event.getY(0);
oldx_1=event.getX(1);
oldy_1=event.getY(1);
}
else if(mStatus==STATUS_TOUCH_MULTI_START)
{
mStatus=STATUS_TOUCH_MULTI_TOUCHING;
}
}
else
{
if(mStatus==STATUS_TOUCH_MULTI_START||mStatus==STATUS_TOUCH_MULTI_TOUCHING)
{
oldx_0=0;
oldy_0=0;
oldx_1=0;
oldy_1=0;
oldX=event.getX();
oldY=event.getY();
}
mStatus=STATUS_Touch_SINGLE;
}
//Log.v("count currentTouch"+currentTouch, "-------");
switch(event.getAction())
{
case MotionEvent.ACTION_DOWN:
//Log.v("count ACTION_DOWN", "-------");
oldX = event.getX();
oldY = event.getY();
break;
case MotionEvent.ACTION_UP:
//Log.v("count ACTION_UP", "-------");
checkBounds();
break;
case MotionEvent.ACTION_POINTER_1_DOWN:
//Log.v("count ACTION_POINTER_1_DOWN", "-------");
break;
case MotionEvent.ACTION_POINTER_UP:
//Log.v("count ACTION_POINTER_UP", "-------");
break;
case MotionEvent.ACTION_MOVE:
//Log.v("count ACTION_MOVE", "-------");
if(mStatus==STATUS_TOUCH_MULTI_TOUCHING)
{
float newx_0=event.getX(0);
float newy_0=event.getY(0);
float newx_1=event.getX(1);
float newy_1=event.getY(1);
float oldWidth= Math.abs(oldx_1-oldx_0);
float oldHeight= Math.abs(oldy_1-oldy_0);
float newWidth= Math.abs(newx_1-newx_0);
float newHeight= Math.abs(newy_1-newy_0);
boolean isDependHeight= Math.abs(newHeight-oldHeight)> Math.abs(newWidth-oldWidth);
float ration=isDependHeight?((float)newHeight/(float)oldHeight):((float)newWidth/(float)oldWidth);
int centerX=mDrawableDst.centerX();
int centerY=mDrawableDst.centerY();
int _newWidth=(int) (mDrawableDst.width()*ration);
int _newHeight=(int) ((float)_newWidth/oriRationWH);
float tmpZoomRation=(float)_newWidth/(float)mDrawableSrc.width();
if(tmpZoomRation>=maxZoomOut)
{
_newWidth=(int) (maxZoomOut*mDrawableSrc.width());
_newHeight=(int) ((float)_newWidth/oriRationWH);
}
else if(tmpZoomRation<=minZoomIn)
{
_newWidth=(int) (minZoomIn*mDrawableSrc.width());
_newHeight=(int) ((float)_newWidth/oriRationWH);
}
mDrawableDst.set(centerX-_newWidth/2, centerY-_newHeight/2, centerX+_newWidth/2, centerY+_newHeight/2);
invalidate();
Log.v("width():"+(mDrawableSrc.width())+"height():"+(mDrawableSrc.height()), "new width():"+(mDrawableDst.width())+"new height():"+(mDrawableDst.height()));
Log.v(""+(float)mDrawableSrc.height()/(float)mDrawableSrc.width(), "mDrawableDst:"+(float)mDrawableDst.height()/(float)mDrawableDst.width());
oldx_0=newx_0;
oldy_0=newy_0;
oldx_1=newx_1;
oldy_1=newy_1;
}
else if(mStatus==STATUS_Touch_SINGLE)
{
int dx=(int)(event.getX()-oldX);
int dy=(int)(event.getY()-oldY);
oldX=event.getX();
oldY=event.getY();
if(!(dx==0&&dy==0))
{
mDrawableDst.offset((int)dx, (int)dy);
invalidate();
}
}
break;
}
// Log.v("event.getAction():"+event.getAction()+"count:"+event.getPointerCount(), "-------getX:"+event.getX()+"--------getY:"+event.getY());
return true;
}
@Override
protected void onDraw(Canvas canvas) {
// super.onDraw(canvas);
if (mDrawable == null) {
return; // couldn't resolve the URI
}
if (mDrawable.getIntrinsicWidth() == 0 || mDrawable.getIntrinsicHeight() == 0) {
return; // nothing to draw (empty bounds)
}
configureBounds();
//Log.v("mTempDst", "---------"+mDrawableDst);
//Log.v("mTempDst:", "---------"+mDrawable.getBounds().width());
//Log.v("mTempDst:", "---------"+mDrawable.getBounds().height());
//Log.v("mTempDst:", "-------ration:"+(float)mDrawable.getBounds().width()/(float)mDrawable.getBounds().height());
mDrawable.draw(canvas);
canvas.save();
canvas.clipRect(mDrawableFloat, Region.Op.DIFFERENCE);
canvas.drawColor(Color.parseColor("#a0000000"));
canvas.restore();
mFloatDrawable.draw(canvas);
}
protected void configureBounds()
{
if(isFrist)
{
oriRationWH=((float)mDrawable.getIntrinsicWidth())/((float)mDrawable.getIntrinsicHeight());
final float scale = mContext.getResources().getDisplayMetrics().density;
int w= Math.min(getWidth(), (int)(mDrawable.getIntrinsicWidth()*scale+0.5f));
int h=(int) (w/oriRationWH);
int left = (getWidth()-w)/2;
int top = (getHeight()-h)/2;
int right=left+w;
int bottom=top+h;
mDrawableSrc.set(left,top,right,bottom);
mDrawableDst.set(mDrawableSrc);
int floatWidth=dipTopx(mContext, cropWidth);
int floatHeight=dipTopx(mContext, cropHeight);
if(floatWidth>getWidth())
{
floatWidth=getWidth();
floatHeight=cropHeight*floatWidth/cropWidth;
}
if(floatHeight>getHeight())
{
floatHeight=getHeight();
floatWidth=cropWidth*floatHeight/cropHeight;
}
int floatLeft=(getWidth()-floatWidth)/2;
int floatTop = (getHeight()-floatHeight)/2;
mDrawableFloat.set(floatLeft, floatTop,floatLeft+floatWidth, floatTop+floatHeight);
isFrist=false;
}
mDrawable.setBounds(mDrawableDst);
mFloatDrawable.setBounds(mDrawableFloat);
}
protected void checkBounds()
{
int newLeft = mDrawableDst.left;
int newTop = mDrawableDst.top;
boolean isChange=false;
if(mDrawableDst.left<-mDrawableDst.width())
{
newLeft=-mDrawableDst.width();
isChange=true;
}
if(mDrawableDst.top<-mDrawableDst.height())
{
newTop=-mDrawableDst.height();
isChange=true;
}
if(mDrawableDst.left>getWidth())
{
newLeft=getWidth();
isChange=true;
}
if(mDrawableDst.top>getHeight())
{
newTop=getHeight();
isChange=true;
}
mDrawableDst.offsetTo(newLeft, newTop);
if(isChange)
{
invalidate();
}
}
public Bitmap getCropImage()
{
Bitmap tmpBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Config.RGB_565);
Canvas canvas = new Canvas(tmpBitmap);
mDrawable.draw(canvas);
Matrix matrix=new Matrix();
float scale=(float)(mDrawableSrc.width())/(float)(mDrawableDst.width());
matrix.postScale(scale, scale);
Bitmap ret = Bitmap.createBitmap(tmpBitmap, mDrawableFloat.left, mDrawableFloat.top, mDrawableFloat.width(), mDrawableFloat.height(), matrix, true);
tmpBitmap.recycle();
tmpBitmap=null;
Bitmap newRet= Bitmap.createScaledBitmap(ret, cropWidth, cropHeight, false);
ret.recycle();
ret=newRet;
return ret;
}
public int dipTopx(Context context, float dpValue) {
final float scale = context.getResources().getDisplayMetrics().density;
return (int) (dpValue * scale + 0.5f);
}
}
浮选框
package huiwuhan.com.self_headpicture.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import huiwuhan.com.self_headpicture.R;
/**
* 头像图片选择框的浮层
*
* @author Administrator
*/
public class FloatDrawable extends Drawable {
private Context mContext;
private Drawable mCropPointDrawable;
private Paint mLinePaint = new Paint();
{
mLinePaint.setARGB(200, 50, 50, 50);
mLinePaint.setStrokeWidth(1F);
mLinePaint.setStyle(Paint.Style.STROKE);
mLinePaint.setAntiAlias(true);
mLinePaint.setColor(Color.WHITE);
}
public FloatDrawable(Context context) {
super();
this.mContext = context;
init();
}
private void init() {
mCropPointDrawable = mContext.getResources().getDrawable(R.mipmap.clip_point);
}
public int getCirleWidth() {
return mCropPointDrawable.getIntrinsicWidth();
}
public int getCirleHeight() {
return mCropPointDrawable.getIntrinsicHeight();
}
@Override
public void draw(Canvas canvas) {
int left = getBounds().left;
int top = getBounds().top;
int right = getBounds().right;
int bottom = getBounds().bottom;
Rect mRect = new Rect(
left + mCropPointDrawable.getIntrinsicWidth() / 2,
top + mCropPointDrawable.getIntrinsicHeight() / 2,
right - mCropPointDrawable.getIntrinsicWidth() / 2,
bottom - mCropPointDrawable.getIntrinsicHeight() / 2);
//方框
canvas.drawRect(mRect, mLinePaint);
//左上
mCropPointDrawable.setBounds(left, top, left + mCropPointDrawable.getIntrinsicWidth(), top + mCropPointDrawable.getIntrinsicHeight());
mCropPointDrawable.draw(canvas);
//右上
mCropPointDrawable.setBounds(right - mCropPointDrawable.getIntrinsicWidth(), top, right, top + mCropPointDrawable.getIntrinsicHeight());
mCropPointDrawable.draw(canvas);
//左下
mCropPointDrawable.setBounds(left, bottom - mCropPointDrawable.getIntrinsicHeight(), left + mCropPointDrawable.getIntrinsicWidth(), bottom);
mCropPointDrawable.draw(canvas);
//右下
mCropPointDrawable.setBounds(right - mCropPointDrawable.getIntrinsicWidth(), bottom - mCropPointDrawable.getIntrinsicHeight(), right, bottom);
mCropPointDrawable.draw(canvas);
}
@Override
public void setBounds(Rect bounds) {
super.setBounds(new Rect(
bounds.left - mCropPointDrawable.getIntrinsicWidth() / 2,
bounds.top - mCropPointDrawable.getIntrinsicHeight() / 2,
bounds.right + mCropPointDrawable.getIntrinsicWidth() / 2,
bounds.bottom + mCropPointDrawable.getIntrinsicHeight() / 2));
}
@Override
public void setAlpha(int alpha) {
// TODO Auto-generated method stub
}
@Override
public void setColorFilter(ColorFilter cf) {
// TODO Auto-generated method stub
}
@Override
public int getOpacity() {
// TODO Auto-generated method stub
return 0;
}
}
布局文件
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/activity_main"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="huiwuhan.com.self_headpicture.activity.SecondActivity">
<huiwuhan.com.self_headpicture.view.CropImageView
android:id="@+id/cropImg"
android:layout_weight="1.0"
android:layout_width="fill_parent"
android:layout_height="0dp"/>
<Button
android:id="@+id/save"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="保存"/>
</LinearLayout>
Activity中的逻辑
package huiwuhan.com.self_headpicture.activity;
import android.content.Intent;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import huiwuhan.com.self_headpicture.R;
import huiwuhan.com.self_headpicture.utils.FileUtil;
import huiwuhan.com.self_headpicture.view.CropImageView;
public class SecondActivity extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.second_main);
final CropImageView mView = (CropImageView) findViewById(R.id.cropImg);
mView.setDrawable(getResources().getDrawable(R.mipmap.precrop),200,200);
findViewById(R.id.save).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
new Thread(new Runnable(){
@Override
public void run() {
FileUtil.writeImage(mView.getCropImage(), FileUtil.SDCARD_PAHT+"/crop.png", 100);
Intent mIntent=new Intent();
mIntent.putExtra("cropImagePath", FileUtil.SDCARD_PAHT+"/crop.png");
setResult(RESULT_OK, mIntent);
finish();
}
}).start();
}
});
}
}
工具类
package huiwuhan.com.self_headpicture.utils;
import android.graphics.Bitmap;
import android.os.Environment;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.RandomAccessFile;
import huiwuhan.com.self_headpicture.applications.SelfHeadPictureApplication;
/**
* 与文件相关的类,主要负责文件的读写
*
* @author 杨龙辉 2012.04.07
*/
public final class FileUtil {
// ------------------------------ 手机系统相关 ------------------------------
public static final String NEWLINE = System.getProperty("line.separator");// 系统的换行符
public static final String APPROOT = "UMMoka";// 程序的根目录
public static final String ASSERT_PATH = "file:///android_asset";//apk的assert目录
public static final String RES_PATH = "file:///android_res";//apk的assert目录
//----------------------------------存放文件的路径后缀------------------------------------
public static final String CACHE_IMAGE_SUFFIX = File.separator + APPROOT + File.separator + "images" + File.separator;
public static final String CACHE_VOICE_SUFFIX = File.separator + APPROOT + File.separator + "voice" + File.separator;
public static final String CACHE_MATERIAL_SUFFIX = File.separator + APPROOT + File.separator + "material" + File.separator;
public static final String LOG_SUFFIX = File.separator + APPROOT + File.separator + "Log" + File.separator;
// ------------------------------------数据的缓存目录-------------------------------------------------------
public static String SDCARD_PAHT;// SD卡路径
public static String LOCAL_PATH;// 本地路径,即/data/data/目录下的程序私有目录
public static String CURRENT_PATH = "";// 当前的路径,如果有SD卡的时候当前路径为SD卡,如果没有的话则为程序的私有目录
static {
init();
}
public static void init() {
SDCARD_PAHT = Environment.getExternalStorageDirectory().getPath();// SD卡路径
LOCAL_PATH = SelfHeadPictureApplication.getInstance().getApplicationContext().getFilesDir().getAbsolutePath();// 本地路径,即/data/data/目录下的程序私有目录
if (android.os.Environment.getExternalStorageState().equals(android.os.Environment.MEDIA_MOUNTED)) {
CURRENT_PATH = SDCARD_PAHT;
} else {
CURRENT_PATH = LOCAL_PATH;
}
}
/**
* 得到与当前存储路径相反的路径(当前为/data/data目录,则返回/sdcard目录;当前为/sdcard,则返回/data/data目录)
*
* @return
*/
public static String getDiffPath() {
if (CURRENT_PATH.equals(SDCARD_PAHT)) {
return LOCAL_PATH;
}
return SDCARD_PAHT;
}
public static String getDiffPath(String pathIn) {
return pathIn.replace(CURRENT_PATH, getDiffPath());
}
// ------------------------------------文件的相关方法--------------------------------------------
/**
* 将数据写入一个文件
*
* @param destFilePath 要创建的文件的路径
* @param data 待写入的文件数据
* @param startPos 起始偏移量
* @param length 要写入的数据长度
* @return 成功写入文件返回true, 失败返回false
*/
public static boolean writeFile(String destFilePath, byte[] data, int startPos, int length) {
try {
if (!createFile(destFilePath)) {
return false;
}
FileOutputStream fos = new FileOutputStream(destFilePath);
fos.write(data, startPos, length);
fos.flush();
if (null != fos) {
fos.close();
fos = null;
}
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 从一个输入流里写文件
*
* @param destFilePath 要创建的文件的路径
* @param in 要读取的输入流
* @return 写入成功返回true, 写入失败返回false
*/
public static boolean writeFile(String destFilePath, InputStream in) {
try {
if (!createFile(destFilePath)) {
return false;
}
FileOutputStream fos = new FileOutputStream(destFilePath);
int readCount = 0;
int len = 1024;
byte[] buffer = new byte[len];
while ((readCount = in.read(buffer)) != -1) {
fos.write(buffer, 0, readCount);
}
fos.flush();
if (null != fos) {
fos.close();
fos = null;
}
if (null != in) {
in.close();
in = null;
}
return true;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
public static boolean appendFile(String filename, byte[] data, int datapos, int datalength) {
try {
createFile(filename);
RandomAccessFile rf = new RandomAccessFile(filename, "rw");
rf.seek(rf.length());
rf.write(data, datapos, datalength);
if (rf != null) {
rf.close();
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
/**
* 读取文件,返回以byte数组形式的数据
*
* @param filePath 要读取的文件路径名
* @return
*/
public static byte[] readFile(String filePath) {
try {
if (isFileExist(filePath)) {
FileInputStream fi = new FileInputStream(filePath);
return readInputStream(fi);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* 从一个数量流里读取数据,返回以byte数组形式的数据。
* </br></br>
* 需要注意的是,如果这个方法用在从本地文件读取数据时,一般不会遇到问题,但如果是用于网络操作,就经常会遇到一些麻烦(available()方法的问题)。所以如果是网络流不应该使用这个方法。
*
* @param in 要读取的输入流
* @return
* @throws IOException
*/
public static byte[] readInputStream(InputStream in) {
try {
ByteArrayOutputStream os = new ByteArrayOutputStream();
byte[] b = new byte[in.available()];
int length = 0;
while ((length = in.read(b)) != -1) {
os.write(b, 0, length);
}
b = os.toByteArray();
in.close();
in = null;
os.close();
os = null;
return b;
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
/**
* 读取网络流
*
* @param in
* @return
*/
public static byte[] readNetWorkInputStream(InputStream in) {
ByteArrayOutputStream os = null;
try {
os = new ByteArrayOutputStream();
int readCount = 0;
int len = 1024;
byte[] buffer = new byte[len];
while ((readCount = in.read(buffer)) != -1) {
os.write(buffer, 0, readCount);
}
in.close();
in = null;
return os.toByteArray();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (null != os) {
try {
os.close();
} catch (IOException e) {
e.printStackTrace();
}
os = null;
}
}
return null;
}
/**
* 将一个文件拷贝到另外一个地方
*
* @param sourceFile 源文件地址
* @param destFile 目的地址
* @param shouldOverlay 是否覆盖
* @return
*/
public static boolean copyFiles(String sourceFile, String destFile, boolean shouldOverlay) {
try {
if (shouldOverlay) {
deleteFile(destFile);
}
FileInputStream fi = new FileInputStream(sourceFile);
writeFile(destFile, fi);
return true;
} catch (FileNotFoundException e) {
e.printStackTrace();
}
return false;
}
/**
* 判断文件是否存在
*
* @param filePath 路径名
* @return
*/
public static boolean isFileExist(String filePath) {
File file = new File(filePath);
return file.exists();
}
/**
* 创建一个文件,创建成功返回true
*
* @param filePath
* @return
*/
public static boolean createFile(String filePath) {
try {
File file = new File(filePath);
if (!file.exists()) {
if (!file.getParentFile().exists()) {
file.getParentFile().mkdirs();
}
return file.createNewFile();
}
} catch (IOException e) {
e.printStackTrace();
}
return true;
}
/**
* 删除一个文件
*
* @param filePath 要删除的文件路径名
* @return true if this file was deleted, false otherwise
*/
public static boolean deleteFile(String filePath) {
try {
File file = new File(filePath);
if (file.exists()) {
return file.delete();
}
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
/**
* 删除 directoryPath目录下的所有文件,包括删除删除文件夹
*
* @param
*/
public static void deleteDirectory(File dir) {
if (dir.isDirectory()) {
File[] listFiles = dir.listFiles();
for (int i = 0; i < listFiles.length; i++) {
deleteDirectory(listFiles[i]);
}
}
dir.delete();
}
/**
* 字符串转流
*
* @param str
* @return
*/
public static InputStream String2InputStream(String str) {
ByteArrayInputStream stream = new ByteArrayInputStream(str.getBytes());
return stream;
}
/**
* 流转字符串
*
* @param is
* @return
*/
public static String inputStream2String(InputStream is) {
BufferedReader in = new BufferedReader(new InputStreamReader(is));
StringBuffer buffer = new StringBuffer();
String line = "";
try {
while ((line = in.readLine()) != null) {
buffer.append(line);
}
} catch (IOException e) {
e.printStackTrace();
}
return buffer.toString();
}
//批量更改文件后缀
public static void reNameSuffix(File dir, String oldSuffix, String newSuffix) {
if (dir.isDirectory()) {
File[] listFiles = dir.listFiles();
for (int i = 0; i < listFiles.length; i++) {
reNameSuffix(listFiles[i], oldSuffix, newSuffix);
}
} else {
dir.renameTo(new File(dir.getPath().replace(oldSuffix, newSuffix)));
}
}
public static void writeImage(Bitmap bitmap, String destPath, int quality) {
try {
FileUtil.deleteFile(destPath);
if (FileUtil.createFile(destPath)) {
FileOutputStream out = new FileOutputStream(destPath);
if (bitmap.compress(Bitmap.CompressFormat.JPEG, quality, out)) {
out.flush();
out.close();
out = null;
}
}
} catch (IOException e) {
e.printStackTrace();
}
}
}
本文介绍了一个自定义的Android视图CropImageView,用于实现图片的裁剪功能。该视图支持用户通过触控来调整裁剪区域的大小与位置,并提供了相应的逻辑处理与绘制方法。
1万+

被折叠的 条评论
为什么被折叠?



