一、 实现拍照、选择图片并裁剪图片效果

按照之前博客的风格,首先看下实现效果。

二、 uCrop项目应用

想起之前看到的Yalantis/uCrop效果比较绚,但是研究源码之后发现在定制界面方面还是有一点的限制,于是在它的基础上做了修改Android-Crop,把定制界面独立出来,让用户去自由设置。下图为使用Android-Crop实现的模仿微信选择图片并裁剪Demo。

三、 实现思路

比较简单的选择设备图片裁剪,并将裁剪后的图片保存到指定路径;

调用系统拍照,将拍照图片保存在SD卡,然后裁剪图片并将裁剪后的图片保存在指定路径。

流程图如下所示:

四、 选择框实现

这里通过PopupWindow来实现,当然也可以根据需求采用其他方式实现。实现效果如下图所示:

1. XML布局

android:layout_width="fill_parent"

android:layout_height="wrap_content"

android:gravity="center_horizontal"

android:orientation="vertical">

android:id="@+id/pop_layout"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_alignParentBottom="true"

android:background="#444"

android:gravity="center_horizontal"

android:orientation="vertical">

android:id="@+id/picture_selector_take_photo_btn"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginLeft="10dip"

android:layout_marginRight="10dip"

android:layout_marginTop="10dp"

android:background="#4d69ff"

android:padding="10dp"

android:text="拍照"

android:textColor="#CEC9E7"

android:textSize="18sp"

android:textStyle="bold" />

android:id="@+id/picture_selector_pick_picture_btn"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginLeft="10dip"

android:layout_marginRight="10dip"

android:layout_marginTop="5dp"

android:background="#4d69ff"

android:padding="10dp"

android:text="从相册选择"

android:textColor="#CEC9E7"

android:textSize="18sp"

android:textStyle="bold" />

android:id="@+id/picture_selector_cancel_btn"

android:layout_width="match_parent"

android:layout_height="wrap_content"

android:layout_marginBottom="15dip"

android:layout_marginLeft="10dip"

android:layout_marginRight="10dip"

android:layout_marginTop="20dp"

android:background="@android:color/white"

android:padding="10dp"

android:text="取消"

android:textColor="#373447"

android:textSize="18sp"

android:textStyle="bold" />

2. 代码编写

public SelectPicturePopupWindow(Context context) {

super(context);

LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);

mMenuView = inflater.inflate(R.layout.layout_picture_selector, null);

takePhotoBtn = (Button) mMenuView.findViewById(R.id.picture_selector_take_photo_btn);

pickPictureBtn = (Button) mMenuView.findViewById(R.id.picture_selector_pick_picture_btn);

cancelBtn = (Button) mMenuView.findViewById(R.id.picture_selector_cancel_btn);

// 设置按钮监听

takePhotoBtn.setOnClickListener(this);

pickPictureBtn.setOnClickListener(this);

cancelBtn.setOnClickListener(this);

}

创建SelectPicturePopupWindow的时候设置按钮的监听。这里编写一个选择监听接口:

/**

* 选择监听接口

*/

public interface OnSelectedListener {

void OnSelected(View v, int position);

}

回调的参数为点击的按钮View以及当前按钮的索引,那么只要在选择监听里面返回接口的回调就可以啦。

@Override

public void onClick(View v) {

switch (v.getId()) {

case R.id.picture_selector_take_photo_btn:

if(null != mOnSelectedListener) {

mOnSelectedListener.OnSelected(v, 0);

}

break;

case R.id.picture_selector_pick_picture_btn:

if(null != mOnSelectedListener) {

mOnSelectedListener.OnSelected(v, 1);

}

break;

case R.id.picture_selector_cancel_btn:

if(null != mOnSelectedListener) {

mOnSelectedListener.OnSelected(v, 2);

}

break;

}

}

PopupWindow的初始化创建、监听设置好之后,只要提供显示与隐藏两个方法就可以了。

/**

* 把一个View控件添加到PopupWindow上并且显示

*

* @param activity

*/

public void showPopupWindow(Activity activity) {

popupWindow = new PopupWindow(mMenuView, // 添加到popupWindow

ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);

popupWindow.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));

popupWindow.showAtLocation(activity.getWindow().getDecorView(), Gravity.CENTER | Gravity.BOTTOM, 0, 0);

popupWindow.setAnimationStyle(android.R.style.Animation_InputMethod); // 设置窗口显示的动画效果

popupWindow.setFocusable(false); // 点击其他地方隐藏键盘 popupWindow

popupWindow.update();

}

/**

* 移除PopupWindow

*/

public void dismissPopupWindow() {

if (popupWindow != null && popupWindow.isShowing()) {

popupWindow.dismiss();

popupWindow = null;

}

}

OK,到这里选择框的实现就完成了。

五、使用选择框

通过我们上面对选择框的封装,使用起来就比较简单了,只需要初始化及设置选择的监听就可以啦。

1.初始化选择框

mSelectPicturePopupWindow = new SelectPicturePopupWindow(mContext);

mSelectPicturePopupWindow.setOnSelectedListener(this);

2.设置选择框监听

@Override

public void OnSelected(View v, int position) {

switch (position) {

case 0:

// TODO: "拍照"按钮被点击了

break;

case 1:

// TODO: "从相册选择"按钮被点击了

break;

case 2:

// TODO: "取消"按钮被点击了

break;

}

}

然后在Fragment上进行封装,我们取名为PictureSelectFragment。

六、拍照并保存图片

调用系统的拍照,并把拍摄的图片保存到指定位置。

@Override

public void OnSelected(View v, int position) {

switch (position) {

case 0:

// "拍照"按钮被点击了

mSelectPicturePopupWindow.dismissPopupWindow();

Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

//下面这句指定调用相机拍照后的照片存储的路径

takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mTempPhotoPath)));

startActivityForResult(takeIntent, CAMERA_REQUEST_CODE);

break;

case 1:

// TODO: "从相册选择"按钮被点击了

break;

case 2:

// TODO: "取消"按钮被点击了

break;

}

}

这里的指定位置为sd卡本目录下

mTempPhotoPath = Environment.getExternalStorageDirectory() + File.separator + "photo.jpeg";

当拍摄照片完成时会回调到onActivityResult,我们在这里处理图片的裁剪就可以了。

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {

if (resultCode == mActivity.RESULT_OK) {

switch (requestCode) {

case CAMERA_REQUEST_CODE:

// TODO: 调用相机拍照

break;

}

}

super.onActivityResult(requestCode, resultCode, data);

}

七、相册选择图片

调用系统的选择图片

@Override

public void OnSelected(View v, int position) {

switch (position) {

case 0:

// "拍照"按钮被点击了

mSelectPicturePopupWindow.dismissPopupWindow();

Intent takeIntent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

// 下面这句指定调用相机拍照后的照片存储的路径

takeIntent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(new File(mTempPhotoPath)));

startActivityForResult(takeIntent, CAMERA_REQUEST_CODE);

break;

case 1:

// "从相册选择"按钮被点击了

mSelectPicturePopupWindow.dismissPopupWindow();

Intent pickIntent = new Intent(Intent.ACTION_PICK, null);

// 如果限制上传到服务器的图片类型时可以直接写如:"image/jpeg 、 image/png等的类型"

pickIntent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");

startActivityForResult(pickIntent, GALLERY_REQUEST_CODE);

break;

case 2:

// TODO: "取消"按钮被点击了

break;

}

}

当拍选择图片完成时会回调到onActivityResult,在这里处理选择的返回结果。

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {

if (resultCode == mActivity.RESULT_OK) {

switch (requestCode) {

case CAMERA_REQUEST_CODE:

// TODO: 调用相机拍照

break;

case GALLERY_REQUEST_CODE:

// TODO: 直接从相册获取

break;

}

}

super.onActivityResult(requestCode, resultCode, data);

}

八、使用Crop裁剪图片

裁剪图片,这里设置宽高比为1:1,最大尺寸为512*512,当然可以根据自己的需求来设置。

/**

* 裁剪图片方法实现

*

* @param uri

*/

public void startCropActivity(Uri uri) {

UCrop.of(uri, mDestinationUri)

.withAspectRatio(1, 1)

.withMaxResultSize(512, 512)

.withTargetActivity(CropActivity.class)

.start(mActivity, this);

}

CropActiivty裁剪完成时会回调到onActivityResult,在这里处理选择的返回结果。

@Override

