最近,小灵狐得知了一种能够加快修炼速度的绝世秘法,那便是修炼android神功。小灵狐打算用android神功做一个app,今天他的修炼内容就是头像功能。可是小灵狐是个android小白啊,所以修炼过程也是困难重重。下面我们来看看他的修炼过程吧。

控件

小灵狐想要能够拥有头像,那么首先就要有显示头像的地方,也就是控件。首先可以采用ImageView来,但是小灵狐不喜欢用ImageView来,他选择了另一种控件——CircleImageView。这是一种能够将图片圆形化的控件,用法和ImageView是完全一样的,就是会自动把头像转化成圆形的。要使用这个控件,首先要引入第三方库de.hdodenhof:circleimageview:2.2.0,剩下的就和ImageView一样了。比如他在布局中加入了如下的样式:

<de.hdodenhof.circleimageview.CircleImageView
android:id="@+id/civ_my_avatar" // 控件id,唯一表示控件
android:layout_width="100dp" // 设置宽为100dp
android:layout_height="100dp" // 设置高为100dp
android:layout_gravity="center" // 设置在布局中的显示位置为中间
/>

之后小灵狐发现这样子如果没有图片的话,就看不见这个控件,他就想要在控件外围显示一个圈,在这样就算没有头像,那么这个圈也会显示出来,让人一看就知道这里是要显示头像的。于是,他就在上面的代码中加入了以下的设置

app:civ_border_width="1dp"            // 设置边框宽度,默认为0,这样就看不到那个圈了
app:civ_border_overlay="true" // 设置边框是否覆盖在图片上,默认为false
app:civ_border_color="@color/silver" // 设置边框的颜色,默认为黑色,这里用了一种自定义的颜色——银色
app:civ_fill_color="@color/whiteSmoke"// 设置填充底色,默认为透明,这里用了自定义的颜色——白烟色

加上了上面的设置,便可以显示出一个头像的圈圈了。这样,基本的东西就准备好了,但是还是没有图片。如果想要显示某张图片(假设图片为drawable文件夹下的一张名为“fox”的图片),只要再加上下面的设置就好了。

android:src="@drawable/fox"

如果想要了解更多的关于CircleImageView的内容,可以参考

CircleImageView的项目源码链接的项目源码链接

修改头像

现在已经能够成功显示出头像了,但是小灵狐喜欢用头像来表达自己的心情,所以他经常会换头像,这可怎么办呢?不急,在更换头像之前,我们需要能够选择一张用来替换的图片。小灵狐想要点击头像就可以选择系统相册里面的图片并把它设为头像。我们一步步来。

设置头像框监听事件

我们给头像框设置一个监听事件,使得点击它后会调用系统相册。这就要先给头像框设置监听事件了。给控件设置监听事件只要两步就好啦,首先创建一个对应控件类型的对象,并通过findViewById(int id)函数找到控件id将对象与空间进行绑定。然后就可以通过对象来调用setOnClickListener()函数,从而达到监听的效果,代码如下:

// 根据上面指定的控件id创建CircleImageView控件对象
CircleImageView circleImageView = (CircleImageView) findViewById(R.id.civ_my_avatar);
// 调用监听函数
circleImageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
// 相关操作
}
});

调用系统相册

设置好监听事件后,就可以在“相关操作”的地方添加响应事件,这里我们需要的是能够调用系统相册。但是小灵狐发现要调用系统相册,需要先申请SD卡读写权限。申请读写权限主要分为两个步骤:一是在注册文件(AndroidManifest.xml)里声明所需权限,这里需要的是SD卡读写权限,所以应该添加如下声明:

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

WRITE_EXTERNAL_STORAGE权限是同时授予对SD卡读和写的能力。二是在Activity中查看权限,如果没有授予,需要动态申请,代码如下:

if (ContextCompat.checkSelfPermission(InfoSettingActivity.this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) !=
PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(InfoSettingActivity.this, new String[]{
Manifest.permission.WRITE_EXTERNAL_STORAGE}, 1);
} else {
// 调用系统相册
}

对于上面的代码,小灵狐给出了解释:ContexCompat.checkSelfPermission()函数用来查看权限,该函数的第一个参数是一个上下文context,第二个参数就是刚才我们在注册文件中声明的权限,若该函数返回的值等于PackageManager.PERMISSION_GRANTED,说明用户已经授权,我们可以直接进行后续操作。如果不是,就需要动态申请权限,ActivityCompat.requestPermissions()函数用来动态申请权限,并且需要重写onRequestPermissionsResult()函数,重写代码如下:

@Override
// 申请用户权限
public void onRequestPermissionsResult(int requestCode, String[] permissions,
int[] grantResults) {
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.
PERMISSION_GRANTED) {
// 用户允许授权
/* ————调用系统相册————*/
} else {
// 用户拒绝授权
Toast.makeText(this, "You denied the permission",
Toast.LENGTH_SHORT).show();
}
break;
default:
}
}

动态申请权限的时候会弹出一个权限申请框,询问是否授权,对于这样的弹框,用过安卓手机的人应该都不陌生吧。这时候如果用户点击允许,那么便成功获得对SD卡的读写权限,可以开开心心的进行后续操作了,如果被用户拒绝,那么就不能成功的执行后续操作了。

做了这一切,还是没有说怎么调用系统相册,不急,我们继续看看小灵狐是怎么做的吧。其实调用系统相册很简单,只有三句代码,我们把它们用函数openAlbum()进行封装,这样只要把上面出现“调用系统相册”的注释全部改为openAlbum(),就可以成功的执行调用系统相册的操作了。

 private void openAlbum() {
// 使用Intent来跳转
Intent intent = new Intent("android.intent.action.GET_CONTENT");
// setType是设置类型,只要系统中满足该类型的应用都会被调用,这里需要的是图片
intent.setType("image/*");
// 打开满足条件的程序,CHOOSE_PHOTO是一个常量,用于后续进行判断,下面会说
startActivityForResult(intent, CHOOSE_PHOTO);
}

选择图片

我们知道开启一个活动直接用startActivity()就可以,但上面使用startActivityForResult()来开启活动而不直接用startActivity(),这是为什么呢?注意到多了'forResult',顾名思义,不难想象出原因。是因为我们不仅仅是要打开系统相册,更重要的是能够在系统相册中选择一张图片并且返回图片的url,所以要'forResult'。为了这个'result',我们需要重写onActivityResult()方法:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
switch (requestCode) {
// 上面的CHOOSE_PHOTO就是在这里用于判断
case CHOOSE_PHOTO:
// 判断手机系统版本号
if (resultCode == RESULT_OK) {
if (Build.VERSION.SDK_INT >= 19) {
// 手机系统在4.4及以上的才能使用这个方法处理图片
handleImageOnKitKat(data);
} else {
// 手机系统在4.4以下的使用这个方法处理图片
handleImageBeforeKitKat(data);
}
}
break;
default:
break;
}
}

上面的代码还是很好理解的,但是可能会感到奇怪,为什么要判断Build.VERSION.SDK_INT的值呢?自习观察两个函数的命名,差了一个before,所以大概能猜出点什么了吧。没错,这两个函数都是用来处理图片的,那他们的区别是什么呢?Android系统从4.4版本开始,选取相册中的图片不再返回图片真实的Uri了,而是一个分装过的Uri,因此如果是4.4版本以上的系统要对这个Uri进行解析才行。handleImageOnKitKat()函数包含了对Uri的解析,4.4版本以后的就要用这个函数,而4.4版本之前的就要用handleImageBeforeKitKat()函数了,现在明白了before是指什么了吧。因为要区分手机系统版本,所以我们加了对Build.VERSION.SDK_INT的判断。好了,现在我们明白了要用到这两个函数来处理图片,可是这两个函数到底是什么啊,我们继续往下看。

