.:: CODE SNIPPET ::.

"Your time is limited, so don't waste it living someone else's life"

Lựa chọn và Cắt hình ảnh trong Android


Hôm nay, mình hướng dẫn các bạn một cách để chúng ta lựa chọn và cắt hình ảnh trong thiết bị Android.
Yêu cầu của bài toán như sau:
Chúng ta cần một ứng dụng cho phép lựa chọn hình ảnh
+ Từ bộ nhớ (storage) hoặc
+ Chụp ảnh từ camera
Và sau đó ứng dụng cho phép chúng ta cắt hình ảnh theo một hình chữ nhật nào đấy mà chúng ta muốn.
Hướng giải quyết chung của mình như sau: sử dụng intent
1.Vấn đề đọc ảnh từ bộ nhớ máy
Ở đây, hình ảnh từ máy thì có nhiều dạng mở rộng khác nhau, vì vậy, itent của mình sẽ được sử dụng loại MIME mở rộng cho các loại hình ảnh như sau: “image/*”. Hơn nữa, ở đây coi như là chúng ta đang muốn sử dụng nhiều loại dữ liệu khác nhau, vì vậy mình sẽ dùng Action trong intent là Intent.ACTION_GET_CONTENT. Intent.ACTION_GET_CONTENT cho phép người dùng có thể sử dụng một URI đến một danh sách các dữ liệu và người dùng có thể lựa chọn nó. Nó còn có thể cho người dùng tạo ra dữ liệu khi nó chạy như là tạo ra hình ảnh, âm thanh.

Intent intent = new Intent();
intent.setType("image/*");//
intent.setAction(Intent.ACTION_GET_CONTENT);
startActivityForResult(Intent.createChooser(intent,"Complete action using"), PICK_FROM_FILE);

Thêm một tí thông tin về Intent.ACTION_GET_CONTENT: Có hai trường hợp có thể sử dụng action này
+ Nếu bạn muốn một kiểu dữ liệu xác định nào đó, ví dụ như một thông tin liên lạc của một người nào đó, thì bạn sẽ đặt kiểu MIME vào loại dữ liệu mà bạn muốn. Hệ thống sẽ tự động dùng một ứng dụng tốt nhất để chọn kiểu dữ liệu đó cho bạn.
+ Nếu bạn muốn rằng có nhiều kiểu dữ liệu và người dùng có thể lựa chọn. Ví dụ như trong một ứng dụng soạn email, người dùng có thể attach thêm vào file hay hình lung tung gì đó. Trong trường hợp này, bạn sẽ phủ lên action này bằng một chooser( thông qua createChooser(Intent, CharSequence)). Cái này cung cấp một giao diện tốt hơn để người dùng có thể chọn. Lúc này, bạn thường phải sử dụng loại MIME mở rộng.
2. Vấn đề chụp ảnh từ camera
Lúc này, mình sử dụng intent ACTION_IMAGE_CAPTURE để gọi sử dụng ứng dụng chụp ảnh của device. Một việc quan trọng khác lúc này đó là lưu hình ảnh chụp được (một cách tạm thời) vào bộ nhớ. Ở đây, mình khai báo một URI chung để sử xác định ảnh đang làm việc:

Intent intent 	 = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
File directory=new File(Environment.getExternalStorageDirectory().getAbsolutePath()+File.separator+"ImageCrop");
directory.mkdirs();
mImageUri = Uri.fromFile(new File(directory,"tmp_avatar_" + String.valueOf(System.currentTimeMillis()) + ".jpg"));
intent.putExtra(android.provider.MediaStore.EXTRA_OUTPUT, mImageUri);
try {
	intent.putExtra("return-data", true);
	startActivityForResult(intent, PICK_FROM_CAMERA);
} catch (ActivityNotFoundException e) {
	e.printStackTrace();
}

Trong đoạn này, chúng ta sẽ tạo ra ở bộ nhớ một thư mục ImageCrop để lưu hình ảnh của ứng dụng.
Lưu ý: URI đã tạo ra (mImageUri) sẽ được cùng chung là khi chụp ảnh, hình ảnh sẽ được lưu tại vị trí đó và với tên đó, tí nữa khi load lên cũng vậy.
3. Vấn đề cắt ảnh
Đây là phần xử lý dài dòng nhất trong chương trình. Các bạn sẽ đọc code, mình đã comment code rất kỹ phần này như sau:

private void doCrop() {
		final ArrayList cropOptions = new ArrayList();
    	//Mở ứng dụng crop ảnh thông qua intent
    	Intent intent = new Intent("com.android.camera.action.CROP");
    	//Tạo một MIME mở giúp truy suất nhiều loại hình ảnh
        intent.setType("image/*");

        //kiểm tra xem trong máy có ứng dụng crop ảnh nào hay chưa? Và đưa vào danh sách
        List list = getPackageManager().queryIntentActivities( intent,0);
        //đếm có bao nhiêu ứng dụng crop ảnh
        int size = list.size();

        if (size == 0) {
        	//Nếu không có ứng dụng crop ảnh nào thì hiển thị cảnh báo và dừng
        	Toast.makeText(this, "Can not find image crop app", Toast.LENGTH_SHORT).show();

            return;
        } else {
        	//Nếu có thì thực hiện những thủ tục
        	//Xác định dữ liệu ảnh cần thực hiện crop
        	intent.setData(mImageUri);
            //Kích thước lớn nhất của ảnh sau khi crop
            intent.putExtra("outputX", 200);
            intent.putExtra("outputY", 200);
            //Bỏ đi hai giá trị bên dưới giúp cho chúng ta
            //có thể chỉnh kích thước khung cách dễ dàng hơn
            /*intent.putExtra("aspectX", 1);
            intent.putExtra("aspectY", 1);*/
            intent.putExtra("scale", true);
            intent.putExtra("return-data", true);

        	if (size == 1) {
        		//Nếu có 1 ứng dụng crop ảnh thì mở ứng dụng đó
        		//Khởi tạo 1 intent mới và sử dụng lại những thông số vừa truyền vào bên trên
        		Intent i 		= new Intent(intent);
        		//Đọc ra thông tin về ứng dụng về ứng dụng crop ảnh đầu tiên trong danh sách (vì chỉ có 1 ứng dụng)
	        	ResolveInfo res	= list.get(0);
	        	//Truyền thông tin thành phần về ứng dụng crop ảnh vào intent tạo bên trên
	        	i.setComponent( new ComponentName(res.activityInfo.packageName, res.activityInfo.name));
	        	//Khởi động intent này và xác định kết quả của Activity sẽ được truyền vào tình huống CROP_FROM_CAMERA
	        	startActivityForResult(i, CROP_FROM_CAMERA);
        	} else {
        		/*
        		 Giờ nếu có nhiều ứng dụng crop ảnh thì làm sao?
        		 Đến lúc này là lúc sẽ dùng đến các lớp CropOption và CropOptionAdapter.
        		 Việc cần làm là sẽ tạo ra một CropOptionAdapter để truyền vào một danh sách chọn,
        		 điều này cho phép người dùng lựa chọn ứng dụng nào được dùng để crop ảnh
        		 Và khi nằm trên danh sách, thì một ứng dụng crop ảnh sẽ bao gồm
        		 một tiêu đề: title
        		 một icon:    icon
        		 một intent hoạt động: appIntent
        		 Đầu tiên chúng ta sẽ duyệt qua danh sách những ứng dụng crop ảnh đã tìm thấy

        		 */
		        for (ResolveInfo res : list) {
		        	//Với mỗi ứng dụng, chúng ta sẽ khai báo một CropOption
		        	final CropOption co = new CropOption();
		        	//Gán tiêu đề cho nó
		        	co.title 	= getPackageManager().getApplicationLabel(res.activityInfo.applicationInfo);
		        	//Gán hình icon cho nó
		        	co.icon		= getPackageManager().getApplicationIcon(res.activityInfo.applicationInfo);
		        	//Gán cho nó 1 intent để hoạt động, intent này cũng được khởi tạo dựa vào intent đã tạo,
		        	//nhằm sử dụng lại những thông tin đã truyền vào
		        	co.appIntent= new Intent(intent);

		        	co.appIntent.setComponent( new ComponentName(res.activityInfo.packageName, res.activityInfo.name));

		            cropOptions.add(co);
		        }

		        //Tạo ra một CropOptionAdapter dựa vào danh sách các CropOption (CropOptions) đã tạo
		        CropOptionAdapter adapter = new CropOptionAdapter(getApplicationContext(), cropOptions);
		        //Tạo một thông báo để hiển thị những ứng dụng crop ảnh
		        //Và cho phép người dùng lựa chọn ứng dụng nào
		        AlertDialog.Builder builder = new AlertDialog.Builder(this);
		        builder.setTitle("Choose Crop App");
		        builder.setAdapter( adapter, new DialogInterface.OnClickListener() {
		            public void onClick( DialogInterface dialog, int item ) {
		                startActivityForResult( cropOptions.get(item).appIntent, CROP_FROM_CAMERA);
		            }
		        });

		        DialogInterface.OnCancelListener lstner=new OnCancelListener() {

					public void onCancel(DialogInterface dialog) {
						// TODO Auto-generated method stub
						if (mImageUri != null ) {
		                    getContentResolver().delete(mImageUri, null, null );
		                    mImageUri = null;
		                }
					}
				};
		        builder.setOnCancelListener( lstner );

		        AlertDialog alert = builder.create();

		        alert.show();
        	}
        }
}

