【Android】Android自定义带board的圆角控件

简介: 介绍 圆角控件常用于头像,按钮,图标等,用途十分广泛,而且常常配合board使用。 在IOS中,UIVIew的CALayer层已经提供了圆角和board的方法,所以圆角控件的制作非常简单,只需要类似以下简单代码即可实现: view.layer.cornerRadius = 20; view.layer.borderColor = [UIColor yellow

介绍

圆角控件常用于头像,按钮,图标等,用途十分广泛,而且常常配合board使用。


在IOS中,UIVIew的CALayer层已经提供了圆角和board的方法,所以圆角控件的制作非常简单,只需要类似以下简单代码即可实现:

    view.layer.cornerRadius = 20;
    view.layer.borderColor = [UIColor yellowColor].CGColor;
    view.layer.borderWidth = 10;
    view.clipsToBounds = YES;

而在Android中,系统并没有提供这么直接的方式,所以想要实现同样的效果需要费点心了。 本文将会 介绍一种在Android中实现带board属性的圆角控件的方法。该方法定义了一个RoundImageView重写了ImageView的onDraw方法,并加入了一些自定义的属性,最后实现以上同样效果的代码非常简洁:

    <com.me.obo.circleboardimage.circleview.RoundImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@drawable/head"
        attr:border_width="3dp"
        attr:corner_y="40dp"
        attr:corner_x="40dp"
        attr:border_color="#FFFFFF"/>

下面就介绍如何实现这样一个RoundImageView

遮罩

在Android中,遮罩功能非常强大,可以用于生成两张图片相互截取的结果。


具体参数和遮罩的效果可以见链接: Android 实现遮罩

在RoundImageView中,遮罩用于清除控件外圈的内容,从而获取到圆角的效果。这里的思路是先获取到控件本身所绘制的图片,之后获取圆角矩形遮罩图片,然后两个图片采用DST_IN遮罩模式来截取中间的遮罩效果,即:


代码实现如下:


        // 获取imageview原先的图片
        super.onDraw(mDestCanvas);

        // 创建矩形,表示圆角矩形
        if (mRoundRectClip == null) {
            mRoundRectClip = new RectF(mBorderValue, mBorderValue, getWidth() - mBorderValue, getHeight() - mBorderValue);
        }
        // 绘制圆角矩形
        mSrcCanvas.drawRoundRect(mRoundRectClip,mCornerXValue,mCornerYValue,mNomalPaint);

        // 使用遮罩画笔扣除原图中的圆角矩形外面的部分
        mDestCanvas.drawBitmap(mSrcBitmap,0,0,mPaintClip);


在获取到遮罩处理的图片之后,为其添加board将会变得容易,也就是在外圈绘制出一个带有宽度的圆即可。


代码实现如下:


        // 创建board的矩形
        if (mRoundRectBorder == null) {
            mRoundRectBorder = new RectF(mBorderValue / 2, mBorderValue / 2, getWidth() - mBorderValue / 2, getHeight() - mBorderValue / 2);
        }
        // 绘制board
        mDestCanvas.drawRoundRect(mRoundRectBorder, mCornerXValue, mCornerYValue, mPaintBoard);


整个RoundImageView完整的代码如下:

package com.me.obo.circleboardimage.circleview;

import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.RectF;
import android.util.AttributeSet;
import android.widget.ImageView;

import com.me.obo.circleboardimage.R;


/**
 * Created by obo on 16/5/11.
 * Email:obo1993@gmail.com
 * Git:https://github.com/OboBear
 * Blog:http://blog.csdn.net/leilba
 */
public class RoundImageView extends ImageView {

    private float mCornerXValue ;
    private float mCornerYValue ;
    private float mBorderValue ;
    private int mBorderColor ;

    public RoundImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 获取自定义参数
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.RoundImageView);
        // board颜色
        mBorderColor = typedArray.getColor(R.styleable.RoundImageView_border_color,Color.argb(0,0,0,0));
        // 获取X方向曲率
        mCornerXValue = typedArray.getDimension(R.styleable.RoundImageView_corner_x,0);
        // 获取Y方向曲率
        mCornerYValue = typedArray.getDimension(R.styleable.RoundImageView_corner_y,0);
        // board宽
        mBorderValue = typedArray.getDimension(R.styleable.RoundImageView_border_width,0);
        // 用完需要recycle
        typedArray.recycle();

        initPaints();
    }

    private void initPaints() {
        // 创建普通画笔
        if (mNomalPaint == null) {
            mNomalPaint = new Paint();
            mNomalPaint.setAntiAlias(true);
        }

        // 创建遮罩画笔
        if (mPaintClip == null) {
            mPaintClip = new Paint();
            mPaintClip.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_IN));
            mPaintClip.setAntiAlias(true);
        }

        // 创建board的画笔
        if (mPaintBoard == null) {
            mPaintBoard = new Paint();
            mPaintBoard.setColor(mBorderColor);
            mPaintBoard.setStyle(Paint.Style.STROKE);
            mPaintBoard.setStrokeWidth(mBorderValue);
            mPaintBoard.setAntiAlias(true);
        }
    }

    Bitmap mDestBitmap;
    Bitmap mSrcBitmap;
    Canvas mDestCanvas;
    Canvas mSrcCanvas;
    Paint mPaintClip;
    Paint mNomalPaint;
    Paint mPaintBoard;
    RectF mRoundRectClip;
    RectF mRoundRectBorder;

    @Override
    protected void onDraw(Canvas canvas) {
        // 创建遮罩图片和画布
        if (mDestBitmap == null) {
            mDestBitmap = Bitmap.createBitmap(getWidth(), getHeight(), Bitmap.Config.ARGB_8888);
            mSrcBitmap = Bitmap.createBitmap(getWidth(),getHeight(), Bitmap.Config.ARGB_8888);
            mDestCanvas = new Canvas(mDestBitmap);
            mSrcCanvas = new Canvas(mSrcBitmap);
        }
        // 获取imageview原先的图片
        super.onDraw(mDestCanvas);

        // 创建矩形,表示圆角矩形
        if (mRoundRectClip == null) {
            mRoundRectClip = new RectF(mBorderValue, mBorderValue, getWidth() - mBorderValue, getHeight() - mBorderValue);
        }
        // 绘制圆角矩形
        mSrcCanvas.drawRoundRect(mRoundRectClip,mCornerXValue,mCornerYValue,mNomalPaint);

        // 使用遮罩画笔扣除原图中的圆角矩形外面的部分
        mDestCanvas.drawBitmap(mSrcBitmap,0,0,mPaintClip);

        // 创建board的矩形
        if (mRoundRectBorder == null) {
            mRoundRectBorder = new RectF(mBorderValue / 2, mBorderValue / 2, getWidth() - mBorderValue / 2, getHeight() - mBorderValue / 2);
        }
        // 绘制board
        mDestCanvas.drawRoundRect(mRoundRectBorder, mCornerXValue, mCornerYValue, mPaintBoard);

        // 绘制最终的圆角带有board的图
        canvas.drawBitmap(mDestBitmap,0,0,mNomalPaint);
    }

}


自定义Attribute

可能有人要问,圆角弯曲度和board的参数是如何传递到RoundImageView里的。实际上,这里采用了自定义Attribute的方式,该操作需要到res/values 目录下创建 attr.xml 文件,在里面写上自定义的参数名称以及类型:

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="RoundImageView">
        <attr name="border_width" format="dimension"/>
        <attr name="border_color" format="color"/>
        <attr name="corner_x" format="dimension"/>
        <attr name="corner_y" format="dimension"/>
    </declare-styleable>
</resources>

有了这个,就可以在RoundImageView的构造方法里面,获取到自定义attri的值了

    public RoundImageView(Context context, AttributeSet attrs) {
        super(context, attrs);
        // 获取自定义参数
        TypedArray typedArray = context.obtainStyledAttributes(attrs,
                R.styleable.RoundImageView);
        // board颜色
        mBorderColor = typedArray.getColor(R.styleable.RoundImageView_border_color,Color.argb(0,0,0,0));
        // 获取X方向曲率
        mCornerXValue = typedArray.getDimension(R.styleable.RoundImageView_corner_x,0);
        // 获取Y方向曲率
        mCornerYValue = typedArray.getDimension(R.styleable.RoundImageView_corner_y,0);
        // board宽
        mBorderValue = typedArray.getDimension(R.styleable.RoundImageView_border_width,0);
        // 用完需要recycle
        typedArray.recycle();

        initPaints();
    }

在layout里使用的时候,需要在layout的xml开头里面申请自定义的参数即可

xmlns:attr="http://schemas.android.com/apk/res-auto"
最后,在layout里面调用RoundImageView的代码非常简洁:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:attr="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@android:color/darker_gray"
    android:orientation="vertical">

    <com.me.obo.circleboardimage.circleview.RoundImageView
        android:layout_width="80dp"
        android:layout_height="80dp"
        android:src="@drawable/head"
        attr:border_width="3dp"
        attr:corner_y="40dp"
        attr:corner_x="40dp"
        attr:border_color="#FFFFFF"/>

    <com.me.obo.circleboardimage.circleview.RoundImageView
        android:layout_width="180dp"
        android:layout_height="180dp"
        android:src="@drawable/head"
        attr:border_width="2dp"
        attr:corner_y="40dp"
        attr:corner_x="40dp"
        attr:border_color="#FF0000"
        android:scaleType="fitXY"/>
    
