从0开始学自定义View -1
PS:好久没有写博客了,之前的东西有所忘记,百度一下竟然查到了自己的写过的博客,访问量还可以,一开始的写博客的初衷是把自己不会的记录下来,现在没想到也有博友会关注我,这就给了我动力,工作之余把零零碎碎的东西总结一下,供大家参考。下面的博文是我自己的总结再加上博友的一些见解整合而成,共同进步。
初识View
在安卓中所有的样式都可以说是一个视图,TextView,Button,ImageView...这些官方已经给出的view已经无法满足我们的日常生活所需了,这个时候,我们就可以自定义View,随心所欲创建属于我们自己的View。那么我们应该怎么去做呢,首先要干嘛,其次又干嘛,最后干嘛呢,这都是过程中的一个节点。下面呢我们就从第一步开始。
一张图认识View

View的坐标系
getTop(); //获取子View左上角距父View顶部的距离
getLeft(); //获取子View左上角距父View左侧的距离
getBottom(); //获取子View右下角距父View顶部的距离
getRight(); //获取子View右下角距父View左侧的距离
MotionEvent中 get 和 getRaw 的区别
event.getX(); //触摸点相对于其所在组件坐标系的坐标
event.getY(); event.getRawX(); //触摸点相对于屏幕默认坐标系的坐标
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文件中使用会报错
public MyView(Context context) {
super(context);
init();
}
public MyView(Context context, @Nullable AttributeSet attrs) {
super(context, attrs);
this.context = context;
init();
}
public MyView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init();
}
onMeasure测量控件
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
//以下大小均为获取父布局尺寸
measureWidth = MeasureSpec.getSize(widthMeasureSpec);
measureHeight = MeasureSpec.getSize(heightMeasureSpec);
maxWidth = getMaxWidth(context);
maxHeight = getMaxHeight(context);
Log.e("measure", maxWidth + " -- " + maxHeight);
measureWidthMode = MeasureSpec.getMode(widthMeasureSpec);
measureHeightMode = MeasureSpec.getMode(heightMeasureSpec);
Log.e("measure", "measureWidth:" + measureWidth + "measureHeight:" + measureHeight + "measureWidthMode:" + measureWidthMode + "measureHeightMode:" + measureHeightMode);
} // 获取最大宽度
public int getMaxWidth(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
return dm.widthPixels;
} // 获取最大高度
public int getMaxHeight(Context context) {
WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
DisplayMetrics dm = new DisplayMetrics();
wm.getDefaultDisplay().getMetrics(dm);
return dm.heightPixels;
}
widthMeasureSpec,heightMeasureSpec 这两个参数不是简单的整数类型,而是2位整数(模式类型)和30位整数(实际数值) 的组合,所以我们要通过MeasureSpec 获取模式int值 和 获取数值int值。这个时候才是我们想要的尺寸,比如说屏幕是1080 * 1920,获取的值也就是1080 * 1920。
onDraw绘制View
这个方法就厉害了,所有的绘制工作都是里面的canvas去完成,canvas翻译过来是帆布的意思,对我们来说就是画布,画布有了,还差画笔,有笔有布有多彩墨水才能画出大好河山嘛,这里先介绍画笔Paint
1:初始画笔 ----- Paint的使用
canvas.drawCircle(100, 100, 50, paint)
|
返回值
|
简介
|
|
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 | 以线条宽度为直径,在开头和结尾分别添加一个半圆。 |
paint = new Paint();
paint.setColor(Color.BLACK);//画笔颜色
paint.setStrokeWidth(5f);//边的宽度
paint.setStyle(Paint.Style.STROKE);//描边

上面canvas.drawCircle(100, 100, 50, paint)和paint的创建 都是写在onDraw方法里的。上整体代码
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//样式一
canvas.drawCircle(200, 200, 100, paint); //样式二
Path arcPath = new Path();
arcPath.addArc(new RectF(100, 100, 500, 500), 120, 300); // Paint paint = new Paint();
// paint.setStyle(Paint.Style.STROKE);
// canvas.drawPath(arcPath, paint); Path borderPath = new Path();
Paint paint = new Paint();
paint.setStyle(Paint.Style.STROKE);
paint.setStrokeCap(Paint.Cap.ROUND);
paint.setStrokeWidth(100);
paint.getFillPath(arcPath, borderPath); // getFillPath // 测试画笔,注意设置为 STROKE
Paint testPaint = new Paint();
testPaint.setStyle(Paint.Style.STROKE);
testPaint.setStrokeWidth(2);
testPaint.setAntiAlias(true);
// 绘制通过 getFillPath 获取到的 Path
canvas.drawPath(borderPath, testPaint);
}

好了,到这里对canvas画简单图案是告一段落了,那么我们之前获取到的尺寸是干嘛用的呢,下面我们对拖拽进行讲解,拖拽其实就是down,move,up对着三者的一个解析,当我们手指按下的时候将会出发down,手指一动时触发move,手指抬起时触发up。那么我们怎么去监听他的,有方法,那就是onTouchEvent,触摸方法的事件分发机制我们下节讲。这里我们直接上代码
拖拽图案
@Override
public boolean onTouchEvent(MotionEvent event) {
//手指按下
int action = event.getAction();
//获取手机触摸的坐标
int x = (int) event.getX();
int y = (int) event.getY();
switch (action){
case MotionEvent.ACTION_DOWN://按下,获取小球初始的位置
startLeft = getLeft();
startRight = getRight();
startTop = getTop();
startBottom = getBottom();
lastX = x;
lastY = y;
break;
case MotionEvent.ACTION_MOVE://移动,小球跟随手指的移动
Log.e("event","getRawX():"+event.getRawX()+" getRawY():"+event.getRawY());
Log.e("event","getX():"+event.getX()+" getY():"+event.getY());
int offsetX = x - lastX;
int offsetY = y - lastY;
layout(getLeft()+offsetX,getTop()+offsetY,
getRight()+offsetX,getBottom()+offsetY);
break;
case MotionEvent.ACTION_UP://当手指抬起时,回到小球初始的位置
// layout(startLeft, startTop, startRight, startBottom);
break;
}
return true;
}
好了,到这里,就可以拖拽你画的图案了。下一篇讲解View的事件分发机制。
从0开始学自定义View -1的更多相关文章
- 推翻自己和过往,重学自定义View
http://blog.csdn.net/lfdfhl/article/details/51671038 深入探讨Android异步精髓Handler 站在源码的肩膀上全解Scroller工作机制 A ...
- 自定义View实战
PS:上一篇从0开始学自定义View有博友给我留言说要看实战,今天我特意写了几个例子,供大家参考,所画的图案加上动画看着确实让人舒服,喜欢的博友可以直接拿到自己的项目中去使用,由于我这个写的是demo ...
- 自定义View和ViewGroup
为了扫除学习中的盲点,尽可能多的覆盖Android知识的边边角角,决定对自定义View做一个稍微全面一点的使用方法总结,在内容上面并没有什么独特的地方,其他大神们的博客上面基本上都有讲这方面的内容,如 ...
- wing带你玩转自定义view系列(3)模仿微信下拉眼睛
发现了爱神的自定义view系列,我只想说一个字:凸(艹皿艹 ) !!相见恨晚啊,早看到就不会走这么多弯路了 另外相比之下我这完全是小儿科..所以不说了,这篇是本系列完结篇....我要从零开始跟随爱哥脚 ...
- 新手自定义view练习实例之(二) 波浪view
本系列是为新手准备的自定义view练习项目(大牛请无视),相信在学习过程中,想学自定义view又无从下手,不知道做什么.本系列为新手提供了一系列自定义view的简单实例.看过理解之后,自己实现,相信会 ...
- 新手自定义view练习实例之(一) 泡泡弹窗
转载请注明出处:http://blog.csdn.net/wingichoy/article/details/50455412 本系列是为新手准备的自定义view练习项目(大牛请无视),相信在学习过程 ...
- 自定义View和ViewGroup(有这一篇就够了)
为了扫除学习中的盲点,尽可能多的覆盖Android知识的边边角角,决定对自定义View做一个稍微全面一点的使用方法总结,在内容上面并没有什么独特的地方,其他大神们的博客上面基本上都有讲这方面的内容,如 ...
- 【转载】自定义View,有这一篇就够了
为了扫除学习中的忙点,尽可能多的覆盖Android知识的边边角角,决定对自定义View做一个稍微全面一点的使用方法总结,在内容上面并没有什么独特的地方,其他大神们博客上面基本上都有讲这方面的内容,如果 ...
- 自定义View系列教程01--常用工具介绍
站在源码的肩膀上全解Scroller工作机制 Android多分辨率适配框架(1)- 核心基础 Android多分辨率适配框架(2)- 原理剖析 Android多分辨率适配框架(3)- 使用指南 自定 ...
随机推荐
- shell脚本编程(ubantu)
项目 内容 这个作业属于那个课程 这里是链接 作业要求在哪里 这里是链接 学号-姓名 17041506-张政 作业学习目标 了解shell脚本的概念及使用:掌握shell脚本语言的基本语法:学习简单的 ...
- 《深入理解 Java 虚拟机》读书笔记:垃圾收集器与内存分配策略
正文 垃圾收集器关注的是 Java 堆和方法区,因为这部分内存的分配和回收是动态的.只有在程序处于运行期间时才能知道会创建哪些对象,也才能知道需要多少内存. 虚拟机栈和本地方法栈则不需要过多考虑回收的 ...
- Error: opening registry key 'Software\JavaSoft\Java Runtime Environment' could not find java.dll
Error: opening registry key 'Software\JavaSoft\Java Runtime Environment'Error: could not find java.d ...
- AJ学IOS 之BLOCK的妙用_利用block实现链式编程
AJ分享,必须精品 一:场景 我们有个对象人,他有两个方法,一个是学习study,一个是跑步run, 这个人有个怪癖,跑完步之后必须学习,为了实现这个方法并且能调用方便,我们让跑步和学习都回返回自己这 ...
- AJ学IOS(36)UI之手势事件旋转_缩放_拖拽
AJ分享,必须精品 效果 完成一个图片的捏合缩放,拖拽,旋转动作. 设计思路 拖拽: 首先是最简单的拖拽 //拖拽 -(void)panTest { UIPanGestureRecognizer *p ...
- Juli函数
- Python语言-selenium webdriver操作记录汇总
1.控制浏览器大小 set_window_size() 设置浏览器大小 该方法有两个参数,第一个参数是宽,第二个是高 maximize_window() 设置浏览器全屏显示,无参数 chrome谷歌浏 ...
- 熬夜整理出来的干货:Python+Pycharm+PyQT5可视化程序设计入门
前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:朱淑强 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自 ...
- SPFA()判环
1 SPFA()判负环 SPFA()判负环的原理就是在求最短路的过程中,如果存在负环,比如说要求从A到a的最短距离,设为s,但是经过a->c->b->a可以更短,所以如果一直经过a- ...
- N - Aroma's Search CodeForces - 1293D math+greedy
作为DIV2的D题来讲,这个题目不算难. 题目大意:再规定的时间内寻找宝藏,第i个宝藏的位置为a*x(i-1)+b,a*y(i-1)+b.然后给出初始位置xs,ys和时间t让求再时间t内能够寻找到多少 ...