PS:好久没有写博客了,之前的东西有所忘记,百度一下竟然查到了自己的写过的博客,访问量还可以,一开始的写博客的初衷是把自己不会的记录下来,现在没想到也有博友会关注我,这就给了我动力,工作之余把零零碎碎的东西总结一下,供大家参考。下面的博文是我自己的总结再加上博友的一些见解整合而成,共同进步。

初识View

在安卓中所有的样式都可以说是一个视图,TextView,Button,ImageView...这些官方已经给出的view已经无法满足我们的日常生活所需了,这个时候,我们就可以自定义View,随心所欲创建属于我们自己的View。那么我们应该怎么去做呢,首先要干嘛,其次又干嘛,最后干嘛呢,这都是过程中的一个节点。下面呢我们就从第一步开始。

一张图认识View

在这张图中坐标系和我们数学中的不一样,这里的Y轴下方是正数,X轴右方是正数,其中的View(浅蓝色背景)为我们自定义的View,MotionEvent是手指点击的位置,我们对View进行移动,也是根据MotionEvent返回的xy坐标点进行绘制的。

View的坐标系

注意:View的坐标系统是相对于父控件而言的.
  1. getTop(); //获取子View左上角距父View顶部的距离
  2. getLeft(); //获取子View左上角距父View左侧的距离
  3. getBottom(); //获取子View右下角距父View顶部的距离
  4. getRight(); //获取子View右下角距父View左侧的距离

MotionEvent中 get 和 getRaw 的区别

  1. event.getX(); //触摸点相对于其所在组件坐标系的坐标
  2. event.getY();
  3.  
  4. event.getRawX(); //触摸点相对于屏幕默认坐标系的坐标
  5. event.getRawY();

这里只是讲解了一些View的独有关于坐标的方法,因为在平常自定义的时候了解这些方法就已经可以对View进行操作了,比如说拖拽,拉伸,收缩等。

自定义View实战

自定义View如何做,怎么做,往往都是第一步比较难,之后对View美化就相对来说比较简单了。下面我写了几个步骤

  • 继承View

    • MyView extends View重写里面重要的3-4个构造方法
  • onMeasure测量控件
    • onMeasure(int widthMeasureSpec, int heightMeasureSpec) 获取屏幕,自定义View父组件尺寸等。
  • onDraw绘制View
    • onDraw(Canvas canvas)使用canvas去绘制View,并展示出来

我们就根据上面步骤一一解答

继承View

我这里写了三个构造方法,也可以写四个,但如果只写一个会出现问题,比如说在XML文件中使用会报错

  1. public MyView(Context context) {
  2. super(context);
  3. init();
  4. }
  5.  
  6. public MyView(Context context, @Nullable AttributeSet attrs) {
  7. super(context, attrs);
  8. this.context = context;
  9. init();
  10. }
  11.  
  12. public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
  13. super(context, attrs, defStyleAttr);
  14. init();
  15. }

onMeasure测量控件

  1. @Override
  2. protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  3. super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  4. //以下大小均为获取父布局尺寸
  5. measureWidth = MeasureSpec.getSize(widthMeasureSpec);
  6. measureHeight = MeasureSpec.getSize(heightMeasureSpec);
  7. maxWidth = getMaxWidth(context);
  8. maxHeight = getMaxHeight(context);
  9. Log.e("measure", maxWidth + " -- " + maxHeight);
  10. measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
  11. measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
  12. Log.e("measure", "measureWidth:" + measureWidth + "measureHeight:" + measureHeight + "measureWidthMode:" + measureWidthMode + "measureHeightMode:" + measureHeightMode);
  13. }
  14.  
  15. // 获取最大宽度
  16. public int getMaxWidth(Context context) {
  17. WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  18. DisplayMetrics dm = new DisplayMetrics();
  19. wm.getDefaultDisplay().getMetrics(dm);
  20. return dm.widthPixels;
  21. }
  22.  
  23. // 获取最大高度
  24. public int getMaxHeight(Context context) {
  25. WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
  26. DisplayMetrics dm = new DisplayMetrics();
  27. wm.getDefaultDisplay().getMetrics(dm);
  28. return dm.heightPixels;
  29. }

widthMeasureSpec,heightMeasureSpec 这两个参数不是简单的整数类型,而是2位整数(模式类型)和30位整数(实际数值) 的组合,所以我们要通过MeasureSpec 获取模式int值 和 获取数值int值。这个时候才是我们想要的尺寸,比如说屏幕是1080 * 1920,获取的值也就是1080 * 1920。

onDraw绘制View

这个方法就厉害了,所有的绘制工作都是里面的canvas去完成,canvas翻译过来是帆布的意思,对我们来说就是画布,画布有了,还差画笔,有笔有布有多彩墨水才能画出大好河山嘛,这里先介绍画笔Paint

1:初始画笔 ----- Paint的使用

  1. canvas.drawCircle(100, 100, 50, paint)
这是一个要绘制圆形图片的代码,两个100分别是XY轴坐标,50是半径,paint是画笔,他的意思是说,我要画一个圆点是(100,100)半径是50的圆。样式是paint,这个时候我们就要对paint去绘制,官方文档上画笔有 100 个左右的公开方法,常用方法标出颜色。下图是网页提供的表格。
返回值
简介
int
getFlags()
获取画笔相关的一些设置(标志)。
int
getFlags()
获取画笔相关的一些设置(标志)。
void
setFlags(int flags)
设置画笔的标志位。
void
set(Paint src)
复制 src 的画笔设置。
void
reset()
将画笔恢复为默认设置。
int
getAlpha()
只返回颜色的alpha值。
void
setAlpha(int a)
设置透明度。
int
getColor()
返回画笔的颜色。
void
setColor(int color)
设置颜色。
void
setARGB(int a, int r, int g, int b)
设置带透明通道的颜色。
float
getStrokeWidth()
返回描边的宽度。
void
setStrokeWidth(float width)
设置线条宽度。
Paint.Style
getStyle()
返回paint的样式,用于控制如何解释几何元素(除了drawBitmap,它总是假定为FILL_STYLE)。
void
setStyle(Paint.Style style)
设置画笔绘制模式(填充,描边,或两者均有)。
Paint.Cap
getStrokeCap()
返回paint的Cap,控制如何处理描边线和路径的开始和结束。
void
setStrokeCap(Paint.Cap cap)
设置线帽。
Paint.Join
getStrokeJoin()
返回画笔的笔触连接类型。
void
setStrokeJoin(Paint.Join join)
设置连接方式。
float
getStrokeMiter()
返回画笔的笔触斜接值。用于在连接角度锐利时控制斜接连接的行为。
void
setStrokeMiter(float miter)
设置画笔的笔触斜接值。用于在连接角度锐利时控制斜接连接的行为。
PathEffect
getPathEffect()
获取画笔的 patheffect 对象。
PathEffect
setPathEffect(PathEffect effect)
设置 Path 效果。
boolean
getFillPath(Path src, Path dst)
将任何/所有效果(patheffect,stroking)应用于src,并将结果返回到dst。
结果是使用此画笔绘制绘制 src 将与使用默认画笔绘制绘制 dst 相同(至少从几何角度来说是这样的)。
Style
简介
Paint.Style.FILL
填充内容,也是画笔的默认模式。
Paint.Style.STROKE
描边,只绘制图形轮廓。
Paint.Style.FILL_AND_STROKE
描边+填充,同时绘制轮廓和填充内容。
Cap 简介
Paint.Cap.BUTT 无线帽,也是默认类型。
Paint.Cap.SQUARE 以线条宽度为大小,在开头和结尾分别添加半个正方形。
Paint.Cap.ROUND 以线条宽度为直径,在开头和结尾分别添加一个半圆。
 
下面我们只画一个最简单的
  1. paint = new Paint();
  2. paint.setColor(Color.BLACK);//画笔颜色
  3. paint.setStrokeWidth(5f);//边的宽度
  4. paint.setStyle(Paint.Style.STROKE);//描边

