Hướng dẫn cách xây dựng tính năng sử dụng để chụp hình và chọn hình trong Gallery trên thiết bị

Chào mọi người , mình là Vũ chúng mình lại gặp nhao ^_^ !!! Hôm nay mình sẽ giới thiệu cho các bạn về việc sử dụng chọn hình và chụp hình trên thiết bị kết hợp với việc sử dụng Sidebar Menu để làm 1 trang profile nho nhỏ hén ^_^ !

Bước chuẩn bị :

Gradle :

dependencies {
    compile fileTree(dir: 'libs', include: ['*.jar'])
    testCompile 'junit:junit:4.12'
    compile 'com.android.support:appcompat-v7:23.1.0'
    compile 'de.hdodenhof:circleimageview:2.1.0'
    compile 'com.android.support:design:23.1.0'
    compile 'com.android.support:support-v4:23.1.0'
}

Bước 1 :Xây dựng một sidebar Menu ( các bạn có thể xem tại đây )

Bước 2 :Sau khi xây dựng xong một side bar Menu thì các bạn tiếp tục đến bước chỉnh sửa lại tương ứng trong menu> activity_navigation_drawer.xml

<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android">

    <group android:checkableBehavior="single">
        <item android:id="@+id/nav_camara" android:icon="@android:drawable/ic_menu_camera"
            android:title="Import" />
        <item android:id="@+id/nav_gallery" android:icon="@android:drawable/ic_menu_gallery"
            android:title="Gallery" />

    </group>
</menu>

Rồi các bạn vào NavigationActivity.class

@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    if (id == R.id.nav_camara) {
        // Handle the camera action
    } else if (id == R.id.nav_gallery) {

    }
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}

Sửa hàm này lại tương ứng vì chúng ta chỉ sử dụng 2 mục trong menu

Sau đó các bạn nút phải lên Drawable và chọn New 1 Image Assets mới

Trong image Assets các bạn chọn qua clip art , Chọn Asset Type là Action Bar and Tab Icon , rồi choose một clip art nào đó bạn muốn , ở đây mình chọn hình con người để tượng trưng cho người chưa có avatar nhé ^_^

Sau đó các bạn vào chỉnh sửa cho nav_main.xmlvới hình profile chuẩn là hình trên ta sẽ được :

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    android:paddingBottom="@dimen/activity_vertical_margin"
    android:layout_height="250dp"
    tools:context="com.example.hello.createscreenshotexample.nav_main"
    android:background="@drawable/cloud4">

    <de.hdodenhof.circleimageview.CircleImageView
        android:layout_width="70dp"
        android:layout_height="70dp"
        android:id="@+id/profile_img"
        android:layout_marginLeft="24dp"
        android:layout_alignParentLeft="true"
        android:layout_alignParentStart="true"
        android:layout_marginStart="20dp"
        android:layout_centerVertical="true"
        android:background="#FFFFFF"
        android:src="@drawable/ic_action_name"
        />
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="Vũ Trần"
        android:textStyle="bold"
        android:paddingTop="20dp"
        android:id="@+id/name"
        android:layout_marginStart="24dp"
        android:layout_marginLeft="24dp"
        android:layout_below="@+id/profile_img"/>
    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="[email protected]"
        android:paddingTop="20dp"
        android:id="@+id/email"
        android:layout_marginStart="24dp"
        android:layout_marginLeft="24dp"
        android:layout_below="@+id/name"/>

</RelativeLayout>

Ok , vậy là ta đã có được một trang profile với side bar menu rồi đó . Giờ đến bước thiết lập activity chụp hình và chọn hình cho 2 menu nảy chúng ta vừa để vào

Bước 3 : Thiết lập điều hướng Intent Activity Built-in mà Android cung cấp

Các bạn mở file NavigationActivity.class khai báo 2 biến sau

private final int CameraCode = 1111;
private final int GalleryCode = 1112;

Sau đó các bạn viết thêm 2 hàm này

public void gotoCaptureCamera(){
    Intent takePicture = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
    startActivityForResult(takePicture, CameraCode);
}

public void gotoGalleryChoose(){
    Intent pickPhoto = new Intent(Intent.ACTION_PICK,
            android.provider.MediaStore.Images.Media.EXTERNAL_CONTENT_URI);
    startActivityForResult(pickPhoto , GalleryCode);
}

Để mình giải thích cho các bạn việc 2 biến CameraCode và GalleryCode tại sao phải khai báo . Đó là vì 2 biến đó sẽ là 2 biến action code , action code tượng trưng cho một số nào đó lưu lại để các bạn có thể phân biệt action mình vừa thực hiện . Vì khi chụp hình la các bạn đang ở một activity khác , chụp hình xong nó sẽ trả cả bạn về activity hiện tại và để có được kết quả các bạn thực hiện ở activity chụp hình thì nó sẽ dụng ACTION CODE để nhận diện .

Action Code mình để ở đây cho action CAMERA là 1111 và GALLERY là 1112

Sau đó các bạn khai báo 2 hàm trên vào action menu tương ứng của navigation bar

@SuppressWarnings("StatementWithEmptyBody")
@Override
public boolean onNavigationItemSelected(MenuItem item) {
    // Handle navigation view item clicks here.
    int id = item.getItemId();

    if (id == R.id.nav_camara) {
        // Handle the camera action
        gotoCaptureCamera();

    } else if (id == R.id.nav_gallery) {
        gotoGalleryChoose();
    }
    DrawerLayout drawer = (DrawerLayout) findViewById(R.id.drawer_layout);
    drawer.closeDrawer(GravityCompat.START);
    return true;
}

