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; } } } }