自定义圆形图片、可控位置圆角图片

   2016-09-14 0
核心提示:一.原理1.下面的Xfermode子类可以改变这种行为:AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。PorterDuffXfermode 这是一个非常强大的转换模式,

一.原理

1.下面的Xfermode子类可以改变这种行为:

AvoidXfermode 指定了一个颜色和容差,强制Paint避免在它上面绘图(或者只在它上面绘图)。

PixelXorXfermode 当覆盖已有的颜色时,应用一个简单的像素XOR操作。

PorterDuffXfermode 这是一个非常强大的转换模式,使用它,可以使用图像合成的16条Porter-Duff规则的任意一条来控制Paint如何与已有的Canvas图像进行交互。

2.Porter-Duff 效果图:

二、实现

1.定义抽象类 AbsRoundImageView (继承ImageView)

private static final PorterDuffXfermode xFermode = new PorterDuffXfermode(PorterDuff.Mode.DST_IN);

private Paint mBitmapPaint;

/**
 * 图片可视区
 */
protected Path roundPath;

/**
 * 图片边框
 */
protected Path borderPath;

/**
 * 边框宽度
 */
protected float borderWidth;

/**
 * 边框颜色
 */
protected int borderColor;

private Paint borderPaint;

2.自定义属性

<declare-styleable name="AbsRoundImageView">
    <attr name="borderWidth" format="dimension|reference"/>
    <attr name="borderColor" format="color|reference"/>
</declare-styleable>
protected void initAttrs(AttributeSet attrs){
    if (attrs != null) {
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.AbsRoundImageView);
        borderWidth = ta.getDimension(R.styleable.AbsRoundImageView_borderWidth, 0);
        borderColor = ta.getColor(R.styleable.AbsRoundImageView_borderColor, 0);
        ta.recycle();
    }
}

3.onDraw,圆形、圆角图片绘制流程都是一样的,绘制图片,绘制边框。

@Override
protected void onDraw(Canvas canvas) {
    drawImage(canvas);
    drawBorder(canvas);
}
  • 绘制图片