Rồi sau khi các bạn chụp hình về thì các bạn tự hỏi nó quay lại nó sẽ trả về đâu ?

Câu trả lời là hàm onActivityResult(), Các bạn có thể override hàm trên trong activity navigation

Sau khi override hàm onActivityResult() thì các bạn bổ sung trong hàm những đoạn code sau :

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


     if (requestCode == CameraCode && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extras.get("data");
    }
    if(requestCode == GalleryCode && resultCode == RESULT_OK){
        try {
            final Uri imageUri = data.getData();
            final InputStream imageStream = getContentResolver().openInputStream(imageUri);
            final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream); 
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG).show();
        }
     }
}

Như hồi nảy mình đã giải thích , action Code chính là mã nhận diện 1 activty đã thực hiện xong khi nảy của các bạn

Sau khi hoàn hành xong 1 activity thì nó sẽ trả về trong biến data dựa trên action code là requestCode , và xác định bằng resultCode ( Chọn OK hoặc cancel trong lúc chụp )

Từ đó hình sẽ được lưu và get trong “data” , từ đó các bạn để nó vào 1 biến hình tạm để rồi set nó lên image profile của mình .

Tuy nhiên đối với chọn từ Gallery thì hơi khác chút là kết quả trả về sẽ là 1 Uri , từ Uri đó mình sẽ bỏ vào 1 InputStream để convert nó về Bitmap để set lên CircleImageView

Hồi nảy phía trên hình profile của mình có

 <de.hdodenhof.circleimageview.CircleImageView
    android:layout_width="70dp"
    android:layout_height="70dp"
    android:id="@+id/profile_img"
    android:layout_marginLeft="24dp"
    android:layout_alignParentLeft="true"
    android:layout_alignParentStart="true"
    android:layout_marginStart="20dp"
    android:layout_centerVertical="true"
    android:background="#FFFFFF"
    android:src="@drawable/ic_action_name"
    />

Thì các bạn lấy id ra

Từ đó khai báo ánh xạ ròi set lên :

Các bạn khai báo 1 biến global

CircleImageView imageProfile;

Sau đó ánh xạ cho nó trên hàm onCreateOptionsMenu()

imageProfile = (CircleImageView) findViewById(R.id.profile_img);

Sau đó các bạn sửa lại hàm onActivityResult() như sau :

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


     if (requestCode == CameraCode && resultCode == RESULT_OK) {
        Bundle extras = data.getExtras();
        Bitmap imageBitmap = (Bitmap) extras.get("data");
         imageProfile.setImageBitmap(imageBitmap);
    }
    if(requestCode == GalleryCode && resultCode == RESULT_OK){
        try {
            final Uri imageUri = data.getData();
            final InputStream imageStream = getContentResolver().openInputStream(imageUri);
            final Bitmap selectedImage = BitmapFactory.decodeStream(imageStream);
            imageProfile.setImageBitmap(selectedImage);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
            Toast.makeText(this, "Something went wrong", Toast.LENGTH_LONG).show();
        }
     }
}

Okay , như vậy là đã xong được 95% việc rồi đấy . Giờ chúng ta chạy lên và test thử :

Okay , vậy là done , còn 1 vấn đề nữa là vì chúng ta chạy trên máy ảo , nên vấn đề này có lẽ là khó gặp phải nhưng nếu chúng ta chạy trên máy thật , thì sau khi chúng ta set hình cho profile , nếu chúng ta hay người dùng có lỡ vô tình hay cố ý xoay màn hình device từ dọc sang ngang hay ngược lại thì ….. Hình chúng ta vừa set sẽ biến mất O_O

Bởi vì trong Android , việc xoay màn hình sẽ khiến cho toàn bộ Activity bị xóa đi và làm mới lại hoàn toàn , các data chúng ta set lên sẽ bị biến mất . Nên trong activity class , các bạn override thêm hàm

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
}

***Và đặc biệt lưu ý hàm chỉ có 1 tham số là Bundle truyền vào thôi nhé ^_^ .

Trong hàm này các bạn cho

@Override
protected void onSaveInstanceState(Bundle outState) {
    super.onSaveInstanceState(outState);
    Bitmap imageSaveAvoidRotating = ((BitmapDrawable)imageProfile.getDrawable()).getBitmap();
    outState.putParcelable("image",imageSaveAvoidRotating);
}

Do vậy các bạn để ý trên hàm onCreate(Bundle saveInstancedState) , thì outstate.putParcelable sẽ bỏ vào biến saveInstanceState ở đó của các bạn => saveInstancedState != null . Do vậy để khôi phục lại được ảnh thì ta phải lấy từ trong saveInstancedState ra .

Có 2 cách ,

Cách 1 : Trong hàm onCreate(Bundle saveInstanceState) các bạn cho thêm

if(savedInstanceState != null){
    Bitmap getImage = savedInstanceState.getParcelable("image");
    imageProfile.setImageBitmap(getImage);
}

Cách 2 : các bạn override hàm onRestoreInstancedState(Bundle savedInstanceState)

@Override
protected void onRestoreInstanceState(Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);
    if(savedInstanceState != null){
        Bitmap getImage = savedInstanceState.getParcelable("image");
        imageProfile.setImageBitmap(getImage);
    }
}

Như vậy là chúng ta có thể khắc phục được trường hợp đó rồi đấy ^_^

Đến đây là hết , chúc các bạn thành công !

Cảm ơn các bạn đã xem !

______________________________________________________________

VuTNQ ^_^ !!!!!!!!!!!!!!!!!!!!!!!!!

results matching ""

    No results matching ""