Android如何在ImageView上设置来自URI的图片

6

更新了更多的代码

我正在尝试通过编程方式获取刚拍摄的照片并将其设置为ImageView

按下拍照按钮,

picture_button.setOnClickListener(new View.OnClickListener() {
    public void onClick(View v) {
        takePhoto(mTextureView);
    }
});

它运行takePhoto方法:

public void takePhoto(View view) {
    try {
        mImageFile = createImageFile();
        final ImageView latest_picture = (ImageView) findViewById(R.id.latest_picture);
        final RelativeLayout latest_picture_container = (RelativeLayout) findViewById(R.id.latest_picture_container);
        final String mImageFileLocationNew = mImageFileLocation.replaceFirst("^/", "");
        Toast.makeText(getApplicationContext(), "" + mImageFile, Toast.LENGTH_SHORT).show();
        Uri uri = Uri.fromFile(mImageFile);
        Toast.makeText(getApplicationContext(), ""+uri, Toast.LENGTH_LONG).show();
        latest_picture.setImageURI(uri);
        latest_picture_container.setVisibility(View.VISIBLE);
    } catch (IOException e){
        e.printStackTrace();
    }
    lockFocus();
    captureStillImage();
}

这段代码运行了createImageFile()captureStillImage()函数。

private void captureStillImage() {
    try {
        CaptureRequest.Builder captureStillBuilder = mCameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
        captureStillBuilder.addTarget(mImageReader.getSurface());
        int rotation = getWindowManager().getDefaultDisplay().getRotation();
        captureStillBuilder.set(CaptureRequest.JPEG_ORIENTATION, ORIENTATIONS.get(rotation));
        CameraCaptureSession.CaptureCallback captureCallback = new CameraCaptureSession.CaptureCallback() {
            @Override
            public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request, TotalCaptureResult result) {
                super.onCaptureCompleted(session, request, result);
                //Toast.makeText(getApplicationContext(), "Image Taken", Toast.LENGTH_SHORT).show();
                unLockFocus();
            }
        };
        mCameraCaptureSession.capture(captureStillBuilder.build(), captureCallback, null);
    } catch (CameraAccessException e) {
        e.printStackTrace();
    }
}

File createImageFile() throws IOException {
    String timestamp = new SimpleDateFormat("yyyyMMdd").format(new Date());
    String subFolder = "";
    if(pref_session_unique_gallery.equals("yes")){
        if(event_name != null){
            subFolder = event_name;
        } else {
            subFolder = timestamp;
        }
    } else {
        subFolder = "_GEN";
    }
    if(event_name == null){
        event_name = "";
    } else {
        event_name = event_name + "_";
    }
    String imageFileName = "CPB_"+event_name+timestamp+"_";
    File storageDirectory = new File(Environment.getExternalStorageDirectory() + File.separator + "CPB" + File.separator + subFolder);
    storageDirectory.mkdir();
    File image = File.createTempFile(imageFileName, ".jpg", storageDirectory);
    mImageFileLocation = image.getAbsolutePath();
    return image;
}

图片保存在这里:
private static class ImageSaver implements Runnable {
    private final Image mImage;
    private ImageSaver(Image image) {
        mImage = image;
    }
    @Override
    public void run() {
        ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes);

        FileOutputStream fileOutputStream = null;

        try {
            fileOutputStream = new FileOutputStream(mImageFile);
            fileOutputStream.write(bytes);

        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            mImage.close();
            if(fileOutputStream != null){
                try {
                    fileOutputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }

            }
        }

我在`createImageFile()`函数中得到了正确的图像路径,并且每次都会通过toast显示它。但是,即使对于`latest_picture_container`设置了可见性属性,在这之后仍然无法正常工作。如果我注释掉`InputStream`、`Bitmap`和`setImageBitmap`,那么`latest_picture_container`就会像平常一样显示。不确定出了什么问题。
由于某种原因,URI在文件后面返回了三个斜杠。
file:///storage/0/...

你确定你获取到了正确的图片路径吗?你尝试打印输出路径了吗? - capt.swag
@4k3R 我刚发现路径在 file:// 后面多了一个 /,所以我正在努力找出它是从哪里来的,并想办法摆脱它。 - ntgCleaner
@4k3R,如果需要的话,我已经添加了创建图像的方法。 - ntgCleaner
你把图片存储在哪里了? - tiny sunlight
我还没有看到你是如何将图片存储在文件中的。图片在哪里?file:///storage/0/... 是正常的URI格式。 - tiny sunlight
显示剩余2条评论
4个回答

6
AssetManager#open() 方法仅适用于您应用程序的资产; 即,您项目中的文件夹 /assets 中的文件。由于您正在尝试打开外部文件,因此应改为使用从路径创建的 File 对象上的 FileInputStream。此外,takePhoto() 方法在写入文件之前将图像设置到 ImageView 上,这将导致空的 ImageView

由于您的应用程序直接使用相机,因此我们可以从写入图像文件的同一字节数组中解码图像,并节省不必要的存储读取。此外,在您的代码示例中,文件写入是在单独的线程上进行的,因此我们可以利用它来执行图像解码,以最小化对 UI 线程的影响。

首先,我们将创建一个接口,通过该接口,ImageSaver 可以将图像传回 Activity 以在 ImageView 中显示。

public interface OnImageDecodedListener {
    public void onImageDecoded(Bitmap b);
}

然后,我们需要稍微修改ImageSaver类的构造函数,以接受一个Activity参数。我还添加了一个File参数,这样Activity对应的字段就不需要是static了。

private static class ImageSaver implements Runnable {
    private final Activity mActivity;
    private final Image mImage;
    private final File mImageFile;

    public ImageSaver(Activity activity, Image image, File imageFile) {
        mActivity = activity;
        mImage = image;
        mImageFile = imageFile;
    }

    @Override
    public void run() {
        ByteBuffer byteBuffer = mImage.getPlanes()[0].getBuffer();
        byte[] bytes = new byte[byteBuffer.remaining()];
        byteBuffer.get(bytes);

        final Bitmap b = BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
        mActivity.runOnUiThread(new Runnable() {
                @Override
                public void run() {
                    ((OnImageDecodedListener) mActivity).onImageDecoded(b);
                }
            }
        );

        FileOutputStream fileOutputStream = null;
        ...
    }
}

一旦我们获得了图像数据的字节数组,就会对其进行解码并将其传回到Activity中,因此它不必等待文件写入,可以在后台静默进行。由于我们正在“接触”View,所以需要在UI线程上调用接口方法。

Activity将需要实现该接口,我们可以将与View相关的内容从takePhoto()移动到onImageDecoded()方法中。

public class MainActivity extends Activity
    implements ImageSaver.OnImageDecodedListener {
    ...

    @Override
    public void onImageDecoded(Bitmap b) {
        final ImageView latest_picture =
            (ImageView) findViewById(R.id.latest_picture);
        final RelativeLayout latest_picture_container =
            (RelativeLayout) findViewById(R.id.latest_picture_container);

        latest_picture.setImageBitmap(b);
        latest_picture_container.setVisibility(View.VISIBLE);
    }

    public void takePhoto(View view) {
        try {
            mImageFile = createImageFile();
            captureStillImage();
            lockFocus();
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }
    ...
}

最后,我们需要在ImageReaderonImageAvailable()方法中实际执行ImageSaver。再次参考示例,代码如下:
private final ImageReader.OnImageAvailableListener mOnImageAvailableListener = 
    new ImageReader.OnImageAvailableListener() {
    @Override
    public void onImageAvailable(ImageReader reader) {
        mBackgroundHandler.post(new ImageSaver(MainActivity.this,
                                               reader.acquireNextImage(),
                                               mImageFile)
        );
    }
};

1
真是太棒了,Mike!感谢你所付出的所有努力和帮助。 - ntgCleaner

1
我通常使用Picasso库。 将该依赖项添加到build.gradle。
compile 'com.squareup.picasso:picasso:2.5.2'

由于您已经获取了文件路径,将路径设置为File对象,然后让Picasso从手机中获取图像,并将其设置为所需的ImageView。
File fileLocation = new File(uri) //file path, which can be String, or Uri

Picasso.with(this).load(fileLocation).into(imageView);

我建议您在应用程序中进行任何类型的图像活动时使用Picasso,无论是在线还是离线。Picasso由Square制作,是最好的图像加载和缓存工具...

1
尝试这个简单的代码。
ImageView imgView = view.findViewById(R.id.lw_foto);

imgView.setImageURI(mListaContactos.get(i).foto);

0
public static String getEDirectory(String folderName,Activity activity) {
    if(Environment.MEDIA_MOUNTED.equalsIgnoreCase(Environment.getExternalStorageState())){
        LogUtils.e( activity.getExternalFilesDir(folderName).getAbsolutePath());
        return activity.getExternalFilesDir(folderName).getAbsolutePath();
    }else{
        return activity.getFilesDir().getAbsolutePath() +File.separator+folderName;
    }
}    
public static Uri getImageUri(String shortname,Activity activity) {

    if (shortname == null) {
        shortname = "www.jpg";
    }
    String path = DataStore.getEDirectory("pure",activity);
    File files = new File(path);
    files.mkdirs();

    File file = new File(path, shortname);
    return Uri.fromFile(file);

}
    public static Uri attemptStartCamera(Activity ctx){
        Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
        String latestfilename = System.currentTimeMillis() + ".jpg";
        Uri imageUri = getImageUri(latestfilename,ctx);
        intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
        ctx.startActivityForResult(intent, MsgCodes.PICK_CAMERA);
        return imageUri;
    }

在Activity中:
mUri = attemptStartCamera(Activity.this);

这样,您就可以获得Uri。

您可以在onActivityResult中加载您的图像:

public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    if(requestCode==MsgCodes.PICK_CAMERA && resultCode == RESULT_OK){
        mImageView.setImageUri(mUri);
    }
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接