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

Android头像更换之详细操作的更多相关文章

  1. Windows7 64位系统搭建Cocos2d-x-2.2.1最新版以及Android交叉编译环境(详细教程)

    Windows7 64位系统搭建Cocos2d-x-2.2.1最新版以及Android交叉编译环境(详细教程) 声明:本教程在参考了以下博文,并经过自己的摸索后实际操作得出,本教程系本人原创,由于升级 ...

  2. UiAutomator环境搭建及详细操作

    一.环境搭建 1.1 必备条件 JDK SDK(API高于15) Eclipse(安装ADT插件) ANT(用于编译生成的jar) 安装JDK并添加环境变量 1.2 详细步骤 1.安装JDK并添加环境 ...

  3. Android可更换布局的换肤方案

    换肤,顾名思义,就是对应用中的视觉元素进行更新,呈现新的显示效果.一般来说,换肤的时候只是更新UI上使用的资源,如颜色,图片,字体等等.本文介绍一种笔者自己使用的基于布局的Android换肤方案,不仅 ...

  4. Android中时间戳的详细解释

    Android中时间戳的详细解释: (1).定义: 时间戳就是根据当前系统时间生成的一组随机数字. (2).作用: 作为对数据唯一性的一种判断依据.避免了重复修改数据所带来的错误! (3).应用: ( ...

  5. (47) odoo详细操作手册

    odoo 8 详细操作手册, ERP(Odoo8.0)操作手册-v1.10(陈伟明).pdf 链接: http://pan.baidu.com/s/1hsp0bVQ 密码: r9tt 花了将近9个月时 ...

  6. yii2 rbac权限控制详细操作步骤

    作者:白狼 出处:http://www.manks.top/article/yii2_rbac_description本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 ...

  7. android Bitmap类方法属性 详细说明

    (转:http://blog.csdn.net/ymangu666/article/details/37729109) 1.  BitMap类public void recycle()——回收位图占用 ...

  8. yii2权限控制rbac之详细操作步骤

    本篇的主题是 rbac权限控制的详细操作步骤,注意是操作步骤哦,关于配置与rbac的搭建,我们在博文 yii2搭建完美后台并实现rbac权限控制实例教程说的再清楚不过了. 但是,在很多人的反馈下,说是 ...

  9. Android webservice的用法详细讲解

    Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...

随机推荐

  1. Java异常处理原则与技巧总结

    一  处理原则 Java异常代码中我们使用异常的目的是让异常的异常类型来提示“什么”被抛出了--- 即出了什么问题:用异常的栈打印信息来跟踪异常在“哪里”抛出 --- 即哪里出了问题: 异常提示信息来 ...

  2. Spring中PropertiesLoaderUtils应用

    FileSystemResource fileSystemResource =new FileSystemResource("D:/home/conf/mail.properties&quo ...

  3. Go Web 编程之 响应

    概述 上一篇文章中,我们介绍了请求的结构与处理.本文将详细介绍如何响应客户端的请求.其实在前面几篇文章中,我们已经使用过响应的功能--通过http.ResponseWriter发送字符串给客户端. 但 ...

  4. 三、Spring Cloud之软负载均衡 Ribbon

    前言 上一节我们已经学习了Eureka 注册中心,其实我们也使用到了Ribbon ,只是当时我们没有细讲,所以我们现在一起来学习一下Ribbon. 什么是Ribbon 之前接触到的负载均衡都是硬负载均 ...

  5. forkjoin及其性能分析,是否比for循环快?

    最近看了网上的某公开课,其中有讲到forkjoin框架.在这之前,我丝毫没听说过这个东西,很好奇是什么东东.于是,就顺道研究了一番. 总感觉这个东西,用的地方很少,也有可能是我才疏学浅.好吧,反正问了 ...

  6. Java ArrayList类的简单介绍

    ArrayList类的说明: ArrayList类是List接口的实现类,java.util.ArrayList集合数据存储的结构是数组结构. 特点: 元素增删慢,查找快.(由于日常开发中使用最多的功 ...

  7. MySQL UTF-8 常用字符排序规则

    排序规则 解释 utf8_general_ci 不区分大小写 utf8_general_cs 区分大小写 utf8_bin 区分大小写,字符串每个字符串用二进制数据编译存储,且支持存储二进制数据

  8. dp-多重背包

    (推荐 : http://blog.csdn.net/insistgogo/article/details/11176693 ) 学会了前两个背包 , 学这个背包还是很轻松的 . 多重背包 , 顾名思 ...

  9. 逆元(inv)

    推荐博客 : http://blog.csdn.net/baidu_35643793/article/details/75268911 通常我们在计算除法取模时,并不能直接的取模后再去相除,答案会有问 ...

  10. 20190925Java课堂记录(一)

    判断字符串是否回文 设计思想 利用递归,每次返回长度减二的字符串,最终返回结果 源程序代码 import java.util.Scanner; public class palindrome { st ...