</LinearLayout>
效果如下:



资源下载

RoundImageView


目录
相关文章
|
1月前
|
Android开发 开发者
安卓应用开发中的自定义视图
【9月更文挑战第37天】在安卓开发的海洋中,自定义视图犹如一座座小岛,等待着勇敢的探索者去发现其独特之处。本文将带领你踏上这段旅程,从浅滩走向深海,逐步揭开自定义视图的神秘面纱。
35 3
|
30天前
|
数据可视化 Android开发 开发者
安卓应用开发中的自定义View组件
【10月更文挑战第5天】在安卓应用开发中,自定义View组件是提升用户交互体验的利器。本篇将深入探讨如何从零开始创建自定义View,包括设计理念、实现步骤以及性能优化技巧,帮助开发者打造流畅且富有创意的用户界面。
63 0
|
5天前
|
搜索推荐 前端开发 Android开发
安卓应用开发中的自定义视图实现
【10月更文挑战第30天】在安卓开发的海洋中,自定义视图是那抹不可或缺的亮色,它为应用界面的个性化和交互体验的提升提供了无限可能。本文将深入探讨如何在安卓平台创建自定义视图,并展示如何通过代码实现这一过程。我们将从基础出发,逐步引导你理解自定义视图的核心概念,然后通过一个实际的代码示例,详细讲解如何将理论应用于实践,最终实现一个美观且具有良好用户体验的自定义控件。无论你是想提高自己的开发技能,还是仅仅出于对安卓开发的兴趣,这篇文章都将为你提供价值。
|
7天前
|
Android开发 开发者 UED
安卓开发中自定义View的实现与性能优化
【10月更文挑战第28天】在安卓开发领域,自定义View是提升应用界面独特性和用户体验的重要手段。本文将深入探讨如何高效地创建和管理自定义View,以及如何通过代码和性能调优来确保流畅的交互体验。我们将一起学习自定义View的生命周期、绘图基础和事件处理,进而探索内存和布局优化技巧,最终实现既美观又高效的安卓界面。
19 5
|
30天前
|
XML 前端开发 Java
安卓应用开发中的自定义View组件
【10月更文挑战第5天】自定义View是安卓应用开发的一块基石,它为开发者提供了无限的可能。通过掌握其原理和实现方法,可以创造出既美观又实用的用户界面。本文将引导你了解自定义View的创建过程,包括绘制技巧、事件处理以及性能优化等关键步骤。
|
2月前
|
Android开发 开发者
安卓开发中的自定义视图:从入门到精通
【9月更文挑战第19天】在安卓开发的广阔天地中,自定义视图是一块充满魔力的土地。它不仅仅是代码的堆砌,更是艺术与科技的完美结合。通过掌握自定义视图,开发者能够打破常规,创造出独一无二的用户界面。本文将带你走进自定义视图的世界,从基础概念到实战应用,一步步展示如何用代码绘出心中的蓝图。无论你是初学者还是有经验的开发者,这篇文章都将为你打开一扇通往创意和效率的大门。让我们一起探索自定义视图的秘密,将你的应用打造成一件艺术品吧!
58 10
|
1月前
|
XML 存储 Java
浅谈Android的TextView控件
浅谈Android的TextView控件
24 0
|
2月前
|
XML 编解码 Android开发
安卓开发中的自定义视图控件
【9月更文挑战第14天】在安卓开发中,自定义视图控件是一种高级技巧,它可以让开发者根据项目需求创建出独特的用户界面元素。本文将通过一个简单示例,引导你了解如何在安卓项目中实现自定义视图控件,包括创建自定义控件类、处理绘制逻辑以及响应用户交互。无论你是初学者还是有经验的开发者,这篇文章都会为你提供有价值的见解和技巧。
43 3
|
2月前
|
前端开发 Android开发 开发者
安卓应用开发中的自定义视图基础
【9月更文挑战第13天】在安卓开发的广阔天地中,自定义视图是一块神奇的画布,它允许开发者将想象力转化为用户界面的创新元素。本文将带你一探究竟,了解如何从零开始构建自定义视图,包括绘图基础、触摸事件处理,以及性能优化的实用技巧。无论你是想提升应用的视觉吸引力,还是追求更流畅的交互体验,这里都有你需要的金钥匙。
|
2月前
|
缓存 搜索推荐 Android开发
安卓应用开发中的自定义View组件实践
【9月更文挑战第10天】在安卓开发领域,自定义View是提升用户体验和实现界面个性化的重要手段。本文将通过一个实际案例,展示如何在安卓项目中创建和使用自定义View组件,包括设计思路、实现步骤以及可能遇到的问题和解决方案。文章不仅提供了代码示例,还深入探讨了自定义View的性能优化技巧,旨在帮助开发者更好地掌握这一技能。