Hello, mình là Dương Vũ. Đây là bài viết thứ 2 trong loạt bài hướng dẫn xây dựng ứng dụng chụp ảnh và filter camera trên Android. Ở bài viết trước mình đã hướng dẫn xây dựng màn hình Splash có chức năng hiện ra dòng giới thiệu về ứng dụng, đồng thời xin cấp các permission sử dụng trong ứng dụng. Bài này, ta sẽ cùng nhau xây dựng module chính của ứng dụng: module camera và chụp ảnh. Sau khi hoàn thành bài viết này, ứng dụng của ta sẽ có thêm các chức năng:
- Camera kèm theo hiệu ứng (filter)
- Hiển thị danh sách filter để lựa chọn khi chụp ảnh
- Lưu trữ ảnh trên bộ nhớ thiết bị
- Các chức năng cơ bản của camera: quay trước, quay sau, on/off chế độ chụp flash.
Nào, bắt đầu nhé!
[toc]
Màn hình Main
Ok, mình sẽ sử dụng luôn lớp MainActivity mà IDE tạo ra cho lúc khởi tạo dự án để viết chức năng camera. Màn hình này bao gồm 1 camera view có kích thước chiều rộng full màn hình, chiều cao bằng chiều rộng (tỉ lệ 1:1). Có 1 button để chụp ảnh, 1 recyclerView để hiển thị danh sách hiệu ứng, các button chức năng của camera (on/off flash, đổi camera). Ngoài ra màn hình còn có thêm 1 button cho phép chọn ảnh từ gallery. Ok, ta hãy bắt tay vào xây dựng layout cho nó.
Xây dựng layout
Ta chỉnh sửa lại file activity_main.xml như sau:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout 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"> <RelativeLayout android:id="@+id/tb_camera" android:layout_width="match_parent" android:layout_height="45dp" android:background="@color/toolbar_color"> <ImageView android:id="@+id/bt_close" android:layout_width="@dimen/_30sdp" android:layout_height="wrap_content" android:layout_centerVertical="true" android:layout_marginLeft="@dimen/_4sdp" android:adjustViewBounds="true" android:padding="@dimen/_8sdp" android:src="@drawable/ic_close_camera" /> <ImageView android:layout_width="@dimen/_30sdp" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_centerVertical="true" android:layout_marginLeft="@dimen/_4sdp" android:layout_marginRight="@dimen/_4sdp" android:adjustViewBounds="true" android:padding="@dimen/_6sdp" android:src="@drawable/ic_done_camera" /> <TextView style="@style/UTM_AvoBold" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:text="CAMERA" android:textColor="#3c3837" android:textSize="14sp" /> </RelativeLayout> <RelativeLayout android:id="@+id/rl_camera_area" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_below="@+id/tb_camera"> <org.wysaid.view.CameraRecordGLSurfaceView android:id="@+id/c_view" android:layout_width="match_parent" android:layout_height="match_parent" /> <SeekBar android:id="@+id/seekBar" android:layout_width="match_parent" android:layout_height="80dp" android:layout_alignParentTop="true" android:layout_gravity="center_horizontal|top" android:visibility="gone" /> <ImageView android:id="@+id/bt_rotate_camera" android:layout_width="@dimen/_40sdp" android:layout_height="@dimen/_40sdp" android:layout_alignParentBottom="true" android:padding="@dimen/_10sdp" android:src="@drawable/ic_rotate_camera" /> <ImageView android:id="@+id/bt_flash_mode" android:layout_width="@dimen/_40sdp" android:layout_height="@dimen/_40sdp" android:layout_alignParentBottom="true" android:layout_alignParentRight="true" android:padding="@dimen/_10sdp" android:src="@drawable/ic_turn_on_flash" /> <ImageView android:layout_width="@dimen/_55sdp" android:layout_height="@dimen/_55sdp" android:layout_centerInParent="true" android:src="@drawable/ic_focus_camera" /> </RelativeLayout> <android.support.v7.widget.RecyclerView android:id="@+id/rv_filter" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_alignParentBottom="true" android:background="@color/toolbar_color"> </android.support.v7.widget.RecyclerView> <RelativeLayout android:id="@+id/rl_camera_action" android:layout_width="match_parent" android:layout_height="wrap_content" android:layout_above="@+id/rv_filter" android:background="@color/toolbar_color"> <com.makeramen.roundedimageview.RoundedImageView xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/iv_pick_image" android:layout_width="@dimen/_40sdp" android:layout_height="@dimen/_40sdp" android:layout_centerVertical="true" android:layout_margin="@dimen/_10sdp" android:scaleType="fitXY" android:src="@drawable/default_avatar" app:riv_border_color="@android:color/white" app:riv_border_width="3dp" app:riv_corner_radius="@dimen/_5sdp" app:riv_mutate_background="true" app:riv_oval="false" /> <ImageView android:id="@+id/bt_take_picture" android:layout_width="@dimen/_50sdp" android:layout_height="@dimen/_50sdp" android:layout_centerInParent="true" android:layout_margin="@dimen/_10sdp" android:scaleType="fitXY" android:src="@drawable/ic_take_picture" /> </RelativeLayout> </RelativeLayout>
Bạn đừng lo lắng khi nhìn sang preview thấy layout của ta như 1 mớ shit. Ta sẽ chỉnh sửa lại kích thước cho chúng ở phần code Java. Ở đây, ta chỉ cần lưu ý tới 1 view đặc biệt, đó là org.wysaid.view.CameraRecordGLSurfaceView. Đây chính là view thuôc thư viện xử lý ảnh mà mình đã trình bày ở bài đầu tiên. Nó sẽ có nhiệm vụ hiển thị camera preview cho chúng ta.
MainActivity.java
Khai báo layout đã xong, giờ ta sẽ chuyển tới phần Java code. Việc đầu tiên là ta sẽ bắt MainActivity kế thừa BaseActivity ta đã tạo ra (chứ không phải kế thừa AppCompatActivity như mặc định), sau đó sẽ là bindView cho nó:
package fbphoto.thou.com.myapplication; import android.support.v7.widget.RecyclerView; import android.widget.ImageView; import android.widget.RelativeLayout; import com.makeramen.roundedimageview.RoundedImageView; import org.wysaid.view.CameraRecordGLSurfaceView; import butterknife.Bind; /** * Created by Computer on 2/12/2018. */ public class MainActivity extends BaseActivity { @Bind(R.id.rl_camera_area) RelativeLayout rlCameraView; @Bind(R.id.c_view) CameraRecordGLSurfaceView cameraView; @Bind(R.id.bt_flash_mode) ImageView btFlashMode; @Bind(R.id.rv_filter) RecyclerView rvFilter; @Bind(R.id.iv_pick_image) RoundedImageView ivPickImage; @Override protected int getLayoutId() { return R.layout.activity_main; } @Override protected void createView() { } }
Setup camera
Tiếp theo, mình sẽ viết 1 phương thức setUpCameraView, phương thức này sẽ chỉnh sửa lại kích thước của cameraView, do ở phần layout xml, kích thước của cameraView chưa đúng tỉ lệ 1:1. Ngoài ra, ta cũng sẽ bắt các sự kiện cho các button on/off flash và config 1 số thuộc tính của cameraview:
private void setUpCameraView() { int screenWidth = DeviceUtils.getScreenWidth(this); int screenHeight = DeviceUtils.getScreenHeight(this); //set vùng camera thành vùng vuông rlCameraView.getLayoutParams().height = screenWidth; cameraView.getLayoutParams().height = screenWidth; cameraView.presetCameraForward(true); cameraView.presetRecordingSize(screenHeight, screenHeight); cameraView.setPictureSize(screenHeight, screenHeight, true); // > 4MP cameraView.setZOrderOnTop(false); cameraView.setZOrderMediaOverlay(true); btFlashMode.setOnClickListener(new View.OnClickListener() { int flashIndex = 0; String[] flashModes = { Camera.Parameters.FLASH_MODE_TORCH, Camera.Parameters.FLASH_MODE_AUTO, }; @Override public void onClick(View v) { cameraView.setFlashLightMode(flashModes[flashIndex]); ++flashIndex; flashIndex %= flashModes.length; if (flashIndex == 0) { btFlashMode.setImageResource(R.drawable.ic_turn_on_flash); } else { btFlashMode.setImageResource(R.drawable.ic_turn_off_flash); } } }); }
Ok, việc setup cho cameraview đến đây gần như đã xong. Tuy nhiên còn 1 bước nữa. Như đã giới thiệu ở bài trước, mình có sử dụng những file LUT chứa trong thư mục assets để tạo ra hiệu ứng cho camera. Vì vậy, ta cần khai báo (load) các file LUT này cho đối tượng cameraView của chúng ta. Trước hết, hãy tạo 1 callback theo dõi việc load các file LUT success hay failed:
private CGENativeLibrary.LoadImageCallback mLoadImageCallback = new CGENativeLibrary.LoadImageCallback() { @Override public Bitmap loadImage(String name, Object arg) { Log.i(Common.LOG_TAG, "Loading file: " + name); AssetManager am = getAssets(); InputStream is; try { is = am.open(name); } catch (IOException e) { Log.e(Common.LOG_TAG, "Can not open file " + name); return null; } return BitmapFactory.decodeStream(is); } @Override public void loadImageOK(Bitmap bmp, Object arg) { Log.i(Common.LOG_TAG, "Loading bitmap over, you can choose to recycle or cache"); bmp.recycle(); } };
Sau khi tạo callback xong, ta thêm câu lệnh sau vào phương thức setUpCameraView để load các file LUT:
CGENativeLibrary.setLoadImageCallback(mLoadImageCallback, null);
Ok, vậy là các bước config cho cameraView đã hoàn thành. Ta sẽ gọi hàm setUpCameraView vừa viết ở trong hàm createView:
@Override protected void createView() { setUpCameraView(); }
và không quên tắt camera khi rời khỏi activity hiện tại:
@Override public void onResume() { super.onResume(); cameraView.onResume(); } @Override public void onPause() { super.onPause(); CameraInstance.getInstance().stopCamera(); cameraView.release(null); cameraView.onPause(); }
Xây dựng list filter
Bây giờ việc tiếp theo của ta là hiển thị list các filter để cho người dùng chọn trong lúc chụp ảnh. Ta sẽ dùng recyclerView để hiển thị các filter này. Việc đầu tiên chúng ta cần làm để xây dựng list filter là định nghĩa 1 đối tượng chứa các thông tin về filter đó.
Đối tượng FilterData.
Mình sẽ tạo 1 package model. Bên trong chứa lớp FilterData có nội dung như sau:
package fbphoto.thou.com.model; import java.io.Serializable; /** * Created by Computer on 2/8/2018. */ public class FilterData implements Serializable { String name; String rule; int imageId; public FilterData(String name, String rule, int imageId) { this.name = name; this.rule = rule; this.imageId = imageId; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getRule() { return rule; } public void setRule(String rule) { this.rule = rule; } public int getImageId() { return imageId; } public void setImageId(int imageId) { this.imageId = imageId; } }
Như các bạn thấy lớp này chỉ là 1 lớp Java thuần túy, không có gì đặc biệt cả. Nó chứa 3 trường:
- name: Tên hiển thị trên app của filter.
- rule: config của filter để thư viện xử lý ảnh thực hiện theo. Thực chất đây là tên các file LUT. Nếu các bạn tìm hiểu sâu thêm về thư viện xử lý ảnh này, các bạn sẽ thấy rằng có thể viết các config để thực hiện bất kỳ hiệu ứng nào ta mong muốn (chỉ cần có giá trị tham số). Tuy nhiên mình sẽ viết ở 1 bài khác. Ở bài này ta chỉ cần quan tâm đến tên config chứa file LUT mà thôi.
- imageId: Id ảnh preview của filter. Mình sẽ đặt sẵn các ảnh preview ứng với từng filter trong thư mục drawable. Và trường imageId này sẽ có giá trị là id của các ảnh đó để hiện thị lên trên app.
Như vậy là ta đã xây dựng xong đối tượng chứa các thông tin về 1 filter. Giờ ta sẽ xây dựng adapter hiển thị list filter.
Xây dựng List Filter Adapter
Đầu tiên ta sẽ tạo layout cho các item của adapter, đặt tên là item_list_filter.xml:
<?xml version="1.0" encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:id="@+id/ll_filter" android:layout_width="@dimen/_80sdp" android:layout_height="wrap_content" android:layout_marginBottom="@dimen/_6sdp" android:layout_marginLeft="@dimen/_3sdp" android:layout_marginRight="@dimen/_3sdp" android:background="@drawable/bg_item_filter_unselected" android:gravity="center_horizontal" android:orientation="vertical"> <ImageView android:id="@+id/iv_filter_image" android:layout_width="@dimen/_65sdp" android:layout_height="@dimen/_65sdp" android:layout_marginTop="@dimen/_2sdp" android:scaleType="fitXY" /> <TextView android:id="@+id/tv_filter_name" style="@style/Roboto_Regular" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginTop="@dimen/_3sdp" android:textColor="#3c3837" android:textSize="14sp" /> </LinearLayout>
Hãy để ý tới dòng:
android:background="@drawable/bg_item_filter_unselected"
Khi 1 filter được chọn, nó sẽ có 1 background riêng, để dễ phân biệt với các filter còn lại. Ta sẽ viết 2 file xml để thể hiện 2 trạng thái (được chọn và không được chọn) của filter. Trong thư mục drawable, tạo file bg_item_filter_selected.xml tương ứng với trạng thái filter đang được chọn:
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> <stroke android:width="1dp" android:color="@color/colorPrimary"></stroke> </shape>
Tạo thêm 1 file bg_item_filter_unselected.xml
<?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http://schemas.android.com/apk/res/android"> </shape>
Như các bạn thấy, ở trạng thái không được chọn, ta sẽ set cho filter đó có 1 background trống trơn.
Ok, mọi thứ liên quan đến layout đã xong, giờ ta sẽ viết adapter cho list filter. Ta tạo package adapter và định nghĩa lớp ListFilterAdapter như sau:
package fbphoto.thou.com.adapter; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.ImageView; import android.widget.TextView; import java.util.List; import butterknife.Bind; import butterknife.ButterKnife; import fbphoto.thou.com.model.FilterData; import fbphoto.thou.com.myapplication.R; /** * Created by Computer on 2/8/2018. */ public class ListFilterAdapter extends RecyclerView.Adapter<ListFilterAdapter.FilterViewHolder> { List<FilterData> listFilter; int currentPosition = 0; public ListFilterAdapter(List<FilterData> listFilter) { this.listFilter = listFilter; } @Override public FilterViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_filter, parent, false); return new FilterViewHolder(view); } @Override public void onBindViewHolder(FilterViewHolder holder, int position) { holder.setData(listFilter.get(position), position); } @Override public int getItemCount() { return listFilter.size(); } class FilterViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.ll_filter) View llFilter; @Bind(R.id.iv_filter_image) ImageView ivFilterImage; @Bind(R.id.tv_filter_name) TextView tvFilterName; public FilterViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } public void setData(final FilterData filterData, final int position) { ivFilterImage.setImageResource(filterData.getImageId()); tvFilterName.setText(filterData.getName()); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (onFilterSelect != null) { int oldFocusPosition = currentPosition; currentPosition = position; notifyItemChanged(oldFocusPosition); notifyItemChanged(position); onFilterSelect.onSelect(filterData); } } }); if (position == currentPosition) { llFilter.setBackgroundResource(R.drawable.bg_item_filter_selected); } else { llFilter.setBackgroundResource(R.drawable.bg_item_filter_unselected); } } } public interface OnFilterSelect { void onSelect(FilterData filterData); } OnFilterSelect onFilterSelect; public OnFilterSelect getOnFilterSelect() { return onFilterSelect; } public void setOnFilterSelect(OnFilterSelect onFilterSelect) { this.onFilterSelect = onFilterSelect; } public int getCurrentPosition() { return currentPosition; } public void setCurrentPosition(int currentPosition) { this.currentPosition = currentPosition; } }
Adapter này cực kỳ đơn giản, nó chỉ chứa 1 interface xử lý sự kiện chọn filter, các bạn hãy tìm hiểu nhé. Ok, như vậy ta đã xây dựng xong adapter, giờ sẽ hiển thị nó lên app.
Hiển thị list filter
Quay trở lại MainActivity, mình sẽ tạo ra 1 mảng các String, mảng này chứa tên các file LUT mà mình sẽ truyền vào các đối tượng FilterData:
public static final String EFFECT_CONFIGS[] = { "@adjust lut original.png", "@adjust lut natural01.png", "@adjust lut natural02.png", "@adjust lut pure01.png", "@adjust lut pure02.png", "@adjust lut lovely01.png", "@adjust lut lovely02.png", "@adjust lut lovely03.png", "@adjust lut lovely04.png", "@adjust lut warm01.png", "@adjust lut warm02.png", "@adjust lut cool01.png", "@adjust lut cool02.png", "@adjust lut vintage.png", "@adjust lut gray.png", };
Và bây giờ sẽ tạo 1 phương thức setUpListFilterEffect để hiển thị list các filter lên recyclerview:
FilterData seletedFilterData = new FilterData("None", EFFECT_CONFIGS[0], 0); private void setUpListFilterEffect() { //list danh sách hiệu ứng nằm ngang rvFilter.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false)); //create list filter List<FilterData> listFilter = new ArrayList<>(); int[] imageFilterId = {R.drawable.original_1, R.drawable.natural_1, R.drawable.natural_2 , R.drawable.pure_1, R.drawable.pure_2, R.drawable.pinky_1 , R.drawable.pinky_2, R.drawable.pinky_3, R.drawable.pinky_4 , R.drawable.warm_1, R.drawable.warm_2, R.drawable.cool_1 , R.drawable.cool_2, R.drawable.mood, R.drawable.bw}; for (int i = 0; i < EFFECT_CONFIGS.length; i++) { //listFilter.add(new FilterData(EFFECT_CONFIGS[i], imageFilterId[i])); if (i == 0) { listFilter.add(new FilterData("Original", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 1) { listFilter.add(new FilterData("Natural 1", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 2) { listFilter.add(new FilterData("Natural 2", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 3) { listFilter.add(new FilterData("Pure 1", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 4) { listFilter.add(new FilterData("Pure 2", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 5) { listFilter.add(new FilterData("Pinky 1", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 6) { listFilter.add(new FilterData("Pinky 2", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 7) { listFilter.add(new FilterData("Pinky 3", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 8) { listFilter.add(new FilterData("Pinky 4", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 9) { listFilter.add(new FilterData("Warm 1", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 10) { listFilter.add(new FilterData("Warm 2", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 11) { listFilter.add(new FilterData("Cool 1", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 12) { listFilter.add(new FilterData("Cool 2", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 13) { listFilter.add(new FilterData("Mood", EFFECT_CONFIGS[i], imageFilterId[i])); } else if (i == 14) { listFilter.add(new FilterData("B&W", EFFECT_CONFIGS[i], imageFilterId[i])); } } ListFilterAdapter filterAdapter = new ListFilterAdapter(listFilter); filterAdapter.setOnFilterSelect(new ListFilterAdapter.OnFilterSelect() { @Override public void onSelect(FilterData filterData) { seletedFilterData = filterData; cameraView.setFilterWithConfig(filterData.getRule()); } }); rvFilter.setAdapter(filterAdapter); }
Như các bạn thấy, phương thức trên khởi tạo ra 1 list các đối tượng FilterData và truyền list đó vào adapter để hiển thị lên recyclerView. Đồng thời ta cũng implement các câu lệnh khi có 1 filter được chọn:
filterAdapter.setOnFilterSelect(new ListFilterAdapter.OnFilterSelect() { @Override public void onSelect(FilterData filterData) { seletedFilterData = filterData; cameraView.setFilterWithConfig(filterData.getRule()); } });
Ok, hy vọng code mình viết đủ rõ ràng để các bạn có thể hiểu. Giờ ta sẽ gọi phương thức setUpListFilterEffect này trong hàm createView:
@Override protected void createView() { setUpCameraView(); setUpListFilterEffect(); }
Chức năng chụp ảnh
Ta sử dụng ButterKnife để bắt sự kiện click vào button chụp ảnh:
@OnClick({R.id.bt_take_picture}) public void onTakePictureClick() { showToast("Đang chụp ảnh..."); cameraView.takeShot(new CameraRecordGLSurfaceView.TakePictureCallback() { @Override public void takePictureOK(Bitmap bmp) { File file = new File(Environment.getExternalStorageDirectory() + "/FilterImageDemo"); if (!file.exists()) { file.mkdirs(); } if (bmp != null) { String imagePath = ImageUtil.saveBitmap(bmp, file.getAbsolutePath() + "/" + System.currentTimeMillis() + ".jpg"); bmp.recycle(); //showToast("Đã xong!"); Bundle bundle = new Bundle(); bundle.putString(Constant.KEY_IMAGE_PATH, imagePath); bundle.putSerializable(Constant.KEY_FILTER, seletedFilterData); showActivity(CameraResultActivity.class, bundle); } else { showToast("Ôi, có lỗi rồi!"); } } }); }
Như bạn thấy, khi click vào nút chụp ảnh, ta sẽ lưu lại 1 file jpg trong thư mục FilterImageDemo và nhảy sang màn hình chỉnh sửa ảnh (CameraResultActivity). Ta cũng truyền dữ liệu về filter đang được chọn và đường dẫn ảnh đã lưu sang màn hình chỉnh sửa. Tuy nhiên giờ ta chưa cần quan tâm đến màn hình chỉnh sửa ảnh này. Đơn giản hãy tạo 1 activity có tên CameraResultActivity và khai báo nó trong file AndroidManifest:
<activity android:name=".CameraResultActivity" android:screenOrientation="portrait"></activity>
Chức năng tùy chọn camera trước (sau)
Ta sử dụng ButterKnife để bắt sự kiện click cho button đảo camera:
@OnClick(R.id.bt_rotate_camera) public void rotateCamera() { cameraView.switchCamera(); }
Chức năng đóng màn hình camera
Ta sử dụng ButterKnife để bắt sự kiện click cho button thoát khỏi màn hình camera:
@OnClick(R.id.bt_close) public void closeCamera() { finish(); }
Chức năng chọn ảnh trong gallery
Ta viết thêm 1 chức năng cho phép người dùng chọn ảnh trong gallery để chỉnh sửa thay vì chụp ảnh trực tiếp.
@OnClick(R.id.iv_pick_image) public void pickImage() { Bundle bundle = new Bundle(); showActivity(CameraResultActivity.class, bundle); }
Như các bạn thấy khi ấn vào button pick image, ta đơn giản hiển thị màn hình CameraResultActivity. Khác với khi chụp ảnh, ta không truyền thêm dữ liệu nào khi chuyển qua màn hình CameraResultActivity này. Như vậy ở trong lớp CameraResultActivity, ta sẽ cần xử lý 2 trường hợp:
- Không có data truyền sang (chọn ảnh trong gallery)
- Có data truyền sang, hiển thị ảnh với các dữ liệu truyền từ màn hình chụp ảnh.
Tổng kết
Như vậy ở bài này mình đã hướng dẫn xong tính năng chụp ảnh. Ở bài tiếp theo (và cũng là bài cuối cùng) mình sẽ hướng dẫn nốt về chức năng chỉnh sửa cũng như chia sẻ ảnh được chọn. Hãy theo dõi nhé.