public void onActivityResult(int requestCode, int resultCode, Intent data) {

if (resultCode == mActivity.RESULT_OK) {

switch (requestCode) {

case CAMERA_REQUEST_CODE: // 调用相机拍照

File temp = new File(mTempPhotoPath);

startCropActivity(Uri.fromFile(temp));

break;

case GALLERY_REQUEST_CODE: // 直接从相册获取

startCropActivity(data.getData());

break;

case UCrop.REQUEST_CROP:

// TODO: 裁剪图片结果

break;

case UCrop.RESULT_ERROR:

// TODO: 裁剪图片错误

break;

}

}

super.onActivityResult(requestCode, resultCode, data);

}

CropActivity的界面如下所示:

当然也可以轻松设计成如下两图:

1. XML布局

xmlns:fab="http://schemas.android.com/apk/res-auto"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:clipToPadding="true"

android:fitsSystemWindows="true">

android:layout_width="match_parent"

android:layout_height="match_parent"

android:layout_below="@+id/toolbar"

android:background="#000">

android:id="@+id/weixin_act_ucrop"

android:layout_width="match_parent"

android:layout_height="match_parent"

android:visibility="invisible" />

android:layout_width="match_parent"

android:layout_height="match_parent">

android:id="@+id/crop_act_save_fab"

android:layout_width="wrap_content"

android:layout_height="wrap_content"

android:layout_gravity="bottom|right"

android:layout_margin="@dimen/fab_margin"

android:src="@mipmap/ic_done_white"

fab:fabSize="normal" />

可以发现非常简单,只有一个主要的CropView,这就是uCrop框架为我们提供的。

2. 代码编写

@Override

protected void initViews() {

initToolBar();

mGestureCropImageView = mUCropView.getCropImageView();

mOverlayView = mUCropView.getOverlayView();

// 设置允许缩放

mGestureCropImageView.setScaleEnabled(true);

// 设置禁止旋转

mGestureCropImageView.setRotateEnabled(false);

// 设置外部阴影颜色

mOverlayView.setDimmedColor(Color.parseColor("#AA000000"));

// 设置周围阴影是否为椭圆(如果false则为矩形)

mOverlayView.setOvalDimmedLayer(false);

// 设置显示裁剪边框

mOverlayView.setShowCropFrame(true);

// 设置不显示裁剪网格

mOverlayView.setShowCropGrid(false);

final Intent intent = getIntent();

setImageData(intent);

}

private void setImageData(Intent intent) {

Uri inputUri = intent.getParcelableExtra(UCrop.EXTRA_INPUT_URI);

mOutputUri = intent.getParcelableExtra(UCrop.EXTRA_OUTPUT_URI);

if (inputUri != null && mOutputUri != null) {

try {

mGestureCropImageView.setImageUri(inputUri);

} catch (Exception e) {

setResultException(e);

finish();

}

} else {

setResultException(new NullPointerException("Both input and output Uri must be specified"));

finish();

}

// 设置裁剪宽高比

if (intent.getBooleanExtra(UCrop.EXTRA_ASPECT_RATIO_SET, false)) {

float aspectRatioX = intent.getFloatExtra(UCrop.EXTRA_ASPECT_RATIO_X, 0);

float aspectRatioY = intent.getFloatExtra(UCrop.EXTRA_ASPECT_RATIO_Y, 0);

if (aspectRatioX > 0 && aspectRatioY > 0) {

mGestureCropImageView.setTargetAspectRatio(aspectRatioX / aspectRatioY);

} else {

mGestureCropImageView.setTargetAspectRatio(CropImageView.SOURCE_IMAGE_ASPECT_RATIO);

}

}

// 设置裁剪的最大宽高

if (intent.getBooleanExtra(UCrop.EXTRA_MAX_SIZE_SET, false)) {

int maxSizeX = intent.getIntExtra(UCrop.EXTRA_MAX_SIZE_X, 0);

int maxSizeY = intent.getIntExtra(UCrop.EXTRA_MAX_SIZE_Y, 0);

if (maxSizeX > 0 && maxSizeY > 0) {

mGestureCropImageView.setMaxResultImageSizeX(maxSizeX);

mGestureCropImageView.setMaxResultImageSizeY(maxSizeY);

} else {

Log.w(TAG, "EXTRA_MAX_SIZE_X and EXTRA_MAX_SIZE_Y must be greater than 0");

}

}

}

以上为CropView的配置,更多配置请参考项目源码。

最重要的,裁剪保存图片:

private void cropAndSaveImage() {

OutputStream outputStream = null;

try {

final Bitmap croppedBitmap = mGestureCropImageView.cropImage();

if (croppedBitmap != null) {

outputStream = getContentResolver().openOutputStream(mOutputUri);

croppedBitmap.compress(Bitmap.CompressFormat.JPEG, 85, outputStream);

croppedBitmap.recycle();

setResultUri(mOutputUri, mGestureCropImageView.getTargetAspectRatio());

finish();

} else {

setResultException(new NullPointerException("CropImageView.cropImage() returned null."));

}

} catch (Exception e) {

setResultException(e);

finish();

} finally {

BitmapLoadUtils.close(outputStream);

}

}

PictureSelectFragment处理裁剪成功的返回值

/**

* 处理剪切成功的返回值

*

* @param result

*/

private void handleCropResult(Intent result) {

deleteTempPhotoFile();

final Uri resultUri = UCrop.getOutput(result);

if (null != resultUri && null != mOnPictureSelectedListener) {

Bitmap bitmap = null;

try {

bitmap = MediaStore.Images.Media.getBitmap(mActivity.getContentResolver(), resultUri);

} catch (FileNotFoundException e) {

e.printStackTrace();

} catch (IOException e) {

e.printStackTrace();

}

mOnPictureSelectedListener.onPictureSelected(resultUri, bitmap);

} else {

Toast.makeText(mContext, "无法剪切选择图片", Toast.LENGTH_SHORT).show();

}

}

处理裁剪失败的返回值

/**

* 处理剪切失败的返回值

*

* @param result

*/

private void handleCropError(Intent result) {

deleteTempPhotoFile();

final Throwable cropError = UCrop.getError(result);

if (cropError != null) {

Log.e(TAG, "handleCropError: ", cropError);

Toast.makeText(mContext, cropError.getMessage(), Toast.LENGTH_LONG).show();

} else {

Toast.makeText(mContext, "无法剪切选择图片", Toast.LENGTH_SHORT).show();

}

}

这里设置了选择的回调接口,便于封装抽取。

/**

* 图片选择的回调接口

*/

public interface OnPictureSelectedListener {

/**

* 图片选择的监听回调

*

* @param fileUri

* @param bitmap

*/

void onPictureSelected(Uri fileUri, Bitmap bitmap);

}

经过五、六、七步骤,我们的PictureSelectFragment就搞定了,在使用的时候只要继承它,几行代码就搞定了。

九、PictureSelectFragment使用

// 设置图片点击监听

mPictureIv.setOnClickListener(new View.OnClickListener() {

@Override

public void onClick(View v) {

selectPicture();

}

});

// 设置裁剪图片结果监听

setOnPictureSelectedListener(new OnPictureSelectedListener() {

@Override

public void onPictureSelected(Uri fileUri, Bitmap bitmap) {

mPictureIv.setImageBitmap(bitmap);

String filePath = fileUri.getEncodedPath();

String imagePath = Uri.decode(filePath);

Toast.makeText(mContext, "图片已经保存到:" + imagePath, Toast.LENGTH_LONG).show();

}

});

OK,经过我们上面的封装及基类抽取,在使用的时候还是非常简单的。

十、下载

更多内容大家可以参考专题《Android图片处理》进行学习。

以上就是本文的全部内容,希望对大家学习Android软件编程有所帮助。

android裁剪图片功能,Android实现拍照、选择图片并裁剪图片功能相关推荐

  1. android 笔记 源码,Android一款类似印象笔记的App,随时记录您的生活点滴

    Android一款类似印象笔记的App,随时记录您的生活点滴 一款类似印象笔记的App,随时记录您的生活点滴,但时目前功能还没达到印象笔记那样,但是本人后期将通过版本迭代的方式来逐渐完善该App达到印 ...

  2. 如何去水印而不损图片?码住这三个方法学会图片怎么去水印

    当我们在网上"冲浪"时,会看到一些喜欢的图片,想要保存当做壁纸或头像,但是保存下来后却发现带有水印,导致画面被遮挡,影响美观,放弃又舍不得,这个时候可以使用一些软件去除图片上的水印 ...

  3. takephoto 框架_GitHub - Smecking/TakePhoto: 一款用于在Android设备上获取照片(拍照或从相册、文件中选择)、裁剪图片、压缩图片的开源工具库...

    TakePhoto是一款用于在Android设备上获取照片(拍照或从相册.文件中选择).裁剪图片.压缩图片的开源工具库,目前最新版本4.0.2. 3.0以下版本及API说明,详见TakePhoto2. ...

  4. Android拍照和相册+系统裁剪功能返回图片

    最近在使用一加3手机,Android系统6.0,进行测试的时候,发现调用手机的拍照和相册选择图片的功能返回的时候都无法调用系统的裁剪功能,Log日志也没有输出有用的信息.经过在网上大量的查找资料,拍照 ...

  5. 小米7.0 android 图片裁剪失败,Android 7.0适配 -- FileProvider 拍照、选择相册、裁切图片, 小米机型适配...

    需求: 最近把APP的TargetSdk从21提高至25后,测试时, 在Android7.0以上的系统上,爆出了一些异常. 在个别小米等机型也存在一些异常. 问题分析: FileUriExposedE ...

  6. android根据中心裁剪图片,拍照,选择照片并进行裁剪,适配Android 7.0

    实现步骤 1.在application节点下面添加provider android:name="android.support.v4.content.FileProvider" a ...

  7. android 自定义相册选择,Android图片选择器,支持拍照、从相册选择、裁剪、自定义主题...

    在很多项目中都会用到图片选择器,比如在选择头像的时候,还会要求选择图片进行裁剪后再进行上传:而有的项目要支持拍照后进行图片裁剪再进行上传.由于Android系统兼容性问题,我们不得不考虑自己实现图片裁 ...

  8. android 微信相册功能,Android仿微信选择图片和拍照功能

    本文实例为大家分享了 Android微信选择图片的具体代码,和微信拍照功能,供大家参考,具体内容如下 1.Android6.0系统,对于权限的使用都是需要申请,选择图片和拍照需要申请Manifest. ...

  9. android 视频录制尺寸裁剪,galleryfinal 实现Android图片单选/多选、拍照、裁剪、压缩。视频选择和录制。...

    RxGalleryFinal是一个android图片/视频文件选择器.其支持多选.单选.拍摄和裁剪,主题可自定义,无强制绑定第三方图片加载器. 1.首先加入权限 2.在module gradle中项目 ...

最新文章

  1. 大白话聊聊 Kafka 的架构原理和网络设计,它的性能高在什么地方?
  2. 给Resnet加人工经验提升30%的准确率
  3. ecshop“发货查询”中加入收货人、收货地址、发货时间、配送方式
  4. 史上最具体Android集成QQ,微信,微博分享(不用第三方)持续更新中
  5. C++ 如何用创建txt文件,并且写入内容(汇总)
  6. java中如何数组是如何赋值的?
  7. linux操作系统进程间通信IPC之管道pipe及FIFO
  8. 新书推荐 |《5G安全技术与标准》
  9. Flutter CupertinoSegmentedControl 分段组件
  10. 数据科学 IPython 笔记本 7.6 Pandas 中的数据操作
  11. ​“国产”AI框架争相开源,“领头羊”百度飞桨将扔重磅炸弹?
  12. Android之汽车音频
  13. 【Android学习笔记】ONTOUCHEVENT, ONCLICK及ONLONGCLICK的调用机制
  14. 制作ecc证书(linux命令行)
  15. php繁体转为简体的函数,繁体中文转换为简体中文的PHP函数
  16. 【计算机视觉】opencv靶标相机姿态解算1之基本概念(空间旋转、旋转轴)
  17. 【2022牛客多校5 A题 Don‘t Starve】DP
  18. 矩阵(一):SVD分解
  19. 向list中增加元素的三种方法
  20. 网络应用之JavaScript

热门文章

  1. SAS软件介绍单元测验
  2. 截图转换为高清高分辨率的方法,word中插入的图片转换为高分辨率的方法
  3. Windows下Arduino安装ardublock(附文件下载链接)
  4. 哈工大刘挺:自然语言处理中的可解释性问题
  5. 无人机水利应用,有力支撑黄河防凌监测工作
  6. 内网安全之:内网渗透流程
  7. c语言水仙花数素数,【C语言】斐波那契分数数列和、水仙花数、素数
  8. 发动机冷却系统的控制论文综述
  9. ncode安装中的问题解决
  10. win10打不开cmd,windows找不到文件cmd怎么办?