通常情况下,Android实现自定义控件无非三种方式。

  Ⅰ、继承现有控件,对其控件的功能进行拓展。

  Ⅱ、将现有控件进行组合,实现功能更加强大控件。

  Ⅲ、重写View实现全新的控件

  本文来讨论最难的一种自定义控件形式,重写View来实现全新的控件。

  首先,我们要明白在什么样的情况下,需要重写View来实现一种全新的控件,一般当我们遇到了原生控件无法满足我们现有的需求的时候,我们此时就可以考虑创建一个全新的View来实现我们所需要的功能。创建一个全新View实现自定义控件,无非分成这么几步:

  Ⅰ、在OnMeasure()方法中,测量自定义控件的大小,使自定义控件能够自适应布局各种各样的需求。

  Ⅱ、在OnDraw()方法中,利用哼哈二将(Canvas与Paint)来绘制要显示的内容。

  Ⅲ、在OnLayout()方法中来确定控件显示位置。

  Ⅳ、在OnTouchEvent()方法处理控件的触摸事件。

  相应的思维导图如下:

  多说无益,我们通过几个小案例,来讲解到底如何实现自定义控件。

  一、一个带有比例进度的环形控件

  首先看一下这个控件的示意图:

  通过这个简单的示意图,我们对于项目所完成的比例,就一目了然了。通过这个简单的示意图,我们可以很快速的把这个图形分成三个部分。Ⅰ、外层的环,Ⅱ、里面的园,三、相应文字。有了这个思路以后,我们只需要在onDraw()方法中一个个进行绘制罢了。我这里为了简单起见,把这个控件的宽度设置为屏幕的宽度。

  首先,还是老样子,为自定义控件设置一些自定义属性,便于调用者对其进行扩展,自定义属性的设置代码为:

  <declare-styleable name="circleView">
<attr name="textSize" format="dimension" />
<attr name="text" format="string" />
<attr name="circleColor" format="color" />
<attr name="arcColor" format="color" />
<attr name="textColor" format="color" />
<attr name="startAngle" format="integer" />
<attr name="sweepAngle" format="integer" />
</declare-styleable>

  Ⅰ、textSize——对应中间文本文字的大小

  Ⅱ、text——对应中间文本

  Ⅲ、circleColor——对应内圆的颜色

  Ⅳ、arcColor——对应外环的颜色

  Ⅴ、textColor——对应文本的颜色

  Ⅵ、startAngle——对应外环的起始角度

  Ⅶ、sweepAngle——对应外环扫描角度

  紧接着,就是在自定义控件的初始化方法中来获取这些自定义属性:

TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.circleView);
if (ta != null) {
circleColor = ta.getColor(R.styleable.circleView_circleColor, 0);
arcColor = ta.getColor(R.styleable.circleView_arcColor, 0);
textColor = ta.getColor(R.styleable.circleView_textColor, 0);
textSize = ta.getDimension(R.styleable.circleView_textSize, 50);
text = ta.getString(R.styleable.circleView_text);
startAngle = ta.getInt(R.styleable.circleView_startAngle, 0);
sweepAngle = ta.getInt(R.styleable.circleView_sweepAngle, 90);
ta.recycle();
}

  这里在多说一嘴子,为了释放更多的资源,一定要将TypedArray这个对象进行资源的释放。

  接下来,在OnMeasure()方法中,初始化要绘制画笔样式,获取屏幕的宽度,计算中间位置的坐标,以及指定外接矩形的宽高代码如下:

    private void init() {
int length = Math.min(width, height);
mCircleXY = length / 2;
mRadius = length * 0.5f / 2;
mCirclePaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mCirclePaint.setColor(circleColor);
mRectF = new RectF(length * 0.1f, length * 0.1f, length * 0.9f,
length * 0.9f); mArcPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mArcPaint.setColor(arcColor);
mArcPaint.setStyle(Paint.Style.STROKE);
mArcPaint.setStrokeWidth((width * 0.1f)); mTextPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mTextPaint.setTextSize(textSize);
mTextPaint.setColor(textColor);
mTextPaint.setTextAlign(Align.CENTER); }

  将我们设置的自定义属性,设置给不同笔刷。

  做了这么多准备以后,我们所需的就是在OnDraw方法中绘制内圆、外环与文字。代码如下:

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
drawSth(canvas);
} private void drawSth(Canvas canvas) {
canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);
canvas.drawArc(mRectF, startAngle, sweepAngle, false, mArcPaint);
canvas.drawText(text, 0, text.length(), mCircleXY, mCircleXY + textSize
/ 4, mTextPaint);
}

  需要指出的是,画环形需要在一个指定矩形区域画取,并且要指定起始角度,扫描角度,这些变量都是自定义属性。 

  这个自定义控件的最终的运行效果为:

  二、动态条形图

  条形图,应该在图表展示系统中再普通不过的一种图标了。静态示意图就像这样:

   通过这个简单的示意图,我们所需要做的是,绘制一个个的矩形,然后将每一个矩形x坐标平移一定的单位,我们还看到这么一个现象:每个条形图的起始点不一致,而终止点是一样的。起始坐标用个Random(随机函数)刚刚好,实现静态条形图就是这样的思路:

   首先,在OnMeasure()方法中计算出每个矩形宽与高,这里为了方便起见,每个矩形默认的高为屏幕的高,每个矩形的宽这里定义为屏幕的宽度乘以80%除以矩形的个数。然后根据宽与高来初始化笔刷(Paint)。为什么要根据宽与高来初始化笔刷了,这里我为了使自定义View更加的逼真,我这里使用LinearGradient(线性渲染器)进行了渲染,这个对象需要使用矩形宽与高。需要指出来的是这个自定义控件是动态的,我只需要onDraw方法不断发生重绘,这里为了防止控件刷新太快,我这里每隔300毫秒刷新视图。这个控件的完整源代码如下:

public class VolumneView extends View {

    private Paint mPaint;
private int mCount;
private int mWidth;
private int mRectHeight;
private int mRectWidth;
private LinearGradient mLinearGradient;
private double mRandom;
private float mcurrentHeight; public static final int OFFSET = 5; public VolumneView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
initView(context, attrs); } private void initView(Context context, AttributeSet attrs) {
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setColor(Color.GREEN);
mPaint.setStyle(Paint.Style.FILL);
TypedArray ta = context.obtainStyledAttributes(attrs,
R.styleable.volumneView);
if (ta != null) {
mCount = ta.getInt(R.styleable.volumneView_count, 6);
ta.recycle();
}
} public VolumneView(Context context, AttributeSet attrs) {
this(context, attrs, 0);
} public VolumneView(Context context) {
this(context, null);
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
mWidth = getMeasuredWidth();
mRectHeight = getMeasuredHeight();
mRectWidth = (int) (mWidth * 0.8 / mCount);
mLinearGradient = new LinearGradient(0, 0, mRectWidth, mRectHeight,
Color.GREEN, Color.YELLOW, TileMode.CLAMP);
mPaint.setShader(mLinearGradient); } @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
for (int i = 0; i < mCount; i++) {
mRandom = Math.random();
mcurrentHeight = (float) (mRectHeight * mRandom);
float width = (float) (mWidth * 0.4 / 2 + OFFSET);
canvas.drawRect(width + i * mRectWidth, mcurrentHeight, width
+ (i + 1) * mRectWidth, mRectHeight, mPaint);
}
postInvalidateDelayed(300);
}

  最终,运行效果如下:

  后记,通过这两个简单控件,相信大家对于自定义控件基本步骤有说了解,当然,要真正做好自定义控件的话,我们还需要按这个步骤一步步的来。任重而道远。

  本人才疏学浅,恳请吐槽。