Các bạn download code tại đây nhé

password: hoangminhuit

Advertisements

7 responses to “Lựa chọn và Cắt hình ảnh trong Android

  1. Pingback: My Homepage

  2. Texax Chat Lines November 11, 2012 at 1:25 PM

    whoah this blog is wonderful i love reading your posts. Keep up the good work! You know, many people are looking around for this information, you can help them greatly.

  3. Pingback: Vẽ hình chữ nhật để khoanh vùng ảnh cần chụp trên camera « .:: Hoàng Minh ::.

  4. Hoàng Minh November 23, 2012 at 6:41 PM

    Texax Chat Lines :

    whoah this blog is wonderful i love reading your posts. Keep up the good work! You know, many people are looking around for this information, you can help them greatly.

    Can you read Vietnamese?

  5. TranThuan December 23, 2012 at 10:02 PM

    Bạn cho mình hỏi:

    1. Mình có 1 ảnh lớn (VD: Background được lấy từ bên ngoài) sau đó mình có một hình ảnh nhỏ hơn (vd: Avarta….).

    –> Yêu cầu cắt nền hình ảnh lớn, theo viền hình ảnh nhỏ???
    Bạn có ví dụ hoặc phương pháp làm ơn chỉ mình

    Thanks so much.

  6. Hoàng Minh December 26, 2012 at 12:32 AM

    Mình chưa hiểu rõ câu hỏi của bạn cho lắm!!
    Nếu bạn đơn giản là muốn cắt trên ảnh lớn ra một phần ảnh với kích thước là bằng kích thước ảnh nhỏ mà bạn đã nói (tại một tọa độ nào đó trên ảnh lớn) thì bạn làm như sau:
    1.Chuyển ảnh lớn thành dạng byte[]
    2.Đọc tất cả phần dữ liệu (đã có ở bước 1) tại tọa độ đã có và kích thước như ảnh nhỏ vào một mảng int.
    3.Tạo ra một ảnh với kích thước bằng kích thước ảnh nhỏ.
    4.Copy dữ liệu từ mảng đã có ở bước 2 vào ảnh tạo ra ở bước 3.
    Còn nếu không phải thì bạn có thể giải thích lại câu hỏi của bạn (có hình minh họa thì tốt hơn) được không.

  7. Thangbk July 16, 2014 at 3:52 PM

    Bạn cho mình hỏi làm thế nào để set cứng được cái crop được

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: