注意1:
1、关键部分:
Drawing the layout is a two pass process: a measure pass and a layout pass.
所以一个view执行OnDraw时最关键的是measure和layout。其实这很好理解的,一个view需要绘制出来,那么必须知道他要占多大的空间也就是measure,还得知道在哪里绘制,也就是把view放在哪里即layout。把这两部分掌握好也就可以随意自定义view了。至于viewGroup中如何绘制就参考官方文档,其实就是一个分发绘制,直到child是一个view自己进行绘制。
2、重写一个view一般情况下只需要重写onDraw()方法。那么什么时候需要重写onMeasure()、onLayout()、onDraw() 方法呢,这个问题只要把这几个方法的功能弄清楚就应该知道怎么做了。
①、如果需要绘制View的图像,那么需要重写onDraw()方法。(这也是最常用的重写方式。)
②、如果需要改变view的大小,那么需要重写onMeasure()方法。
③、如果需要改变View的(在父控件的)位置,那么需要重写onLayout()方法。
④、根据上面三种不同的需要你可以组合出多种重写方案。
3、按类型划分,自定义View的实现方式可分为三种:自绘控件、组合控件、以及继承控件。
注意2:
View的三种测量模式
ViewGroup会为childView指定测量模式,下面简单介绍下三种测量模式:
1、EXACTLY:表示设置了精确的值,一般当childView设置其宽、高为精确值、match_parent时,ViewGroup会将其设置为EXACTLY;
2、AT_MOST:表示子布局被限制在一个最大值内,一般当childView设置其宽、高为wrap_content时,ViewGroup会将其设置为AT_MOST;
3、UNSPECIFIED:表示子布局想要多大就多大,一般出现在AadapterView的item的heightMode中、ScrollView的childView的heightMode中;此种模式比较少见。
【备注】:
上面的每一行都有一个一般,意思上述不是绝对的,对于childView的mode的设置还会和ViewGroup的测量mode有一定的关系;当然了,这是第一篇自定义ViewGroup,而且绝大部分情况都是上面的规则,所以为了通俗易懂,暂不深入讨论其他内容。
注意3:
三种类型的自定义View
(1)自绘控件:
1、概念:自绘控件的意思就是,这个View上所展现的内容全部都是自己绘制出来的。
2、自绘控件的步骤:
1、绘制View :
绘制View主要是onDraw()方法中完成。通过参数Canvas来处理,相关的绘制主要有drawRect、drawLine、drawPath等等。
Canvas绘制的常用方法:
drawColor() 填充颜色
drawLine() 绘制线
drawLines() 绘制线条
drawOval() 绘制圆
drawPaint()
drawPath() 绘制路径
drawPicture() 绘制图片
drawPoint() 绘制点
drawPoints() 绘制点
drawRGB() 填充颜色
drawRect() 绘制矩形
drawText() 绘制文本
drawTextOnPath() 在路径上绘制文本
2、刷新View :(刷新view的方法这里主要有:)
invalidate(int l,int t,int r,int b)
刷新局部,四个参数分别为左、上、右、下
invalidate()
整个view刷新。执行invalidate类的方法将会设置view为无效,最终重新调用onDraw()方法。
invalidate()是用来刷新View的,必须是在UI线程中进行工作。在修改某个view的显示时,调用invalidate()才能看到重新绘制的界面。invalidate()的调用是把之前的旧的view从主UI线程队列中pop掉。
invalidate(Rect dirty)
刷新一个矩形区域
3、控制事件:(例如处理以下几个事件)
onSaveInstanceState() 处理屏幕切换的现场保存事件
onRestoreInstanceState() 屏幕切换的现场还原事件
onKeyDown() 处理按键事件
onMeasure() 当控件的父元素正要放置该控件时调用
(2)组合控件:
概念:
组合控件的意思就是,不需要自己去绘制视图上显示的内容,而只是用系统原生的控件就好了,但可以将几个系统原生的控件组合到一起,这样创建出的控件就被称为组合控件。
(3)继承控件
概念:
继承控件的意思就是,我们并不需要自己重头去实现一个控件,只需要去继承一个现有的控件,然后在这个控件上增加一些新的功能,就可以形成一个自定义的控件了。
案例一:
自定义ImageView控件:自定义ImageView控件继承ImageView
备注:
短句:使用短句生成构造方法 ViewConstructor
自定义ImageView控件类

