[Android]调用系统照相机拍照,并保存到指定位置

__椎名真白 2018-04-05 04:50:18
为何这么蒙圈。
原来也写过调用系统照相机拍照的demo,不过都是拍照完事就返回,设置到ImageView上,不涉及存储。
这次实现的目标是将拍摄的照片存储到指定位置上。即获取手机外部存储路径后,创建一个根目录,然后创建三个文件夹,photo,voice,video,分别存储图片、音频、视频。首先写相机模块。
思路是先获取权限,创建好目录,先让要拍摄的照片有个窝。然后拍照,将照片的uri返回。最后把拍摄的照片放到创建好的文件夹中。然后。。。写完发现手机上也找不到我创建好的目录,图库里也找不到拍摄的照片,没有照片就算了,窝都给端了。
log打印一堆信息,不是错误,只是警告。不知道为啥不是错误提示。总之就是找不到窝,也找不到孩子。
手机是Android7.0的系统,查了一下说是要用内容提供者来操作?我也不知道咋弄了。查来查去也查不出个问什么。
这是log截图:



看蒙圈了硬。只能让大佬帮帮忙,看一下代码错在哪里了,要怎么修改才好。内容有点乱,大佬多费心了!
...全文
1773 17 打赏 收藏 转发到动态 举报
写回复
用AI写文章
17 条回复
切换为时间正序
请发表友善的回复…
发表回复
悬针泼墨 2018-04-07
  • 打赏
  • 举报
回复
引用 16 楼 Lone1yCode 的回复:
引用 15 楼 Micalll 的回复:
本地存的照片能转到服务器上吗?
肯定可以啊。。。
好的,谢谢!
__椎名真白 2018-04-06
  • 打赏
  • 举报
回复
引用 10楼x443137254 的回复:
/** * 使用相机 */ private void useCamera() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/test/" + System.currentTimeMillis() + ".jpg"); file.getParentFile().mkdirs(); //改变Uri com.xykj.customview.fileprovider注意和xml中的一致 Uri uri = FileProvider.getUriForFile(this, "com.xykj.customview.fileprovider", file); //添加权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, REQUEST_CAMERA); } 网上别人写的方法,然后7.0不能直接拿uri,要用上面这种方式。大意是建一个文件,然后拿到这个文件的uri封装进intent里,这样相机拍的照片就直接存进你那个文件了,拿出来就直接读文件。之前你那个报IO错误我以为你拿到bitmap了
参照着网上的根据内容提供者获取uri,在主配置文件里注册,然后又建了一个xml存路径,没获取到,直接报错了,明天我试试这个方法,可能是我的调用方法姿势不对。
x443137254 2018-04-06
  • 打赏
  • 举报
回复
/** * 使用相机 */ private void useCamera() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/test/" + System.currentTimeMillis() + ".jpg"); file.getParentFile().mkdirs(); //改变Uri com.xykj.customview.fileprovider注意和xml中的一致 Uri uri = FileProvider.getUriForFile(this, "com.xykj.customview.fileprovider", file); //添加权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, REQUEST_CAMERA); } 网上别人写的方法,然后7.0不能直接拿uri,要用上面这种方式。大意是建一个文件,然后拿到这个文件的uri封装进intent里,这样相机拍的照片就直接存进你那个文件了,拿出来就直接读文件。之前你那个报IO错误我以为你拿到bitmap了
__椎名真白 2018-04-06
  • 打赏
  • 举报
回复
引用 15 楼 Micalll 的回复:
本地存的照片能转到服务器上吗?
肯定可以啊。。。
悬针泼墨 2018-04-06
  • 打赏
  • 举报
回复
本地存的照片能转到服务器上吗?
__椎名真白 2018-04-06
  • 打赏
  • 举报
回复
写了个博客记录一下。 具体解决办法https://blog.csdn.net/Lone1yCode/article/details/79833842
__椎名真白 2018-04-06
  • 打赏
  • 举报
