Android头像更换之详细操作
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头像更换之详细操作的更多相关文章
- Windows7 64位系统搭建Cocos2d-x-2.2.1最新版以及Android交叉编译环境(详细教程)
Windows7 64位系统搭建Cocos2d-x-2.2.1最新版以及Android交叉编译环境(详细教程) 声明:本教程在参考了以下博文,并经过自己的摸索后实际操作得出,本教程系本人原创,由于升级 ...
- UiAutomator环境搭建及详细操作
一.环境搭建 1.1 必备条件 JDK SDK(API高于15) Eclipse(安装ADT插件) ANT(用于编译生成的jar) 安装JDK并添加环境变量 1.2 详细步骤 1.安装JDK并添加环境 ...
- Android可更换布局的换肤方案
换肤,顾名思义,就是对应用中的视觉元素进行更新,呈现新的显示效果.一般来说,换肤的时候只是更新UI上使用的资源,如颜色,图片,字体等等.本文介绍一种笔者自己使用的基于布局的Android换肤方案,不仅 ...
- Android中时间戳的详细解释
Android中时间戳的详细解释: (1).定义: 时间戳就是根据当前系统时间生成的一组随机数字. (2).作用: 作为对数据唯一性的一种判断依据.避免了重复修改数据所带来的错误! (3).应用: ( ...
- (47) odoo详细操作手册
odoo 8 详细操作手册, ERP(Odoo8.0)操作手册-v1.10(陈伟明).pdf 链接: http://pan.baidu.com/s/1hsp0bVQ 密码: r9tt 花了将近9个月时 ...
- yii2 rbac权限控制详细操作步骤
作者:白狼 出处:http://www.manks.top/article/yii2_rbac_description本文版权归作者,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出 ...
- android Bitmap类方法属性 详细说明
(转:http://blog.csdn.net/ymangu666/article/details/37729109) 1. BitMap类public void recycle()——回收位图占用 ...
- yii2权限控制rbac之详细操作步骤
本篇的主题是 rbac权限控制的详细操作步骤,注意是操作步骤哦,关于配置与rbac的搭建,我们在博文 yii2搭建完美后台并实现rbac权限控制实例教程说的再清楚不过了. 但是,在很多人的反馈下,说是 ...
- Android webservice的用法详细讲解
Android webservice的用法详细讲解 看到有很多朋友对WebService还不是很了解,在此就详细的讲讲WebService,争取说得明白吧.此文章采用的项目是我毕业设计的webserv ...
随机推荐
- Java网络编程系列之TCP连接状态
1.TCP连接状态 LISTEN:Server端打开一个socket进行监听,状态置为LISTEN SYN_SENT:Client端发送SYN请求给Server端,状态由CLOSED变为SYN_SEN ...
- 《图解机器学习-杉山将著》读书笔记---CH1
CH1 什么是机器学习 重点提炼 机器学习的种类: 常分为:监督学习.无监督学习.强化学习等 监督学习是学生从老师那获得知识,老师提供对错指示 无监督学习是在没有老师的情况下,学生自习 强化学习是在没 ...
- 一条SQL注入引出的惊天大案2:无限战争
前情回顾: 经过黑衣人和老周的合作,终于清除了入侵Linux帝国的网页病毒,并修复了漏洞.不曾想激怒了幕后的黑手,一场新的风雨即将来临. 详情参见:一条SQL注入引出的惊天大案 风云再起 小Q是L ...
- vue不常用到的v-model修饰符
v-model的input事件同步输入框的数据根据输入的内容实时改变.v-model.lazy则是与change事件同步,即失去焦点或是回车才更新 v-model.number 将输入的数字转换为Nu ...
- 【转】Zookeeper原理
ZooKeeper是一个分布式的,开放源码的分布式应用程序协调服务,它包含一个简单的原语集,分布式应用程序可以基于它实现同步服务,配置维护和命名服务等.Zookeeper是hadoop的一个子项目,其 ...
- echart两组柱状图对比时,不同类型根据各类型的最大值为基准进行展示
项目中遇到的问题:因为数据太小,箭头的地方展示不出来,这时的两组对比数据是根据一个最大值为基准进行渲染的.但我们想实现不同类型的对比根据不同的基准值渲染. 理想效果如下图: 实现代码: option ...
- Java 第一次课堂测验
周一下午进行了开学来java第一次课堂测验,在课堂上我只完成了其中一部分,现代码修改如下: 先定义 ScoreInformation 类记录学生信息: /** * 信1805-1 * 胡一鸣 * 20 ...
- dfs 序 欧拉序
推荐博客 :https://www.cnblogs.com/stxy-ferryman/p/7741970.html DFS序其实就是一棵树顺次访问的结点的顺序,例如下面这棵树 它的 dfs 序就是 ...
- nginx服务无法停止(Windows)
本人一般停止nginx服务都是通过Windows自带的任务管理器来强制结束nginx进程实现的,如图 2.但是 这次我通过同样的方法来结束nginx服务,发现nginx的进程无法结束 3.首先我要 ...
- Kubernetes concepts 系列
kubernetes concepts overview Pod overview Replication Controller Pod Liftcycle Termination Of Pod Re ...