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

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. SaToken学习笔记-03

    SaToken学习笔记-03 如果排版有问题,请点击:传送门 核心思想 所谓权限验证,验证的核心就是一个账号是否拥有一个权限码 有,就让你通过.没有?那么禁止访问! 再往底了说,就是每个账号都会拥有一 ...

  2. Python入门:ChainMap 有效管理多个上下文

    摘要: Python的ChainMap从collections模块提供用于管理多个词典作为单个的有效工具. 本文分享自华为云社区<从零开始学python | ChainMap 有效管理多个上下文 ...

  3. MySQL-07-information_schema/show

    information_schema.tables视图 DESC information_schema.TABLES /** TABLE_SCHEMA ---->库名 TABLE_NAME -- ...

  4. 关于XSS简单介绍与waf bypass的一些思路整理

    很久没写东西了,今天整理一点儿思路 简单说一下XSS XSS(cross site script)即跨站脚本,侧重于"脚本"这一层概念,是一种常见web安全漏洞.攻击者通过往web ...

  5. java JNI介绍

    java JNI介绍 目录 java JNI介绍 1. Java调用C++代码 2.C++代码调用java代码 JNI是Java Native Interface的全称. oracle文档中是这样描述 ...

  6. 【微服务技术专题】Netflix动态化配置服务-微服务配置组件变色龙Archaius

    前提介绍 如果要设计开发一套微服务基础架构,参数化配置是一个非常重要的点,而Netflix也开源了一个叫变色龙Archaius的配置中心客户端,而且Archaius可以说是比其他客户端具备更多生产级特 ...

  7. TaskAwaiter<TResult> 结构

    参考网址:https://docs.microsoft.com/zh-cn/dotnet/api/system.runtime.compilerservices.taskawaiter-1?view= ...

  8. leaflet 的 marker 弹框 iframe 嵌套代码

    A页面 嵌套 B页面的代码 主要处理  leaflet 的 marker 的 popopen,     marker的点击的显示/隐藏 pop   会导致pop中的页面的内容,消失,不在页面中,导致b ...

  9. vue:Missing space before value for key 'components'

    原因是Vue对语法比较严格,而eslint是一个语法检查工具,对语法要求极其苛刻严格,于是就error了 解决办法是关闭eslint的语法规则,找到build/webpack.base.conf.js ...

  10. 10.SpringMVC之格式化、校验

    数据格式化 数据格式化的注解: 数据校验JSR303 Hibernate Validator扩展注解 启动 springMVC数据校验 转换.格式化.校验出错处理: