今天我们来讲一下有关自定义控件的问题,今天讲的这篇是从布局自定义开始的,难度不大,一看就明白,估计有的同学或者开发者看了说,这种方式多此一举,但是小编我不这么认为,多一种解决方式,就多一种举一反三的学习。下一次或者过几天我会从自定义属性,在布局文件中使用属性的方式再讲一篇关于自定义控件的文章,希望对大家能够有所帮助。

昨天我们学习了自定义带图片和文字的ImageTextButton,非常简单,我承诺给大家要讲一下用自定义属性的方式学习真正的实现自定义控件,在布局文件中使用属性的方式就需要用到attr.xml这个文件,以前很多同学问我这个是干什么的,现在学了这篇内容,你就差不多知道了,以后就别再问了。自定义圆形控件 RoundImageView ,我相信大家在开发中会经常遇到设置圆形头像的情况,因为这样的头像显得漂亮。怎么做呢?先看效果图:

讲之前解释一下attr.xml的作用,我用土话废话说,这样容易理解:比如我自定义一个控件,怎么实现呢,以RoundImageView为例,首先是继承ImageView,然后实现其构造函数,在构造函数中,获取attr.xml中的属性值(再次解释:这里获取的具体的这个属性的值是怎么来的呢?比如颜色和宽度,这个在attr.xml中定义了相关的名字,而在使用RoundImageView的xml布局文件中,我们会为其设置值,这里需要用的值,就是从那里设置的),并设置在本控件中,然后继承onDraw方法,画出自己想要的图形或者形状即可。

由于我在代码中加了很多注释,我就直接给大家介绍代码了哈。





第一步:定义RoundImageView的属性配置文件:attr.xml

<?xml version="1.0" encoding="utf-8"?>
<resources> <declare-styleable name="round_image_view">
<attr name="border_width" format="dimension" />
<attr name="border_incolor" format="color" />
<attr name="border_outcolor" format="color"></attr>
</declare-styleable> </resources>

第二步:自定义圆形控件RoundImageView并继承ImageView

package net.loonggg.rivd.demo.view;

