googlesamples/android-topeka学习笔记(二)-----圆形Avatar研究

本文探讨了如何在Android中实现圆形头像,通过分析谷歌的示例项目googlesamples/android-topeka,讲解了AvaterView的工作原理,包括利用invalidate()刷新视图,设置ClipToOutline以及自定义轮廓提供者RoundOutlineProvider。同时提到了Android L的Clipping Views特性,并对比了使用BitmapDrawable和RoundedBitmapDrawable创建圆形图片的方法。

项目中有用到圆形头像选择,我们看看google是怎样实现的吧!
这里写图片描述

左边是点击前,右边点击后会显示一个圆弧.
widget下有个 AvaterView

/**
 * A simple view that wraps an avatar.
 */
public class AvatarView extends ImageView implements Checkable {

    private boolean mChecked;

    public AvatarView(Context context) {
        this(context, null);
    }

    public AvatarView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public AvatarView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    public void setChecked(boolean b) {
        mChecked = b;
        invalidate();

    }

    @Override
    public boolean isChecked() {
        return mChecked;
    }

    @Override
    public void toggle() {
        setChecked(!mChecked);
    }

    /**
     * Set the image for this avatar. Will be used to create a round version of this avatar.
     *
     * @param resId The image's resource id.
     */
    @SuppressLint("NewApi")
    public void setAvatar(@DrawableRes int resId) {
        if (ApiLevelHelper.isAtLeast(Build.VERSION_CODES.LOLLIPOP)) {
            setClipToOutline(true);
            setImageResource(resId);
        } else {
            setAvatarPreLollipop(resId);
        }
    }

    private void setAvatarPreLollipop(@DrawableRes int resId) {
        Drawable drawable = ResourcesCompat.getDrawable(getResources(), resId,
                getContext().getTheme());
        BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
        @SuppressWarnings("ConstantConditions")
        RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(getResources(),
                bitmapDrawable.getBitmap());
        roundedDrawable.setCircular(true);
        setImageDrawable(roundedDrawable);
    }

    @Override
    protected void onDraw(@NonNull Canvas canvas) {
        super.onDraw(canvas);
        if (mChecked) {
            Drawable border = ContextCompat.getDrawable(getContext(), R.drawable.selector_avatar);
            border.setBounds(0, 0, getWidth(), getHeight());
            border.draw(canvas);
        }
    }

    @Override
    @SuppressLint("NewApi")
    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
        super.onSizeChanged(w, h, oldw, oldh);
        if (ApiLevelHelper.isLowerThan(Build.VERSION_CODES.LOLLIPOP)) {
            return;
        }
        if (w > 0 && h > 0) {
            setOutlineProvider(new RoundOutlineProvider(Math.min(w, h)));
        }
    }
}

资源文件selecter_avatar.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"
       android:shape="oval">
    <stroke
            android:width="@dimen/stroke_width_avatar"
            android:color="@color/topeka_primary" />
    <solid android:color="@android:color/transparent" />
</shape>

分为:Lollipop和pre Lollipop这个是用一个小工具类实现api的:helper/ApiLevelHelper.java

/**
 * Encapsulates checking api levels.
 */
public class ApiLevelHelper {

    private ApiLevelHelper() {
        //no instance
    }

    /**
     * Checks if the current api level is at least the provided value.
     *
     * @param apiLevel One of the values within {@link Build.VERSION_CODES}.
     * @return <code>true</code> if the calling version is at least <code>apiLevel</code>.
     * Else <code>false</code> is returned.
     */
    public static boolean isAtLeast(int apiLevel) {
        return Build.VERSION.SDK_INT >= apiLevel;
    }

    /**
     * Checks if the current api level is at lower than the provided value.
     *
     * @param apiLevel One of the values within {@link Build.VERSION_CODES}.
     * @return <code>true</code> if the calling version is lower than <code>apiLevel</code>.
     * Else <code>false</code> is returned.
     */
    public static boolean isLowerThan(int apiLevel) {
        return Build.VERSION.SDK_INT < apiLevel;
    }
}

现在分析过程:
用isChecked();来标记是否画圆弧.
invalidate()
是用来刷新View的,必须是在UI线程中进行工作。比如在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。
invalidate()后系统会自动调用view的 onDraw()方法,(为裁减好的图像装一个蓝色的外环)

这里写图片描述

这里用到 Android L的 Clipping Views(裁剪视图)和自定义图轮廓
只有几句代码:
setClipToOutline(true);
setImageResource(resId);

setOutlineProvider(new RoundOutlineProvider(Math.min(w, h)));

再跳到自定义widget/outlineprovider/RoundOutlineProvider.java

/**
 * Creates round outlines for views.
 */
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
public class RoundOutlineProvider extends ViewOutlineProvider {

    private final int mSize;

    public RoundOutlineProvider(int size) {
        if (0 > size) {
            throw new IllegalArgumentException("size needs to be > 0. Actually was " + size);
        }
        mSize = size;
    }

    @Override
    public final void getOutline(View view, Outline outline) {
        outline.setOval(0, 0, mSize, mSize);
    }

}

典型的自定义视图轮廓:

在你的代码中自定义视图的轮廓:
1. 继承ViewOutlineProvider类
2. 重写getOutline() getOutline()方法
3. 使用View.setOutlineProvider()方法分配新的轮廓给你的视图

注意

  • ContextCompat.getDrawable(getContext(), R.drawable.selector_avatar);

ResourcesCompat.getDrawable(getResources(), resId,
getContext().getTheme());
直接调用文件好么?根本不是什么构造函数的时候传context

  • BitmapDrawable bitmapDrawable = (BitmapDrawable) drawable;
    BitmapDrawable是什么?
    点击进源码:

    A Drawable that wraps a bitmap and can be tiled, stretched, or aligned. You can create a
    BitmapDrawable from a file path, an input stream, through XML inflation, or from a object.

功能:显示缩略图,大小为40*40

        //通过openRawResource获取一个inputStream对象  
        InputStream inputStream = getResources().openRawResource(R.drawable.test);  
        //通过一个InputStream创建一个BitmapDrawable对象  
        BitmapDrawable drawable = new BitmapDrawable(inputStream);  
        //通过BitmapDrawable对象获得Bitmap对象  
        Bitmap bitmap = drawable.getBitmap();  
        //利用Bitmap对象创建缩略图  
        bitmap = ThumbnailUtils.extractThumbnail(bitmap, 40, 40);  
        //imageView 显示缩略图的ImageView  
        imageView.setImageBitmap(bitmap);
//功能:image2从image1中截取120*120大小后显示,截取起始坐标为(x,y)

BitmapDrawable bitmapDrawable = (BitmapDrawable) image1.getDrawable();
//获取第一个图片显示框中的位图
Bitmap bitmap = bitmapDrawable.getBitmap();
//显示图片的指定区域
image2.setImageBitmap(Bitmap.createBitmap(bitmap, x, y, 120, 120));
image2.setAlpha(alpha);
  • RoundedBitmapDrawable —实现圆角和圆形
    但是在I/O大会之后,Google发布了新的Support lib,其中有一个是RoundedBitmapDrawable类,通过这个类可以很容易实现圆角和圆形图片。

1.首先需要添加support-v4依赖

在build.gralde的dependencies中添加下面代码:

dependencies {
    //...其他依赖
    compile 'com.android.support:support-v4:21.+'
    compile 'com.android.support:appcompat-v7:21.+'
    compile 'com.android.support:support-v4:21.+'
}

2.写代码
stackoverflow/how-to-use-roundedbitmapdrawable
关键用法:
圆形图:

  RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(getResources(),
                bitmapDrawable.getBitmap());
        roundedDrawable.setCircular(true);

圆角图

     RoundedBitmapDrawable roundedDrawable = RoundedBitmapDrawableFactory.create(getResources(),
                bitmapDrawable.getBitmap());
        roundedDrawable.setCircular(true);

补充一个带图的:[Material Design]使用RoundedBitmapDrawable快速生成圆角和圆形图片

可生成圆形、方形、及方形的组合头像。项目地址:https://github.com/Pedroafa/avatar-android 效果图:如何使用:首先创建个ImageView<ImageView             android:id="@ id/roundedAvatar"             android:layout_height="fill_parent"             android:layout_width="fill_parent"/>2. //通过AvatarDrawableFactory生成各种形状的Drawable AvatarDrawableFactory avatarDrawableFactory = new AvatarDrawableFactory(getResources()); BitmapFactory.Options options = new BitmapFactory.Options(); options.inMutable = false; Bitmap avatar = BitmapFactory.decodeResource(getResources(), R.drawable.avatar, options); //圆形的 Drawable roundedAvatarDrawable = avatarDrawableFactory.getRoundedAvatarDrawable(avatar);//生成圆形的Drawable ImageView roundedAvatarView = (ImageView)rootView.findViewById(R.id.roundedAvatar); roundedAvatarView.setImageDrawable(roundedAvatarDrawable);其他形状的://圆形的且带边框的 Drawable borderedRoundedAvatarDrawable = avatarDrawableFactory.getBorderedRoundedAvatarDrawable(avatar); ImageView borderedRoundedAvatarView = (ImageView)rootView.findViewById(R.id.borderedRoundedAvatar); borderedRoundedAvatarView.setImageDrawable(borderedRoundedAvatarDrawable); //方形的 Drawable squaredAvatarDrawable = avatarDrawableFactory.getSquaredAvatarDrawable(avatar); ImageView squaredAvatarView = (ImageView)rootView.findViewById(R.id.squaredAvatar); squaredAvatarView.setImageDrawable(squaredAvatarDrawable); //俩个方形的组合头像 Drawable doubleSquaredAvatarDrawable = avatarDrawableFactory.getSquaredAvatarDrawable(avataravatar); ImageView doubleSquaredAvatarView = (ImageView)rootView.findViewById(R.id.doubleSquaredAvatar); doubleSquaredAvatarView.setImageDrawable(doubleSquaredAvatarDrawable); //三个方形的组合头像 Drawable tripleSquaredAvatarDrawable = avatarDrawableFactory.getSquaredAvatarDrawable(avataravataravatar); ImageView tripleSquaredAvatarView = (ImageView)rootView.findViewById(R.id.tripleSquaredAvatar); tripleSquaredAvatarView.setImageDrawable(tripleSquaredAvatarDrawable); //四个方形的组合头像 Drawable quadrupleSquaredAvatarDrawable = avatarDrawableFactory                     .getSquaredAvatarDrawable(avataravataravataravatar); ImageView quadrupleSquaredAvatarView = (ImageView)rootView.findViewById(R.id.quadrupleSquaredAvatar); quadrupleSquaredAvatarView.setImageDrawable(quadrupleSquaredAvatarDrawable);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值