Android默认头像那些事儿

Posted 冷不冷

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android默认头像那些事儿相关的知识,希望对你有一定的参考价值。

android应用市场中几乎所有APP中都会涉及用户体系.当然也就需要页面去处理用户信息的展示、用户头像的展示等.对于用户头像展示,有很多优秀的图片加载框架,平常写项目会经常使用到这些图片加载框架,使用起来也很方便,效率也比较高.但是面对不同的需求时,处理的方式可能就有些不同.下面分析几种需求.

文章使用Glide框架, 当然其他的图片加载框架处理方式应该相似,就不多做介绍.简单介绍下Glide的集成配置

首先配置一下Glide.在build.grade中添加对Gilde的引用及网络框架的引用

compile 'com.github.bumptech.glide:glide:3.7.0'
//OkHttp 3.x
compile 'com.github.bumptech.glide:okhttp3-integration:1.4.0@aar'
compile 'com.squareup.okhttp3:okhttp:3.2.0’

然后配置一下权限

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

接着就可以使用Glide了.


分析

1) 第一种需求

设置一个默认的头像图片显示,这种需求是最常见,也是最容易处理的.
用Glide一句代码就可以实现,如下

Glide.with(Activity).load("头像地址URL").placeholder(R.mipmap.xxxx).dontAnimate().into(ImageView);

with中的参数可以是Activity,fragment,context.
placeholder中的参数就是项目中默认图片的资源.
dontAnimate方法是取消动画效果
into中的参数当然就是需要展示图像的ImageView

2) 第二种需求

设置一个圆形(圆角)的默认头像图片显示,这种需求也比较常见,唯一的问题就是圆形(圆角)的图片展示.对于圆形图片展示,因为用到Glide所以这里想到最简便的解决方法就是找一个继承自ImageView的自定义控件.有很多,文章选用CircleImageView来进行演示,有兴趣的童鞋可以深入看一下实现.
github地址https://github.com/hdodenhof/CircleImageView

那面对这种需求,就可以这样写

Glide.with(Activity).load("头像地址URL")
        .placeholder(R.mipmap.xxxx).dontAn imate().into(CircleImageView);

into中参数为圆形自定义控件.

3) 第三种需求

设置一个带有随机背景颜色和文字的默认图片展示,这种需求往往出现在通讯录,好友等场景下,文字的目的是为了让用户更快的找到自己想找的目标(当然,我也是最近写项目,产品提的这种需求),随机背景颜色是为了更加美观(我认为的- -~).
好了,面对这种需求,得先考虑一下怎么去做,如果去适应Glide,那就看一下placeholder方法,可以看到Glide提供了两种方法,如下

/**
 * @inheritDoc
 */
@Override
public DrawableRequestBuilder<ModelType> placeholder(int resourceId) 
    super.placeholder(resourceId);
    return this;


/**
 * @inheritDoc
 */
@Override
public DrawableRequestBuilder<ModelType> placeholder(Drawable drawable) 
    super.placeholder(drawable);
    return this;

第一个方法是之前用到的,参数是一个默认的资源文件,第二个重载方法的参数是一个Drawable,显然文字是变化的,资源文件并不合适,只能在第二个重载方法上考虑一下.怎么做呢?其实很简单,可以通过安卓提供的方法创建一个带有文字和背景颜色的Drawable对象.贴一下完整代码,比较简单

package com.angent.defaultavatardemo.utils;

import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

/**
 * Created by junweiliu on 17/1/18.
 */
public class MakeRandomPhoto 
    /**
     * 默认字体大小
     */
    private final int DEFAULT_FONT_SIZE = 20;
    /**
     * 默认字体大小
     */
    private final int DEFAULT_FONT_COLOR = Color.WHITE;
    /**
     * 默认的图片宽
     */
    private final int DEFAULT_WIDTH = 60;
    /**
     * 默认的图片高
     */
    private final int DEFAULT_HEIGHT = 60;
    /**
     * 默认显示的文字数目
     */
    private final int DEFAULT_SHOW_NUM = 2;
    /**
     * 默认的图片颜色
     */
    private final int DEFAULT_COLOR = Color.BLUE;
    /**
     * 图片宽高
     */
    private int width = DEFAULT_WIDTH, height = DEFAULT_HEIGHT;
    /**
     * 图片颜色
     */
    private int color = DEFAULT_COLOR;
    /**
     * 文字大小
     */
    private int fontSize = DEFAULT_FONT_SIZE;
    /**
     * 文字颜色
     */
    private int fontColor = DEFAULT_FONT_COLOR;
    /**
     * 默认显示的文字数
     */
    private int showNum = DEFAULT_SHOW_NUM;
    /**
     * 画笔
     */
    private Paint mPaint;
    /**
     * 画笔属性
     */
    private Paint.FontMetrics fm;
    /**
     * 单例
     */
    public static MakeRandomPhoto instance;


    /**
     * 获取单例
     *
     * @return
     */
    public static MakeRandomPhoto getInstance() 
        if (instance == null) 
            synchronized (MakeRandomPhoto.class) 
                if (instance == null) 
                    instance = new MakeRandomPhoto();
                
            
        
        return instance;
    

    /**
     * 设置图片宽
     *
     * @return
     */
    public MakeRandomPhoto setWidth(int width) 
        if (0 == width) 
            this.width = DEFAULT_WIDTH;
         else 
            this.width = width;
        
        return instance;
    

    /**
     * 设置图片背景颜色
     *
     * @return
     */
    public MakeRandomPhoto setBackGroudColor(int color) 
        if (0 == color) 
            this.color = DEFAULT_COLOR;
         else 
            this.color = color;
        
        return this;
    

    /**
     * 设置图片高
     *
     * @return
     */
    public MakeRandomPhoto setHeight(int height) 
        if (0 == height) 
            this.height = DEFAULT_HEIGHT;
         else 
            this.height = height;
        
        return this;
    

    /**
     * 设置文字颜色
     *
     * @return
     */
    public MakeRandomPhoto setTxtColor(int fontcolor) 
        if (0 == fontcolor) 
            this.fontColor = DEFAULT_FONT_COLOR;
         else 
            this.fontColor = fontcolor;
        
        return this;
    

    /**
     * 设置文字大小
     *
     * @return
     */
    public MakeRandomPhoto setTxtSize(int fontsize) 
        if (0 == fontsize) 
            this.fontSize = DEFAULT_FONT_SIZE;
         else 
            this.fontSize = fontsize;
        
        return this;
    

    /**
     * 设置显示文字个数
     *
     * @return
     */
    public MakeRandomPhoto setShowNum(int showNum) 
        if (0 == showNum) 
            this.showNum = DEFAULT_SHOW_NUM;
         else 
            this.showNum = showNum;
        
        return this;
    

    /**
     * 创建随机图片
     *
     * @return
     */
    public Bitmap makeRandomPhotoBp(String txt) 
        if (null == txt || "".equals(txt)) 
            txt = "    ";
        
        // 处理展示的文字
        if (txt.length() > showNum) 
            txt = txt.substring(txt.length() - showNum);
        
        // 这里使用RGB_565,系统开销小一点
        Bitmap bp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
        Canvas c = new Canvas(bp);
        mPaint = new Paint();
        mPaint.setColor(fontColor);
        mPaint.setTextSize(fontSize);
        mPaint.setTextAlign(Paint.Align.CENTER);
        mPaint.setAntiAlias(true);
        fm = mPaint.getFontMetrics();
        // 设置背景颜色
        c.drawColor(ColorUtils.getRandomColor(txt.hashCode()));
        // 居中显示
        c.drawText(txt, width / 2, height / 2 - fm.descent + (fm.descent - fm.ascent) / 2, mPaint);
        c.save(Canvas.ALL_SAVE_FLAG);//保存
        c.restore();//
        return bp;
    


    /**
     * 创建随机图片(Drawable)
     *
     * @return
     */
    public Drawable makeRandomPhotoDrawable(String txt) 
        Drawable db = new BitmapDrawable(makeRandomPhotoBp(txt));
        return db;
    

    /**
     * 创建群组图像
     *
     * @param leftTopBp
     * @param rightTopBp
     * @param leftBottomBp
     * @param rightBottomBp
     * @return
     */
    public Bitmap makeGroupPhotoBp(Bitmap leftTopBp, Bitmap rightTopBp, Bitmap leftBottomBp, Bitmap rightBottomBp) 
        Bitmap bp = Bitmap.createBitmap(width * 2, height * 2, Bitmap.Config.RGB_565);
        Canvas c = new Canvas(bp);
        Paint mPaint = new Paint();
        // 绘制左上角
        c.drawBitmap(leftTopBp, 0, 0, mPaint);
        // 绘制右上角
        c.drawBitmap(rightTopBp, width, 0, mPaint);
        // 绘制左下角
        c.drawBitmap(leftBottomBp, 0, height, mPaint);
        // 绘制右下角
        c.drawBitmap(rightBottomBp, width, height, mPaint);
        c.save(Canvas.ALL_SAVE_FLAG);//保存
        c.restore();
        return bp;
    

    /**
     * 创建群组图片(Drawable)
     *
     * @return
     */
    public Drawable makeGroupPhotoDrawable(Bitmap leftTopBp, Bitmap rightTopBp, Bitmap leftBottomBp, Bitmap rightBottomBp) 
        Drawable db = new BitmapDrawable(makeGroupPhotoBp(leftTopBp, rightTopBp, leftBottomBp, rightBottomBp));
        return db;
    

使用方法,例如在适配器中

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) 
        viewHolder = null;
        UserBean bean = mDatas.get(position);
        if (convertView == null) 
            convertView = mInflater.from(mContext).inflate(R.layout.item_user, null);
            viewHolder = new ViewHolder();
            viewHolder.userNameTv = (TextView) convertView.findViewById(R.id.tv_user_name);
            viewHolder.userPhotoIv = (ImageView) convertView.findViewById(R.id.ci_user_photo);
            viewHolder.userPhotoTv = (TextView) convertView.findViewById(R.id.tv_user_photo);
            convertView.setTag(viewHolder);
         else 
            viewHolder = (ViewHolder) convertView.getTag();
        
        viewHolder.userNameTv.setText(bean.getUserName());
        // Glide加载
        Glide.with(mContext).load(bean.getUserPhoto())
                .placeholder(MakeRandomPhoto.getInstance().setWidth(48).setHeight(48).setTxtSize(20).setTxtColor(Color.parseColor("#ffffff")).setShowNum(2).makeRandomPhotoDrawable(bean.getUserName())).dontAnimate().into(viewHolder.userPhotoIv);
        return convertView;
    

封装了一个制作默认头像的工具类,提供方法设置相关属性,每个设置方法都会返回对象本身是为了方便连缀使用,看一下核心方法

/**
 * 创建随机图片
 *
 * @return
 */
public Bitmap makeRandomPhotoBp(String txt) 
    if (null == txt || "".equals(txt)) 
        txt = "    ";
    
    if (txt.length() > showNum) 
        txt = txt.substring(txt.length() - showNum);
    
    // 这里使用RGB_565,系统开销小一点
    Bitmap bp = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565);
    Canvas c = new Canvas(bp);
    mPaint = new Paint();
    mPaint.setColor(fontColor);
    mPaint.setTextSize(fontSize);
    mPaint.setTextAlign(Paint.Align.CENTER);
    mPaint.setAntiAlias(true);
    fm = mPaint.getFontMetrics();
    // 设置背景颜色
    c.drawColor(ColorUtils.getRandomColor(txt.hashCode()));
    // 居中显示
    c.drawText(txt, width / 2, height / 2 - fm.descent + (fm.descent - fm.ascent) / 2, mPaint);
    c.save(Canvas.ALL_SAVE_FLAG);//保存
    c.restore();//
    return bp;

首先对传入的文字进行处理,截取出需要显示的文字,默认显示字符串的最后两个字符,当然也可以自己设置.然后创建了一个Bitmap,这里使用RGB_565而不是ARGB_8888,是因为RGB_565的开销要小于ARGB_8888,一个背景色对画质的要求不是很高.最后就是创建画布,在画布中去完成背景色和文字的绘制.有一个ColorUtils,这个是封装的一个随机颜色工具,一起看一下.

package com.angent.defaultavatardemo.utils;

import android.graphics.Color;
import android.support.annotation.IntRange;

import com.angent.defaultavatardemo.R;

/**
 * Created by junweiliu on 17/1/18.
 */
public class ColorUtils 
    /**
     * 随机颜色起始
     */
    private static final int RANDOM_COLOR_START_RANGE = 0;
    /**
     * 随机颜色终止
     */
    private static final int RANDOM_COLOR_END_RANGE = 7;


    /**
     * 获取随机color名称
     *
     * @param colorPosition 唯一值,这里用string的hashcode
     * @return
     */
    public static String getRandomColorName(@IntRange(from = RANDOM_COLOR_START_RANGE, to = RANDOM_COLOR_END_RANGE)
                                            int colorPosition) 
        colorPosition = Math.abs(colorPosition) % RANDOM_COLOR_END_RANGE;
        return String.format("random_color_%d", colorPosition + 1);
    

    /**
     * 获取随机color颜色
     *
     * @param colorPosition 唯一值,这里用string的hashcode
     * @return
     */
    public static int getRandomColor(@IntRange(from = RANDOM_COLOR_START_RANGE, to = RANDOM_COLOR_END_RANGE)
                                     int colorPosition) 
        String colorNmae = getRandomColorName(colorPosition);
        int resId = Color.parseColor("#8a8a8a");
        if (colorNmae.contains("1")) 
            resId = Color.parseColor("#8a8a8a");
         else if (colorNmae.contains("2")) 
            resId = Color.parseColor("#f7b54e");
         else if (colorNmae.contains("3")) 
            resId = Color.parseColor("#17c295");
         else if (colorNmae.contains("4")) 
            resId = Color.parseColor("#4da9eb");
         else if (colorNmae.contains("5")) 
            resId = Color.parseColor("#b38979");
         else if (colorNmae.contains("6")) 
            resId = Color.parseColor("#568aad");
         else if (colorNmae.contains("7")) 
            resId = Color.parseColor("#f2725e");
         else 
            resId = Color.parseColor("#8a8a8a");
        
        return resId;
    

    /**
     * 获取color资源
     *
     * @param colorPosition 唯一值,这里用string的hashcode
     * @return
     */
    public static int getCircleColorBgId(@IntRange(from = RANDOM_COLOR_START_RANGE, to = RANDOM_COLOR_END_RANGE)
                                         int colorPosition) 
        String colorNmae = getRandomColorName(colorPosition);
        int resId = R.mipmap.random_color_one;
        if (colorNmae.contains("1")) 
            resId = R.mipmap.random_color_one;
         else if (colorNmae.contains("2")) 
            resId = R.mipmap.random_color_two;
         else if (colorNmae.contains("3")) 
            resId = R.mipmap.random_color_three;
         else if (colorNmae.contains("4")) 
            resId = R.mipmap.random_color_four;
         else if (colorNmae.contains("5")) 
            resId = R.mipmap.random_color_five;
         else if (colorNmae.contains("6")) 
            resId = R.mipmap.random_color_six;
         else if (colorNmae.contains("7")) 
            resId = R.mipmap.random_color_seven;
         else 
            resId = R.mipmap.random_color_one;
        
        return resId;
    

ColorUtils的主要作用就是通过字符串的hashcode值,来生成一个随机的颜色值,当然相同字符串的hashcode值是相同的,这样就保证了每个文字对应一个相同的随机颜色.
ColorUtils中还提供了一个获取随机背景图片的方法,这个方法是干嘛用的呢?
这个就是为了第二种方案写的,如果不依赖Glide,不使用placeholder方法.怎么实现这种随机背景颜色上带有文字的默认头像呢,看一下布局文件,可能就懂了.

<?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:orientation="horizontal">
    <TextView
            android:id="@+id/tv_user_photo"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_centerVertical="true"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_marginTop="10dp"
            android:gravity="center"
            android:textColor="#ffffff"
            android:textSize="20sp"
            android:visibility="visible"
    />

    <com.angent.defaultavatardemo.widget.CircleImageView
            android:id="@+id/ci_user_photo"
            android:layout_width="48dp"
            android:layout_height="48dp"
            android:layout_centerVertical="true"
            android:layout_marginBottom="10dp"
            android:layout_marginLeft="16dp"
            android:layout_marginRight="16dp"
            android:layout_marginTop="10dp"
            android:visibility="visible"/>


    <TextView
            android:id="@+id/tv_user_name"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_toRightOf="@+id/ci_user_photo"
            android:text="名字"
            android:textColor="#333333"
            android:textSize="16sp"/>

</RelativeLayout>

在CircleImageView下边有一个TextView,位置刚好和CircleImageView重合,并且在下层.再看一下代码中的写法

    @Override
    public View getView(final int position, View convertView, ViewGroup parent) 
        ...
        viewHolder.userNameTv.setText(bean.getUserName());
        // 设置随机颜色背景
        viewHolder.userPhotoTv.setBackgroundResource(ColorUtils.getCircleColorBgId(bean.getUserName().hashCode()));
        // 设置文字
        viewHolder.userPhotoTv.setText(bean.getUserName().substring(bean.getUserName().length() - 2));
        // 加载图像
        Glide.with(mContext).load(bean.getUserPhoto()).dontAnimate().into(viewHolder.userPhotoIv);
        return convertView;
    

应该很简单,这种方案的思路就是,如果Glide加载图片成功了,就把地下的TextView遮住,如果没有加载成功,那么下层的TextView就作为默认头像显示.

应该还有其他更好的方案,这里就提供这两种.测试了一下两种方案的性能,基本没有差别.推荐第二种方案,显示时比较清晰

最后看一下效果图吧


源码地址

源码下载

以上是关于Android默认头像那些事儿的主要内容,如果未能解决你的问题,请参考以下文章

BBS那些事儿

详解 ViewModel 的那些事儿

详解 ViewModel 的那些事儿

写作那些事儿:多平台同步

android环境搭建中的那些事儿

Android Handler那些事儿,消息屏障?IdelHandler?ANR?