Android基础--调用系统相机,打开相册,展示到界面,出现的一些问题Uri为空,data为null,图片错乱问题

本文详细介绍在Android应用中如何正确地处理从相册选取和相机拍摄的图片,包括解决4.4及以上版本中出现的问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

转载请标明出处:
http://blog.youkuaiyun.com/android_it/article/details/51200633
本文出自:【老甩哥的优快云博客】

在开发中,我们一定会遇到调用系统相机,打开相册,裁剪图片并展示到界面,这里我给大家总结一下:
在介绍之前先说下一个概念性问题:
一、URI 
  通用资源标志符(Universal Resource Identifier, 简称”URI”)。
  Uri代表要操作的数据,Android上可用的每种资源 - 图像、视频片段等都可以用Uri来表示。
  URI一般由三部分组成:
    访问资源的命名机制。
    存放资源的主机名。
    资源自身的名称,由路径表示。
  Android的Uri由以下三部分组成: “content://”、数据的路径、标示ID(可选)
  举些例子,如:
    所有联系人的Uri: content://contacts/people
    某个联系人的Uri: content://contacts/people/5
    所有图片Uri: content://media/external
    某个图片的Uri:content://media/external/images/media/4

一:首先说下打开相册,你将会遇到的问题,打开相册选取照片,你也许会这样子去写代码:
我们先创建文件夹,保存打开相册或者拍照之后存储图片的新地方:

/**
     * 创建File对象,用于存储照片
     */

    public File saveFile(String filename) {
        String storageState = Environment.getExternalStorageState();// 获取sd卡的状态
        if (Environment.MEDIA_MOUNTED.equals(storageState)) {// 如果已挂载状态
            //存储在SD卡文件夹image下
            dir = new File(Environment.getExternalStorageDirectory().getPath() +"/fsh_image");
            if(!dir.exists()){
                dir.mkdirs();
            }

            file = new File(Environment.getExternalStorageDirectory().getPath() +"/fsh_image/"+ filename + ".jpg");
            if(!file.exists()){
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }else{
            //存储在本地文件夹image下
            dir = new File("/fsh_image");
            if(!dir.exists()){
                dir.mkdirs();
            }
            file = new File("/fsh_image/" + filename + ".jpg");
            if(!file.exists()){
                try {
                    file.createNewFile();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        return file;
    }

/**
     * Bitmap对象保存为图片文件
     */
    public  File saveBitmapFile(Bitmap bitmap,String filename){
        File file = saveFile(filename);
        BufferedOutputStream bos;

        try {
            bos = new BufferedOutputStream(new FileOutputStream(file));
            bitmap.compress(Bitmap.CompressFormat.JPEG, 100, bos);
            bos.flush();
            bos.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }


        return file;

    }

现在书写打开相册代码:

imageUri = Uri.fromFile(saveFile("photo"));
        Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        intent.setType("image/*");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, TAKE_PHOTO);
 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case TAKE_PHOTO:
                if (resultCode == RESULT_OK) {
                    try {
                        Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),data.getData());
                        picture.setImageBitmap(bitmap);
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
        }

是不是很熟悉,但是这个在Android4.4以下的版本的手机运行绝对是没有问题,当你拿4.4以上版本的手机运行,就会出现各种各样的问题,可能会出现选取照片,返回的照片不是你选中的照片,照片错乱了,还有可能直接返回的时候,程序崩溃了。
通过查阅资料,有的朋友说在Android4.4建议使用 Intent.ACTION_OPEN_DOCUMENT,通过测试,可以发现,打开显示最近,或者相册,都是显示的网格形状的图片,但是选择图片的时候依然是会错乱。是不是找不到北了,不过通过反复的查阅,代码修改为:

imageUri = Uri.fromFile(saveFile("photo"));
        Intent intent = new Intent(Intent.ACTION_PICK,android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
intent.setType("image/*");
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, TAKE_PHOTO);

从上面我们可以看到我们的Intent里面用到了action为Intent.ACTION_PICK,uri为android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI,这个打开相册为列表,通过选择图片,就不会再出现上面的情况了。成功了!

二.说下打开相机返回图片的问题,先落上代码:

imageUri = Uri.fromFile(saveFile("camera"));
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        startActivityForResult(intent, TAKE_CAMERA);
 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case TAKE_CAMERA://相机返回
                if (resultCode == RESULT_OK) {
                    try {

                                Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),data.getData());
                                picture.setImageBitmap(bitmap);
                    }  catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;
       }
}

