前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Android项目模块提取 | 拍照、从本地相册选择图片,UI、动画等提取

Android项目模块提取 | 拍照、从本地相册选择图片,UI、动画等提取

作者头像
凌川江雪
发布2022-01-20 12:20:57
8940
发布2022-01-20 12:20:57
举报
文章被收录于专栏:李蔚蓬的专栏

对应项目github代码

https://github.com/aaLiweipeng/XiaoYunEC/commit/5ed0d2d02e204b408aa38b3e50c5e3e2e00d2259

效果

第三方库

代码语言:javascript
复制
    //工具包
    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'

权限

代码语言:javascript
复制
    <!--网络权限-->
    <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,逻辑处理】

代码语言:javascript
复制
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();
        }
    }
}

FileUtil

https://github.com/aaLiweipeng/XiaoYunEC/blob/master/xiaoyun_core/src/main/java/com/lwp/xiaoyun_core/util/file/FileUtil.java

https://github.com/Blankj/AndroidUtilCode/releases/tag/1.7.1

代码语言:javascript
复制
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;
    }
}

CameraImageBean

代码语言:javascript
复制
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;
    }
}

RequestCodes

代码语言:javascript
复制
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;//扫描二维码
}

push_bottom_in.xml

代码语言:javascript
复制
<?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>

push_bottom_out.xml

代码语言:javascript
复制
<?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>

btn_border.xml

代码语言:javascript
复制
<?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>

btn_border_nativephoto.xml

代码语言:javascript
复制
<?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>

btn_border_takephoto.xml

代码语言:javascript
复制
<?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> 

dialog_camera_panel.xml

代码语言:javascript
复制
<?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>

style.xml

代码语言:javascript
复制
<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> 

调用

XiaoYunCamera.java

代码语言:javascript
复制
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弹框 内容:三个按钮,拍照 选图 取消
    }
}

回调

https://github.com/aaLiweipeng/XiaoYunEC/commit/0dc55e754356b84fe436db7dcdc0424f9ce68846
代码语言:javascript
复制
@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;
            }
        }
    }
本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2021.09.09 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 对应项目github代码
  • 效果
  • 第三方库
  • 权限
  • FileUtil
  • CameraImageBean
  • RequestCodes
  • push_bottom_in.xml
  • push_bottom_out.xml
  • btn_border.xml
  • btn_border_nativephoto.xml
  • btn_border_takephoto.xml
  • dialog_camera_panel.xml
  • style.xml
  • 调用
  • XiaoYunCamera.java
  • 回调
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档