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

初识View

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

一张图认识View

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

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) 
这是一个要绘制圆形图片的代码,两个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 以线条宽度为直径,在开头和结尾分别添加一个半圆。
 
下面我们只画一个最简单的
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的更多相关文章

  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. EL表达式---自定义函数(转)

    EL表达式---自定义函数(转) 有看到一个有趣的应用了,转下来,呵呵!! 1.定义类MyFunction(注意:方法必须为 public static) package com.tgb.jstl;  ...

  2. python填写问卷星,疫情上报

    #!!!注意:修改main里的url为真实的url,按需修改 50行 set_data中的submitdata # 61行 ip 修改为 真ip # submittype可能有错误,在151行 # 提 ...

  3. ValidForm.js的使用注意点

    dataType的值不能为"", 否则会导致错误发生:Uncaught TypeError: Cannot read property '0' of null,http请求可以发送 ...

  4. 实例讲解Springboot整合MongoDB进行CRUD操作的两种方式

    1 简介 Springboot是最简单的使用Spring的方式,而MongoDB是最流行的NoSQL数据库.两者在分布式.微服务架构中使用率极高,本文将用实例介绍如何在Springboot中整合Mon ...

  5. 落谷 P1734 最大约数和

    题目描述 选取和不超过S的若干个不同的正整数,使得所有数的约数(不含它本身)之和最大. 输入格式 输入一个正整数S. 输出格式 输出最大的约数之和. 输入输出样例 输入 #1复制 11 输出 #1复制 ...

  6. Wireshark的两种过滤器与BPF过滤规则

    Wirshark使用的关键就在于过滤出想要的数据包,下面介绍怎么过滤. 抓包过滤器 Wirshark有两种过滤器,一个是抓包过滤器,一个是显示过滤器,他们之间的区别在于抓包过滤器只抓取你设置的规则,同 ...

  7. Django文档阅读-Day3

    Django文档阅读-Day3 Writing your first Django app, part 3 Overview A view is a "type" of Web p ...

  8. 模糊字符串匹配:FuzzyWuzzy

    FuzzyWuzzy 模糊字符串匹配,它使用Levenshtein Distance来计算简单易用的包中序列之间的差异. 前置条件 Python 2.7 or higher difflib pytho ...

  9. OAuth - 四种方式

    OAuth 2.0 的标准是 RFC 6749 文件.该文件先解释了 OAuth 是什么. OAuth 引入了一个授权层,用来分离两种不同的角色:客户端和资源所有者.......资源所有者同意以后,资 ...

  10. 解决centos ping不通外网

    先确认三件事: 一.ip 二.网关 三.dns 一就不说了,设置好本地ip和掩码就行了,二网关   添加默认网关,命令:route add defaule gw 192.168.1.1 这是 你用ro ...