@TargetApi(19)
private void handleImageOnKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = null;
// 如果是document类型的Uri,,则通过document id处理
if (DocumentsContract.isDocumentUri(this, uri)) {
String docId = DocumentsContract.getDocumentId(uri);
if ("com.android.providers.media.documents".equals(uri.getAuthority())) {
// 解析出数字格式的id
String id = docId.split(":")[1];
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 ("file".equalsIgnoreCase(uri.getScheme())) {
// 如果是file类型的Uri,直接获取图片路径
imagePath = uri.getPath();
} else if ("content".equalsIgnoreCase(uri.getScheme())) {
// 如果是content类型的Uri,使用普通方式处理
imagePath = getImagePath(uri, null);
} // 根据得到的图片路径显示图片
} private void handleImageBeforeKitKat(Intent data) {
Uri uri = data.getData();
String imagePath = getImagePath(uri, null); // 根据得到的图片路径显示图片
}

从上面的代码我们也可以看出这两个函数的差别。

显示图片

现在我们就差最后一步了,就是把图片显示出来。安卓中常用的显示图片的函数有两个setImageResource()setImageBitmap() ,前者的参数只能是 int id ,也就是说它只能显示在drawable文件夹下的图片,但是我们要从相册里面获得,显然要用后者,我们可以把显示图片的过程分装成一个函数:

private void displayImage(String imagePath){
if(imagePath!=null){
Bitmap bitmap= BitmapFactory.decodeFile(imagePath);
circleImageView.setImageBitmap(bitmap);
}else{
Toast.makeText(this,"failed to get image",Toast.LENGTH_SHORT).show();
}
}

做到这里,我们就完成了用户头像的功能啦。小灵狐迫不及待的从相册中随便找了一张表情包试用,成功的把表情包显示在了头像框内。但是,事情真的就这么简单吗?太天真了。

图片压缩

小灵狐实现了用户头像,非常开心。赶紧拿出手机,拍了一张高清图片,准备作为自己的头像。但是,当他选择该图片之后,却惊讶的发现竟然没有显示出来,而且程序还会崩溃,这可把小灵狐急坏了。经过研究学习,他发现,原来是图片太大了,直接加载进去,会导致内存泄漏,从而使得图片无法正常显示,甚至是程序奔溃。所以在显示图片之前,要经过一定的压缩,然后再加载进去。Bitmap压缩都是围绕这个来做文章:Bitmap所占用的内存 = 图片长度 x 图片宽度 x 一个像素点占用的字节数。3个参数,任意减少一个的值,就达到了压缩的效果。所以,根据这个,小灵狐修改了displayImage() 函数,采用了如下的压缩方式:

//显示图片
private void displayImage(String imagePath){
if(imagePath!=null){
BitmapFactory.Options options = new BitmapFactory.Options();// 解析位图的附加条件
options.inJustDecodeBounds = true; // 不去解析位图,只获取位图头文件信息
Bitmap bitmap= BitmapFactory.decodeFile(imagePath,options);
circleImageView.setImageBitmap(bitmap);
int btwidth = options.outWidth; // 获取图片的宽度
int btheight = options.outHeight; //获取图片的高度 int dx = btwidth/100; // 获取水平方向的缩放比例
int dy = btheight/100; // 获取垂直方向的缩放比例 int sampleSize = 1; // 设置默认缩放比例 // 如果是水平方向
if (dx>dy&&dy>1) {
sampleSize = dx;
} //如果是垂直方向
if (dy>dx&&dx>1) {
sampleSize = dy;
}
options.inSampleSize = sampleSize; // 设置图片缩放比例
options.inJustDecodeBounds = false; // 真正解析位图
// 把图片的解析条件options在创建的时候带上
bitmap = BitmapFactory.decodeFile(imagePath, options);
circleImageView.setImageBitmap(bitmap); // 设置图片
}else{
Toast.makeText(this,"failed to get image",Toast.LENGTH_SHORT).show();
}
}

关于图片压缩的方式网上有很多种方式方法,其实内容和原理都是大同小异,如果想要了解更多的方法,这里有几个参考链接可以看一看:

bitmap的六种压缩方式,Android图片压缩

Android应用开发中三种常见的图片压缩方法

Android图片压缩(质量压缩和尺寸压缩)&Bitmap转成字符串上传

黑科技

小灵狐学习了图片的压缩,也自己写了一种压缩方式,但是他发现到底要把图片压缩到什么程度很难把握,而且不同的屏幕,不同的手机效果也不同。有没有其他的方法来加载图片,能够有效的避免因图片太大而导致内存泄漏的问题呢?经过不断地探索发现,小灵狐发现了一个很好用的加载图片方式——glide。这是一种神奇的黑科技,关于它的使用方法,可以参考以下博客,讲的非常清楚,非常好,我就不再啰嗦啦。

Glide的基本用法

从源码的角度理解Glide的执行流程

总结

到此,用户头像功能算是全部实现了。小灵狐从零开始,一步步摸索出来,经历了很多的困难,当然也学到了很多。现在,小灵狐终于可以把自己的高清图片作为自己的头像啦。这部分的修炼暂告一段落,敬请期待以后的修炼。

android开发——用户头像的更多相关文章

  1. Android开发--用户定位服务--UserLocation

    Android开发--用户定位服务--UserLocation 2013-01-28 08:32:26     我来说两句      作者:BruceZhang 收藏    我要投稿 [java] & ...

  2. Android开发——用户在屏幕上的手势识别

    个定点决定.四个属性分别为left(1),top(2),right(3),bottom(4). 数字为图上标出的距离.显然这四个属性是相对于父容器来定的,均可以通过get()方法获取. 因此很容易得出 ...

  3. Android开发周报:Android L默认加密用户数据

    Android开发周报:Android L默认加密用户数据 新闻 <iCloud前车之鉴,Android L默认开启加密功能>:iCloud 艳照风波再起,第二波女星照片流出,大量女星的裸 ...

  4. Android开发技巧——高亮的用户操作指南

    Android开发技巧--高亮的用户操作指南 2015-12-15补记: 发现使用PopupWindow进行遮罩层的显示,在华为P7上会有问题.具体表现为:画出来的高亮部分会偏下.原因为:通过view ...

  5. h5开发中,利用微信或者QQ登陆以后获取用户头像在canvas画布显示问题

    在实际开发上先的h5页面产品中,总会遇到各种坑,好多坑都是安卓和iPhone端兼容的问题(用电脑谷歌浏览器输入  chrome://inspect/#devices可以用手机USB调试,打开) eg: ...

  6. Android开发之制作圆形头像自定义View,直接引用工具类,加快开发速度。带有源代码学习

    作者:程序员小冰,CSDN博客:http://blog.csdn.net/qq_21376985 QQ986945193 博客园主页:http://www.cnblogs.com/mcxiaobing ...

  7. 05 . Go+Vue开发一个线上外卖应用(Session集成及修改用户头像到Fastdfs)

    用户头像上传 功能介绍 在用户中心中,允许用户更换自己的头像.因此,我们开发上传一张图片到服务器,并保存成为用户的头像. 接口解析 在用户模块的控制器MemberController中,解析头像上传的 ...

  8. Android开发优化之——对Bitmap的内存优化

    http://blog.csdn.net/arui319/article/details/7953690 在Android应用里,最耗费内存的就是图片资源.而且在Android系统中,读取位图Bitm ...

  9. Android开发周报:Flyme OS开源、经典开源项目解析

    Android开发周报:Flyme OS开源.经典开源项目解析 新闻 <魅族Flyme OS源码上线Github> :近日魅族正式发布了MX5,并且在发布会上,魅族还宣布Flyme OS开 ...

随机推荐

  1. Tomcat 8005/8009/8080/8443端口的作用

    --关闭tomcat进程所用.当执行shutdown.sh关闭tomcat时就是连接8005端口执行“SHUTDOWN”命令--由此,我们直接telnet8005端口执行“SHUTDOWN”(要大写, ...

  2. webpack4 系列教程(十五):开发模式与webpack-dev-server

    作者按:因为教程所示图片使用的是 github 仓库图片,网速过慢的朋友请移步<webpack4 系列教程(十五):开发模式与 webpack-dev-server>原文地址.更欢迎来我的 ...

  3. git入门 创建版本库, 版本管理 分支 标签

    参考: https://www.liaoxuefeng.com/wiki/0013739516305929606dd18361248578c67b8067c8c017b000 GIT最流行的分布式版本 ...

  4. Javascript动态引用CSS文件的2种方法介绍

    最近做一个项目,需要javascript动态插入样式,结果以前的方法失效了!查了2个小时的原因竟然是自己手贱,这个最后再说! javascript插入样式在前端开发中应用比较广泛,特别是在修改前端表现 ...

  5. Angular6+ng-zorro实现登录页面

    一.效果图 二.html代码 <div class="login-container"> <div class="login-box"> ...

  6. react-conponent-todo

    <!DOCTYPE html> <html> <head> <script src="../../build/react.js">& ...

  7. DEM山体阴影原理以及算法具体解释

    山体阴影原理以及算法具体解释 山体阴影基本原理: 山体阴影是假想一个光源在某个方向和某个太阳高度的模拟下.用过临近像元的计算来生成一副0-255的灰度图. 一.山体阴影的主要參数: 1.  太阳光线的 ...

  8. 28.Odoo产品分析 (四) – 工具板块(1) – 项目(1)

    查看Odoo产品分析系列--目录 "项目管理"是一个用于管理你的项目,且将它们与其他应用关联起来的非常灵活的模块,他允许您的公司管理项目阶段,分配团队,甚至跟踪与项目相关的时间和工 ...

  9. 直接通过Binder的onTransact完成跨进程通信

    1.具体代码: 服务端实现: public class IPCService extends Service { private static final String DESCRIPTOR = &q ...

  10. WPF窗体程序入口 自定义窗体启动页面

    一张图体现一切: