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网络编程系列之TCP连接状态

    1.TCP连接状态 LISTEN:Server端打开一个socket进行监听,状态置为LISTEN SYN_SENT:Client端发送SYN请求给Server端,状态由CLOSED变为SYN_SEN ...

  2. 《图解机器学习-杉山将著》读书笔记---CH1

    CH1 什么是机器学习 重点提炼 机器学习的种类: 常分为:监督学习.无监督学习.强化学习等 监督学习是学生从老师那获得知识,老师提供对错指示 无监督学习是在没有老师的情况下,学生自习 强化学习是在没 ...

  3. 一条SQL注入引出的惊天大案2:无限战争

    前情回顾: 经过黑衣人和老周的合作,终于清除了入侵Linux帝国的网页病毒,并修复了漏洞.不曾想激怒了幕后的黑手,一场新的风雨即将来临. 详情参见:一条SQL注入引出的惊天大案   风云再起 小Q是L ...

  4. vue不常用到的v-model修饰符

    v-model的input事件同步输入框的数据根据输入的内容实时改变.v-model.lazy则是与change事件同步,即失去焦点或是回车才更新 v-model.number 将输入的数字转换为Nu ...

  5. 【转】Zookeeper原理

    ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等.Zookeeper是hadoop的一个子项目,其 ...

  6. echart两组柱状图对比时,不同类型根据各类型的最大值为基准进行展示

    项目中遇到的问题:因为数据太小,箭头的地方展示不出来,这时的两组对比数据是根据一个最大值为基准进行渲染的.但我们想实现不同类型的对比根据不同的基准值渲染. 理想效果如下图: 实现代码: option ...

  7. Java 第一次课堂测验

    周一下午进行了开学来java第一次课堂测验,在课堂上我只完成了其中一部分,现代码修改如下: 先定义 ScoreInformation 类记录学生信息: /** * 信1805-1 * 胡一鸣 * 20 ...

  8. dfs 序 欧拉序

    推荐博客 :https://www.cnblogs.com/stxy-ferryman/p/7741970.html DFS序其实就是一棵树顺次访问的结点的顺序,例如下面这棵树 它的 dfs 序就是 ...

  9. nginx服务无法停止(Windows)

    本人一般停止nginx服务都是通过Windows自带的任务管理器来强制结束nginx进程实现的,如图 2.但是 这次我通过同样的方法来结束nginx服务,发现nginx的进程无法结束   3.首先我要 ...

  10. Kubernetes concepts 系列

    kubernetes concepts overview Pod overview Replication Controller Pod Liftcycle Termination Of Pod Re ...