如果以上代码,你能顺利运行的话,那恭喜你,你的测试机太好了,但是现在百分之90的手机都会蹦了,是不是很苦恼,如果崩溃,你可以看下打印的Log,显示 java.lang.RuntimeException: Failure delivering result ResultInfo{who=null, request=1, result=-1, data=null} to activity就是这么一段错误,但是也有的会报错是Uri uri = data.getData();uri为null,这个我没有遇到,遇到最多的就是data为null.
既然data为null,你一定会想到,那就做个判断吧,既然在onActivityResult返回的data为空了,那怎么通过data的值获取bitmap,展示图片呢,方法当然是有的,最前面我们已经拿到imageUri,通过之前的uri也是可以拿到图片的,代码修改为:

 @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        switch (requestCode) {
            case TAKE_CAMERA://相机返回
                if (resultCode == RESULT_OK) {
                    try {
                        if(null!=data&&null!=data.getData()){
                                Bitmap bitmap = MediaStore.Images.Media.getBitmap(this.getContentResolver(),data.getData());
                                picture.setImageBitmap(bitmap);
                                saveBitmapFile(bitmap,"camera");

                        }else{
                            Bitmap bitmap = BitmapFactory.decodeStream(getContentResolver().openInputStream(imageUri));
                            picture.setImageBitmap(bitmap);
                            saveBitmapFile(bitmap,"camera");
                        }
                    }  catch (IOException e) {
                        e.printStackTrace();
                    }
                }
                break;

这样子,就避免了data为null,避免了程序崩溃,但是大家有没有看到saveBitmapFile(bitmap,”camera”);我代码里面展示了,这个是获取拍照之后的File,通常拿到之后,我们会作为参数,然后调用接口,传递给后台服务器,有的人说,你在之前不是已经有saveFile(“camera”)了吗,怎么还写呢,这个就是为了防止data为null,没有保存到指定的文件夹里面。

通过以上的修改,你可以测试99.99%手机,就不会再出现问题了!是不是很爽!

看了老半天了吧,自己写一遍,运行代码,一定是屡试不爽了吧!但是我最开始提到的Uri,你可能忽略了,接下来,就来分析,为什么会出现的问题,想看吗?等着下文继续!

<think>好的,我现在需要帮用户解决在MyEclipse中创建一个分析数据页面的问题。用户提供了前端代码片段,看起来是一个包含表格的JSP页面,可能用于显示采购数据。用户要求使用Servlet2.5和名为lssRkqkDAO的DAO。首先,我需要理清用户的需求,然后逐步构建解决方案。首先,用户的现有代码片段看起来有些混乱,可能是在复制过程中格式错乱了。里面有很多被拆分的HTML标签,比如<divclass=“rightinfo”>应该是<div class="rightinfo">。此外,使用了JSTL的<c:forEach>标签来遍历数据,变量名可能有误,比如{somerkqk }应该是${someRkqk},而var=“xsqk”中的引号是中文的,需要改为英文引号。这些前端的问题可能需要用户自行调整,但作为后端部分,我需要关注如何从数据库获取数据并通过Servlet传递到JSP。接下来,用户需要创建一个分析数据的页面,所以可能需要一个Servlet来处理请求,调用DAO获取数据,然后转发到JSP显示。由于使用Servlet2.5,所以需要在web.xml中配置Servlet,而不是使用注解。首先,我需要设计DAO层。DAO名为lssRkqkDAO,应该有一个方法获取数据,比如getAllRkqk(),返回一个包含采购信息的列表。假设每个采购信息对象(Rkqk)有name、price、sl、dtime等属性,以及对应的getter和setter方法。 然后,编写Servlet,比如AnalysisServlet,在doGet方法中调用DAO获取数据,将结果存储在请求属性中,然后转发到JSP页面。Servlet的配置需要在web.xml中添加<servlet>和<servlet-mapping>。 接下来是JSP页面,用户提供的代码需要调整,正确使用EL表达式和JSTL标签。例如,<c:forEach items="${someRkqk}" var="xsqk">,这里的someRkqk应该与Servlet中设置的属性名一致。同时,表格中的每一列需要正确显示对象的属性,如${xsqk.name}。 需要注意的是,用户可能没有提供数据库表结构,所以需要假设一个结构,比如表名、字段名,或者让用户自行调整SQL查询。此外,数据库连接部分,通常在DAO中使用JDBC,需要配置数据源或者直接连接,这里可能需要使用传统的JDBC方式,比如DriverManager.getConnection。 可能还需要处理异常,比如SQLException,并在Servlet中处理异常,转发到错误页面。但根据用户要求,代码要简单一些,可能暂时不处理异常,留待用户后续补充。现在,分步骤来构建代码:1. 创建Rkqk实体类,包含属性:id(编号)、name(名称)、price(价格)、sl(数量)、dtime(购买时间)。需要生成getter和setter方法。 2.创建lssRkqkDAO类,其中包含getAllRkqk()方法,执行SQL查询,获取所有记录,并封装成Rkqk对象的列表返回。这里需要处理数据库连接,执行查询,遍历ResultSet,创建对象并添加到列表。 3. 创建AnalysisServlet,继承HttpServlet,在doGet方法中调用DAO获取数据,将列表存入request属性,转发到analysis.jsp。 4. 创建analysis.jsp页面,使用JSTL展示数据表格。需要引入JSTL核心标签库,并正确使用<c:forEach>遍历数据。 5.配置web.xml,注册AnalysisServlet,并设置其映射路径,比如/analysis。 在编写代码时,需要注意Servlet 2.5的配置方式,以及JSP页面中的标签是否正确。例如,JSP页面的<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>是否正确引入,项目是否包含了JSTL的库。 可能遇到的问题:用户的环境是否正确配置了数据库驱动,比如MySQL的JDBC驱动,或者在DAO中使用的数据库连接信息是否正确。此外,用户提供的JSP代码中有很多标签被拆分,需要用户自己修正,可能在回答中提醒用户注意这些前端代码的格式问题。最后,测试整个流程:访问Servlet的URL,触发doGet方法,获取数据,跳转到JSP显示表格。如果数据正确显示,则成功。否则需要检查DAO的SQL查询、数据库连接、字段映射等问题。 现在,将这些思考整理成步骤化的回答,并提供示例代码,同时指出需要注意的地方,比如数据库连接的配置,前端标签的修正等。此外,根据用户提供的代码中的变量名,确保Servlet中设置的属性名与JSP中的EL表达式一致,比如someRkqk。</think>以下是基于Servlet 2.5和JSP的实现方案: 1. 首先创建实体类`Rkqk.java`: ```java public class Rkqk { private int id; private String name; private double price; private int sl; private Date dtime; // 省略getter/setter } ``` 2. 创建DAO类`lssRkqkDAO.java`: ```java public class lssRkqkDAO { public List<Rkqk> getAllRkqk() throws SQLException { List<Rkqk> list = new ArrayList<>(); Connection conn = null; try { conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/dbname", "user", "pass"); Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery("SELECT * FROM rkqk_table"); while(rs.next()) { Rkqk obj = new Rkqk(); obj.setId(rs.getInt("id")); obj.setName(rs.getString("name")); obj.setPrice(rs.getDouble("price")); obj.setSl(rs.getInt("sl")); obj.setDtime(rs.getDate("dtime")); list.add(obj); } } finally { if(conn != null) conn.close(); } return list; } } ``` 3. 创建Servlet`AnalysisServlet.java`: ```java public class AnalysisServlet extends HttpServlet { protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { try { List<Rkqk> data = new lssRkqkDAO().getAllRkqk(); request.setAttribute("rkqkList", data); request.getRequestDispatcher("/analysis.jsp").forward(request, response); } catch (Exception e) { throw new ServletException(e); } } } ``` 4. 在web.xml中添加配置: ```xml <servlet> <servlet-name>AnalysisServlet</servlet-name> <servlet-class>com.yourpackage.AnalysisServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>AnalysisServlet</servlet-name> <url-pattern>/analysis</url-pattern> </servlet-mapping> ``` 5. 创建JSP页面`analysis.jsp`: ```jsp <%@ page contentType="text/html;charset=UTF-8" %> <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %> <html> <head> <title>采购分析</title> </head> <body> <div class="rightinfo"> <table class="tablelist"> <thead> <tr> <th>编号</th> <th>名称</th> <th>价格</th> <th>数量</th> <th>购买时间</th> </tr> </thead> <tbody> <c:forEach items="${rkqkList}" var="xsqk" varStatus="status"> <tr> <td>${status.index + 1}</td> <td>${xsqk.name}</td> <td>${xsqk.price}</td> <td>${xsqk.sl}</td> <td>${xsqk.dtime}</td> </tr> </c:forEach> </tbody> </table> </div> </body> </html> ``` 实现说明: 1. DAO层使用原生JDBC实现数据访问 2. Servlet负责业务逻辑处理和请求转发 3. JSP使用JSTL标签展示数据 4. 需要确保MySQL驱动包已加入项目 5. 数据库连接参数需要根据实际环境配置
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值