Android 更换用户头像(拍照、相册选取)

android:layout_width=“match_parent”

android:layout_height=“1dp”

android:background=“#EEEEEE” />

<TextView

android:id=“@+id/tv_cancel”

android:layout_width=“match_parent”

android:layout_height=“wrap_content”

android:background=“#FFF”

android:foreground=“?android:attr/selectableItemBackground”

android:gravity=“center”

android:padding=“16dp”

android:text=“取消”

android:textColor=“#000” />

这是一个弹窗的布局文件,里面提供你选择拍照、打开相册、取消。而且从命名来看,这是一个底部弹窗。所以需要一个地方去触发这个弹窗从屏幕底部出现。下面打开activity_main.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”

xmlns:tools=“http://schemas.android.com/tools”

android:layout_width=“match_parent”

android:layout_height=“match_parent”

android:gravity=“center”

android:orientation=“vertical”

tools:context=“.MainActivity”>

<com.google.android.material.imageview.ShapeableImageView

android:id=“@+id/iv_head”

android:layout_width=“200dp”

android:layout_height=“200dp”

android:onClick=“changeAvatar”

android:src=“@mipmap/ic_launcher”

app:shapeAppearanceOverlay=“@style/circleImageStyle” />

这里我用了一个ShapeableImageView,这是material库里面的一个控件,你只要知道它比普通的ImageView要🐮🍺就可以了,想要详细了解的看看Android Material UI控件之ShapeableImageView

这里我指定了app:shapeAppearanceOverlay=“@style/circleImageStyle”,也就是说它变成了一个圆形图片控件。

布局就写完了。

四、权限请求


进入到MainActivity,先声明变量

//权限请求

private RxPermissions rxPermissions;

先写一个Toast提示方法。

/**

  • Toast提示

  • @param msg

*/

private void showMsg(String msg) {

Toast.makeText(this, msg, Toast.LENGTH_SHORT).show();

}

然后可以写一个checkVersion()方法,用于检查当前的Android版本,并且给你提示。

/**

  • 检查版本

*/

private void checkVersion() {

//Android6.0及以上版本

if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

//如果你是在Fragment中,则把this换成getActivity()

rxPermissions = new RxPermissions(this);

//权限请求

rxPermissions.request(Manifest.permission.CAMERA, Manifest.permission.WRITE_EXTERNAL_STORAGE)

.subscribe(granted -> {

if (granted) {//申请成功

showMsg(“已获取权限”);

} else {//申请失败

showMsg(“权限未开启”);

}

});

} else {

//Android6.0以下

showMsg(“无需请求动态权限”);

}

}

然后你要在onCreate()中调用checkVersion()。使用户一进入这个页面就进行检查版本和授权。

不过这里还要防范一个问题,那就是假如用户没有通过权限。再创建一个变量

//是否拥有权限

private boolean hasPermissions = false;

然后赋值

在这里插入图片描述

只有权限全部通过授权之后才会是true。

五、底部弹窗显示


如果我没有猜错的话,你的activity_main.xml中还有一个地方报错。

不过不要担心,先增加两个变量

//底部弹窗

private BottomSheetDialog bottomSheetDialog;

//弹窗视图

private View bottomView;

然后新增一个changeAvatar()方法,里面的代码如下:

/**

  • 更换头像

  • @param view

*/

public void changeAvatar(View view) {

bottomSheetDialog = new BottomSheetDialog(this);

bottomView = getLayoutInflater().inflate(R.layout.dialog_bottom, null);

bottomSheetDialog.setContentView(bottomView);

bottomSheetDialog.getWindow().findViewById(R.id.design_bottom_sheet).setBackgroundColor(Color.TRANSPARENT);

TextView tvTakePictures = bottomView.findViewById(R.id.tv_take_pictures);

TextView tvOpenAlbum = bottomView.findViewById(R.id.tv_open_album);

TextView tvCancel = bottomView.findViewById(R.id.tv_cancel);

//拍照

tvTakePictures.setOnClickListener(v -> {

showMsg(“拍照”);

bottomSheetDialog.cancel();

});

//打开相册

tvOpenAlbum.setOnClickListener(v -> {

showMsg(“打开相册”);

bottomSheetDialog.cancel();

});

//取消

tvCancel.setOnClickListener(v -> {

bottomSheetDialog.cancel();

});

bottomSheetDialog.show();

}

这个方法就是配置弹窗的视图,并且绑定视图中的控件,设置点击事件。现在你再去看你的activity_main.xml布局,就不会报错了。并且如果你现在运行的话,当你点击图片是底部会出现弹窗。然后点击弹窗中的三个控件,或者弹窗外阴影区域都会关闭弹窗。

六、工具类