private void drawImage(Canvas canvas) {
    Drawable drawable = getDrawable();
    if(!isInEditMode() && drawable != null) {
        try {
            Bitmap bitmap;
            if (drawable instanceof ColorDrawable) {
                bitmap = Bitmap.createBitmap(2, 2, Bitmap.Config.ARGB_8888);
            } else {
                bitmap = Bitmap.createBitmap(drawable.getIntrinsicWidth(), drawable.getIntrinsicHeight(), Bitmap.Config.ARGB_8888);
            }
            Canvas drawCanvas = new Canvas(bitmap);
            drawable.setBounds(0, 0, drawCanvas.getWidth(), drawCanvas.getHeight());
            drawable.draw(drawCanvas);

            Bitmap roundBm = getRoundBitmap();
            mBitmapPaint.reset();
            mBitmapPaint.setFilterBitmap(false);
            mBitmapPaint.setXfermode(xFermode);
            drawCanvas.drawBitmap(roundBm, 0, 0, mBitmapPaint);
            mBitmapPaint.setXfermode(null);
            canvas.drawBitmap(bitmap, 0, 0, mBitmapPaint);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
  • 绘制边框
private void drawBorder(Canvas canvas) {
    borderPaint.setStyle(Paint.Style.STROKE);
    borderPaint.setColor(borderColor);
    canvas.drawPath(borderPath, borderPaint);
}

4.定义抽象方法

/**
 * 初始化边框Path
 */
protected abstract void initBorderPath();

/**
 * 初始化图片区域Path
 */
protected abstract void initRoundPath();

三、圆角图片CircleImageView,继承AbsRoundImageView并实现抽象方法

  • 通过path的addCircle方法确定圆形区域
@Override
protected void initRoundPath() {
    roundPath.reset();
    final int width = getWidth();
    final int height = getHeight();
    final float cx = width * 0.5f;
    final float cy = height * 0.5f;
    final float radius = Math.min(width, height) * 0.5f;
    roundPath.addCircle(cx, cy, radius, Path.Direction.CW);
}
@Override
protected void initBorderPath() {
    borderPath.reset();
    final float halfBorderWidth = borderWidth * 0.5f;
    final int width = getWidth();
    final int height = getHeight();
    final float cx = width * 0.5f;
    final float cy = height * 0.5f;
    final float radius = Math.min(width, height) * 0.5f;
    borderPath.addCircle(cx, cy, radius - halfBorderWidth, Path.Direction.CW);
}

四、圆角图片RoundImageView继承AbsRoundImageView并实现抽象方法

1.自定义属性,因为要控制四个角的弧度,所以要再定义四个属性

<declare-styleable name="RoundImageView">
    <attr name="leftTopRadius" format="dimension|reference"/>
    <attr name="rightTopRadius" format="dimension|reference"/>
    <attr name="rightBottomRadius" format="dimension|reference"/>
    <attr name="leftBottomRadius" format="dimension|reference"/>
</declare-styleable>
protected void initAttrs(AttributeSet attrs) {
    super.initAttrs(attrs);
    if (attrs != null) {
        TypedArray ta = getContext().obtainStyledAttributes(attrs, R.styleable.RoundImageView);
        leftTopRadius = ta.getDimension(R.styleable.RoundImageView_leftTopRadius, 0);
        rightTopRadius = ta.getDimension(R.styleable.RoundImageView_rightTopRadius, 0);
        rightBottomRadius = ta.getDimension(R.styleable.RoundImageView_rightBottomRadius, 0);
        leftBottomRadius = ta.getDimension(R.styleable.RoundImageView_leftBottomRadius, 0);
        ta.recycle();
    }
}

2.关键是控制四个位置的圆角,path的addRoundRect有不同参数个数的,其中有一个三个参数如下,float数组类型的参数便是控制四个位置弧度的。

/**
 * Add a closed round-rectangle contour to the path. Each corner receives
 * two radius values [X, Y]. The corners are ordered top-left, top-right,
 * bottom-right, bottom-left
 *
 * @param rect The bounds of a round-rectangle to add to the path
 * @param radii Array of 8 values, 4 pairs of [X,Y] radii
 * @param dir  The direction to wind the round-rectangle's contour
 */
public void addRoundRect(RectF rect, float[] radii, Direction dir) {
    if (rect == null) {
        throw new NullPointerException("need rect parameter");
    }
    addRoundRect(rect.left, rect.top, rect.right, rect.bottom, radii, dir);
}

使用这个方法才可以控制圆角位置,radii 参数是一个含有8个值,4对坐标的数组, 每两个值控制一个方向的值,左上角开始,依次按照顺时针方向

0,1位置 - 左上角

2,3位置 - 右上角

4,5位置 - 右下角

6,7位置 - 左下角

  • 所以区域可以这样写
@Override
protected void initRoundPath() {
    roundPath.reset();
    final int width = getWidth();
    final int height = getHeight();
    leftTopRadius = Math.min(leftTopRadius, Math.min(width, height) * 0.5f);
    rightTopRadius = Math.min(rightTopRadius, Math.min(width, height) * 0.5f);
    rightBottomRadius = Math.min(rightBottomRadius, Math.min(width, height) * 0.5f);
    leftBottomRadius = Math.min(leftBottomRadius, Math.min(width, height) * 0.5f);

    RectF rect = new RectF(0, 0, width, height);
    roundPath.addRoundRect(rect,
            new float[]{leftTopRadius, leftTopRadius, rightTopRadius, rightTopRadius,
                    rightBottomRadius, rightBottomRadius, leftBottomRadius, leftBottomRadius},
            Path.Direction.CW);
}
@Override
protected void initBorderPath() {
    borderPath.reset();
    /**
     * 乘以0.5会导致border在圆角处不能包裹图片
     */
    final float halfBorderWidth = borderWidth * 0.35f;
    final int width = getWidth();
    final int height = getHeight();
    leftTopRadius = Math.min(leftTopRadius, Math.min(width, height) * 0.5f);
    rightTopRadius = Math.min(rightTopRadius, Math.min(width, height) * 0.5f);
    rightBottomRadius = Math.min(rightBottomRadius, Math.min(width, height) * 0.5f);
    leftBottomRadius = Math.min(leftBottomRadius, Math.min(width, height) * 0.5f);

    RectF rect = new RectF(halfBorderWidth, halfBorderWidth,
            width - halfBorderWidth, height - halfBorderWidth);
    borderPath.addRoundRect(rect,
            new float[]{leftTopRadius, leftTopRadius, rightTopRadius, rightTopRadius,
                    rightBottomRadius, rightBottomRadius, leftBottomRadius, leftBottomRadius},
            Path.Direction.CW);
}

五、使用

布局

<com.beiing.roundimage.CircleImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:scaleType="centerCrop"
        android:src=http://www.tuicool.com/articles/"@mipmap/head"
        app:borderWidth="2dp"
        app:borderColor="#8c9eff"
        />
<com.beiing.roundimage.RoundImageView
                android:layout_width="100dp"
                android:layout_height="100dp"
                android:scaleType="centerCrop"
                android:src=http://www.tuicool.com/articles/"@mipmap/test"
                app:borderWidth="2dp"
                app:borderColor="#8c9eff"
                app:leftTopRadius="10dp"
                app:rightTopRadius="30dp"
                app:rightBottomRadius="10dp"
                app:leftBottomRadius="30dp"
                />

测试常见的图片加载库加载

  • Glide
circleImageViewGlide = (CircleImageView) findViewById(R.id.circle_image_glide);
 Glide.with(this).load("http://img2.imgtn.bdimg.com/it/u=1939271907,257307689&fm=21&gp=0.jpg").into(circleImageViewGlide);
  • Picasso
roundImageViewPicasso = (RoundImageView) findViewById(R.id.round_image_picasso);
 Picasso.with(this).load("http://img0.imgtn.bdimg.com/it/u=2263418180,3668836868&fm=206&gp=0.jpg").fit().into(roundImageViewPicasso);
  • xUtils3
roundImageViewXutils = (RoundImageView) findViewById(R.id.round_image_xutils);
x.image().bind(roundImageViewXutils, "http://img0.imgtn.bdimg.com/it/u=2263418180,3668836868&fm=206&gp=0.jpg",
                new ImageOptions.Builder().setCrop(true).build());

扩展

继承AbsRoundImageView,实现抽象方法,绘制自定义形状的图片。

效果图

自定义圆形图片、可控位置圆角图片

下载:https://github.com/LineChen/RoundImageView

 
标签: Bitmap 安卓开发
反对 0举报 0 评论 0
 

免责声明:本文仅代表作者个人观点,与乐学笔记(本网)无关。其原创性以及文中陈述文字和内容未经本站证实,对本文以及其中全部或者部分内容、文字的真实性、完整性、及时性本站不作任何保证或承诺,请读者仅作参考,并请自行核实相关内容。
    本网站有部分内容均转载自其它媒体,转载目的在于传递更多信息,并不代表本网赞同其观点和对其真实性负责,若因作品内容、知识产权、版权和其他问题,请及时提供相关证明等材料并与我们留言联系,本网站将在规定时间内给予删除等相关处理.

  • 如何进行网络视频截图/获取视频的缩略图
    如何进行网络视频截图/获取视频的缩略图
    小编导读:获取视频的缩略图,截图正在播放的视频某一帧,是在音视频开发中,常遇到的问题。本文是主要用于点播中截图视频,同时还可以获取点播视频的缩略图进行显示,留下一个问题,如下图所示, 如果要获取直播中节目视频缩略图,该怎么做呢?(ps:直播是直
  • 不得不知道的图片加载框架之Glide
    不得不知道的图片加载框架之Glide
    简介在泰国举行的谷歌开发者论坛上,谷歌为我们介绍了一个名叫 Glide 的图片加载库,作者是bumptech。这个库被广泛的运用在google的开源项目中,包括2014年google I/O大会上发布的官方app。特点(1)使用简单(2)可配置度高,自适应程度高(3)支持常见图片
  • 一个实用方便的图片控件SImageView
    一个实用方便的图片控件SImageView
    控件介绍 这是一个简单到 sImageView.setImageUrls(http://img3.cache.netease.com/ent/2009/4/17/20090417104402666a4.jpg); 设置一个网址即可显示图片的控件相对 ImageView 功能的扩展的控件, 但是没有继承 ImageView 直接继承的 View . 比如 QQ群组头像 ,
  • Android开发艺术探索学习笔记(三)—Android性能优化之Bitmap导致的内存溢出
    Android开发艺术探索学习笔记(三)—Android性能
    原本计划是按照章节顺序学习《Android开发艺术探索》这本书的,Android性能优化这部分也是本书的最后一章。但是周末的时候,友盟线下反馈的公司项目的一个错误让我不得不提前学习这一块的知识。先看看线下反馈的错误吧:java.lang.OutOfMemoryError:应用程序
  • Android图形图像使用总结
    Android图形图像使用总结
    一.图形特效(一)特效的实现方式在Android中,提供了3种方式实现特效,setXXX方法,postXXX和preXXX()方法。1.setXXX方法用于直接设置Matrix的值,每使用一次setXXX()方法,整个的Matrix都会变掉。2.postXXX方法用于采用后乘的方式为Matrix设置值,可以连续多次
  • AsyncTask 工作原理(上)
    AsyncTask 是一种轻量级是异步任务类,它可以在线程池中执行后台任何,将执行的进度和最终结果传递给主线程,并在主线程中更新UI。AsyncTask 是一个抽象类,其构造函数//Params:传入doInBackground 中的参数类型//Progress: 后台执行进度的类型,传入onProgre
  • vysor 原理以及 Android 同屏方案
    vysor是一个免root实现电脑控制手机的chrome插件,目前也有几款类似的通过电脑控制手机的软件,不过都需要root权限,并且流畅度并不高。vysor没有多余的功能,流畅度也很高,刚接触到这款插件时我惊讶于它的流畅度以及免root,就一直对它的实现原理很感兴趣。
  • 简单实现 Android 图片三级缓存机制
    简单实现 Android 图片三级缓存机制
    用户在使用我们的APP时,通常会重复浏览一些图片,这时如果每一次浏览都需要通过网络获取图片,那么将会非常流量。为了节省用户流量,提高图片加载效率,我们通常使用图片三级缓存策略,即通过网络、本地、内存三级缓存图片,来减少不必要的网络交互,避免浪
  • Picasso 解析 (1)- 一张图片是如何加载出来的
    前言Picasso是JakeWharton大神在github上的一个开源图片加载框架,使用起来极其方便,甚至只需要一行代码就可以搞定图片加载:Picasso.with(context).load(http://i.imgur.com/DvpvklR.png).into(imageView);具体如何使用该框架我就不在这里赘述了,大家可以
  • Android的oom详解
    Android的oom详解
    oom的定义OOM(out of memory)即内存泄露。一个程序中,已经不需要使用某个对象,但是因为仍然有引用指向它垃圾回收器就无法回收它,当该对象占用的内存无法被回收时,就容易造成内存泄露。Android的oom原因1.资源对象没关闭造成的内存泄露,try catch final
    09-12 BitmapOOM
点击排行