Mobile Development 12 min read

Simplifying Android Photo Capture with the PhotoTaker Utility

This article explains why traditional Android photo capture using Intents and onActivityResult is cumbersome, especially in list adapters, and introduces a Fragment‑based PhotoTaker utility that streamlines camera and gallery access, handles dynamic permissions, and works across Android versions.

Beike Product & Technology
Beike Product & Technology
Beike Product & Technology
Simplifying Android Photo Capture with the PhotoTaker Utility

Android photo capture is traditionally cumbersome because it requires using an Intent, startActivityForResult , and handling the result in onActivityResult , which separates the request and callback. The article first shows pseudo‑code illustrating this flow.

1 private File photoFile = createFile(); // 创建file用来保存拍照的路径
 2 
 3 onClick.. goToPhoto // 通过Intent去拍照
 4 
 5 public static void goToPhoto(Activity activity,File file) {
 6   Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
 7   intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(file));
 8   activity.startActivityForResult(intent, ConstantUtil.TAKE_PHOTOS); //startActivityForResult 
 9 }
10 
11 // 只能在Activity或者Fragment的onActivityResult回调方法中接受拍照结果
12 @Override
13 protected void onActivityResult(int requestCode, int resultCode, Intent data) {
14   switch (requestCode) {
15     case ConstantUtil.TAKE_PHOTOS:
16       if (photoFile != null && photoFile.exists()) {
17         // 拍照完成,拿到图片
18       }
19       break;
20   }
21 }

It then outlines the classic steps: declare a File member to store the photo path, launch the camera via Intent, and retrieve the result in onActivityResult , noting the difficulty when the request originates from a list item in an Adapter.

由于拍照的回调和拍照是隔离的,也就是说我们只能在onActivityResult,也就是Activity或者Fragment中才能拿到拍照的结果。而我们拍照的地方是在Adapter里,即使我们可以通过接口把拍照的行为放到Activity或Fragment中处理,但是一样是隔离的,我们无法关联拍照和拍照结果。

To simplify, the author proposes using a hidden Fragment to aggregate the request and callback, similar to dynamic‑permission libraries like RxPermission.

Android 6.0 Dynamic Permission Adaptation

Dynamic permission requests must also be initiated from an Activity or Fragment, with results returned in onRequestPermissionsResult , so the same Fragment‑based approach can be reused.

PhotoTaker

The PhotoTaker utility consists of a TakePhotoFragment that forwards activity results to PhotoTaker, and the PhotoTaker class that handles permission requests, file creation, camera launch (including FileProvider handling for Android 7.0+), and album selection.

public class TakePhotoFragment extends Fragment {
  private PhotoTaker photoTaker;

  public void bindRxPhotoTaker(PhotoTaker photoTaker) {
    this.photoTaker = photoTaker;
  }

  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setRetainInstance(true);
  }

  @Override
  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);
    photoTaker.onActivityResult(requestCode, resultCode, data);
  }
}

public class PhotoTaker {
  private static final String TAG = "PhotoTaker";
  private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("yyyyMMddmmss");
  public static int RESULT_TAKE_PHOTO = 1;
  public static int RESULT_PEEK_ALBUM = 2;

  private TakePhotoFragment takePhotoFragment;
  private BaseDialog dialog;
  private FragmentActivity activity;
  private String savePhotoPath;
  private Callback callback;

  public PhotoTaker(FragmentActivity activity, Callback callback) {
    this.activity = activity;
    this.callback = callback;
    takePhotoFragment = getRxTakePhotoFragment(activity);
    takePhotoFragment.bindRxPhotoTaker(this);
  }

  private TakePhotoFragment getRxTakePhotoFragment(FragmentActivity activity) {
    TakePhotoFragment fragment = findRxPermissionsFragment(activity);
    boolean isNewInstance = fragment == null;
    if (isNewInstance) {
      fragment = new TakePhotoFragment();
      FragmentManager fragmentManager = activity.getSupportFragmentManager();
      fragmentManager.beginTransaction().add(fragment, TAG).commitAllowingStateLoss();
      fragmentManager.executePendingTransactions();
    }
    return fragment;
  }

  private TakePhotoFragment findRxPermissionsFragment(FragmentActivity activity) {
    return (TakePhotoFragment) activity.getSupportFragmentManager().findFragmentByTag(TAG);
  }

  public void show() {
    if (dialog == null) {
      dialog = new BaseDialog(activity);
      dialog.setGravity(Gravity.BOTTOM);
      dialog.setContentView(R.layout.dialog_take_photo);
      dialog.setLayout(-1, -2);
      dialog.findViewById(R.id.tv_take_photo).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.dismiss();
          takePhoto();
        }
      });
      dialog.findViewById(R.id.tv_peek_album).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.dismiss();
          peekImageFromAlbum(takePhotoFragment, RESULT_PEEK_ALBUM);
        }
      });
      dialog.findViewById(R.id.tv_cancel).setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
          dialog.dismiss();
        }
      });
    }
    dialog.show();
  }

  private void peekImageFromAlbum(Fragment fragment, int resultCode) {
    Intent intent = new Intent(Intent.ACTION_PICK, null);
    intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
    fragment.startActivityForResult(intent, resultCode);
  }

  private void takePhoto() {
    // 申请相机权限
    RxPermissions rxPermissions = new RxPermissions(activity);
    rxPermissions.request(Manifest.permission.CAMERA)
      .subscribe(new Action1
() {
        @Override
        public void call(Boolean granted) {
          if (granted) {
            savePhotoPath = createFile(activity);
            openCamera(takePhotoFragment, savePhotoPath, RESULT_TAKE_PHOTO);
          }
        }
      });
  }

  private void openCamera(Fragment fragment, String outputPath, int resultCode) {
    // 创建File对象,用于存储拍照后的图片
    File outputFile = new File(outputPath);
    FileUtil.createFile(outputFile);
    Uri imageUri;
    if (Build.VERSION.SDK_INT < 24) {
      imageUri = Uri.fromFile(outputFile);
    } else {
      // Android 7.0+ 使用 FileProvider
      imageUri = FileProvider.getUriForFile(fragment.getContext(), "org.jaaksi.photoker.demo.fileprovider", outputFile);
    }
    Intent intent = new Intent("android.media.action.IMAGE_CAPTURE");
    intent.putExtra(MediaStore.EXTRA_OUTPUT, imageUri);
    fragment.startActivityForResult(intent, resultCode);
  }

  public static String createFile(Context context) {
    return context.getExternalFilesDir(null) + "/" + DATE_FORMAT.format(new Date()) + ".jpg";
  }

  public void onActivityResult(int requestCode, int resultCode, Intent data) {
    if (resultCode != Activity.RESULT_OK) return;
    if (requestCode == PhotoTaker.RESULT_TAKE_PHOTO) {
      callback.onResult(savePhotoPath);
    } else if (requestCode == PhotoTaker.RESULT_PEEK_ALBUM) {
      if (data != null) {
        Uri selectedImage = data.getData();
        if (selectedImage == null) return;
        String[] filePathColumns = { MediaStore.Images.Media.DATA };
        Cursor cursor = activity.getContentResolver().query(selectedImage, filePathColumns, null, null, null);
        if (cursor != null) {
          cursor.moveToFirst();
          int columnIndex = cursor.getColumnIndex(filePathColumns[0]);
          String imagePath = cursor.getString(columnIndex);
          callback.onResult(imagePath);
          cursor.close();
        }
      }
    }
  }

  public interface Callback {
    /**
     * @param filePath 图片路径
     */
    void onResult(String filePath); // 暂时只能一次选择一张
  }
}

Usage is straightforward: instantiate PhotoTaker with an Activity and a Callback , then call show() to let the user take a photo or pick one from the gallery; the Callback receives the image file path.

public void onClick(View view) {
  new PhotoTaker(this, new PhotoTaker.Callback() {
    @Override
    public void onResult(String filePath) {
      imageview.setImageURI(Uri.fromFile(new File(filePath)));
    }
  }).show();
}

The demo repository is provided at the end of the article: https://gitee.com/jaaksi/PhotoTaker .

AndroidFragmentCameraDynamic Permissionphoto capture
Beike Product & Technology
Written by

Beike Product & Technology

As Beike's official product and technology account, we are committed to building a platform for sharing Beike's product and technology insights, targeting internet/O2O developers and product professionals. We share high-quality original articles, tech salon events, and recruitment information weekly. Welcome to follow us.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.