这里我会添加两个工具类,用来协助我们开发。

在com.llw.changeavatar下新建一个utils包,在这个包下新建一个BitmapUtils类,里面的代码如下:

package com.llw.changeavatar.utils;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Canvas;

import android.graphics.LinearGradient;

import android.graphics.Paint;

import android.graphics.PorterDuff;

import android.graphics.PorterDuffXfermode;

import android.graphics.RadialGradient;

import android.graphics.RectF;

import android.graphics.Shader;

import android.util.Base64;

import java.io.ByteArrayOutputStream;

import java.io.IOException;

import java.io.InputStream;

import java.net.HttpURLConnection;

import java.net.MalformedURLException;

import java.net.URL;

/**

  • Bitmap工具类

*/

public class BitmapUtils {

/**

  • bitmap转为base64

  • @param bitmap

  • @return

*/

public static String bitmapToBase64(Bitmap bitmap) {

String result = null;

ByteArrayOutputStream baos = null;

try {

if (bitmap != null) {

baos = new ByteArrayOutputStream();

bitmap.compress(Bitmap.CompressFormat.JPEG, 100, baos);

baos.flush();

baos.close();

byte[] bitmapBytes = baos.toByteArray();

result = Base64.encodeToString(bitmapBytes, Base64.DEFAULT);

}

} catch (IOException e) {

e.printStackTrace();

} finally {

try {

if (baos != null) {

baos.flush();

baos.close();

}

} catch (IOException e) {

e.printStackTrace();

}

}

return result;

}

/**

  • base64转为bitmap

  • @param base64Data

  • @return

*/

public static Bitmap base64ToBitmap(String base64Data) {

byte[] bytes = Base64.decode(base64Data, Base64.DEFAULT);

return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);

}

/**

  • url转bitmap

  • @param url

  • @return

*/

public static Bitmap urlToBitmap(final String url){

final Bitmap[] bitmap = {null};

new Thread(() -> {

URL imageurl = null;

try {

imageurl = new URL(url);

} catch (MalformedURLException e) {

e.printStackTrace();

}

try {

HttpURLConnection conn = (HttpURLConnection)imageurl.openConnection();

conn.setDoInput(true);

conn.connect();

InputStream is = conn.getInputStream();

bitmap[0] = BitmapFactory.decodeStream(is);

is.close();

} catch (IOException e) {

e.printStackTrace();

}

}).start();

return bitmap[0];

}

}

然后再新建一个CameraUtils类,代码如下;

package com.llw.changeavatar.utils;

import android.annotation.TargetApi;

import android.content.ContentUris;

import android.content.ContentValues;

import android.content.Context;

import android.content.Intent;

import android.database.Cursor;

import android.graphics.Bitmap;

import android.graphics.BitmapFactory;

import android.graphics.Matrix;

import android.media.ExifInterface;

import android.net.Uri;

import android.os.Build;

import android.os.Environment;

import android.provider.DocumentsContract;

import android.provider.MediaStore;

import android.util.Log;

import android.widget.ImageView;

import java.io.ByteArrayInputStream;

import java.io.ByteArrayOutputStream;

import java.io.File;

import java.io.IOException;

/**

  • 相机、相册工具类

*/

public class CameraUtils {

/**

  • 相机Intent

  • @param context

  • @param outputImagePath

  • @return

*/

public static Intent getTakePhotoIntent(Context context, File outputImagePath) {

// 激活相机

Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);

// 判断存储卡是否可以用,可用进行存储

if (hasSdcard()) {

if (Build.VERSION.SDK_INT < Build.VERSION_CODES.N) {

// 从文件中创建uri

Uri uri = Uri.fromFile(outputImagePath);

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

} else {

//兼容android7.0 使用共享文件的形式

ContentValues contentValues = new ContentValues(1);

contentValues.put(MediaStore.Images.Media.DATA, outputImagePath.getAbsolutePath());

Uri uri = context.getApplicationContext().getContentResolver().insert(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, contentValues);

intent.putExtra(MediaStore.EXTRA_OUTPUT, uri);

}

}

return intent;

}

/**

  • 相册Intent

  • @return

*/