public class SmartImageView extends ImageView {private Handler handler = new Handler(){@Overridepublic void handleMessage(Message msg) {super.handleMessage(msg);switch (msg.what){case 1://自定义ImageView类对象调用此方法setImageBitmap((Bitmap) msg.obj);break;case 2:setImageResource(msg.arg1);break;}}};public SmartImageView(Context context) {this(context, null);}public SmartImageView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public SmartImageView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);}public void setSmartImageView(final String url, final int resId){new Thread(){@Overridepublic void run() {super.run();Message msg = Message.obtain();byte[] data = OkHttpUtils.getByteArrayByUrl(url);if(data != null){Bitmap bitmap = BitmapFactory.decodeByteArray(data,0,data.length);msg.what = 1;msg.obj = bitmap;}else{msg.what = 2;msg.arg1 = resId;}handler.sendMessage(msg);}}.start();}}

xml:

<com.sign.days28selfview01.SmartImageViewandroid:id="@+id/ivPic"android:layout_width="match_parent"android:layout_height="match_parent"android:scaleType="fitXY"android:src="@mipmap/ic_launcher"android:contentDescription="@string/app_name"/>

MainActivity:

 public void download(View view) {//调用自定义ImageView控件类的设置图片方法ivPic.setSmartImageView(url,R.mipmap.ic_launcher);}

案例二:自定义TextView控件
自定义TextView控件类

public class Rular extends TextView {public Rular(Context context) {this(context, null);}public Rular(Context context, AttributeSet attrs) {this(context, attrs, 0);}public Rular(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);
//       设置Ruler中的文字位置。相当于属性中的android:gravitysetGravity(Gravity.BOTTOM);}/*** 重写该方法,实现自己“绘制”控件的样式* 该方法无须手动调用, 系统在绘制该控件时会自动调用该方法绘制该控件* @param canvas*/@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);
//      Paint(画笔Paint p = new Paint();
//      设置画笔颜色p.setColor(Color.YELLOW);//        getWidth()为获取控件的宽度int w = getWidth()/10;int top = 1;for (int i = 0;i<10;i++){
//            canvas的drawRect(float left,float top,float right,float bottom,Paint paint)
//            参数分别为左上右下(顺时针)(左上右下离控件的x,y轴的距离),画笔对象
//            Canvas(画布)canvas.drawRect(i,top,w+i*w,top+w,p);}
//        for循环语句相当于
//        canvas.drawRect(0,top,getWidth(),top+w,p);}
}

布局文件:
MainActivity添加控件时会自动找到控件对应的类,并自动调用其中方法对控件进行相应设置

  <com.sign.days28selfview02ruler.Rularandroid:layout_width="40dp"android:layout_height="40dp"android:background="#0cf"android:text="0cm" /><com.sign.days28selfview02ruler.Rularandroid:layout_width="40dp"android:layout_height="40dp"android:background="#acc"android:text="1cm" /><com.sign.days28selfview02ruler.Rularandroid:layout_width="40dp"android:layout_height="40dp"android:background="#f0d"android:text="2cm" /><com.sign.days28selfview02ruler.Rularandroid:layout_width="40dp"android:layout_height="40dp"android:background="#ae0"android:text="3cm" /><com.sign.days28selfview02ruler.Rularandroid:layout_width="40dp"android:layout_height="40dp"android:background="#0d0"android:text="4cm" />

案例三:
组合控件:
将progressbar与button组合在一起,点击button按钮后,发送一个消息,在主线程中更新progressBar进度并判断是否到100%若不是继续发送消息并根据进度设置Button上文字,若是则不再发送消息并设置Button文字为下载完成
(实际上是ProgressBar在下面,Button在上面,看到的ProgressBar进度实际上是ProgressBar的边框在更新)
备注:在Android的layout样式定义中,可以使用xml文件方便的实现,有时候为了模块的复用,使用include标签可以达到此目的。
在activity_main中:

<include layout="@layout/my_layout"></include>

引入my_layout布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"xmlns:tools="http://schemas.android.com/tools"android:layout_width="match_parent"android:layout_height="70dp"android:orientation="vertical"android:paddingBottom="@dimen/activity_vertical_margin"android:paddingLeft="@dimen/activity_horizontal_margin"android:paddingRight="@dimen/activity_horizontal_margin"android:paddingTop="@dimen/activity_vertical_margin"tools:context="com.sign.days28selfview03progressbutton.MainActivity"><ProgressBarandroid:id="@+id/pbShow"style="?android:attr/progressBarStyleHorizontal"android:layout_width="match_parent"android:layout_height="25dp"android:max="100"android:progress="0"android:progressDrawable="@drawable/my_back"/><Buttonandroid:id="@+id/btn"android:layout_width="match_parent"android:layout_height="match_parent"android:textColor="#0fc"android:textSize="16sp"android:onClick="start"android:text="start"/>
</LinearLayout>

此处注意:
progressbar的progressDrawable属性为一个xml文件
还不太理解??????

<layer-list xmlns:android="http://schemas.android.com/apk/res/android"><item><!--<clipandroid:drawable="@drawable/btn_back_pre"android:gravity="left"></clip>--><clip
            android:clipOrientation="horizontal"android:gravity="left"android:drawable="@drawable/pb_back"></clip></item>
</layer-list>

此处再注意:
第二个clip节点的drawable为名为pb_back的xml文件
**备注:**Android中常常使用shape来定义控件的一些显示属性
参考自:
http://blog.csdn.net/by317966834/article/details/8773518
http://blog.csdn.net/harvic880925/article/details/41850723
pd_back.xml

<shape xmlns:android="http://schemas.android.com/apk/res/android"><!--设置渐变startColor centerColor endColor 起始、中间、结束颜色type 渐变模式 linear 线性渐变 radial 径向渐变 需要指定半径--><gradient
        android:centerColor="#0cd"android:centerX="0"android:centerY="0"android:endColor="#f00"android:gradientRadius="50"android:startColor="#0f0"android:type="radial" /><!--设置填充--><solid android:color="@android:color/holo_blue_bright" /><!--设置dashGap和dashWidth则边框显示为虚线,dashGap为破折线空隙,dashWidth为破折线长度dashGap为0dp时为实线 dashWidth需要指定--><!--width为边框的宽度--><stroke
        android:width="5dp"android:color="@android:color/holo_orange_light"android:dashGap="2dp"android:dashWidth="0dp" /><!--设置矩形的圆角半径--><corners android:radius="5dp" />
</shape>

案例四
自定义ProgressView继承View
效果:一个逐渐转动变大的扇形
在values目录下添加attrs.xml文件

<resources><!--attrs.xml文件   该文件是定义属性名和格式的地方,declare-styleable是给自定义控件添加自定义属性用的需要用<declare-styleable name="ToolBar"></declare-styleable>包围所有属性。其中name为该属性集的名字,主要用途是标识该属性集--><declare-styleable name="MyProgressView"><attr name="sweepStep" format="integer"/><attr name="padding" format="integer"/><attr name="startAngle" format="integer"/><attr name="circleColor" format="color|reference"/><attr name="sweepColor" format="color|reference"/></declare-styleable>
</resources>

自定义ProgressView控件

public class MyProgressView extends View {private static final int DEFAULT_WIDTH = 100;private static final int DEFAULT_HEIGHT = 100;private int sweepAngle = 0;//扇形扫过的角度private int sweepStep = 5;//扇形添加的角度private int padding = 5;//设置扇形与圆之间距离private int startAngle = -90;//设置扇形开始的角度private int circleColor = Color.GREEN;//圆的背景颜色private int sweepColor = Color.BLUE;//扇形的颜色public MyProgressView(Context context) {this(context, null);}public MyProgressView(Context context, AttributeSet attrs) {this(context, attrs, 0);}public MyProgressView(Context context, AttributeSet attrs, int defStyle) {super(context, attrs, defStyle);
/*** TypedArray实例是个属性的容器,context.obtainStyledAttributes()方法返回得到*  context.obtainStyledAttributes(AttributeSet set,@StyleableRes int[] attrs)*  set :节点的属性集合*  attrs :包含attr节点的StyleableRes节点name标识*/TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.MyProgressView);if(array != null){
//            分别获取MyProgress节点中的各个属性sweepStep = array.getInteger(R.styleable.MyProgressView_sweepStep,sweepStep);startAngle = array.getInteger(R.styleable.MyProgressView_startAngle,startAngle);padding = array.getInteger(R.styleable.MyProgressView_padding,padding);sweepColor = array.getInteger(R.styleable.MyProgressView_sweepColor,sweepColor);circleColor = array.getInteger(R.styleable.MyProgressView_circleColor,circleColor);//          array复用  作用:在TypedArray后调用recycle主要是为了缓存。当recycle被调用后,这就说明这个对象从现在可以被重用了array.recycle();}}@Overrideprotected void onDraw(Canvas canvas) {super.onDraw(canvas);Paint p = new Paint();//        消除锯齿p.setAntiAlias(true);//        画圆
//        设置圆的颜色p.setColor(circleColor);
//        前两个参数分别确定圆心x、y坐标,第三个参数为圆的半径,第四个参数为画笔canvas.drawCircle(getWidth()/2,getWidth()/2,getWidth()/2,p);//        画扇形
//        重新设置画笔颜色为扇形颜色p.setColor(sweepColor);/*** public void drawArc(RectF oval, float startAngle, float sweepAngle, boolean useCenter, Paint paint)oval :指定圆弧的外轮廓矩形区域。startAngle: 圆弧起始角度,单位为度。0度为水平向左sweepAngle: 圆弧扫过的角度,顺时针方向,单位为度,从右中间开始为零度。useCenter: 如果为True时,在绘制圆弧时将圆心包括在内,通常用来绘制扇形。paint: 画笔*/canvas.drawArc(new RectF(padding,padding,getWidth()-padding,getWidth()-padding),startAngle,sweepAngle,true,p);//        每画完一个扇形累加已扫过的角度sweepAngle += sweepStep;//        当已扫过的角度为360时,将已扫过的角度置空sweepAngle= sweepAngle >= 360 ? 0:sweepAngle;//         刷新整个界面(每次画完一个扇形就刷新整个界面,看起来就像扇形在更新进度)invalidate();}/*** 设置控件自身的宽和高* @param widthMeasureSpec* @param heightMeasureSpec*/@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int widthMode = MeasureSpec.getMode(widthMeasureSpec);int widthSize = MeasureSpec.getSize(widthMeasureSpec);int heightSize = MeasureSpec.getSize(heightMeasureSpec);switch (widthMode){case MeasureSpec.AT_MOST:widthSize = DEFAULT_WIDTH;heightSize = DEFAULT_HEIGHT;break;case MeasureSpec.EXACTLY:widthSize = heightSize = Math.min(widthSize,heightSize);break;case MeasureSpec.UNSPECIFIED:break;}
//      这个方法决定了当前View的大小setMeasuredDimension(widthSize,heightSize);}
}

activity_main文件:
备注: xmlns:app=”http://schemas.android.com/apk/res-auto”
android中xml中有些控件的属性里面有 “app:..” ,此处的app:是什么意思?和一般的android:有什么区别?
答:这两个是声明的不同的命名空间,android的是系统的,app是自定义的。Android自定义控件的属性,在xml中使用自己自定义的attr的时候,其中有一步就是要自定义一个xml的命名空间后然后再给自定义属性赋值。
参考自:http://zhidao.baidu.com/link?url=628WYzFtOkZXw2aAau3vV8yZhBWN6OApXGFxn5IBTmy7OUUekmOeaQxpWpc9RLr-ecsYG-QzpA1Tz7ncbrEApC5RyYwRIFRvBzXOteg0aky

 <!--注意:此处使用的为wrap_content,在MyProgressView中会走到case MeasureSpec.AT_MOST设置宽和高为100,但其为100px,所以与下一个控件的大小100dp并不相等--><com.sign.days28selfviw04progress.MyProgressView
        android:layout_width="wrap_content"android:layout_height="wrap_content"/><com.sign.days28selfviw04progress.MyProgressView
        android:layout_width="100dp"android:layout_height="100dp"app:circleColor="#0f0"app:sweepColor="#0cc"app:padding="26"app:startAngle="0"app:sweepStep="5"/>

Days28 自定义Veiw(一)相关推荐

  1. android波浪线,android自定义veiw——波浪线

    [实例简介] 详情:http://blog.csdn.net/it_xf/article/details/75014160 [实例截图] [核心代码] src ├── AndroidManifest. ...

  2. QuickContactBadge和AsyncQueryHandler实现联系人列表完美实现

    打造你自己的个性联系人列表 我也是醉了,昨天下午写的博客有点小问题把已经发布的博客修改了下,然后今天博客就丢失了(猛然发现点击打开链接被转载到那里去了,算了还是出现整理下吧,那里好像有的地方说的不明白 ...

  3. Error inflating class 某个View

    现象: 运行XML布局引发异常: 原因: 1.引用自定义View,路径不全. 2.自定义Veiw没有实现全部的构造方法: 如: public class BaseListView extends Li ...

  4. 安卓自定义View进阶-分类与流程

    自定义View绘制流程函数调用链(简化版) 一.自定义View分类 我将自定义View分为了两类(sloop个人分类法,非官方): 1.自定义ViewGroup 自定义ViewGroup一般是利用现有 ...

  5. android自定义进度条百分比跟着走,Android自定义View实现水平带数字百分比进度条...

    这个进度条可以反映真实进度,并且完成百分比的文字时随着进度增加而移动的,所在位置也恰好是真实完成的百分比位置,效果如下: 思路如下:第一部分是左侧的蓝色直线,代表已经完成的进度:第二部分是右侧灰色的直 ...

  6. Carson带你学Android:源码解析自定义View Draw过程

    前言 自定义View是Android开发者必须了解的基础 网上有大量关于自定义View原理的文章,但存在一些问题:内容不全.思路不清晰.无源码分析.简单问题复杂化 等 今天,我将全面总结自定义View ...

  7. Django 基础(12)-Django drf 分页查询(批量查询)、自定义分页器

    文章目录 Django drf 分页查询(批量查询) PageNumberPagination:普通分页 Django 自定义分页器 自定义批量查询的返回结构 LimitOffsetPaginatio ...

  8. iOS自定义backBarButtonItem的点击事件

    最近遇到一个关于导航栏返回按钮的问题,因为之前项目里面都是用的系统默认的返回按钮样式所以没有想过要去更改,后来有需要将返回按钮箭头旁边的文字去掉,同时将该返回按钮的点击事件重新定义.一开始尝试自定义按 ...

  9. Android进阶之光读书笔记——第三章:View体系与自定义View

    第三章 View体系与自定义View 本章将介绍Android中十分重要的View,在多本书中View是必讲的一节,Android群英传就讲了不少的View的知识,那么在这里我们再去复习一遍吧 3.1 ...

最新文章

  1. 量子力学 一 基础7 酉算符与Hausdorff-Campbell公式
  2. GridView滚动条
  3. docker绑定端口主机访问curl: (56) Recv failure: Connection reset by peer
  4. java如何画百分比圆环_canvas绘制百分比圆环进度条
  5. Bootstrap 两端对齐的导航
  6. 华为余承东鸿蒙系统随时可以上线,华为发布鸿蒙系统,余承东称随时可替换安卓...
  7. windbg linux内核调试,使用Windbg调试window内核
  8. powershell自动化操作AD域、Exchange邮箱系列(2)—环境要求、搭建及初步演示
  9. 第六章节 三层架构(一. 三层架构的概述)
  10. Qt学习之路_6(Qt局域网聊天软件)
  11. 小韦XPSP3 V10.0_Ghost精简版
  12. matlab简单几何图形的识别代码,MATLAB识别几何图形
  13. 记一次简单爬虫(豆瓣/dytt)
  14. 手绘 | 7天自助游玩古都西安 3
  15. 【三十五】Python全栈之路--MySQL
  16. 计算机专业硕士论文字数要求,计算机专业硕士论文格式规范
  17. 阿龙的学习笔记---《程序员自我修养-链接、装载与库》读书笔记(三)
  18. android--获取手机的IMSI码 并判断是中国移动\中国联通\中国电信
  19. 抽丝剥茧看华为p20pro, 三星s9+, 小米8 样张低频彩噪
  20. docker制作nginx+nginx-module-vts镜像基于alpine镜像

热门文章

  1. PAT相关的基础知识
  2. c语言里判断回文数的函数,(C语言)回文数的判断
  3. 湖南大学python头歌实训-分支语句
  4. 程序员,你是选择25k的996 还是18k的八小时?
  5. Photoshop高级应用之线稿上色实例:女孩
  6. 外包没有资格 WFH?
  7. 哈工大计算机专硕延毕,哈工大陈慧祥:赴美国留学遇到贵人,6年后因贵人在实验室上吊...
  8. 虚拟机xshell频繁断开解决方案
  9. 江西是否有清华It校区
  10. python 新建cad文件_Python AutoCAD 系统设置的实现方法