import net.loonggg.rivd.demo.R;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.NinePatchDrawable;
import android.util.AttributeSet;
import android.widget.ImageView; /**
*
* @author loongggdroid
*
*/
public class RoundImageView extends ImageView {
private int mBorderThickness = 0;
private Context mContext;
private int defaultColor = 0xFFFFFFFF;
// 外圆边框颜色
private int mBorderOutsideColor = 0;
// 内圆边框颜色
private int mBorderInsideColor = 0;
// RoundImageView控件默认的长、宽
private int defaultWidth = 0;
private int defaultHeight = 0; public RoundImageView(Context context) {
super(context);
mContext = context;
} public RoundImageView(Context context, AttributeSet attrs) {
super(context, attrs);
mContext = context;
// 设置RoundImageView的属性值,比如颜色,宽度等
setRoundImageViewAttributes(attrs);
} public RoundImageView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
mContext = context;
setRoundImageViewAttributes(attrs);
} // 从attr.xml文件中获取属性值,并给RoundImageView设置
private void setRoundImageViewAttributes(AttributeSet attrs) {
TypedArray a = mContext.obtainStyledAttributes(attrs,
R.styleable.round_image_view);
mBorderThickness = a.getDimensionPixelSize(
R.styleable.round_image_view_border_width, 0);
mBorderOutsideColor = a.getColor(
R.styleable.round_image_view_border_outcolor, defaultColor);
mBorderInsideColor = a.getColor(
R.styleable.round_image_view_border_incolor, defaultColor);
a.recycle();
} // 具体解释:比如我自定义一个控件,怎么实现呢,以RoundImageView为例,首先是继承ImageView,然后实现其构造函数,在构造函数中,获取attr中的属性值(再次解释:这里获取的具体的这个属性的值是怎么来的呢?比如颜色和宽度,这个在attr.xml中定义了相关的名字,而在使用RoundImageView的xml布局文件中,我们会设置其值,这里需要用的值,就是从那里设置的),并设置在本控件中,然后继承onDraw方法,画出自己想要的图形或者形状即可
/**
* 这个是继承的父类的onDraw方法
*
* onDraw和下面的方法不用管,基本和学习自定义没关系,就是实现怎么画圆的,你可以改变下面代码试着画三角形头像,哈哈
*/
@Override
protected void onDraw(Canvas canvas) {
Drawable drawable = getDrawable();
if (drawable == null) {
return;
}
if (getWidth() == 0 || getHeight() == 0) {
return;
}
this.measure(0, 0);
if (drawable.getClass() == NinePatchDrawable.class)
return;
Bitmap b = ((BitmapDrawable) drawable).getBitmap();
Bitmap bitmap = b.copy(Bitmap.Config.ARGB_8888, true);
if (defaultWidth == 0) {
defaultWidth = getWidth();
}
if (defaultHeight == 0) {
defaultHeight = getHeight();
}
int radius = 0;
// 这里的判断是如果内圆和外圆设置的颜色值不为空且不是默认颜色,就定义画两个圆框,分别为内圆和外圆边框
if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor != defaultColor) {
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - 2 * mBorderThickness;
// 画内圆
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);
// 画外圆
drawCircleBorder(canvas, radius + mBorderThickness
+ mBorderThickness / 2, mBorderOutsideColor);
} else if (mBorderInsideColor != defaultColor
&& mBorderOutsideColor == defaultColor) {// 这里的是如果内圆边框不为空且颜色值不是默认值,就画一个内圆的边框
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderInsideColor);
} else if (mBorderInsideColor == defaultColor
&& mBorderOutsideColor != defaultColor) {// 这里的是如果外圆边框不为空且颜色值不是默认值,就画一个外圆的边框
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2 - mBorderThickness;
drawCircleBorder(canvas, radius + mBorderThickness / 2,
mBorderOutsideColor);
} else {// 这种情况是没有设置属性颜色的情况下,即没有边框的情况
radius = (defaultWidth < defaultHeight ? defaultWidth
: defaultHeight) / 2;
}
Bitmap roundBitmap = getCroppedRoundBitmap(bitmap, radius);
canvas.drawBitmap(roundBitmap, defaultWidth / 2 - radius, defaultHeight
/ 2 - radius, null);
} /**
* 获取裁剪后的圆形图片
*
* @param bmp
* @param radius
* 半径
* @return
*/
public Bitmap getCroppedRoundBitmap(Bitmap bmp, int radius) {
Bitmap scaledSrcBmp;
int diameter = radius * 2;
// 为了防止宽高不相等,造成圆形图片变形,因此截取长方形中处于中间位置最大的正方形图片
int bmpWidth = bmp.getWidth();
int bmpHeight = bmp.getHeight();
int squareWidth = 0, squareHeight = 0;
int x = 0, y = 0;
Bitmap squareBitmap;
if (bmpHeight > bmpWidth) {// 高大于宽
squareWidth = squareHeight = bmpWidth;
x = 0;
y = (bmpHeight - bmpWidth) / 2;
// 截取正方形图片
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);
} else if (bmpHeight < bmpWidth) {// 宽大于高
squareWidth = squareHeight = bmpHeight;
x = (bmpWidth - bmpHeight) / 2;
y = 0;
squareBitmap = Bitmap.createBitmap(bmp, x, y, squareWidth,
squareHeight);
} else {
squareBitmap = bmp;
}
if (squareBitmap.getWidth() != diameter
|| squareBitmap.getHeight() != diameter) {
scaledSrcBmp = Bitmap.createScaledBitmap(squareBitmap, diameter,
diameter, true);
} else {
scaledSrcBmp = squareBitmap;
}
Bitmap output = Bitmap.createBitmap(scaledSrcBmp.getWidth(),
scaledSrcBmp.getHeight(), Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output); Paint paint = new Paint();
Rect rect = new Rect(0, 0, scaledSrcBmp.getWidth(),
scaledSrcBmp.getHeight()); paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
canvas.drawARGB(0, 0, 0, 0);
canvas.drawCircle(scaledSrcBmp.getWidth() / 2,
scaledSrcBmp.getHeight() / 2, scaledSrcBmp.getWidth() / 2,
paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(scaledSrcBmp, rect, rect, paint);
bmp = null;
squareBitmap = null;
scaledSrcBmp = null;
return output;
} /**
* 画边缘的圆,即内圆或者外圆
*/
private void drawCircleBorder(Canvas canvas, int radius, int color) {
Paint paint = new Paint();
/* 去锯齿 */
paint.setAntiAlias(true);
paint.setFilterBitmap(true);
paint.setDither(true);
paint.setColor(color);
/* 设置paint的 style 为STROKE:空心 */
paint.setStyle(Paint.Style.STROKE);
/* 设置paint的外框宽度 */
paint.setStrokeWidth(mBorderThickness);
canvas.drawCircle(defaultWidth / 2, defaultHeight / 2, radius, paint);
}
}

第三步:在xml配置中使用控件:activity_main.xml

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:loonggg="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:paddingTop="20dp" > <!-- 没有指定圆形ImageView属性时,默认没有外边圆颜色 --> <net.loonggg.rivd.demo.view.RoundImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:src="@drawable/pigu" /> <!-- border_outcolor 外部圆圈的颜色 -->
<!-- border_incolor 内部部圆圈的颜色 -->
<!-- border_width 外圆和内圆的宽度 -->
<!-- 再解释一遍,我们在布局中使用了我们在sttr中定义的属性,并在这里的布局文件中赋了值,所以在RoundImageView类中的结构体设置属性使用的值,就是我们在这里赋的,如果不使用attr.xml文件也可以,这样就是我们在activity中使用这个控件的时候,再设置值,不如这样方便罢了,比如昨天我们讲的【自定义带图片和文字的ImageTextButton】那样罢了 --> <!-- 说明:这里的loonggg可能大家不太明白,这个名字可以随便起,你们也可以自己随便定义,只要上下统一即可,在布局声明的时候一样就行,比如我在布局顶端是这样声明的 xmlns:loonggg="http://schemas.android.com/apk/res-auto" --> <net.loonggg.rivd.demo.view.RoundImageView
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_marginTop="20dp"
android:src="@drawable/pigu"
loonggg:border_incolor="#000fff"
loonggg:border_outcolor="#fff000"
loonggg:border_width="10dp" /> </LinearLayout>

废话不多说了,直接上demo,获取demo的方法跟以前一样,只需在公众号里回复关键字“3”即可获得。

微信公众号:smart_android ,公众号[非著名程序员]可能是东半球最好的技术分享公众号。每天,每周定时推送一些有关移动开发的原创文章和教程。

自定义圆形控件RoundImageView并认识一下attr.xml的更多相关文章

  1. 自定义圆形控件RoundImageView并认识一下attr

    昨天我们学习了自定义带图片和文字的ImageTextButton,非常简单,我承诺给大家要讲一下用自定义属性的方式学习真正的实现自定义控件,在布局文件中使用属性的方式就需要用到attr.xml这个文件 ...

  2. 自定义圆形控件 RoundImageView

    1.自定义圆形控件 RoundImageView package com.ronye.CustomView; import android.content.Context; import androi ...

  3. CircleImageView自定义圆形控件的使用

    1.自定义圆形控件github地址: https://github.com/hdodenhof/CircleImageView 主要的类: package de.hdodenhof.circleima ...

  4. Android开发(二)——自定义圆形控件的使用CircleImageView

    CircleImageView,a fast circular ImageView perfect for profile images. 主要的类CircleImageView: package d ...

  5. 【Android开源】CircleImageView自定义圆形控件的使用

    github地址:https://github.com/hdodenhof/CircleImageView package de.hdodenhof.circleimageview; import e ...

  6. Android - 自定义控件之圆形控件

    自定义控件 - 圈圈 Android L: Android Studio 效果:能够自定义圆圈半径和位置:设定点击效果:改变背景颜色 下面是demo图 点击前: 点击后: 自定义控件一般要继承View ...

  7. 安卓自定义组合控件--toolbar

    最近在学习安卓APP的开发,用到了toolbar这个控件, 最开始使用时include layout这种方法,不过感觉封装性不好,就又改成了自定义组合控件的方式. 使用的工具为android stud ...

  8. Android自定义控件之自定义组合控件

    前言: 前两篇介绍了自定义控件的基础原理Android自定义控件之基本原理(一).自定义属性Android自定义控件之自定义属性(二).今天重点介绍一下如何通过自定义组合控件来提高布局的复用,降低开发 ...

  9. asp.net webform 自定义分页控件

    做web开发一直用到分页控件,自己也动手实现了个,使用用户自定义控件. 翻页后数据加载使用委托,将具体实现放在在使用分页控件的页面进行注册. 有图有真相,给个直观的认识: 自定义分页控件前台代码: & ...

随机推荐

  1. IntelliJ IDEA14.1中java项目Maven中没有配置JDK时的问题

    在IntelliJ IDEA 14.1中使用在java项目中使用Maven时当没有在Maven中配置JDK编译版本.源码版本时,IDEA将默认的编译版本.源码版本设置为jdk5. 在IDEA中Lang ...

  2. Linux nm命令

    一.简介 显示关于对象文件.可执行文件以及对象文件库里的符号信息. 二.选项 http://www.cnblogs.com/wangkangluo1/archive/2012/07/02/257243 ...

  3. Windows Azure CN 超业余性能测试

    先来说说为什么会有这篇文章吧.从朋友那里搞来个Windows Azure CN的测试帐号,在公司的时候领导的朋友有一个阿里云的服务器,平时部署小东西都往上面丢,不过那是人家的东西,还有其他的应用跑在上 ...

  4. 微信小程序开发初体验--教你开发小程序

    微信小程序 微信小程序面世以来受到的关注颇多,直到最近我才动手尝试进行了小程序的开发,总体上感觉还是不错的,有一点不适应的就是要摆脱Web APP开发对DOM的操作.在这里我就把我是如何利用API开发 ...

  5. .NET对象的内存布局

    每个虚拟机都有它自己的对象布局,本文我们将针对sscli源码和windbg调试器来查看不同类型的.net对象布局. 在.net虚拟机里,每个对象都需要保存这些信息: 对象的类型: 对象实例的成员属性( ...

  6. C++浅析——虚函数的动态和静态绑定

    源自一道面试题,觉得很有意思 class CBase { public: virtual void PrintData(int nData = 111); }; void CBase::PrintDa ...

  7. C#基础---C#如何对Json字符串处理

    Json字符串对于做web应用的应该很熟悉,其实在很多请求我们返回的都是Json字符串.那对于C#代码如何处理Json字符串呢,.Net封装了一个类叫做JavaScriptSerializer[MSD ...

  8. MMORPG大型游戏设计与开发(part2 of net)

    网络第二部分的将要给大家描述的是网络代码方面的设计,从基础的代码讲起,了解详细的网络模块构架. 没有放出整个源代码,是因为其中还有许多不足的地方,不过想必大家应该也能猜想出这个项目源码的地址了.不过对 ...

  9. 第15章 设备无关位图_15.2 显示和打印DIB

    15.2.1 探究DIB (1)DIB文件的结构 整个文件 紧凑DIB(整个文件除文件头) 文件头(File Header) 信息头(Information Header) 信息头(Informati ...

  10. vs2015产品密钥

    HM6NR-QXX7C-DFW2Y-8B82K-WTYJV 博主验证有效