拍照、拍照裁剪
相册选取,相册裁剪
以上2篇博客是之前总结的,接下来我们就了解下开源库如何使用相册裁剪的
本博客代码下载 点击打开链接,免积分下载
以前写过2篇关于相册选取、裁剪的demo,今天我们来学习下github上一款开源的相册裁剪开源库
开源库地址 https://github.com/ArthurHub/Android-Image-Cropper
首先我先说下这个开源库需要添加的东东
上面截图中CropImage可以不用,我代码中用的自己的圆角代码
<string name="crop_image_activity_title"></string>
<string name="crop_image_menu_rotate_left">Rotate counter clockwise</string>
<string name="crop_image_menu_rotate_right">Rotate</string>
<string name="crop_image_menu_crop">Crop</string>
<string name="pick_image_intent_chooser_title">Select source</string>
上面是string几个string 字体(我直接就从作者的开源代码中直接拿过来的)
还有attrs.xml中需要的属性
<declare-styleable name="CropImageView">
<attr name="cropGuidelines">
<enum name="off" value="0" />
<enum name="onTouch" value="1" />
<enum name="on" value="2" />
</attr>
<attr name="cropScaleType">
<enum name="fitCenter" value="0" />
<enum name="center" value="1" />
<enum name="centerCrop" value="2" />
<enum name="centerInside" value="3" />
</attr>
<attr name="cropShape">
<enum name="rectangle" value="0" />
<enum name="oval" value="1" />
</attr>
<attr name="cropAutoZoomEnabled" format="boolean" />
<attr name="cropMaxZoom" format="integer" />
<attr name="cropFixAspectRatio" format="boolean" />
<attr name="cropAspectRatioX" format="integer" />
<attr name="cropAspectRatioY" format="integer" />
<attr name="cropInitialCropWindowPaddingRatio" format="float" />
<attr name="cropBorderLineThickness" format="dimension" />
<attr name="cropBorderLineColor" format="color" />
<attr name="cropBorderCornerThickness" format="dimension" />
<attr name="cropBorderCornerOffset" format="dimension" />
<attr name="cropBorderCornerLength" format="dimension" />
<attr name="cropBorderCornerColor" format="color" />
<attr name="cropGuidelinesThickness" format="dimension" />
<attr name="cropGuidelinesColor" format="color" />
<attr name="cropBackgroundColor" format="color" />
<attr name="cropSnapRadius" format="dimension" />
<attr name="cropTouchRadius" format="dimension" />
<attr name="cropShowCropOverlay" format="boolean" />
<attr name="cropShowProgressBar" format="boolean" />
<attr name="cropMinCropWindowWidth" format="dimension" />
<attr name="cropMinCropWindowHeight" format="dimension" />
<attr name="cropMinCropResultWidthPX" format="float" />
<attr name="cropMinCropResultHeightPX" format="float" />
<attr name="cropMaxCropResultWidthPX" format="float" />
<attr name="cropMaxCropResultHeightPX" format="float" />
</declare-styleable>
基本上这个开源库,就需要这些东东就行了
今天我们就写一个小例子,来学习下,能够将代码跑起来,就ok了
首先看我代码中的主布局文件,很简单,只有一个圆角控件,点击它以后,就掉系统的相册应用
<RelativeLayout 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"
tools:context="com.example.cropimageview.MainActivity" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="182dp"
android:layout_marginBottom="28dp"
android:background="#383838" >
<com.example.cropimageview.RoundImageView
android:id="@+id/contactIcon"
android:layout_width="103dp"
android:layout_height="103dp"
android:layout_centerInParent="true"
android:background="@drawable/ic_launcher" />
</RelativeLayout>
</RelativeLayout>
在MainActivity中是如何触发操作的呢?
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/jpeg");
startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE);
不同的手机调取的流程是不一样的(我用的是魅族5),不管如何调用,当你选中你要裁剪的图片(这里还没有进入裁剪页面)就会在onActivityResult带结果返回,这个方法中进入
case REQUEST_CODE_SELECT_IMAGE:
Log.i("Safly", "onActivityResult REQUEST_CODE_SELECT_IMAGE");
Intent intent = new Intent(this,CropImageActivity.class);
intent.setData(data.getData());
startActivityForResult(intent,REQUEST_CODE_IMAGE_CROP);
break;
我们做了什么操作呢?我们紧接着又启动了一个利用开源库的裁剪功能的页面,页面是什么样子的呢? 我们看下布局文件的效果图吧吧,我说下即可(一个返回,一个确定,还有一个CropImageView开源库中的自定义FrameLayout)
那个小裁剪方块可以拖动,可以扩展,待我们选取好需要裁剪的位置图片后,点击确定后
if(!isCompress){
Log.d(TAG,"start compress bitmap...");
byte[] bitmaps = compressBitmap(cropImageView.getCroppedImage());
if(bitmaps != null){
Intent data = new Intent();
data.putExtra(CROPBYTE,bitmaps);
setResult(0, data);
this.finish();
}
isCompress = false;
}
我们在cropImageView.getCroppedImage()获取裁剪得到的bitmap对象,然后判断图片的大小,在判断是否进行压缩,然后setResult(0, data);返回即可,在进行圆角控件设置
case REQUEST_CODE_IMAGE_CROP:
Log.i("Safly", "onActivityResult REQUEST_CODE_IMAGE_CROP");
byte[] source = data.getByteArrayExtra(CropImageActivity.CROPBYTE);
icon = BitmapFactory.decodeByteArray(source,0,source.length);
contactIcon.setImageBitmap(icon);
缺点:1、某些机型,比如nexus选择图片时候,那个左侧的图片列表就不能对图片进行选择,不过n5对那个图库列表就可以进行裁剪
2、还有一个缺点就是裁剪时候,貌似只能裁剪到正方形
3、而且裁剪展现的页面背后需要裁剪的图像,在某些机型不会去覆盖图片的全部,意思就是图片有些部分不能被裁剪到
PS一下:如果你能够了解庞大的源码,这些应该都可以解决的,
########################################代码区#########################################
自己圆角控件attr属性
<declare-styleable name="RoundImageView">
<attr name="outer_border_width" format="dimension" />
<!-- 边框宽度 -->
<attr name="outer_border_color" format="color" />
<!-- 边框颜色 -->
<attr name="corner_angle" format="dimension" />
<!-- 圆角大小 -->
<attr name="show_type">
<!-- 图片类型 -->
<enum name="circle" value="0" />
<!-- 圆形图片 -->
<enum name="round" value="1" />
<!-- 圆角图片 -->
</attr>
</declare-styleable>
activity_main.xml
<RelativeLayout 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"
tools:context="com.example.cropimageview.MainActivity" >
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="182dp"
android:layout_marginBottom="28dp"
android:background="#383838" >
<com.example.cropimageview.RoundImageView
android:id="@+id/contactIcon"
android:layout_width="103dp"
android:layout_height="103dp"
android:layout_centerInParent="true"
android:background="@drawable/ic_launcher" />
</RelativeLayout>
</RelativeLayout>
activity_cropimage_layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#e6e6e6">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="46dp"
android:background="#3C3C3C">
<ImageButton
android:id="@+id/image_back"
android:layout_width="44dp"
android:layout_height="46dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:src="@drawable/back"
android:background="@null"/>
<TextView
android:id="@+id/dirformCrop"
android:layout_width="wrap_content"
android:layout_height="46dp"
android:layout_centerHorizontal="true"
android:layout_alignParentRight="true"
android:gravity="center_vertical"
android:layout_marginRight="10dp"
android:text="确定"
android:textColor="#ffffff"
android:textSize="18sp"/>
</RelativeLayout>
<com.example.cropimageview.utils.CropImageView
android:id="@+id/cropImageView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
app:cropAutoZoomEnabled="false"
app:cropFixAspectRatio="true"/>
</LinearLayout>
MainActivity
package com.example.cropimageview;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
public class MainActivity extends Activity {
RoundImageView contactIcon;
private static final int REQUEST_CODE_SELECT_IMAGE = 0x01;
private static final int REQUEST_CODE_IMAGE_CROP = 0x02;
private Bitmap icon;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
contactIcon = (RoundImageView) findViewById(R.id.contactIcon);
contactIcon.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.addCategory(Intent.CATEGORY_OPENABLE);
intent.setType("image/jpeg");
startActivityForResult(intent, REQUEST_CODE_SELECT_IMAGE);
}
});
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if(data != null){
switch (requestCode){
case REQUEST_CODE_SELECT_IMAGE:
Log.i("Safly", "onActivityResult REQUEST_CODE_SELECT_IMAGE");
Intent intent = new Intent(this,CropImageActivity.class);
intent.setData(data.getData());
startActivityForResult(intent,REQUEST_CODE_IMAGE_CROP);
break;
case REQUEST_CODE_IMAGE_CROP:
Log.i("Safly", "onActivityResult REQUEST_CODE_IMAGE_CROP");
byte[] source = data.getByteArrayExtra(CropImageActivity.CROPBYTE);
icon = BitmapFactory.decodeByteArray(source,0,source.length);
contactIcon.setImageBitmap(icon);
break;
}
}
}
}
CropImageActivity
package com.example.cropimageview;
import java.io.ByteArrayOutputStream;
import android.app.Activity;
import android.content.Intent;
import android.graphics.Bitmap;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Toast;
import com.example.cropimageview.utils.CropImageView;
/**
* Created by dandy on 2016/6/22.
*/
public class CropImageActivity extends Activity implements View.OnClickListener{
private static final String TAG = "CropImageActivity";
private CropImageView cropImageView;
public static final String CROPBYTE = "crop_bytes";
private boolean isCompress = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cropimage_layout);
cropImageView = (CropImageView)findViewById(R.id.cropImageView);
cropImageView.setImageUriAsync(getIntent().getData());
findViewById(R.id.dirformCrop).setOnClickListener(this);
findViewById(R.id.image_back).setOnClickListener(this);
}
@Override
public void onClick(View v) {
switch (v.getId()){
case R.id.image_back:
this.finish();
break;
case R.id.dirformCrop:
if(!isCompress){
Log.d(TAG,"start compress bitmap...");
byte[] bitmaps = compressBitmap(cropImageView.getCroppedImage());
if(bitmaps != null){
Intent data = new Intent();
data.putExtra(CROPBYTE,bitmaps);
setResult(0, data);
this.finish();
}
isCompress = false;
}
break;
}
}
private byte[] compressBitmap(Bitmap source){
isCompress = true;
ByteArrayOutputStream baos = new ByteArrayOutputStream();
int options = 100;
byte[] bytes = null;
do{
baos.reset();
options -=10;
source.compress(Bitmap.CompressFormat.JPEG, options <= 0 ? 0 : options, baos);
if(options == 0 && baos.toByteArray().length / 1024 >= 30){
Toast.makeText(this,"图片裁剪过大,请调整大小",Toast.LENGTH_SHORT).show();
bytes = null;
break;
}
}while ((bytes = baos.toByteArray()).length / 1024 >= 30);
return bytes;
// return baos.toByteArray();
// return BitmapFactory.decodeByteArray(baos.toByteArray(),0,baos.toByteArray().length);
}
}
RoundImageView
package com.example.cropimageview;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.BitmapShader;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.RectF;
import android.graphics.Shader.TileMode;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.Log;
import android.util.TypedValue;
import android.widget.ImageView;
public class RoundImageView extends ImageView{
private static final String TAG = RoundImageView.class.getSimpleName();
/**
* 外部边框的宽和颜色
*/
private static final int DEFAULT_OUTER_BORDER_WIDTH = 0;
private static final int DEFAULT_OUTER_BORDER_COLOR = Color.TRANSPARENT;
private int outerWidth = DEFAULT_OUTER_BORDER_WIDTH;
private int outerColor = DEFAULT_OUTER_BORDER_COLOR;
private static final int COLORDRAWABLE_DIMENSION = 1;
/**
* 显示图片的类型
*/
private static final int TYPE_CIRCLE = 0;
private static final int TYPE_ROUND = 1;
private int showType = TYPE_CIRCLE;
/**
* 圆角大小的默认值
*/
private static final int DEFAULT_CORNER_ANGLE = 10;
/**
* 圆角实际大小值
*/
private int mCornerAngle = 0;
/**
* 圆形图片时候半径大小
*/
private int mCircleRadius = 0;
/**
* 绘图画笔paint
*/
private Paint mBitmapPaint = null;
private Paint mOuterPaint = null;
/**
* 3X3缩小放大矩阵
*/
private Matrix mMatrix = null;
/**
* 渲染图像,为绘制图形着色
*/
private BitmapShader mBitmapShader = null;
/**
* 大小
*/
private int mCircleViewWidth = 0;
private RectF mDrawableRectF = null;
private RectF mOuterRectF = null;
public RoundImageView(Context context) {
this(context, null);
}
public RoundImageView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
init(attrs);
}
/**
* 初始化操作
*/
private void init(AttributeSet attrs){
TypedArray ta = getContext().obtainStyledAttributes(attrs,R.styleable.RoundImageView);
showType = ta.getInt(R.styleable.RoundImageView_show_type, TYPE_CIRCLE);
mCornerAngle = ta.getDimensionPixelSize(R.styleable.RoundImageView_corner_angle, dp2px());
outerWidth = ta.getDimensionPixelSize(R.styleable.RoundImageView_outer_border_width, DEFAULT_OUTER_BORDER_WIDTH);
outerColor = ta.getColor(R.styleable.RoundImageView_outer_border_color, DEFAULT_OUTER_BORDER_COLOR);
ta.recycle();
mMatrix = new Matrix();
mBitmapPaint = new Paint();
mBitmapPaint.setAntiAlias(true);
mOuterPaint = new Paint();
mOuterPaint.setStyle(Paint.Style.STROKE);
mOuterPaint.setAntiAlias(true);
mOuterPaint.setColor(outerColor);
mOuterPaint.setStrokeWidth(outerWidth);
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
/**
* 测量的时候,如果获取的类型是圆形的,则强制把view的宽高设为相同大小,以小的为标准
*/
if(showType == TYPE_CIRCLE){
mCircleViewWidth = Math.min(getMeasuredWidth(), getMeasuredHeight());
mCircleRadius = mCircleViewWidth / 2 - outerWidth / 2;
setMeasuredDimension(mCircleViewWidth, mCircleViewWidth);
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
/**
* 圆角图片的范围
*/
if(showType == TYPE_ROUND){
mOuterRectF = new RectF(0,0,getWidth(),getHeight());
mDrawableRectF = new RectF(outerWidth,outerWidth,getWidth()-outerWidth,getHeight()-outerWidth);
}
}
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if(drawable == null){
drawable = getBackground();
}
if(drawable == null){
Log.e(TAG, "[null] drawable is null.");
return;
}
setShader(getBitmapFromDrawable(drawable));
switch (showType) {
case TYPE_CIRCLE:
canvas.drawCircle(getWidth()/2, getHeight()/2, mCircleRadius, mBitmapPaint);
canvas.drawCircle(getWidth()/2, getHeight()/2, mCircleRadius, mOuterPaint);
break;
case TYPE_ROUND:
canvas.drawRoundRect(mDrawableRectF, mCornerAngle, mCornerAngle, mBitmapPaint);
canvas.drawRoundRect(mOuterRectF, mCornerAngle, mCornerAngle, mOuterPaint);
break;
}
}
@Override
public void setImageBitmap(Bitmap bm) {
if(bm == null){
bm = BitmapFactory.decodeResource(getContext().getResources(),R.drawable.apply_virtual_number_photo);
}
super.setImageBitmap(bm);
}
/**
* 初始化BitmapShader
*/
private void setShader(Bitmap mBitmap){
if(mBitmap == null){
Log.i(TAG, "[null] mBitmap is null.");
return;
}
if(mBitmapShader != null){
mBitmapShader = null;
}
/**
* 将mBitmap作为着色器,也就是在指定的区域内绘制mBitmap
*/
mBitmapShader = new BitmapShader(mBitmap, TileMode.CLAMP, TileMode.CLAMP);
/**
* 缩放比例
*/
float scale = 1.0f;
switch (showType) {
case TYPE_CIRCLE:
/**
* 拿图片的宽高最小值做缩放比例
*/
scale = mCircleViewWidth * 1.0F / Math.min(mBitmap.getWidth(), mBitmap.getHeight());
break;
case TYPE_ROUND:
/**
* 如果图片的宽高与view的宽高不匹配,缩放的宽高一定要大于view的宽高才能填充完整view,所以要取较大值
*/
scale = Math.max(getWidth() * 1.0f /mBitmap.getWidth() , getHeight() * 1.0f / mBitmap.getHeight() );
break;
}
/**
* 变换矩阵设置缩放大小
*/
mMatrix.setScale(scale,scale);
/**
* 设置变换矩阵
*/
mBitmapShader.setLocalMatrix(mMatrix);
/**
* 设置着色器
*/
mBitmapPaint.setShader(mBitmapShader);
}
/**
* 从drawable中获取bitmap
*/
private Bitmap getBitmapFromDrawable(Drawable drawable){
if(drawable == null){
return null;
}
if(drawable instanceof BitmapDrawable){
return ((BitmapDrawable)drawable).getBitmap();
}
try {
Bitmap bitmap = null;
if(drawable instanceof ColorDrawable){
bitmap = Bitmap.createBitmap(COLORDRAWABLE_DIMENSION, COLORDRAWABLE_DIMENSION, Bitmap.Config.ARGB_8888);
}else{
bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(),Bitmap.Config.ARGB_8888);
}
Canvas canvas = new Canvas(bitmap);
drawable.setBounds(0, 0, canvas.getWidth(), canvas.getHeight());
drawable.draw(canvas);
return bitmap;
} catch (OutOfMemoryError e) {
e.printStackTrace();
return null;
}
}
/**
* 根据手机获取合适的像素大小
*/
private int dp2px(){
return (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, DEFAULT_CORNER_ANGLE,
getResources().getDisplayMetrics());
}
}