Android 自定义View 三板斧之三——重写View来实现全新控件的更多相关文章

  1. Android开发系列(十八):自己定义控件样式在drawable目录下的XML实现

    在Android开发的过程中,我们常常须要对控件的样式做一下改变,能够通过用添加背景图片的方式进行改变,可是背景图片放多了肯定会使得APK文件变的非常大. 我们能够用自己定义属性shape来实现. s ...

  2. android自定义View_0——Create a custom view class

    一:创建一个自定义view的原则: 1.符合android的标准 2.能在layout的xml中定义控件属性 3.实现些新功能 4.要兼容android的大多数版本 二:定义一个view的子类 1.可 ...

  3. Android自定义View,高仿QQ音乐歌词滚动控件!

    最近在以QQ音乐为样板做一个手机音乐播放器,源码下篇博文放出.今天我想聊的是这个QQ音乐播放器中歌词显示控件的问题,和小伙伴们一起来探讨怎么实现这个歌词滚动的效果.OK,废话不多说,先来看看效果图: ...

  4. 自定义View的学习(一) 自绘制控件

    一.自绘控件 就是自己绘制的控件,通过onDraw()方法将控件绘制出来  自定义一个可点击的View  这个View可以记住用户点击的次数 public class CounterView exte ...

  5. Android 高级UI设计笔记14:Gallery(画廊控件)之 3D图片浏览

    1. 利用Gallery组件实现 3D图片浏览器的功能,如下: 2. 下面是详细的实现过程如下: (1)这里我是测试性代码,我的图片是自己添加到res/drawable/目录下的,如下: 但是开发中不 ...

  6. 《Android群英传》读书笔记 (2) 第三章 控件架构与自定义控件详解 + 第四章 ListView使用技巧 + 第五章 Scroll分析

    第三章 Android控件架构与自定义控件详解 1.Android控件架构下图是UI界面架构图,每个Activity都有一个Window对象,通常是由PhoneWindow类来实现的.PhoneWin ...

  7. 【Android】11.0 UI开发(二)——列表控件ListView的简单实现1

    ************************ 转载请注明出处:https://www.cnblogs.com/xiaofu007/p/10342462.html ***************** ...

  8. Android UI设计中一些不错的示例及第三方控件

    1.二级ListView自定义布局ExpandableListView http://pan.baidu.com/s/1mhlJh12 密码:nhc2 2.ListView实现各种动画效果ListVi ...

  9. iOS开发——UI进阶篇(二)自定义等高cell,xib自定义等高的cell,Autolayout布局子控件,团购案例

    一.纯代码自定义等高cell 首先创建一个继承UITableViewCell的类@interface XMGTgCell : UITableViewCell在该类中依次做一下操作1.添加子控件 - ( ...

随机推荐

  1. LTS

    LTS(light-task-scheduler)主要用于解决分布式任务调度问题,支持实时任务,定时任务和Cron任务.有较好的伸缩性,扩展性,健壮稳定性而被多家公司使用. 1. 支持分布式,解决多点 ...

  2. Spring Framework 笔记(一):IoC

    一:Spring中重要的概念 1. 容器( container ) : spring容器( ApplicationContext )的工作原则是创建容器中的组件( instance ),处理组件之间的 ...

  3. PHP操作Mongodb之高级查询篇

    本文主要讲解PHP中Mongodb的除了增删改查的一些其他操作. 在PHP操作Mongodb之增删改查篇中我们介绍了PHP中Mongodb的增加.删除.修改及查询数据的操作.本文主要是将查询时用到的高 ...

  4. new 一个button 然后dispose,最后这个button是null吗???

    结果当然不是,button虽然释放了资源,但是它扔指向原来的那个地址,故不等于null 可以用button.isdispose==true判断

  5. 用jQuery修改background图片平铺方式

    <html><head> <title>测试css修改</title></head><body> <img src=&qu ...

  6. javascript面向对象(三)

    主要内容: 利用原型链的方式实现继承: 原型继承的特点:即继承了父类的模板,也继承了父类的原型对象. 类继承:只继承模板(借用构造函数的方式继承). 利用call.apply方法实现: 混合继承: 扩 ...

  7. python json模块

    import json dic = {"name":"liao","age":18} data = json.dumps(dic) #转化为 ...

  8. (Python)继承

    面向对象的另一个特性是继承,继承可以更好的代码重用. 例如一个学校里面的成员有老师.学生.老师和学生都有共同的属性名字和年纪.但老师还有它自己的属性,如工资.学生也有它的属性,如成绩. 因此我们可以设 ...

  9. VB中的API详解

    一.API是什么? 这个我本来不想说的,不过也许你知道其它人不知道,这里为了照顾一下新手,不得不说些废话,请大家谅解. Win32 API即为Microsoft 32位平台的应用程序编程接口(Appl ...

  10. text-shadow文字阴影属性用法

    text-shadow:offset-x:阴影水平移动,负值时向左偏移 text-shadow:offset-y:阴影垂直移动,负值时向上移动 text-shadow:radio-bluer:阴影到实 ...