Android 系统自带图片裁剪功能(适配7.0、8.0、对了还有小米手机)
前段时间写了如何获取相册和拍照之后的照片并且进行显示和上传,这一次是如何进行圆形图像制作,经常看我写的笔记的人会知道,我很懒。那么我就懒的自定义了,目前需求就用原生的就好了,大神的轮子,我会在后面进行推荐。这篇笔记是依赖于:Android调用相册、相机(兼容6.0、7.0、8.0)
文章撰写的,所以不会基本操作的,请先去看看那个。
先上效果哈:
系统自带的裁剪效果
##一、布局设置
由于是圆形头像,所以,我们引用一个举世闻名的项目哈:CircleImageView
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<Button
android:id="@+id/btn_get_pic_form_photo_album"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_margin="10dp"
android:text="调用相册" />
<Button
android:id="@+id/btn_get_Permission"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_margin="10dp"
android:text="动态权限申请" />
<Button
android:id="@+id/btn_get_pic_from_camera"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_margin="10dp"
android:text="调用相机" />
<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/iv_test"
android:layout_width="96dp"
android:layout_height="96dp"
android:layout_centerInParent="true"
android:src="@drawable/ic_launcher_background"
app:civ_border_color="#999999"
app:civ_border_width="2dp" />
</RelativeLayout>
布局没有太大变化,就是把之前的ImageView修改为CircleImageView控件
##二、.java 文件
这里增加了两个方法
######方法一
private void photoClip(Uri uri) {
// 调用系统中自带的图片剪裁
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION | Intent.FLAG_GRANT_WRITE_URI_PERMISSION);
intent.setDataAndType(uri, "image/*");
// 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", 150);
intent.putExtra("outputY", 150);
intent.putExtra("return-data", true);
startActivityForResult(intent, 3);
}
该方法,传入我们拿到的照片的 uri 进行激活 Android 系统的裁剪界面。我是在 onActivityResult 内进行调用该方法。
######方法二
public String saveImage(String name, Bitmap bmp) {
File appDir = new File(Environment.getExternalStorageDirectory().getPath());
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = name + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
该方法 传入获取照片的 bitmap 和裁剪之后的照片名称,生成文件的保存路径,韩小呆将其保存在了本地的根目录了。
完整代码
package com.example.hxd.pictest;
import android.Manifest;
import android.content.Intent;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.Environment;
import android.os.SystemClock;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.v4.content.FileProvider;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.Toast;
import com.bumptech.glide.Glide;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.Objects;
import java.util.logging.Logger;
import de.hdodenhof.circleimageview.CircleImageView;
import pub.devrel.easypermissions.EasyPermissions;
public class MainActivity extends AppCompatActivity implements View.OnClickListener, EasyPermissions.PermissionCallbacks {
private CircleImageView ivTest;
private File cameraSavePath;//拍照照片路径
private Uri uri;
private String[] permissions = {Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE};
private String photoName = System.currentTimeMillis() + ".jpg";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btnGetPicFromCamera = findViewById(R.id.btn_get_pic_from_camera);
Button btnGetPicFromPhotoAlbum = findViewById(R.id.btn_get_pic_form_photo_album);
Button btnGetPermission = findViewById(R.id.btn_get_Permission);
ivTest = findViewById(R.id.iv_test);
btnGetPicFromCamera.setOnClickListener(this);
btnGetPicFromPhotoAlbum.setOnClickListener(this);
btnGetPermission.setOnClickListener(this);
cameraSavePath = new File(Environment.getExternalStorageDirectory().getPath() + "/" + photoName);
}
@Override
public void onClick(View v) {
int id = v.getId();
switch (id) {
case R.id.btn_get_pic_from_camera:
goCamera();
break;
case R.id.btn_get_pic_form_photo_album:
goPhotoAlbum();
break;
case R.id.btn_get_Permission:
getPermission();
break;
}
}
//获取权限
private void getPermission() {
if (EasyPermissions.hasPermissions(this, permissions)) {
//已经打开权限
Toast.makeText(this, "已经申请相关权限", Toast.LENGTH_SHORT).show();
} else {
//没有打开相关权限、申请权限
EasyPermissions.requestPermissions(this, "需要获取您的相册、照相使用权限", 1, permissions);
}
}
private void photoClip(Uri uri) {
// 调用系统中自带的图片剪裁
Intent intent = new Intent("com.android.camera.action.CROP");
intent.setDataAndType(uri, "image/*");
// 下面这个crop=true是设置在开启的Intent中设置显示的VIEW可裁剪
intent.putExtra("crop", "true");
// aspectX aspectY 是宽高的比例
intent.putExtra("aspectX", 1);
intent.putExtra("aspectY", 1);
// outputX outputY 是裁剪图片宽高
intent.putExtra("outputX", 150);
intent.putExtra("outputY", 150);
intent.putExtra("return-data", true);
startActivityForResult(intent, 3);
}
//激活相册操作
private void goPhotoAlbum() {
Intent intent = new Intent();
intent.setAction(Intent.ACTION_PICK);
intent.setType("image/*");
startActivityForResult(intent, 2);
}
//激活相机操作
private void goCamera() {
Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
uri = FileProvider.getUriForFile(MainActivity.this, "com.example.hxd.pictest.fileprovider", cameraSavePath);
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
} else {
uri = Uri.fromFile(cameraSavePath);
}
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
MainActivity.this.startActivityForResult(intent, 1);
}
@Override
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
//框架要求必须这么写
EasyPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
}
//成功打开权限
@Override
public void onPermissionsGranted(int requestCode, @NonNull List<String> perms) {
Toast.makeText(this, "相关权限获取成功", Toast.LENGTH_SHORT).show();
}
//用户未同意权限
@Override
public void onPermissionsDenied(int requestCode, @NonNull List<String> perms) {
Toast.makeText(this, "请同意相关权限,否则功能无法使用", Toast.LENGTH_SHORT).show();
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
String photoPath;
if (requestCode == 1 && resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
photoPath = String.valueOf(cameraSavePath);
photoClip(Uri.fromFile(cameraSavePath));
} else {
photoPath = uri.getEncodedPath();
photoClip(uri);
}
Log.d("拍照返回图片路径:", photoPath);
Glide.with(MainActivity.this).load(photoPath).into(ivTest);
} else if (requestCode == 2 && resultCode == RESULT_OK) {
photoPath = getPhotoFromPhotoAlbum.getRealPathFromUri(this, data.getData());
Log.d("相册返回图片路径:", photoPath);
photoClip(data.getData());
Glide.with(MainActivity.this).load(photoPath).into(ivTest);
} else if (requestCode == 3 && resultCode == RESULT_OK) {
Bundle bundle = data.getExtras();
if (bundle != null) {
//在这里获得了剪裁后的Bitmap对象,可以用于上传
Bitmap image = bundle.getParcelable("data");
//设置到ImageView上
ivTest.setImageBitmap(image);
//也可以进行一些保存、压缩等操作后上传
String path = saveImage("头像", image);
Log.d("裁剪路径:", path);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
public String saveImage(String name, Bitmap bmp) {
File appDir = new File(Environment.getExternalStorageDirectory().getPath());
if (!appDir.exists()) {
appDir.mkdir();
}
String fileName = name + ".jpg";
File file = new File(appDir, fileName);
try {
FileOutputStream fos = new FileOutputStream(file);
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
fos.flush();
fos.close();
return file.getAbsolutePath();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
当代码写成了这样,我以为就可以完事了,但是后来拿出来我的小米4和小米8se 测试的时候,我就懵逼了,出现了如下结果:
bug
描述一下:
1、相册选择照片之后,只要进行裁剪就会出现“保存时发生错误,保存失败”;
2、就是拍照之后进行裁剪无效果。
#三、解决bug
###1、将 photoClip() 方法内添加如下代码,其他代码不进行变动
uritempFile = Uri.parse("file://" + "/" + Environment.getExternalStorageDirectory().getPath() + "/" + System.currentTimeMillis() + ".jpg");
intent.putExtra(MediaStore.EXTRA_OUTPUT, uritempFile);
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString());
intent.putExtra("noFaceDetection", true);
这里的 uritempFile 是个 Uri 。
###2、将裁剪的返回成功的返回方法内添加如下代码:
Picasso.with(EditMyInfoActivity.this).load(uritempFile).into(ivHead);
File file = null;
try {
file = new File(new URI(uritempFile.toString()));
} catch (URISyntaxException e) {
e.printStackTrace();
}
//照片路径
String path = Objects.requireNonNull(file).getPath();
然后,就可以完美的运行了。
##三、推荐一篇自定义裁剪的博文
Android 高仿微信头像截取 打造不一样的自定义控件----作者: 家喻户晓的 张鸿洋
##四、推荐几款裁剪框架
1、uCrop—薄荷,哔哩哔哩 在使用
uCrop
2、android-crop—网易新闻,芒果TV 在使用
android-crop
3、PhotoCrop—快手 使用
PhotoCrop
4、CropBitmap—>出自一位牛x的群友
Android 系统自带图片裁剪功能(适配7.0、8.0、对了还有小米手机)的更多相关文章
- 手机调用系统的拍照和裁剪功能,假设界面有输入框EditText,在一些手机会出现点击EditText会弹出输入法,却不能输入的情况。
1. 拍照裁剪后 点击EditText会弹出输入法,却不能输入.可是点击点一EdtiText就能够输入了,所以我就写了一个看不见的EdtiText,切换焦点,这样就攻克了这个奇怪的这问题,应该是and ...
- 利用 Android 系统原生 API 实现分享功能
利用 Android 系统原生 API 实现分享功能 这篇文章提供一个封装好的 Share2 库供大家参考. GitHub 项目地址:Share2 大家知道,要调用 Android 系统内建的分享功能 ...
- jQuery.YesShow - 图片轮播插件(带图片放大功能)
jQuery.YesShow - 图片轮播插件(带图片放大功能) 使用简单,原文件只要这样就可以了:<div id="yes"> <ul> ...
- Android中实现全屏、无标题栏的两种办法(另附Android系统自带样式的解释)
在进行UI设计时,我们经常需要将屏幕设置成无标题栏或者全屏.要实现起来也非常简单,主要有两种方法:配置xml文件和编写代码设置. 1.在xml文件中进行配置 在项目的清单文件AndroidManife ...
- Android系统自带APP分析——短信app
Android操作系统本身就是一个巨大的开源软件仓库,熟悉它既可以了解到Android系统的设计框架,也可以获得高效的应用程序编写方式.本文所分析的源码来自于Google官方的AOSP源码4.0.1_ ...
- Ecmall系统自带的分页功能
在Ecmall的二次开发中,分页是必不可少的.这个系统已经自带了分页功能,下面来看看如何使用这个分页. 下面是一个自定义的类,用于查看订单的详细情况.关键在于get_order_data()这个方法, ...
- android系统自带的Service原理与使用
1. 说明 android的后台运行在很多service,它们在系统启动时被SystemServer开启,支持系统的正常工作,比如 MountService监听是否有SD卡安装及移除,Clipboar ...
- iOS实现图片裁剪功能,基于TKImageView完善与讲解
1.功能需求:需要实现图片区域裁剪功能. 2.效果图: 3.实现原理:本来想自己实现的,刚好看到一个比较好的库:TKImageView,下载好研究了下,发现基本都能满足我的需求,而且封装的也比 ...
- Android系统更新防互刷功能实现与分析【转】
本文转载自:https://blog.csdn.net/huangyabin001/article/details/44465145 版权声明:本文为博主原创文章,未经博主允许不得转载. htt ...
随机推荐
- Gartner:2019 年 iPaaS 魔力象限
http://www.199it.com/archives/869090.html 企业iPaaS产品支持越来越更广泛的使用场合,正迅速被企业采用以实现混合集成平台策略.这份魔力象限评估了17家供应商 ...
- jsp获取map
1.简单Map User user = new User(); user.setName("zmy"); user.setAge(); user.setBirthday(new D ...
- ansible-playbook实例
准备前提 配置ansible主机详情:https://www.cnblogs.com/security-guard/ nginx的安装 编写nginx的自动部署文件nginx.yml hos ...
- 在执行一行代码之前CLR做的68件事
因为CLR是一个托管环境,所以运行时中有几个组件需要在执行任何代码之前初始化.本文将介绍EE(执行引擎)启动程序,并详细检查初始化过程.68只是一个粗略的指南,它取决于您使用的运行时版本.启用了哪些功 ...
- 洛谷p1458顺序的分数题解
抱歉,您们的蒟蒻yxj不知道怎么插入链接qwq就只好粘个文本的了qwq:https://www.luogu.org/problemnew/show/P1458 没错,是个黄题,因为你们的小蒟蒻只会这样 ...
- ES6-Generator基础用法
Generator简介: 生成器,本身是函数,执行后返回迭代对象,函数内部要配合yield使用Generator函数会分段执行,遇到yield暂停. 使用Generator注意点:function 和 ...
- Java 并发系列之二:java 并发机制的底层实现原理
1. 处理器实现原子操作 2. volatile /** 补充: 主要作用:内存可见性,是变量在多个线程中可见,修饰变量,解决一写多读的问题. 轻量级的synchronized,不会造成阻塞.性能比s ...
- 【操作系统之五】Linux常用命令之grep
一.概念grep(Global search Regular Expression and Print out the line)强大的文本搜索工具,从文本文件或管道数据流中筛选匹配的行及数据,并把匹 ...
- GameZ游戏排名系统
GameZ游戏排名系统 GameZ为他们最新推出的游戏开通了一个网站.世界各地的玩家都可以将自己的游戏得分上传到网站上.这样就可以看到自己在世界上的排名.得分越高,排名就越靠前.当两个玩家的名次相同时 ...
- SpringBoot+Mysql 无法保存emoj表情?
尤记得很久以前,想存 emoj 表情到 mysql 中,需要额外的将 emoj 表情转码之后保存,每次读取时,再解码还原成一下:每次这种 sb 的操作,真心感觉心塞,那么有没有办法直接存呢? mysq ...