上面canvas.drawCircle(100, 100, 50, paint)和paint的创建  都是写在onDraw方法里的。上整体代码

  1. @Override
  2. protected void onDraw(Canvas canvas) {
  3. super.onDraw(canvas);
  4. //样式一
  5. canvas.drawCircle(200, 200, 100, paint);
  6.  
  7. //样式二
  8. Path arcPath = new Path();
  9. arcPath.addArc(new RectF(100, 100, 500, 500), 120, 300);
  10.  
  11. // Paint paint = new Paint();
  12. // paint.setStyle(Paint.Style.STROKE);
  13. // canvas.drawPath(arcPath, paint);
  14.  
  15. Path borderPath = new Path();
  16. Paint paint = new Paint();
  17. paint.setStyle(Paint.Style.STROKE);
  18. paint.setStrokeCap(Paint.Cap.ROUND);
  19. paint.setStrokeWidth(100);
  20. paint.getFillPath(arcPath, borderPath); // getFillPath
  21.  
  22. // 测试画笔,注意设置为 STROKE
  23. Paint testPaint = new Paint();
  24. testPaint.setStyle(Paint.Style.STROKE);
  25. testPaint.setStrokeWidth(2);
  26. testPaint.setAntiAlias(true);
  27. // 绘制通过 getFillPath 获取到的 Path
  28. canvas.drawPath(borderPath, testPaint);
  29. }

好了,到这里对canvas画简单图案是告一段落了,那么我们之前获取到的尺寸是干嘛用的呢,下面我们对拖拽进行讲解,拖拽其实就是down,move,up对着三者的一个解析,当我们手指按下的时候将会出发down,手指一动时触发move,手指抬起时触发up。那么我们怎么去监听他的,有方法,那就是onTouchEvent,触摸方法的事件分发机制我们下节讲。这里我们直接上代码

拖拽图案

  1. @Override
  2. public boolean onTouchEvent(MotionEvent event) {
  3. //手指按下
  4. int action = event.getAction();
  5. //获取手机触摸的坐标
  6. int x = (int) event.getX();
  7. int y = (int) event.getY();
  8. switch (action){
  9. case MotionEvent.ACTION_DOWN://按下,获取小球初始的位置
  10. startLeft = getLeft();
  11. startRight = getRight();
  12. startTop = getTop();
  13. startBottom = getBottom();
  14. lastX = x;
  15. lastY = y;
  16. break;
  17. case MotionEvent.ACTION_MOVE://移动,小球跟随手指的移动
  18. Log.e("event","getRawX():"+event.getRawX()+" getRawY():"+event.getRawY());
  19. Log.e("event","getX():"+event.getX()+" getY():"+event.getY());
  20. int offsetX = x - lastX;
  21. int offsetY = y - lastY;
  22. layout(getLeft()+offsetX,getTop()+offsetY,
  23. getRight()+offsetX,getBottom()+offsetY);
  24. break;
  25. case MotionEvent.ACTION_UP://当手指抬起时,回到小球初始的位置
  26. // layout(startLeft, startTop, startRight, startBottom);
  27. break;
  28. }
  29. return true;
  30. }

好了,到这里,就可以拖拽你画的图案了。下一篇讲解View的事件分发机制。