public static Intent getSelectPhotoIntent() {

Intent intent = new Intent(“android.intent.action.GET_CONTENT”);

intent.setType(“image/*”);

return intent;

}

/**

  • 判断sdcard是否被挂载

*/

public static boolean hasSdcard() {

return Environment.getExternalStorageState().equals(

Environment.MEDIA_MOUNTED);

}

/**

  • 4.4及以上系统处理图片的方法

*/

@TargetApi(Build.VERSION_CODES.KITKAT)

public static String getImageOnKitKatPath(Intent data, Context context) {

String imagePath = null;

Uri uri = data.getData();

Log.d(“uri=intent.getData :”, “” + uri);

if (DocumentsContract.isDocumentUri(context, uri)) {

//数据表里指定的行

String docId = DocumentsContract.getDocumentId(uri);

Log.d(“getDocumentId(uri) :”, “” + docId);

Log.d(“uri.getAuthority() :”, “” + uri.getAuthority());

if (“com.android.providers.media.documents”.equals(uri.getAuthority())) {

String id = docId.split(“:”)[1];

String selection = MediaStore.Images.Media._ID + “=” + id;

imagePath = getImagePath(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, selection, context);

} 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, context);

}

} else if (“content”.equalsIgnoreCase(uri.getScheme())) {

imagePath = getImagePath(uri, null, context);

}

return imagePath;

}

/**

  • 通过uri和selection来获取真实的图片路径,从相册获取图片时要用

*/

public static String getImagePath(Uri uri, String selection, Context context) {

String path = null;

Cursor cursor = context.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;

}

/**

  • 更改图片显示角度

  • @param filepath

  • @param orc_bitmap

  • @param iv

*/

public static void ImgUpdateDirection(String filepath, Bitmap orc_bitmap, ImageView iv) {

//图片旋转的角度

int digree = 0;

//根据图片的filepath获取到一个ExifInterface的对象

ExifInterface exif = null;

try {

exif = new ExifInterface(filepath);

if (exif != null) {

// 读取图片中相机方向信息

int ori = exif.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_UNDEFINED);

// 计算旋转角度

switch (ori) {

case ExifInterface.ORIENTATION_ROTATE_90:

digree = 90;

break;

case ExifInterface.ORIENTATION_ROTATE_180:

digree = 180;

break;

case ExifInterface.ORIENTATION_ROTATE_270:

digree = 270;

break;

default:

digree = 0;

break;

}

}

//如果图片不为0

if (digree != 0) {

// 旋转图片

Matrix m = new Matrix();

m.postRotate(digree);

orc_bitmap = Bitmap.createBitmap(orc_bitmap, 0, 0, orc_bitmap.getWidth(),

orc_bitmap.getHeight(), m, true);

}

if (orc_bitmap != null) {

iv.setImageBitmap(orc_bitmap);

}

} catch (IOException e) {

e.printStackTrace();

exif = null;

}

}

/**

  • 4.4以下系统处理图片的方法

*/

public static String getImageBeforeKitKatPath(Intent data, Context context) {

Uri uri = data.getData();

String imagePath = getImagePath(uri, null, context);

return imagePath;

}

/**

  • 比例压缩

  • @param image

  • @return

*/

public static Bitmap compression(Bitmap image) {

ByteArrayOutputStream outputStream = new ByteArrayOutputStream();

image.compress(Bitmap.CompressFormat.JPEG, 100, outputStream);

//判断如果图片大于1M,进行压缩避免在生成图片(BitmapFactory.decodeStream)时溢出

if (outputStream.toByteArray().length / 1024 > 1024) {

//重置outputStream即清空outputStream

outputStream.reset();

//这里压缩50%,把压缩后的数据存放到baos中

image.compress(Bitmap.CompressFormat.JPEG, 50, outputStream);

}

ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());

BitmapFactory.Options options = new BitmapFactory.Options();

//开始读入图片,此时把options.inJustDecodeBounds 设回true了

options.inJustDecodeBounds = true;

Bitmap bitmap = BitmapFactory.decodeStream(inputStream, null, options);

options.inJustDecodeBounds = false;

int outWidth = options.outWidth;

int outHeight = options.outHeight;

//现在主流手机比较多是800*480分辨率,所以高和宽我们设置为

float height = 800f;//这里设置高度为800f

float width = 480f;//这里设置宽度为480f

//缩放比。由于是固定比例缩放,只用高或者宽其中一个数据进行计算即可

int zoomRatio = 1;//be=1表示不缩放

if (outWidth > outHeight && outWidth > width) {//如果宽度大的话根据宽度固定大小缩放

zoomRatio = (int) (options.outWidth / width);

} else if (outWidth < outHeight && outHeight > height) {//如果高度高的话根据宽度固定大小缩放

zoomRatio = (int) (options.outHeight / height);

}

if (zoomRatio <= 0) {

zoomRatio = 1;

}

options.inSampleSize = zoomRatio;//设置缩放比例

options.inPreferredConfig = Bitmap.Config.RGB_565;//降低图片从ARGB888到RGB565

//重新读入图片,注意此时已经把options.inJustDecodeBounds 设回false了

inputStream = new ByteArrayInputStream(outputStream.toByteArray());

//压缩好比例大小后再进行质量压缩

bitmap = BitmapFactory.decodeStream(inputStream, null, options);

return bitmap;

}

}

工具类里面都有注释,我就不多说了。

七、打开相机、相册


声明变量

//存储拍完照后的图片

private File outputImagePath;

//启动相机标识

public static final int TAKE_PHOTO = 1;

//启动相册标识

public static final int SELECT_PHOTO = 2;

拍照方法:

/**

  • 拍照

*/

private void takePhoto() {

if (!hasPermissions) {

showMsg(“未获取到权限”);

checkVersion();

return;

}

SimpleDateFormat timeStampFormat = new SimpleDateFormat(

“yyyy_MM_dd_HH_mm_ss”);

String filename = timeStampFormat.format(new Date());

outputImagePath = new File(getExternalCacheDir(),

filename + “.jpg”);

Intent takePhotoIntent = CameraUtils.getTakePhotoIntent(this, outputImagePath);

// 开启一个带有返回值的Activity,请求码为TAKE_PHOTO

startActivityForResult(takePhotoIntent, TAKE_PHOTO);

}

打开相册方法:

/**

  • 打开相册

*/

private void openAlbum() {

if (!hasPermissions) {

showMsg(“未获取到权限”);

checkVersion();

return;

}

startActivityForResult(CameraUtils.getSelectPhotoIntent(), SELECT_PHOTO);

}

方法写完了记得要调用才行,如下图在changeAvatar中调用。

在这里插入图片描述

总结

我最近从朋友那里收集到了2020-2021BAT 面试真题解析,内容很多也很系统,包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题等等,可以很好地帮助大家深刻理解Android相关知识点的原理以及面试相关知识

这份资料把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节;还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

这里也分享给广大面试同胞们,希望每位程序猿们都能面试成功~

Android 基础知识点

Java 基础知识点

Android 源码相关分析

常见的一些原理性问题

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2020面试真题解析

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

tent(), SELECT_PHOTO);

}

方法写完了记得要调用才行,如下图在changeAvatar中调用。

在这里插入图片描述

总结

我最近从朋友那里收集到了2020-2021BAT 面试真题解析,内容很多也很系统,包含了很多内容:Android 基础、Java 基础、Android 源码相关分析、常见的一些原理性问题等等,可以很好地帮助大家深刻理解Android相关知识点的原理以及面试相关知识

这份资料把大厂面试中常被问到的技术点整理成了PDF,包知识脉络 + 诸多细节;还有 高级架构技术进阶脑图 帮助大家学习提升进阶,也节省大家在网上搜索资料的时间来学习,也可以分享给身边好友一起学习。

这里也分享给广大面试同胞们,希望每位程序猿们都能面试成功~

Android 基础知识点

[外链图片转存中…(img-ekCVHfpY-1714323350444)]

Java 基础知识点

[外链图片转存中…(img-q7RXHqkd-1714323350444)]

Android 源码相关分析

[外链图片转存中…(img-Mb1q6tvT-1714323350445)]

常见的一些原理性问题

[外链图片转存中…(img-8AM4FNB1-1714323350445)]

腾讯、字节跳动、阿里、百度等BAT大厂 2019-2020面试真题解析

[外链图片转存中…(img-GvkZDjsU-1714323350445)]

网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。

需要这份系统化学习资料的朋友,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

  • 29
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android平台上,实现拍照或从相册选取图片上传多张可以通过以下步骤实现。首先,需要在AndroidManifest.xml文件中添加相机和存储权限。接着,在相应的Activity中,我们可以使用Intent来启动系统相机应用,或者打开系统相册应用。 如果是拍照上传,我们可以创建一个Intent对象,指定Action为MediaStore.ACTION_IMAGE_CAPTURE,然后使用startActivityForResult方法启动相机应用。拍摄完成后,系统会将照片保存在指定的URI路径上。 如果是从相册选取图片上传,我们同样可以创建一个Intent对象,指定Action为Intent.ACTION_PICK,Type为“image/*”,然后使用startActivityForResult方法启动相册应用。 接着,在onActivityResult方法中,我们可以根据requestCode和resultCode来判断是从相机还是相册返回的数据。如果是从相机返回的数据,我们可以通过指定的URI路径来获取拍摄的照片;如果是从相册返回的数据,我们可以通过data.getData()方法来获取选取的图片。 最后,我们可以将获取到的照片进行上传操作,可以是将图片转换成Bitmap对象,然后上传至服务器;或者将图片的URI路径传递给服务器进行上传。如果有多张图片需要上传,可以采用循环的方式依次处理每张图片。 总的来说,实现拍照或从相册选取图片上传多张,需要通过Intent调用系统应用获取图片,然后在onActivityResult方法中处理返回的数据,最后进行图片的上传操作。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值