Thursday, August 1, 2013

Convert between Uri and file path, and load Bitmap from.

What return from Android build-in Gallery App is Uri, in the form of "content://media/...". This exercise demonstrate how to convert the Uri to real file path, in the form of "/storage/sdcard0/...". and convert back to Uri, in the form of "file:///storage/sdcard0/...".  All the three form refer to the same file.

And also demonstrate how to load bitmap from the Uri(s) and file path, then display in ImageView.

Convert between Uri and file path, and load Bitmap from.


package com.example.androiduri;

import java.io.File;
import java.io.FileNotFoundException;

import android.net.Uri;
import android.os.Bundle;
import android.provider.MediaStore;
import android.provider.MediaStore.Images;
import android.support.v4.content.CursorLoader;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.app.Activity;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

public class MainActivity extends Activity {

 Button btnSelFile;
 TextView text1, text2, text3, note;
 ImageView image;
 
 Uri orgUri, uriFromPath;
 String convertedPath;
 
 @Override
 protected void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.activity_main);
  btnSelFile = (Button)findViewById(R.id.selfile);
  text1 = (TextView)findViewById(R.id.text1);
  text2 = (TextView)findViewById(R.id.text2);
  text3 = (TextView)findViewById(R.id.text3);
  note = (TextView)findViewById(R.id.note);
  image = (ImageView)findViewById(R.id.image);
  
  btnSelFile.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View arg0) {
    Intent intent = new Intent(Intent.ACTION_PICK, 
      Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(intent, 0);
   }});
  
  text1.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    image.setImageBitmap(null);
    note.setText("by Returned Uri");
    
    try {
     Bitmap bm = BitmapFactory.decodeStream(
       getContentResolver().openInputStream(orgUri));
     image.setImageBitmap(bm); 
    } catch (FileNotFoundException e) {
     e.printStackTrace(); 
    }
   }});
  
  text2.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    image.setImageBitmap(null);
    note.setText("by Real Path");
    Bitmap bm = BitmapFactory.decodeFile(convertedPath);
    image.setImageBitmap(bm);
   }});
  
  text3.setOnClickListener(new OnClickListener(){

   @Override
   public void onClick(View v) {
    image.setImageBitmap(null);
    note.setText("by Back Uri");
    
    try {
     Bitmap bm = BitmapFactory.decodeStream(
       getContentResolver().openInputStream(uriFromPath));
     image.setImageBitmap(bm); 
    } catch (FileNotFoundException e) {
     e.printStackTrace(); 
    }
   }});
 }

 @Override
 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
  super.onActivityResult(requestCode, resultCode, data);
  
  if(resultCode == RESULT_OK){
   
   image.setImageBitmap(null);
   
   //Uri return from external activity
   orgUri = data.getData();
   text1.setText("Returned Uri: " + orgUri.toString() + "\n");
   
   //path converted from Uri
   convertedPath = getRealPathFromURI(orgUri);
   text2.setText("Real Path: " + convertedPath + "\n");
   
   //Uri convert back again from path
   uriFromPath = Uri.fromFile(new File(convertedPath));
   text3.setText("Back Uri: " + uriFromPath.toString() + "\n");
  }
  
 }

 public String getRealPathFromURI(Uri contentUri) {
  String[] proj = { MediaStore.Images.Media.DATA };
  
  //This method was deprecated in API level 11
  //Cursor cursor = managedQuery(contentUri, proj, null, null, null);
  
  CursorLoader cursorLoader = new CursorLoader(
            this, 
            contentUri, proj, null, null, null);        
  Cursor cursor = cursorLoader.loadInBackground();
  
  int column_index = 
    cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
  cursor.moveToFirst();
  return cursor.getString(column_index); 
 }
 
}


<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="android-er.blogspot.com" />
    <Button 
        android:id="@+id/selfile"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:text="Select File" />
    <TextView
        android:id="@+id/text1"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/text2"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/text3"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <TextView
        android:id="@+id/note"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />
    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

</LinearLayout>


Please notice that the code haven't handle resize on the bitmap. If you load with a large picture, OutOfMemoryError will be caused.

download filesDownload the files.

Related: Load picture with Intent.ACTION_GET_CONTENT


Update@2014-12-27: Not work on KitKat now!

Permission of "android.permission.READ_EXTERNAL_STORAGE" have to be added in AndroidManifest.xml. Refer to Update "android.permission.READ_EXTERNAL_STORAGE" for KitKat.

6 comments:

Etienne said...

Thank you VERY much !
Your articles are always very useful, short and clear, but this one is absolutely amazingly interesting (regarding the number of hours I lost trying to load bitmaps and play with uri and paths)

Anonymous said...

i copy past the code but image not appear in emulator when i make run
can u help me plz ??

Erik said...

- You have to tap on the TextView of "Returned Uri", "Real Path" or "Back Uri" to display the image.

- This example haven't handle re-size of the bitmap. So if the target is too large, it cannot be shown. You can check logcat, "Bitmap too large to be uploaded into a texture" will be shown if this case happen.

Anonymous said...

thanks for yor help,
it is very useful.

fabian said...

message saying cannot resolve method getContentResolver() should i import anything??

Erik said...

hello fabian,

I download and test the example again, without error.

Where you call getContentResolver()? from Activity/ActionBarActivity? or from any other class? If you call from another class, you have to pass context, read HERE.