Android開發之頭像的更換(拍照,從手機照片中選擇)
先說一下昨天未解決的問題:原因是自己在獲取對象時,沒有將新加的圖片屬性加到該對象里,導致一直爆空指針異常。
接下來分析一下頭像更換的具體操作:(參考的書籍是:Android第一行代碼)
先分析一下布局流程:
我是這樣子安排的,先到個人界面如果點擊更換頭像,就會跳出一個界面來選擇:拍照,還是圖庫里選擇。
因此我們需要再新建一個xml來設計:拍照和圖庫選擇
dialog_select_photo.xml:
<?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingLeft="60dp" android:paddingRight="60dp"> <TextView android:id="@+id/tv_select_gallery" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingTop="20dp" android:padding="20dp" android:gravity="center" android:text="從相冊中選取" /> <TextView android:layout_below="@id/tv_select_gallery" android:id="@+id/tv_select_camera" android:layout_width="match_parent" android:layout_height="wrap_content" android:paddingBottom="20dp" android:gravity="center" android:text="拍攝照片" /> </RelativeLayout>
接下來就是對Me_Fragment的操作了
首先設計的是“更換按鈕”的點擊事件:我將它封裝在一個方法中,先來分析拍照更換頭像。
①首先會創建一個File對象,用來存放攝像頭拍下的圖片,把該圖片命名為output_image.jpg,將他存放在sd卡的應用關聯緩存目錄下。接下來判斷如果當前設備的系統版本低於Android7.0,就會調用Uri的fromfile方法將File對象轉換成Uri對象,這個Uri對象標識着output_image.jpg這張圖片的本地路徑。否則,就會調用FileProvider的getUriForFile()方法將File對象轉換成一個封裝過Uri的對象。其中FileProvider是一種特殊的內容提供器。接下來構建一個Intent對象,將他的action指定為android.media.action.IMAGE_CAPTURE,在調用Intent的putExtra()方法制定圖片的輸出地址,傳遞的是剛得到的Uri對象,最后調用startActivityForResult()來啟動活動。既然有forresult來啟動該活動,當然也有onActivityResult方法,拍照成功后調用BitmapFactory的decodeStream方法將output_image.jpg這張照片解析成Bitmap對象,然后設置到ImageView中顯示出來。
最后,我們提到內容提供器,自然要在AndroidManifest.xml中進行注冊。其中provider里的內容就是注冊的,由於我們調@xml/file_paths,自然要在res目錄下,創建一個xml文件,在該文件里創建file_paths.xml
<?xml version="1.0" encoding="utf-8"?> <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.example.countbook"> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <application android:allowBackup="true" android:icon="@mipmap/ic_launcher" android:label="@string/app_name" android:roundIcon="@mipmap/ic_launcher_round" android:supportsRtl="true" android:theme="@style/AppTheme"> <provider android:name="androidx.core.content.FileProvider" android:authorities="com.example.cameraalbumtest.fileprovider" android:exported="false" android:grantUriPermissions="true"> <meta-data android:name="android.support.FILE_PROVIDER_PATHS" android:resource="@xml/file_paths" /> </provider> <activity android:name=".ModifypswActivity"></activity> <activity android:name=".loginActivity"> <intent-filter> <action android:name="android.intent.action.MAIN" /> <category android:name="android.intent.category.LAUNCHER" /> </intent-filter> </activity> <activity android:name=".MainActivity" /> <activity android:name=".HeadActivity" /> <activity android:name=".RegisterActivity" /> </application> </manifest>
file_paths.xml
<?xml version="1.0" encoding="utf-8"?> <path xmlns:android="http://schemas.android.com/apk/res/android"> <external-path name="my_images" path=""></external-path> </path>
②接下來,就是處理圖庫選擇照片,再點擊選擇圖庫時,我們先進行一個運行權限處理動態申請WRITE_EXTERNAL_STORAGE,他表示授予程序對SD卡的讀和寫的能力。
當用戶授權申請后會調用openAlbum方法,在這個方法里先建立一個intent對象,並將action指向android.intent.action.GET_CONTENT,接着設置一些參數,然后啟動startActivityForResult方法,我們在這個方法里傳的參數是CHOOSE_PHOTO,這樣從相冊選完后回到onActivityResult方法里通過case來進行處理,如果是4.4以上的手機會調用handleImageOnKitKat(data);方法,這個方法的邏輯就是如何解析封裝過的Uri,最后將這些值作為參數傳到getImagePath方法中,就可以獲得圖片的真實路徑,拿到后在調用displayImage()將圖片顯示到界面上。
③特別注意:在displayImage這里不能直接使用圖片的路徑,這樣選完照片后不能在界面上呈現頭像,我們將imagepath路徑轉換為uri進行頭像的設置。
④照片信息數據庫的保存方法:我在數據庫表中加了一列urlimage,來存放圖片的路徑,參數是text類型的,我會將無論是拍照還是圖庫選擇的照片,將他們的url對象轉換成String然后更新數據庫里的urlimage數據,每次點到個人信息頁面時都會從數據庫中讀取當前的頭像信息,然后展示到頁面上。
/** * 將圖片轉換成Uri * @param context 傳入上下文參數 * @param path 圖片的路徑 * @return 返回的就是一個Uri對象 */ public static Uri getImageContentUri(Context context, String path) { Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=? ", new String[] { path }, null); if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)); Uri baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { // 如果圖片不在手機的共享圖片數據庫,就先把它插入。 if (new File(path).exists()) { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DATA, path); return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { return null; } } }
Me_Fragment.java
package com.example.countbook; import android.Manifest; import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.Fragment; import android.content.ContentUris; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.provider.DocumentsContract; import android.provider.MediaStore; import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.widget.Button; import android.widget.ImageView; import android.widget.TextView; import android.widget.Toast; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.core.app.ActivityCompat; import androidx.core.content.ContextCompat; import androidx.core.content.FileProvider; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import static android.app.Activity.RESULT_OK; public class Me_Fragment extends Fragment{ String username=null; TextView tv_username=null; Button btn_img=null; TextView tv_psw=null; TextView tv_exit=null; ImageView imageView; UserOperator muserOperator; View view=null; Bundle bundle=null; public static final int TAKE_PHOTO =1; public static final int CHOOSE_PHOTO=2; private Uri imageUri; User bean; @Override public void onCreate(@Nullable Bundle savedInstanceState) { super.onCreate(savedInstanceState); bundle=getArguments(); username=bundle.getString("username"); } @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { view=inflater.inflate(R.layout.me_fragment,null); tv_username=(TextView)view.findViewById(R.id.tv_username); tv_username.setText(username); imageView=(ImageView) view.findViewById(R.id.iv_photo); btn_img=(Button)view.findViewById(R.id.btn_img); tv_psw=(TextView)view.findViewById(R.id.tv_updatepsw); tv_exit=(TextView)view.findViewById(R.id.tv_exit); muserOperator=new UserOperator(view.getContext()); bean=muserOperator.isExit(username); if(bean.image!=null) { imageView.setImageURI(Uri.parse((String) bean.image)); } tv_psw.setOnClickListener(l); tv_exit.setOnClickListener(l); btn_img.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { showTypeDialog(); } }); return view; } private void showTypeDialog() { AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); final AlertDialog dialog = builder.create(); final View view = View.inflate(getActivity(), R.layout.dialog_select_photo, null); TextView tv_select_gallery = (TextView) view.findViewById(R.id.tv_select_gallery); TextView tv_select_camera = (TextView) view.findViewById(R.id.tv_select_camera); tv_select_gallery.setOnClickListener(new View.OnClickListener() {// 在相冊中選取 @Override public void onClick(View v) { if (ContextCompat.checkSelfPermission(getActivity(), Manifest.permission.WRITE_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) { ActivityCompat.requestPermissions(getActivity(), new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1); } else { openAlbum(); } dialog.dismiss(); } }); tv_select_camera.setOnClickListener(new View.OnClickListener() {// 調用照相機 @Override public void onClick(View v) { File outputImage =new File(view.getContext().getExternalCacheDir(),"output_image.jpg"); try { if(outputImage.exists()){ outputImage.delete(); } outputImage.createNewFile(); } catch (IOException e) { e.printStackTrace(); } if(Build.VERSION.SDK_INT>=24){ imageUri= FileProvider.getUriForFile(view.getContext(), "com.example.cameraalbumtest.fileprovider",outputImage); }else{ imageUri=Uri.fromFile(outputImage); } //啟動相機程序 Intent intent=new Intent("android.media.action.IMAGE_CAPTURE"); intent.putExtra(MediaStore.EXTRA_OUTPUT,imageUri); startActivityForResult(intent,TAKE_PHOTO); dialog.dismiss(); } }); dialog.setView(view); dialog.show(); } View.OnClickListener l=new View.OnClickListener() { @Override public void onClick(View v) { switch (v.getId()){ case R.id.tv_exit: Intent intent1=new Intent(view.getContext(),loginActivity.class); startActivity(intent1); break; case R.id.tv_updatepsw: Intent intent2=new Intent(view.getContext(),ModifypswActivity.class); intent2.putExtras(bundle); startActivity(intent2); break; } } }; private void openAlbum() { Intent intent=new Intent("android.intent.action.GET_CONTENT"); intent.setType("image/*"); startActivityForResult(intent,CHOOSE_PHOTO); } @Override public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) { switch (requestCode) { case 1: if(grantResults.length>0&&grantResults[0]==PackageManager.PERMISSION_GRANTED) { openAlbum(); } else { Toast.makeText(view.getContext(),"你否定了相冊請求",Toast.LENGTH_SHORT).show(); } break; default: } } @Override public void onActivityResult(int requestCode, int resultCode, Intent data) { super.onActivityResult(requestCode, resultCode, data); switch (requestCode){ case TAKE_PHOTO: if(resultCode==RESULT_OK){ try { Bitmap bitmap= BitmapFactory.decodeStream(view.getContext().getContentResolver().openInputStream(imageUri)); String ans=imageUri.toString(); bean.image=ans; muserOperator.updateImage(bean); imageView.setImageBitmap(bitmap); } catch (FileNotFoundException e) { e.printStackTrace(); } } break; case CHOOSE_PHOTO: if(resultCode==RESULT_OK){ if(Build.VERSION.SDK_INT>=19){ handleImageOnKitKat(data); }else{ handleImageBeforeKitKat(data); } } break; default: break; } } @TargetApi(19) private void handleImageOnKitKat(Intent data) { String imagePath=null; Uri uri=data.getData(); if(DocumentsContract.isDocumentUri(view.getContext(),uri)) { //如果是document類型的uri則通過document id處理 String docId= DocumentsContract.getDocumentId(uri); if("com.android.providers.media.documents".equals(uri.getAuthority())) { String id=docId.split(":")[1];//解析為數字格式的id String selection= MediaStore.Images.Media._ID +"="+id; imagePath=getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI,selection); } else if("com.android.providers.downloads.documents".equals(uri.getAuthority())) { Uri contentUri= ContentUris.withAppendedId(Uri.parse("content://downloads/public_downloads"),Long.valueOf(docId)); imagePath=getImagePath(contentUri,null); } else if("content".equalsIgnoreCase(uri.getScheme())) { //如果是content類型的uri則使用普通方式處理 imagePath=getImagePath(uri,null); } else if("file".equalsIgnoreCase(uri.getScheme())) { //如果是file類型的uri直接獲取圖片路徑就好 imagePath=uri.getPath(); } displayImage(imagePath);//根據圖片的路徑顯示圖片 } } private void handleImageBeforeKitKat(Intent data) { Uri uri=data.getData(); String imagePath=getImagePath(uri,null); displayImage(imagePath); } private String getImagePath(Uri uri,String selection) { String path=null; //通過uri和selection來獲取真實的圖片路徑 Cursor cursor=view.getContext().getContentResolver().query(uri,null,selection,null,null); if(cursor!=null) { if(cursor.moveToFirst()) { path=cursor.getString(cursor.getColumnIndex(MediaStore.Images.Media.DATA)); } cursor.close(); } return path; } private void displayImage(String imagePath) { if(imagePath!=null) { //Bitmap bitmap= BitmapFactory.decodeFile(imagePath); //imageView.setImageBitmap(bitmap); Uri ans=getImageContentUri(view.getContext(),imagePath); String bf=ans.toString(); bean.image=bf; muserOperator.updateImage(bean); User beef=muserOperator.isExit(username); Log.i("img:",beef.image); imageView.setImageURI(ans); } else { Toast.makeText(view.getContext(),"獲取圖片失敗", Toast.LENGTH_SHORT).show(); } } /** * 將圖片轉換成Uri * @param context 傳入上下文參數 * @param path 圖片的路徑 * @return 返回的就是一個Uri對象 */ public static Uri getImageContentUri(Context context, String path) { Cursor cursor = context.getContentResolver().query(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, new String[] { MediaStore.Images.Media._ID }, MediaStore.Images.Media.DATA + "=? ", new String[] { path }, null); if (cursor != null && cursor.moveToFirst()) { int id = cursor.getInt(cursor.getColumnIndex(MediaStore.MediaColumns._ID)); Uri baseUri = Uri.parse("content://media/external/images/media"); return Uri.withAppendedPath(baseUri, "" + id); } else { // 如果圖片不在手機的共享圖片數據庫,就先把它插入。 if (new File(path).exists()) { ContentValues values = new ContentValues(); values.put(MediaStore.Images.Media.DATA, path); return context.getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values); } else { return null; } } } }