//工具包
implementation 'com.blankj:utilcode:1.7.1'
//动态权限处理
api 'com.github.hotchemi:permissionsdispatcher:3.0.1'
annotationProcessor 'com.github.hotchemi:permissionsdispatcher-processor:3.0.1'
//图片剪裁
implementation 'com.github.yalantis:ucrop:2.2.1-native'
<!--网络权限-->
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<!--拨号权限-->
<uses-permission android:name="android.permission.CALL_PHONE" />
<!--文件权限-->
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<!--相机权限-->
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
照片处理类【UI加载find,逻辑处理】
public class CameraHandler implements View.OnClickListener {
private final AlertDialog DIALOG;
private final PermissionCheckerDelegate DELEGATE;
public CameraHandler(PermissionCheckerDelegate delegate) {
this.DELEGATE = delegate;
DIALOG = new AlertDialog.Builder(delegate.getContext()).create();
}
//弹出Dialog弹框 内容:三个按钮,拍照 选图 取消
final void beginCameraDialog() {
DIALOG.show();
final Window window = DIALOG.getWindow();
if (window != null) {
window.setContentView(R.layout.dialog_camera_panel);//设置弹框布局
window.setGravity(Gravity.BOTTOM);
window.setWindowAnimations(R.style.anim_panel_up_from_bottom);
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
//设置属性
final WindowManager.LayoutParams params = window.getAttributes();
params.width = WindowManager.LayoutParams.MATCH_PARENT;
params.flags = WindowManager.LayoutParams.FLAG_DIM_BEHIND;
params.dimAmount = 0.5f;//设置幕布黑暗程度
window.setAttributes(params);
//设置弹框中 组件的监听
window.findViewById(R.id.photodialog_btn_cancel).setOnClickListener(this);//取消按钮
window.findViewById(R.id.photodialog_btn_take).setOnClickListener(this);//拍照按钮
window.findViewById(R.id.photodialog_btn_native).setOnClickListener(this);//本地按钮
}
}
private String getPhotoName() {
//获取一个 模板格式化后的 文件名(模板:文件头_当前时间.后缀)
return FileUtil.getFileNameByTime("IMG", "jpg");
}
//拍照取图
public void takePhoto() {
final String currentPhotoName = getPhotoName();
//拍照意图
final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
//File 临时文件句柄 临时文件:这里是系统相册目录下的当前文件名的文件临时句柄
//CAMERA_PHOTO_DIR 系统相册目录
final File tempFile = new File(FileUtil.CAMERA_PHOTO_DIR, currentPhotoName);
//兼容7.0及以上的写法
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
final ContentValues contentValues = new ContentValues(1);
contentValues.put(MediaStore.Images.Media.DATA, tempFile.getPath());
//使用 ContentProvider 的方式
final Uri uri = DELEGATE.getContext().getContentResolver().
insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
//需要讲Uri路径转化为实际路径
final File realFile =
FileUtils.getFileByPath(FileUtil.getRealFilePath(DELEGATE.getContext(), uri));
final Uri realUri = Uri.fromFile(realFile);
CameraImageBean.getInstance().setPath(realUri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
} else {
final Uri fileUri = Uri.fromFile(tempFile);
CameraImageBean.getInstance().setPath(fileUri);
intent.putExtra(MediaStore.EXTRA_OUTPUT, fileUri);
}
//使用startActivityForResult()的形式 启动Activity
DELEGATE.startActivityForResult(intent, RequestCodes.TAKE_PHOTO);
}
//本地取图
private void pickPhoto() {
final Intent intent = new Intent();
intent.setType("image/*");//所有的Image类型
intent.setAction(Intent.ACTION_GET_CONTENT);//获取内容
intent.addCategory(Intent.CATEGORY_OPENABLE);
//使用startActivityForResult()的形式 启动Activity
// createChooser 创建选择器
DELEGATE.startActivityForResult
(Intent.createChooser(intent, "选择获取图片的方式"), RequestCodes.PICK_PHOTO);
}
@Override
public void onClick(View v) {
int id = v.getId();
if (id == R.id.photodialog_btn_cancel) {
DIALOG.cancel();
} else if (id == R.id.photodialog_btn_take) {
//拍照取图
takePhoto();
DIALOG.cancel();
} else if (id == R.id.photodialog_btn_native) {
//本地取图
pickPhoto();
DIALOG.cancel();
}
}
}
https://github.com/Blankj/AndroidUtilCode/releases/tag/1.7.1
public class MyFileUtil {
//格式化的模板
private static final String TIME_FORMAT = "_yyyyMMdd_HHmmss";
private static final String SDCARD_DIR =
Environment.getExternalStorageDirectory().getPath();
//系统相机目录!!!
public static final String CAMERA_PHOTO_DIR =
Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DCIM).getPath() + "/Camera/";
public static String getRealFilePath(final Context context, final Uri uri) {
if (null == uri) return null;
final String scheme = uri.getScheme();
String data = null;
if (scheme == null)
data = uri.getPath();
else if (ContentResolver.SCHEME_FILE.equals(scheme)) {
data = uri.getPath();
} else if (ContentResolver.SCHEME_CONTENT.equals(scheme)) {
final Cursor cursor = context.getContentResolver().query(uri, new String[]{MediaStore.Images.ImageColumns.DATA}, null, null, null);
if (null != cursor) {
if (cursor.moveToFirst()) {
final int index = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATA);
if (index > -1) {
data = cursor.getString(index);
}
}
cursor.close();
}
}
return data;
}
public static File getFileByPath(String filePath) {
return isSpace(filePath) ? null : new File(filePath);
}
private static boolean isSpace(String s) {
if (s == null) return true;
for (int i = 0, len = s.length(); i < len; ++i) {
if (!Character.isWhitespace(s.charAt(i))) {
return false;
}
}
return true;
}
//按照 模板 返回 由 文件头 和 当前系统时间 组合而成的 命名字符串
private static String getTimeFormatName(String timeFormatHeader) {
final Date date = new Date(System.currentTimeMillis());
//必须要加上单引号 下面是是格式化模板
final SimpleDateFormat dateFormat = new SimpleDateFormat("'" + timeFormatHeader + "'" + TIME_FORMAT, Locale.getDefault());
return dateFormat.format(date);
}
/**
* @param timeFormatHeader 调用者自己指定的文件头(除去时间部分)
* @param extension 文件的后缀名,同样由 调用者指定
* @return 返回 模板格式化后 的文件名(指定文件头 + 格式化的时间)!!
*
* 模板:文件头_当前时间.后缀
*/
public static String getFileNameByTime(String timeFormatHeader, String extension) {
return getTimeFormatName(timeFormatHeader) + "." + extension;
}
}
public final class CameraImageBean {
private Uri mPath = null;
//单例模式
private static final CameraImageBean INSTANCE = new CameraImageBean();
public static CameraImageBean getInstance(){
return INSTANCE;
}
public Uri getPath() {
return mPath;
}
public void setPath(Uri mPath) {
this.mPath = mPath;
}
}
public class RequestCodes {
public static final int TAKE_PHOTO = 4;//拍照
public static final int PICK_PHOTO = 5;//选择图片
public static final int CROP_PHOTO = UCrop.REQUEST_CROP;//剪裁
public static final int CROP_ERROR = UCrop.RESULT_ERROR;//剪裁错误
public static final int SCAN = 7;//扫描二维码
}
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300">
<!--从下往上的这样一个过程动画-->
<translate
android:fromYDelta="100%p"
android:toYDelta="0" />
<alpha
android:fromYDelta="0.0"
android:toYDelta="1.0" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="300">
<!--从上往下的这样一个过程动画-->
<translate
android:fromYDelta="0"
android:toYDelta="50%p" />
<alpha
android:fromYDelta="1.0"
android:toYDelta="0.0" />
</set>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp"
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<corners
android:bottomLeftRadius="8dp"
android:bottomRightRadius="8dp"
android:topLeftRadius="0dp"
android:topRightRadius="0dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="@android:color/white" />
<corners
android:bottomLeftRadius="0dp"
android:bottomRightRadius="0dp"
android:topLeftRadius="8dp"
android:topRightRadius="8dp" />
</shape>
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:background="@android:color/transparent"
android:orientation="vertical"
android:paddingBottom="10dp">
<android.support.v7.widget.AppCompatButton
android:id="@+id/photodialog_btn_take"
android:layout_width="match_parent"
android:layout_height="50dp"
android:background="@drawable/btn_border_takephoto"
android:gravity="center"
android:text="拍一张"
android:textColor="#323232" />
<View
android:layout_width="match_parent"
android:layout_height="0.5dp"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:gravity="center" />
<android.support.v7.widget.AppCompatButton
android:id="@+id/photodialog_btn_native"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="center"
android:background="@drawable/btn_border_nativephoto"
android:gravity="center"
android:text="从手机相册选择"
android:textColor="#323232" />
<View
android:layout_width="match_parent"
android:layout_height="10dp"
android:layout_gravity="center"
android:background="@android:color/transparent"
android:gravity="center" />
<android.support.v7.widget.AppCompatButton
android:id="@+id/photodialog_btn_cancel"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_gravity="center"
android:background="@drawable/btn_border"
android:gravity="center"
android:text="取消"
android:textColor="#323232" />
</android.support.v7.widget.LinearLayoutCompat>
<resources>
....
<!--从底下往上弹、退场弹回底下的动画-->
<style name="anim_panel_up_from_bottom" parent="@android:style/Animation">
<!--进入动画-->
<item name="android:windowEnterAnimation">@anim/push_bottom_in</item>
<!--退场动画-->
<item name="android:windowExitAnimation">@anim/push_bottom_out</item>
</style>
</resources>
public class XiaoYunCamera {
//需要剪裁的文件
public static Uri createCropFile() {
return Uri.parse(FileUtil.createFile("crop_image",
FileUtil.getFileNameByTime("IMG", "jpg")).getPath());
}
public static void start(PermissionCheckerDelegate delegate) {
new CameraHandler(delegate).beginCameraDialog();//弹出Dialog弹框 内容:三个按钮,拍照 选图 取消
}
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (resultCode == RESULT_OK) {
switch (requestCode) {
case RequestCodes.TAKE_PHOTO:
//获取到 存储照片的 临时文件路径
final Uri resultUri = CameraImageBean.getInstance().getPath();
UCrop.of(resultUri, resultUri)//一参为 欲剪裁图片的路径,二参为 放置剪切完图片的路径
.withMaxResultSize(400, 400)
.start(getContext(), this);//start()用意看源码
break;
case RequestCodes.PICK_PHOTO:
if (data != null) {
final Uri pickPath = data.getData();//拿到用户选择的图片的路径
//从相册选择后 需要有个路径 来存放 剪裁过的图片
final String pickCropPath = XiaoYunCamera.createCropFile().getPath();
UCrop.of(pickPath, Uri.parse(pickCropPath))
.withMaxResultSize(400, 400)
.start(getContext(), this);
}
break;
case RequestCodes.CROP_PHOTO:
final Uri cropUri = UCrop.getOutput(data);
//拿到剪裁后的数据进行处理
@SuppressWarnings("unchecked")
final IGlobalCallback<Uri> callback = CallbackManager
.getInstance()
.getCallback(CallbackType.ON_CROP);//拿到回调接口
if (callback != null) {
callback.executeCallback(cropUri);//执行回调接口方法
}
break;
case RequestCodes.CROP_ERROR:
Toast.makeText(getContext(), "剪裁出错", Toast.LENGTH_SHORT).show();
break;
default:
break;
}
}
}