从0开始学自定义View -1的更多相关文章

  1. 推翻自己和过往,重学自定义View

    http://blog.csdn.net/lfdfhl/article/details/51671038 深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 A ...

  2. 自定义View实战

    PS:上一篇从0开始学自定义View有博友给我留言说要看实战,今天我特意写了几个例子,供大家参考,所画的图案加上动画看着确实让人舒服,喜欢的博友可以直接拿到自己的项目中去使用,由于我这个写的是demo ...

  3. 自定义View和ViewGroup

    为了扫除学习中的盲点,尽可能多的覆盖Android知识的边边角角,决定对自定义View做一个稍微全面一点的使用方法总结,在内容上面并没有什么独特的地方,其他大神们的博客上面基本上都有讲这方面的内容,如 ...

  4. wing带你玩转自定义view系列(3)模仿微信下拉眼睛

    发现了爱神的自定义view系列,我只想说一个字:凸(艹皿艹 ) !!相见恨晚啊,早看到就不会走这么多弯路了 另外相比之下我这完全是小儿科..所以不说了,这篇是本系列完结篇....我要从零开始跟随爱哥脚 ...

  5. 新手自定义view练习实例之(二) 波浪view

    本系列是为新手准备的自定义view练习项目(大牛请无视),相信在学习过程中,想学自定义view又无从下手,不知道做什么.本系列为新手提供了一系列自定义view的简单实例.看过理解之后,自己实现,相信会 ...

  6. 新手自定义view练习实例之(一) 泡泡弹窗

    转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50455412 本系列是为新手准备的自定义view练习项目(大牛请无视),相信在学习过程 ...

  7. 自定义View和ViewGroup(有这一篇就够了)

    为了扫除学习中的盲点,尽可能多的覆盖Android知识的边边角角,决定对自定义View做一个稍微全面一点的使用方法总结,在内容上面并没有什么独特的地方,其他大神们的博客上面基本上都有讲这方面的内容,如 ...

  8. 【转载】自定义View,有这一篇就够了

    为了扫除学习中的忙点,尽可能多的覆盖Android知识的边边角角,决定对自定义View做一个稍微全面一点的使用方法总结,在内容上面并没有什么独特的地方,其他大神们博客上面基本上都有讲这方面的内容,如果 ...

  9. 自定义View系列教程01--常用工具介绍

    站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定 ...

随机推荐

  1. Prometheus 监控MySQL

    目录 0.简介 1.mysql_exporter部署 2.mysql报警规则 0.简介 文中主要监控MySQL/MySQL主从信息 版本:mysql-5.7,mysql_exporter-0.12.1 ...

  2. AJ学IOS(48)多线程网络之多线程简单了解

    AJ分享,必须精品 一:进程和线程 1:什么是进程 进程是指在系统中正在运行的一个应用程序 每个进程之间是独立的,每个进程均运行在其专用且受保护的内存空间内. 比如同时打开QQ.Xcode,系统就会分 ...

  3. php.ini中文详解

    [PHP] ;;;;;;;;;;;;;;;;;;;;;;; ; 关于 php.ini 配置文件 ; ;;;;;;;;;;;;;;;;;;;;;;; ; PHP 的初始化文件, 必须命名为 php.in ...

  4. Three.js如何选中外部模型

    1.问题 three.js中模型选中使用的是射线法,根据摄像机角度,鼠标点击位置和模型选中的distance参数判断来选中模型.对于原生的矢量模型完全没有问题,但是当遇到导入的外部模型,如obj.st ...

  5. 45道SQL数据题详解1

    准备阶段: 创建表: //创建学生表,前面的s表示学生,相应的标签前面加t表示老师 CREATE TABLE students (sno VARCHAR(3) NOT NULL, sname VARC ...

  6. sqlserver2008 服务器实例连接

    一.sqlserver 配置管理器里面,看sqlserver服务那个,如果只有一个mssqlserver,那就用local或者.来访问,如果不是,可能有命名实例.

  7. 当文件目录变得杂乱不堪怎么办,python帮你轻松搞定

    这几天和几个小伙伴,在一起合做一个ppt. 做ppt之前有原版的ppt,和一个word大纲,在制作过程中,又不断添加图片.视频等素材,最终,整个目录变得杂乱不堪(见下图-处理之前) 那我想,可不可以做 ...

  8. D - Free Candies UVA - 10118

    题目链接:https://uva.onlinejudge.org/index.php?option=com_onlinejudge&Itemid=8&page=show_problem ...

  9. 调用sleep后,我做了一个噩梦

    sleep系统调用 我是一个线程,生活在Linux帝国.一直以来辛勤工作,日子过得平平淡淡,可今天早上发生了一件事让我回想起来都后怕. 早上,我还是如往常一样执行着人类编写的代码指令,不多时走到了一个 ...

  10. 【错误】python百分号冲突not enough arguments for format string

    query = "SELECT * FROM devices WHERE devices.`id` LIKE '%{}%'".format("f2333") d ...