回复
引用 10 楼 x443137254 的回复:
/** * 使用相机 */ private void useCamera() { Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); file = new File(Environment.getExternalStorageDirectory().getAbsolutePath() + "/test/" + System.currentTimeMillis() + ".jpg"); file.getParentFile().mkdirs(); //改变Uri com.xykj.customview.fileprovider注意和xml中的一致 Uri uri = FileProvider.getUriForFile(this, "com.xykj.customview.fileprovider", file); //添加权限 intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION); intent.putExtra(MediaStore.EXTRA_OUTPUT, uri); startActivityForResult(intent, REQUEST_CAMERA); } 网上别人写的方法,然后7.0不能直接拿uri,要用上面这种方式。大意是建一个文件,然后拿到这个文件的uri封装进intent里,这样相机拍的照片就直接存进你那个文件了,拿出来就直接读文件。之前你那个报IO错误我以为你拿到bitmap了
已经解决了,我原来使用内容提供者的时候,参数填的不对。另外,通知图库更新也不行,好在找到了新方法。真的非常感谢!!
nettman 2018-04-06
  • 打赏
  • 举报
回复
没搞过,帮顶
__椎名真白 2018-04-05
  • 打赏
  • 举报
回复
跑去新建了一个项目,专门测试这个目标,越弄越搞不懂了。启动相机加一个Uri额外数据的作用是什么,返回图片存储的位置 ../photo?还是这个Uri就应该是一个图片的绝对路径,../photo/2154211542.png这样的?但是加了额外数据之后,onActivityResult的方法里,data就为空了啊,该怎么获取刚刚拍摄的图片?还有OutputStream,它是写文件的,我把图片文件的绝对路径给它,然后通过bitmap.compress(Bitmap.CompressFormat.PNG, 100, os);去写图片,bitmap.compress不是压缩图片吗?我把刚刚拍摄的照片存储到指定位置需要用这个方式吗?我定义的Bitmap是空的,是不是需要将返回的Uri转成Bitmap?手机系统7.0,是不是一定要用内容提供者才行?另外,我原来写的项目里,文件的delete、mkdir、mkdirs、createNewFile没有警告信息,新建的测试项目里就有提示了:

文本:
Result of 'File.mkdirs()' is ignored less... (Ctrl+F1)
Reports any calls to specific methods where the result of that call is ignored. Both methods specified in the inspection's settings and methods annotated with org.jetbrains.annotations.Contract(pure=true) and javax.annotation.CheckReturnValue are checked. Methods in a class or package annotated with javax.annotation.CheckReturnValue are also checked. For many methods, ignoring the result is perfectly legitimate, but for some methods it is almost certainly an error. Examples of methods where ignoring the result of a call is likely to be an error include java.io.inputStream.read(), which returns the number of bytes actually read, any method on java.lang.String or java.math.BigInteger, as all of those methods are side-effect free and thus pointless if ignored.
英文不好,看的不是很明白,用软件翻译了一下,也是一脸懵逼。他为什么会有这个警告?需要怎么解决,还是不用管呢。
本着先会用,再去理解的心态,看了两本书感觉什么都差不多了,结果啥啥都不明白。想当然的写一堆代码,其实一点都不懂。真是特么的烦透了。求大佬讲解一下我提出的问题,我想明白一下这个流程是怎么样的。要实现这样的效果,该如何改进。不胜感激。
x443137254 2018-04-05
  • 打赏
  • 举报
