自定义View(三)(Android群英传)

内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出。

上一篇自定义View(二)(Android群英传)中说的是创建复合控件,这篇介绍第三种自定义View方法,重写View来实现全新的控件

重写View来实现全新的控件

当Android系统原生的控件无法满足我们的需求时,我们就可以完全创建一个新的自定义View来实现需要的功能。创建一个自定义View,难点在于绘制控件和实现交互,这也是评价一个自定义View优劣的标准之一。通常需要继承View类,并重写它的onDraw()、onMeasure()等方法来实现绘制逻辑,同时通过重写onTouchEvent()等触控事件来实现交互逻辑。当然,我们还可以像实现组合控件方式那样,通过引入自定义属性,丰富自定义View的可定制性。
  下面就通过几个实例,让大家了解如何创建一个自定义View,不过为了让程序尽可能简单,我们就不去自定义属性值了。

弧线展示图

在PPT的很多模板中,都有如下图所示的这样一张比例图。

比例图

  这个比例图可以非常清楚地展示一个项目所占的比例,简介明了。因此,实现这样一个自定义View用在我们的程序中,可以让整个程序实现比较清晰地数据展示效果。那么该如何创建一个这样的自定义View呢?很明显,这个自定义View其实分为三个部分,分别是中间的圆形、中间显示的文字和外圈的弧线。既然有了这样的思路,只要在onDraw()方法中一个个去绘制就可以了。这里为了简单,我们把View的绘制长度直接设置为屏幕的宽度。首先,在初始化的时候,设置好绘制三种图形的参数。圆的代码如下所示。

mCircleXY = length / 2;
mRadius = (float) (length * 0.5 / 2);

绘制弧线,需要指定其椭圆的外接矩形,代码如下所示。

mArcRectF = new RectF(
        (float) (length * 0.1),
        (float) (length * 0.1),
        (float) (length * 0.9),
        (float) (length * 0.9));

绘制文字,只需要设置好文字的起始绘制位置即可。
  接下来,我们就可以在onDraw()方法中进行绘制了,代码如下所示。

// 绘制圆
canvas.drawCircle(mCircleXY, mCircleXY, mRadius, mCirclePaint);
// 绘制弧线
canvas.drawArc(mArcRectF, 270, mSweepAngle, false, mArcPaint);
// 绘制文字
canvas.drawText(mShowText, 0, mShowText.length(), mCircleXY, mCircleXY + mShowTextSize / 4, mTextPaint);

相信这些图形如果单独让你去绘制,应该是非常容易的事情,只是这里进行了一下组合,就创建了一个新的View。其实,不论是多么复杂的图形、控件,它都是由这些最基本的图形绘制出来的,关键就在于你如何去分解、设计这些图形,当你脑海中有了一幅设计图之后,剩下的事情就只是对坐标的计算了。
  当然,对于这个简单的View,有一些方法可以让调用者来设置不同的状态值,代码如下所示。

public void setSweepValue(float sweepValue) {
    if (sweepValue != 0) {
        mSweepValue = sweepValue;
    } else {
        mSweepValue = 25;
    }
    this.invalidate();
}

例如,当用户不指定具体的比例值时,可以默认设置为25,而调用者可以通过如下代码来设置相应的比例值。

CircleProgressView circle = (CircleProgressView) findViewById(R.id.circle);
circle.setSweepValue(70);

音频条形图

以下这个问题来源于群里一位开发者的问题,他想实现类似在PC上某些音乐播放器上根据音频音量大小显示音频条形图,如下图所示。

音频条形图

  如果要实现一个如下图所示的静态音频条,相信大家可以很快找到思路,也就是绘制一个个矩形,每个矩形之间稍微偏移一点距离即可。
静态音频条形图

  如下代码就展示了一种计算坐标的方法。

@Override
protected void onDraw(Canvas canvas) {
    super.onDraw(canvas);
    for (int i = 0; i < mRectCount; ++i) {
        canvas.drawRect(mRectWidth * i + offset,
                currentHeight,
                mRectWidth * (i + 1),
                mRectHeight,
                mPaint);
    }
}

如上代码中,我们通过循环创建这些小的矩形,其中currnetHeight就是每个小矩形的高,通过横坐标的不断偏移,就绘制出了这些静态的小矩形。下面我们再让这些小矩形的高度进行随机变化,通过Math.random()方法来随机改变这些高度值,并赋值给currentHeight,代码如下所示。

mRandom = Math.random();
float currentHeight = (float) (mRectHeight * mRandom);

这样,我们就完成了静态效果的绘制,那么如何实现动态效果呢?其实非常简单,只要在onDraw()方法中再去调用invalidete()方法通知View进行重绘就可以了。不过,在这里不需要每次一绘制完新的矩形就通知View进行重绘,这样会因为刷新速度太快反而影响效果。因此,我们可以使用如下代码进行View的延迟重绘,代码如下所示。

postInvalidateDelayed(300);

这样每隔300ms通知View进行重绘,就可以得到一个比较好的视觉效果了。最后,为了让自定义View更加逼真,可以在绘制小矩形的时候,给绘制的Paint对象增加一个LinearGradient渐变效果,这样不同高度的矩形就会有不同颜色的渐变效果,更加能够模拟音频条形图的风格,代码如下所示。

@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
    super.onSizeChanged(w, h, oldw, oldh);
    mWidth = getWidth();
    mRectHeight = getHeight();
    mRectWidth = mWidth / mRectCount;
    mLinearGradient = new LinearGradient(0, 0,
            mRectWidth, mRectHeight,
            Color.YELLOW, Color.GREEN,
            Shader.TileMode.CLAMP);
    mPaint.setShader(mLinearGradient);
}

从这个例子中,我们可以知道,在创建自定义View的时候,需要一步步来,从一个基本的效果开始,慢慢地增加功能,绘制更复杂的效果。不论是多么复杂的自定义View,它一定是慢慢迭代起来的功能,所以不要觉得自定义View有多难。千里之行始于足下,只要开始做,慢慢地就能越来越熟练。
项目地址→RewriteView


原文地址自定义View(三)(Android群英传)
我的自媒体博客blankj小站(OJ、LeetCode、Android开发),欢迎来逛逛。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,172评论 6 493
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,346评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,788评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,299评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,409评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,467评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,476评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,262评论 0 269
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,699评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,994评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,167评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,827评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,499评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,149评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,387评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,028评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,055评论 2 352

推荐阅读更多精彩内容

  • Android 自定义View的各种姿势1 Activity的显示之ViewRootImpl详解 Activity...
    passiontim阅读 171,953评论 25 707
  • 导语 当系统控件不能满足我们的需求的时候,这时候我们就需要自定义控件,根据我们的需求来定制一个能满足我们需求的控件...
    一个有故事的程序员阅读 6,390评论 2 14
  • 内容是博主照着书敲出来的,博主码字挺辛苦的,转载请注明出处,后序内容陆续会码出。 Android给我们提供了丰富的...
    Blankj阅读 2,709评论 1 24
  • Python for you and me:http://pymbook.readthedocs.io/en/la...
    lupinwu阅读 175评论 0 0
  • 当兵后悔俩年,不当兵后悔一辈子。这是我高中一直坚信的真理。 真正有想当兵的想法是在高中,我记得高二下那年班主任在班...
    简言之ii阅读 362评论 2 2