最近在做个人头像的上传,具体是能调用摄像头和从相册进行选择。本篇文章参考的我的同学的博客,大家有兴趣可以去原作者那里去看看:

Hi(。・∀・)ノ (cnblogs.com)

1.使用glide进行图片的展示效果

关于glide

2.PermissionGen动态权限获取

关于PermissionGen

代码:

一、准备工作

1.在AndroidManifest.xml添加权限

    <uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.CAMERA" />

2.在build.gradle中添加依赖

dependencies {

    implementation 'com.github.bumptech.glide:glide:4.10.0'
implementation 'com.lovedise:permissiongen:0.0.6' }

二、主代码

1.util工具类(toast和照片)

import android.content.Context;
import android.widget.Toast; public class ToastUtil {
public static Toast mToast;
public static void showMsg(Context context, String msg){
if(mToast == null){
mToast = Toast.makeText(context,msg,Toast.LENGTH_SHORT);
}else{
mToast.setText(msg);
}
mToast.show();
}
} Toast

Toast

import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.provider.MediaStore; import androidx.appcompat.app.AppCompatActivity; import java.io.File; /**
* CSDN_LQR
* 图片选择工具类
*/
public class LQRPhotoSelectUtils { public static final int REQ_TAKE_PHOTO = 10001;
public static final int REQ_SELECT_PHOTO = 10002;
public static final int REQ_ZOOM_PHOTO = 10003; private AppCompatActivity mActivity;
//拍照或剪切后图片的存放位置(参考file_provider_paths.xml中的路径)
private String imgPath = Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + String.valueOf(System.currentTimeMillis()) + ".jpg";
//FileProvider的主机名:一般是包名+".fileprovider",严格上是build.gradle中defaultConfig{}中applicationId对应的值+".fileprovider"
private String AUTHORITIES = "packageName" + ".fileprovider";
private boolean mShouldCrop = false;//是否要裁剪(默认不裁剪)
private Uri mOutputUri = null;
private File mInputFile;
private File mOutputFile = null; //剪裁图片宽高比
private int mAspectX = 1;
private int mAspectY = 1;
//剪裁图片大小
private int mOutputX = 800;
private int mOutputY = 480;
PhotoSelectListener mListener; /**
* 可指定是否在拍照或从图库选取照片后进行裁剪
* <p>
* 默认裁剪比例1:1,宽度为800,高度为480
*
* @param activity 上下文
* @param listener 选取图片监听
* @param shouldCrop 是否裁剪
*/
public LQRPhotoSelectUtils(AppCompatActivity activity, PhotoSelectListener listener, boolean shouldCrop) {
mActivity = activity;
mListener = listener;
mShouldCrop = shouldCrop;
AUTHORITIES = activity.getPackageName() + ".fileprovider";
imgPath = generateImgePath();
} /**
* 可以拍照或从图库选取照片后裁剪的比例及宽高
*
* @param activity 上下文
* @param listener 选取图片监听
* @param aspectX 图片裁剪时的宽度比例
* @param aspectY 图片裁剪时的高度比例
* @param outputX 图片裁剪后的宽度
* @param outputY 图片裁剪后的高度
*/
public LQRPhotoSelectUtils(AppCompatActivity activity, PhotoSelectListener listener, int aspectX, int aspectY, int outputX, int outputY) {
this(activity, listener, true);
mAspectX = aspectX;
mAspectY = aspectY;
mOutputX = outputX;
mOutputY = outputY;
} /**
* 设置FileProvider的主机名:一般是包名+".fileprovider",严格上是build.gradle中defaultConfig{}中applicationId对应的值+".fileprovider"
* <p>
* 该工具默认是应用的包名+".fileprovider",如项目build.gradle中defaultConfig{}中applicationId不是包名,则必须调用此方法对FileProvider的主机名进行设置,否则Android7.0以上使用异常
*
* @param authorities FileProvider的主机名
*/
public void setAuthorities(String authorities) {
this.AUTHORITIES = authorities;
} /**
* 修改图片的存储路径(默认的图片存储路径是SD卡上 Android/data/应用包名/时间戳.jpg)
*
* @param imgPath 图片的存储路径(包括文件名和后缀)
*/
public void setImgPath(String imgPath) {
this.imgPath = imgPath;
} /**
* 拍照获取
*/
public void takePhoto() {
File imgFile = new File(imgPath);
if (!imgFile.getParentFile().exists()) {
imgFile.getParentFile().mkdirs();
}
Uri imgUri = null; // if (Build.VERSION.SDK_INT >= 24) {//这里用这种传统的方法无法调起相机
// imgUri = FileProvider.getUriForFile(mActivity, AUTHORITIES, imgFile);
// } else {
// imgUri = Uri.fromFile(imgFile);
// }
/*
* 1.现象
在项目中调用相机拍照和录像的时候,android4.x,Android5.x,Android6.x均没有问题,在Android7.x下面直接闪退
2.原因分析
Android升级到7.0后对权限又做了一个更新即不允许出现以file://的形式调用隐式APP,需要用共享文件的形式:content:// URI
3.解决方案
下面是打开系统相机的方法,做了android各个版本的兼容:
* */ if (Build.VERSION.SDK_INT < 24) {
// 从文件中创建uri
imgUri = Uri.fromFile(imgFile);
} else {
//兼容android7.0 使用共享文件的形式
ContentValues contentValues = new ContentValues(1);
contentValues.put(MediaStore.Images.Media.DATA, imgFile.getAbsolutePath());
imgUri = mActivity.getApplication().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);
} Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
intent.putExtra(MediaStore.EXTRA_OUTPUT, imgUri);
mActivity.startActivityForResult(intent, REQ_TAKE_PHOTO);
} /**
* 从图库获取
*/
public void selectPhoto() {
Intent intent = new Intent(Intent.ACTION_PICK, null);
intent.setDataAndType(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, "image/*");
mActivity.startActivityForResult(intent, REQ_SELECT_PHOTO);
// Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
// intent.setType("image/*");
// mActivity.startActivityForResult(intent, REQ_SELECT_PHOTO);
} private void zoomPhoto(File inputFile, File outputFile) {
File parentFile = outputFile.getParentFile();
if (!parentFile.exists()) {
parentFile.mkdirs();
}
Intent intent = new Intent("com.android.camera.action.CROP");
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
intent.setDataAndType(getImageContentUri(mActivity, inputFile), "image/*");
} else {
intent.setDataAndType(Uri.fromFile(inputFile), "image/*");
}
intent.putExtra("crop", "true"); //设置剪裁图片宽高比
intent.putExtra("mAspectX", mAspectX);
intent.putExtra("mAspectY", mAspectY); //设置剪裁图片大小
intent.putExtra("mOutputX", mOutputX);
intent.putExtra("mOutputY", mOutputY); // 是否返回uri
intent.putExtra("return-data", false);
intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(outputFile));
intent.putExtra("outputFormat", Bitmap.CompressFormat.JPEG.toString()); mActivity.startActivityForResult(intent, REQ_ZOOM_PHOTO);
} public void attachToActivityForResult(int requestCode, int resultCode, Intent data) {
if (resultCode == AppCompatActivity.RESULT_OK) {
switch (requestCode) {
case LQRPhotoSelectUtils.REQ_TAKE_PHOTO://拍照
mInputFile = new File(imgPath);
if (mShouldCrop) {//裁剪
mOutputFile = new File(generateImgePath());
mOutputUri = Uri.fromFile(mOutputFile);
zoomPhoto(mInputFile, mOutputFile);
} else {//不裁剪
mOutputUri = Uri.fromFile(mInputFile);
if (mListener != null) {
mListener.onFinish(mInputFile, mOutputUri);
}
}
break;
case LQRPhotoSelectUtils.REQ_SELECT_PHOTO://图库
if (data != null) {
Uri sourceUri = data.getData();
String[] proj = {MediaStore.Images.Media.DATA};
Cursor cursor = mActivity.managedQuery(sourceUri, proj, null, null, null);
int columnIndex = cursor.getColumnIndexOrThrow(MediaStore.Images.Media.DATA);
cursor.moveToFirst();
String imgPath = cursor.getString(columnIndex);
mInputFile = new File(imgPath); if (mShouldCrop) {//裁剪
mOutputFile = new File(generateImgePath());
mOutputUri = Uri.fromFile(mOutputFile);
zoomPhoto(mInputFile, mOutputFile);
} else {//不裁剪
mOutputUri = Uri.fromFile(mInputFile);
if (mListener != null) {
mListener.onFinish(mInputFile, mOutputUri);
}
}
}
break;
case LQRPhotoSelectUtils.REQ_ZOOM_PHOTO://裁剪
if (data != null) {
if (mOutputUri != null) {
//删除拍照的临时照片
File tmpFile = new File(imgPath);
if (tmpFile.exists())
tmpFile.delete();
if (mListener != null) {
mListener.onFinish(mOutputFile, mOutputUri);
}
}
}
break;
}
}
} /**
* 安卓7.0裁剪根据文件路径获取uri
*/
private Uri getImageContentUri(Context context, File imageFile) {
String filePath = imageFile.getAbsolutePath();
Cursor cursor = context.getContentResolver().query(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
new String[]{MediaStore.Images.Media._ID},
MediaStore.Images.Media.DATA + "=? ",
new String[]{filePath}, 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 (imageFile.exists()) {
ContentValues values = new ContentValues();
values.put(MediaStore.Images.Media.DATA, filePath);
return context.getContentResolver().insert(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI, values);
} else {
return null;
}
}
} /**
* 产生图片的路径,带文件夹和文件名,文件名为当前毫秒数
*/
private String generateImgePath() {
return getExternalStoragePath() + File.separator + String.valueOf(System.currentTimeMillis()) + ".jpg";
// return Environment.getExternalStorageDirectory().getAbsolutePath() + File.separator + String.valueOf(System.currentTimeMillis()) + ".jpg";//测试用
} /**
* 获取SD下的应用目录
*/
private String getExternalStoragePath() {
StringBuilder sb = new StringBuilder();
sb.append(Environment.getExternalStorageDirectory().getAbsolutePath());
sb.append(File.separator);
String ROOT_DIR = "Android/data/" + mActivity.getPackageName();
sb.append(ROOT_DIR);
sb.append(File.separator);
return sb.toString();
} public interface PhotoSelectListener {
void onFinish(File outputFile, Uri outputUri);
}
} LQRPhotoSelectUtils

LQRPhotoSelectUtils

2.MainActivity

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat; import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast; import com.bumptech.glide.Glide;
import com.example.camera.utils.LQRPhotoSelectUtils;
import com.example.camera.utils.ToastUtil; import java.io.File; import kr.co.namee.permissiongen.PermissionGen; public class MainActivity extends AppCompatActivity { //定义控件
private Button mBtnTakePhoto;
private Button mBtnSelectPic;
private Button mBtnFinish;
private ImageView mIvPic;
private TextView mTvPath;
private TextView mTvUri; private static final int REQUEST_CODE = 0x001;
private LQRPhotoSelectUtils mLqrPhotoSelectUtils; @Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main); //绑定控件
mBtnTakePhoto = findViewById(R.id.btn_take_photo);
mBtnSelectPic = findViewById(R.id.btn_select_pic);
mTvPath = findViewById(R.id.tvPath);
mTvUri = findViewById(R.id.tvUri);
mIvPic = findViewById(R.id.iv_pic);
mBtnFinish = findViewById(R.id.btn_finish); init();
initListener();
}
private void init() {//初始化
//创建LQRPhotoSelectUtils(一个Activity对应一个LQRPhotoSelectUtils)
mLqrPhotoSelectUtils = new LQRPhotoSelectUtils(this, new LQRPhotoSelectUtils.PhotoSelectListener() {
@Override
public void onFinish(File outputFile, Uri outputUri) {
//当拍照或从图库选取图片成功后回调
mTvPath.setText(outputFile.getAbsolutePath());//显示图片路径
mTvUri.setText(outputUri.toString());//显示图片uri
//System.out.println(outputUri.toString());
Glide.with(MainActivity.this).load(outputUri).into(mIvPic);//使用Glide进行图片展示
}
}, true);//是否裁剪。true裁剪,false不裁剪 //检查权限
if (ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.READ_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED||
ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED||
ContextCompat.checkSelfPermission(MainActivity.this, Manifest.permission.CAMERA)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(MainActivity.this, new String[]{Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.CAMERA}, REQUEST_CODE);
}else{
ToastUtil.showMsg(MainActivity.this, "已经获得了所有权限");
}
} private void initListener(){//初始化监听器
mBtnTakePhoto.setOnClickListener(new View.OnClickListener(){
@Override
public void onClick(View v) {
//获取权限,调用拍照方法
PermissionGen.with(MainActivity.this)
.addRequestCode(LQRPhotoSelectUtils.REQ_TAKE_PHOTO)
.permissions(Manifest.permission.READ_EXTERNAL_STORAGE,
Manifest.permission.WRITE_EXTERNAL_STORAGE,
Manifest.permission.CAMERA
).request();
//拍照
mLqrPhotoSelectUtils.takePhoto();
}
}); mBtnSelectPic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 选择图片
mLqrPhotoSelectUtils.selectPhoto();
}
}); mBtnFinish.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (mTvPath.getText().toString().equals("图片路径:")){
// 如果一张图片都没选
ToastUtil.showMsg(getApplicationContext(), "您还没有选择图片呢!");
}else{
ToastUtil.showMsg(getApplicationContext(), "选择图片成功:"+mTvPath.getText().toString());
Bundle bundle = new Bundle();
Intent intent = new Intent();
bundle.putString("uri", mTvUri.getText().toString());
intent.putExtras(bundle);
setResult(Activity.RESULT_OK, intent);
finish();
}
}
});
} public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
if (requestCode == REQUEST_CODE) {
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
initListener();
} else {
Toast.makeText(this, "您已拒绝授予权限", Toast.LENGTH_LONG).show();
}
}
} @Override
protected void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
super.onActivityResult(requestCode, resultCode, data);
//在Activity中的onActivityResult()方法里与LQRPhotoSelectUtils关联
mLqrPhotoSelectUtils.attachToActivityForResult(requestCode, resultCode, data);
}
}

3.xml界面

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"> <Button
android:id="@+id/btn_take_photo"
android:text="拍照"
android:layout_width="match_parent"
android:layout_height="wrap_content"/> <Button
android:id="@+id/btn_select_pic"
android:text="从图库中选取"
android:layout_width="match_parent"
android:layout_height="wrap_content"/> <TextView
android:id="@+id/tvPath"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="图片路径:"/> <TextView
android:id="@+id/tvUri"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:text="图片Uri:"/>
<ImageView
android:id="@+id/iv_pic"
app:srcCompat="@drawable/head_portrait"
android:layout_width="300dp"
android:layout_height="300dp"/> <Button
android:id="@+id/btn_finish"
android:layout_width="match_parent"
android:layout_height="50dp"
android:text="完成"/>
</LinearLayout>

5.21学习总结——android开发实现用户头像的上传的更多相关文章

  1. Android之修改用户头像并上传服务器(实现手机拍照和SD卡选择上传)

    写了这么多个的APP,最近才把他这个功能写上来,就抽取其中的用户修改头像的相关操作这个功能写了这篇博客,来与大家分享,希望对你有所帮助. 案例包含了: Xutil图片上传 拍照和SD卡选择图片 图片缓 ...

  2. Servlet3.0学习总结(三)——基于Servlet3.0的文件上传

    在Servlet2.5中,我们要实现文件上传功能时,一般都需要借助第三方开源组件,例如Apache的commons-fileupload组件,在Servlet3.0中提供了对文件上传的原生支持,我们不 ...

  3. iOS 开发http post 文件的上传

    iOS开发网络篇—文件的上传 说明:文件上传使用的时POST请求,通常把要上传的数据保存在请求体中.本文介绍如何不借助第三方框架实现iOS开发中得文件上传. 由于过程较为复杂,因此本文只贴出部分关键代 ...

  4. 【Android】图片(文件)上传的请求分析结构

    怎么在android中上传文件,即怎么用Java向服务器上传文件.上传图片,这是个老问题了,在网上能搜到现成的代码,很多朋友用起来也比较熟了,但是为什么这么写,可能很多朋友并不清楚,这篇文章就来分析一 ...

  5. Android okHttp网络请求之文件上传下载

    前言: 前面介绍了基于okHttp的get.post基本使用(http://www.cnblogs.com/whoislcj/p/5526431.html),今天来实现一下基于okHttp的文件上传. ...

  6. iOS开发网络篇—文件的上传

    iOS开发网络篇—文件的上传 说明:文件上传使用的时POST请求,通常把要上传的数据保存在请求体中.本文介绍如何不借助第三方框架实现iOS开发中得文件上传. 由于过程较为复杂,因此本文只贴出部分关键代 ...

  7. Android利用网络编程HttpClient批量上传(一个)

    请尊重他人的劳动成果.转载请注明出处:Android网络编程之使用HttpClient批量上传文件 我曾在<Android网络编程之使用HTTP訪问网络资源>一文中介绍过HttpCient ...

  8. 将本地开发完的SDK代码上传到SVN上面:an error occurred while contacting the repository The server may be unreachable or the URL may be incorrect

    将本地开发完的SDK代码上传到SVN上面:an error occurred while contacting the repository  The server may be unreachabl ...

  9. SpringMVC:学习笔记(10)——整合Ckeditor且实现图片上传

    SpringMVC:学习笔记(10)——整合Ckeditor且实现图片上传 配置CKEDITOR 精简文件 解压之后可以看到ckeditor/lang下面有很多语言的js,如果不需要那么多种语言的,可 ...

随机推荐

  1. Vulnhub----bulldog靶场笔记

    前提条件 kali和bulldog靶机的的ip地址在同一个网段 本测试环境: kali:192.168.56.102 bulldog:192.168.56.101 主机探测 利用kali的netdis ...

  2. Linux进程理解与实践(五)细谈守护进程

    一. 守护进程及其特性      守护进程最重要的特性是后台运行.在这一点上DOS下的常驻内存程序TSR与之相似.其次,守护进程必须与其运行前的环境隔离开来.这些环境包括未关闭的文件描述符,控制终端, ...

  3. Linux 开机关机在线求助与指令输入

    由于那本书版本稍稍有点早,我就跳过学习第二章第三章了(分别是主机规划和虚拟机安装)下图为在自己电脑上安装好的redhat7 4.1 我们来登入 其中第一行是Linux发行商和发行版本(欸,我的这个怎么 ...

  4. Git-04-本地仓库撤销修改

    编辑修改了文件,但是还没有git add之前 直接用 git checkout -- filename 这个命令就可以了 已经 git add 了,但是没有 git commit 之前 1 模拟git ...

  5. MySQL-10-索引应用规范

    建立索引的原则 SQL文件 sql文件下载链接: https://alnk-blog-pictures.oss-cn-shenzhen.aliyuncs.com/blog-pictures/world ...

  6. moco模拟接口具体操作

    1.get请求 [ { "description": "模拟一个没有参数的get请求", "request": { "uri&qu ...

  7. 官宣 .NET 6 预览版 6

    我们很高兴宣布.NET 6 预览版6问世啦.预览版6 是我们RC版发布之前的倒数第二个预览版. 我们将有两个RC版. 此版本本身相对较小,而预览版7会更大. 在那之后,我们将进行质量修复,直到11 月 ...

  8. MATLAB—面向复数和数组的基本运算

    文章目录 一.MATLAB基本运算说明 二.面向复数的计算特点 1.基础知识 2.对复数的基本操作 3.复数的开方问题 二.面向数组 1.数组的输入形式 2.对矩阵中的元素进行并行操作 3.利用数组运 ...

  9. AWD比赛组织指南

    目录 题目构建 平台构建 后端部署流程 前端展示 批量启动 check 题目构建 赛题全部使用docker部署,需准备check脚本和镜像 镜像构建注意事项 1.注意web目录权限 2.注意服务是否自 ...

  10. Azkaban入门(启动一个Simple Example)

    Azkaban简介 azkaban是一个开源的任务调度系统 Azkaban是一套简单的任务调度服务,整体包括三部分webserver.dbserver.executorserver. 开发语言为Jav ...