ArthurHub/Android-Image-Cropper 相册裁剪框架学习

拍照、拍照裁剪

相册选取,相册裁剪

以上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());
    }
}






  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值