回复
试试这么写 File path = new File(Environment.getExternalStorageDirectory() + “/" + SD_APP_DIR_NAME); File albumFile = new File(path, PHOTO_DIR_NAME);//文件的话没有会自动创建的 bitmap.compress(...);
__椎名真白 2018-04-05
  • 打赏
  • 举报
回复
引用 5 楼 x443137254 的回复:
[quote=引用 4 楼 Lone1yCode 的回复:]
[quote=引用 3 楼 x443137254 的回复:]
public void createFile(String flag) {
File file = new File(Environment.getExternalStorageDirectory(),
SD_APP_DIR_NAME);
Log.d(TAG, "定义File:" + file);
if (!file.exists()) {
file.mkdirs();
Log.d(TAG, "指定File不存在,创建目录");
try {
file.createNewFile();
Log.d(TAG, "创建新文件完成:" + file);
} catch (IOException e) {
e.printStackTrace();
}
}

想问一下这个file是文件还是文件夹?不知道能不能两个都创建,没试过
文件夹。先建的窝,拍照完事再装孩子。现在窝也找不着,孩子也没有。[/quote]
然而事实上你建了个文件夹,然后在文件夹所在的路径又建了个跟文件夹名字一样的文件...后面那个albumFile也是一样的操作。File这个类既可以表示文件,也可以表示文件夹,然而你一旦指定了,类型就确定下来了,然后,你是先建的文件夹,所以这个file就被系统确认为一个文件夹了,也就不能createNewFile了,所以你想往这个file(文件夹)里面写数据是写不进去的[/quote]按照刚刚说的,更新了一下代码逻辑。删除掉了指定文件夹位置时的两个createNewFile,在保存孩子时才使用createNewFile,但是最后还是这个方法出错。原来的错误提示已经没有了。现在只剩下保存的时候的问题了。想不通为啥啊。。我更新了代码的逻辑,看起来没啥问题了。结果还是出问题。

实在是搞不懂了。理解不行,用也不会用,一天了,啥都没干,特么得。
__椎名真白 2018-04-05
  • 打赏
  • 举报
回复
啊不对,我说错了,应该说这个file是一个目录,我想在这个目录下创建一个photo文件夹,最后将需要保存的图片文件到 photo/ 下。这个Log写的有点捉急。。着急想着看流程来着。我查了一下mkdir、mkdirs、createNewFile的使用,mkdir不能创建多级目录,mkdirs可以,也就是说我这么使用mkdirs是可以实现我想要的结果的,createNewFile是只能创建文件。我的写法确实有问题,我改一下,看看能不能正常使用。谢谢大哥哥。。。
x443137254 2018-04-05
  • 打赏
  • 举报
回复
引用 4 楼 Lone1yCode 的回复:
[quote=引用 3 楼 x443137254 的回复:] public void createFile(String flag) { File file = new File(Environment.getExternalStorageDirectory(), SD_APP_DIR_NAME); Log.d(TAG, "定义File:" + file); if (!file.exists()) { file.mkdirs(); Log.d(TAG, "指定File不存在,创建目录"); try { file.createNewFile(); Log.d(TAG, "创建新文件完成:" + file); } catch (IOException e) { e.printStackTrace(); } } 想问一下这个file是文件还是文件夹?不知道能不能两个都创建,没试过
文件夹。先建的窝,拍照完事再装孩子。现在窝也找不着,孩子也没有。[/quote] 然而事实上你建了个文件夹,然后在文件夹所在的路径又建了个跟文件夹名字一样的文件...后面那个albumFile也是一样的操作。File这个类既可以表示文件,也可以表示文件夹,然而你一旦指定了,类型就确定下来了,然后,你是先建的文件夹,所以这个file就被系统确认为一个文件夹了,也就不能createNewFile了,所以你想往这个file(文件夹)里面写数据是写不进去的
__椎名真白 2018-04-05
  • 打赏
  • 举报
回复
引用 3 楼 x443137254 的回复:
public void createFile(String flag) { File file = new File(Environment.getExternalStorageDirectory(), SD_APP_DIR_NAME); Log.d(TAG, "定义File:" + file); if (!file.exists()) { file.mkdirs(); Log.d(TAG, "指定File不存在,创建目录"); try { file.createNewFile(); Log.d(TAG, "创建新文件完成:" + file); } catch (IOException e) { e.printStackTrace(); } } 想问一下这个file是文件还是文件夹?不知道能不能两个都创建,没试过
文件夹。先建的窝,拍照完事再装孩子。现在窝也找不着,孩子也没有。
x443137254 2018-04-05
  • 打赏
  • 举报
回复
public void createFile(String flag) { File file = new File(Environment.getExternalStorageDirectory(), SD_APP_DIR_NAME); Log.d(TAG, "定义File:" + file); if (!file.exists()) { file.mkdirs(); Log.d(TAG, "指定File不存在,创建目录"); try { file.createNewFile(); Log.d(TAG, "创建新文件完成:" + file); } catch (IOException e) { e.printStackTrace(); } } 想问一下这个file是文件还是文件夹?不知道能不能两个都创建,没试过
__椎名真白 2018-04-05
  • 打赏
  • 举报
回复
代码如下(从活动代码里抽出来的关键部分,其他也都是控件关联和点击事件的逻辑,无关紧要):

import static com.my.wordbar.R.id.rl_everyday;
import static com.my.wordbar.R.id.rl_review;
import static com.my.wordbar.R.id.rl_test;
import static com.my.wordbar.R.id.rl_translate;
import static com.my.wordbar.R.id.rl_word;

public class MainActivity extends BaseActivity implements View.OnClickListener, NavigationView.OnNavigationItemSelectedListener {

    //Log Tag
    private static final String TAG = "MainActivity";

    private String mFilePath;               //用于存储程序在外部储存卡的路径(根目录)
    private String mPath;                   //用于存储最终目录,即根目录 / 要操作(存储文件)的文件夹

    public static final String SD_APP_DIR_NAME = "WordBar"; //存储程序在外部SD卡上的根目录的名字
    public static final String PHOTO_DIR_NAME = "photo";    //存储照片在根目录下的文件夹名字
    public static final String VOICE_DIR_NAME = "voice";    //存储音频在根目录下的文件夹名字
    public static final String VIDEO_DIR_NAME = "video";    //存储视频在根目录下的文件夹名字

    public static final int PHOTO_RESULT_CODE = 100;        //标志符,图片的结果码,判断是哪一个Intent
    public static final int VOICE_RESULT_CODE = 101;        //标志符,音频的结果码,判断是哪一个Intent
    public static final int VIDEO_RESULT_CODE = 102;        //标志符,视频的结果码,判断是哪一个Intent

    @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //设置屏幕不随手机旋转
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
        //设置屏幕直向显示
        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);

        // android 7.0系统解决拍照的问题
        StrictMode.VmPolicy.Builder builder = new StrictMode.VmPolicy.Builder();
        StrictMode.setVmPolicy(builder.build());
        builder.detectFileUriExposure();
    }

    /**
     * 为标题栏按钮设置点击事件
     */
    @RequiresApi(api = Build.VERSION_CODES.JELLY_BEAN)
    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.main_bar_camera: {
				//启动照相机
                startCamera();
                break;
            }
            case R.id.main_bar_recorder: {
                Toast.makeText(this, "录音\n功能未完成", Toast.LENGTH_SHORT).show();
                break;
            }
            case R.id.main_bar_settings: {
                Intent intent = new Intent(MainActivity.this, SettingActivity.class);
                startActivity(intent);
                finish();
                break;
            }
            case android.R.id.home: {
                break;
            }
        }
        return true;
    }

    /**
     * 启动相机,首先获取权限,无权限不能使用,创建文件,并要求返回uri
     */
    @TargetApi(Build.VERSION_CODES.JELLY_BEAN)
    private void startCamera() {
        requestRunTimePermission(new String[]{Manifest.permission.READ_EXTERNAL_STORAGE,
                Manifest.permission.CAMERA}, new PermissionListener() {
            @Override
            public void onGranted() {
                Intent intent = new Intent();
                intent.setAction(MediaStore.ACTION_IMAGE_CAPTURE);
                intent.addCategory(Intent.CATEGORY_DEFAULT);
                //创建文件夹
                createFile(PHOTO_DIR_NAME);
                // 把文件地址转换成Uri格式
                Uri uri = Uri.fromFile(new File(mPath));
                //设置系统相机拍摄照片完成后图片文件的存放地址
                intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);
                startActivityForResult(intent, PHOTO_RESULT_CODE);
            }

            @Override
            public void onGranted(List<String> grantedPermission) {
                Toast.makeText(MainActivity.this, "程序需要足够的权限才可以使用照相机!",
                        Toast.LENGTH_SHORT).show();
            }

            @Override
            public void onDenied(List<String> deniedPermission) {
                Toast.makeText(MainActivity.this, "程序需要足够的权限才可以使用照相机!",
                        Toast.LENGTH_SHORT).show();
            }
        });
    }

    /**
     * 处理返回结果。
     * 1、图片
     * 2、音频
     * 3、视频
     *
     * @param requestCode 请求码
     * @param resultCode  结果码 成功 -1 失败 0
     * @param data        返回的数据
     */
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {

        super.onActivityResult(requestCode, resultCode, data);

        if (resultCode == Activity.RESULT_OK) {
            Log.d(TAG, "请求码:"+requestCode+"  结果码:"+resultCode+"  data:"+data);
            switch (requestCode) {
                case PHOTO_RESULT_CODE: {
                    Log.d(TAG, "结果返回:" + PHOTO_RESULT_CODE);
                    Bitmap bitmap = null;
                    Intent intent = new Intent(Intent.ACTION_MEDIA_SCANNER_SCAN_FILE);
                    Uri uri = Uri.fromFile(new File(mPath));
                    intent.setData(uri);
                    this.sendBroadcast(intent);
                    savePhotoToSD(bitmap);
                    break;
                }
                case VOICE_RESULT_CODE: {
                    saveVoiceToSD();
                    break;
                }
                case VIDEO_RESULT_CODE: {
                    saveVideoTOSD();
                    break;
                }
            }
        }
    }

    /**
     * 保存照片到SD卡的指定位置
     */
    private void savePhotoToSD(Bitmap bitmap) {
        BufferedOutputStream os = null;
        File file = new File(mPath, Calendar.getInstance().getTimeInMillis() + ".png");
        Log.d(TAG, "保存的文件名:"+file);
        if (file.exists()) {
            file.delete();
            Log.d(TAG, "文件已存在!删除!");
        }
        try {
            file.createNewFile();
            Log.d(TAG, "创建文件:"+file);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            os = new BufferedOutputStream(new FileOutputStream(file));
            bitmap.compress(Bitmap.CompressFormat.PNG, PHOTO_RESULT_CODE, os);
            Log.d(TAG, "存储照片完成");
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } finally {
            if (os != null) {
                try {
                    os.flush();
                    os.close();
                    Log.d(TAG, "刷新、关闭流");
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

    /**
     * 保存音频到SD卡的指定位置
     */
    private void saveVoiceToSD() {

    }

    /**
     * 保存视频到SD卡的指定位置
     */
    private void saveVideoTOSD() {

    }

    /**
     * 创建根目录和文件夹
     *
     * @param flag 传递过来的标志位,分为图片、音频、视频三种
     */
    public void createFile(String flag) {
        File file = new File(Environment.getExternalStorageDirectory(),
                SD_APP_DIR_NAME);
        Log.d(TAG, "定义File:" + file);
        if (!file.exists()) {
            file.mkdirs();
            Log.d(TAG, "指定File不存在,创建目录");
            try {
                file.createNewFile();
                Log.d(TAG, "创建新文件完成:" + file);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
        mFilePath = file.getPath();

        Log.d(TAG, "根目录的路径是:" + mFilePath);

        File albumFile = null;

        switch (flag) {
            case PHOTO_DIR_NAME:
                albumFile = new File(mFilePath, PHOTO_DIR_NAME);
                Log.d(TAG, "创建图片目录:" + albumFile);
                break;
            case VOICE_DIR_NAME:
                albumFile = new File(mFilePath, VOICE_DIR_NAME);
                Log.d(TAG, "创建音频目录:" + albumFile);
                break;
            case VIDEO_DIR_NAME:
                albumFile = new File(mFilePath, VIDEO_DIR_NAME);
                Log.d(TAG, "创建视频目录:" + albumFile);
                break;
        }

        if (!albumFile.exists()) {
            albumFile.mkdir();
            Log.d(TAG, "指定ablumFile不存在,创建目录:" + albumFile);
            try {
                albumFile.createNewFile();
                Log.d(TAG, "创建新目录完成:" + albumFile);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        mPath = albumFile.getAbsolutePath();
        Log.d(TAG, "创建的新文件夹的绝对路径是:" + mPath);
    }

    /**
     * 加载toolbar文件
     */
    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        //加载toolbar布局
        getMenuInflater().inflate(R.menu.main_activity_toolbar, menu);
        return true;
    }

    /**
     * 让菜单同时显示图标和文字
     */
    @Override
    public boolean onMenuOpened(int featureId, Menu menu) {
        if (menu != null) {
            if (menu.getClass().getSimpleName().equalsIgnoreCase("MenuBuilder")) {
                try {
                    Method method = menu.getClass().getDeclaredMethod("setOptionalIconsVisible", Boolean.TYPE);
                    method.setAccessible(true);
                    method.invoke(menu, true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
        return true;
    }
}
导包也省了,文字太多不让发
__椎名真白 2018-04-05
  • 打赏
  • 举报
回复
还有,指定savePhotoToSD的方法,执行完第一句后,下面的log就不打了。。跑丢了。finally的log都没有。


80,351

社区成员

发帖
与我相关
我的任务
社区描述
移动平台 Android
androidandroid-studioandroidx 技术论坛(原bbs)
社区管理员
  • Android
  • yechaoa
  • 失落夏天
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