一般自定义view需要重写的方法

void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
void onSizeChanged(int w, int h, int oldw, int oldh)
void onDraw(Canvas canvas)
复制代码

一般自定义ViewGroup需要重新的方法

void onMeasure(int widthMeasureSpec, int heightMeasureSpec)
void onSizeChanged(int w, int h, int oldw, int oldh)
void onLayout(boolean changed, int left, int top, int right, int bottom)
void onDraw(Canvas canvas)
复制代码

可以看出,自定义ViewGroup时必须要重写onLayout()方法(依次排列子view),而自定义View没有子View,所以不需要onLayout(),后面会分别给出自定义View和自定义ViewGroup的例子.
在网上看到一张View的生命周期图,觉得还不错:

先来看下自定义View的作用:
1.在onMeasure中根据测量模式和ViewGroup给出的建议宽和高,计算出自己的宽和高;
2.在onDraw中绘制自己的形态。

绘制自定义View时,如果需要自定义属性,可以在res/values的目录下创建一个attr.xml(名字可以任意起),如:

<declare-styleable name="ColorCircleView">
<attr name="circle_color" format="color" />
<attr name="stroke_width" format="integer" />
</declare-styleable>
复制代码

我们定义了自定义属性名字和取值类型,format类型有
string,color,demension,integer,enum,reference,float,boolean,fraction,flag
然后在构造函数中获得自定义属性:

//获取自定义属性
TypedArray ta = context.obtainStyledAttributes(attrs, R.styleable.ColorCircleView);
int cirlceColor = ta.getColor(R.styleable.ColorCircleView_circle_color, Color.RED);
int stokeWidth = ta.getInt(R.styleable.ColorCircleView_stroke_width, 10);
ta.recycle();
复制代码

在XML中使用自定义属性,如:

<org.ninetripods.mq.ColorCircleView
android:layout_width="90dp"
android:layout_height="90dp"
custom:circle_color="#3F51B5"
custom:stroke_width="20" />
复制代码

别忘了在XML最上面加上:xmlns:custom="http://schemas.android.com/apk/res-auto"

接下来看下他的几个常用方法:

  • ###onMeasure
    MeasureSpec是View的一个内部类,一般用到它的MeasureSpecMode(测量模式)和Size(测量大小),其中MeasureSpecMode有以下三种模式:
    ######UNSPECIFIED:
    The parent has not imposed any constraint on the child. It can be whatever size it wants.
    父view对子view没有任何限制,子view可以是任何大小
    ######EXACTLY:
    The parent has determined an exact size for the child. The child is going to be given those bounds regardless of how big it wants to be.
    父view已经强制设置了子view的大小,一般是MATCH_PARENT和固定值
    ######AT_MOST:
    The child can be as large as it wants up to the specified size.
    子view限制在一个最大范围内,一般是WARP_CONTENT
    最后测量完成后通过**setMeasuredDimension(int measuredWidth, int measuredHeight) **将测量的宽高回传给父View。
  • ###onSizeChanged
    在onMeasure()后执行,只有大小发生了变化才会执行onSizeChange
  • ###onDraw
    系统给我们提供了空白的Canvas空白画布,我们可以通过Canvas和Paint来绘制我们想要的图形。
    注:onDraw()函数可能会多次调用,所以避免在onDraw()函数中去new 对象

自定义View例子,先上图:

###代码已上传到github:自定义View(饼状图)
核心代码(代码中有详细注释):

public class CakeView extends View {
//装载的饼状圆数据
private List<CakeBean> beanList;
//画圆的矩形
private RectF mRectF;
//右边的小矩形
private RectF iRectF;
private Paint mPaint;
private int mRWidth, mRHeight;
private float rotateDegree;//每个圆弧的起始角度
private float sumValue = 0;//所有值的和
private float diameter;//圆的直径
private float textY;//绘制文字的Y坐标
private float mRectHeight = 40;//矩形高度
private float mRectWidth = 80;//矩形宽度
private float mMargin = 40;//矩形和圆的距离
private Context mContext; public CakeView(Context context) {
//CakeView cakeView=new CakeView(context);
// 在代码中new CakeView()会调用这个构造函数
this(context, null);
} public CakeView(Context context, AttributeSet attrs) {
//InflateLayoutManager时会调用这个构造函数
this(context, attrs, 0);
} public CakeView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
mContext = context;
init();
} private void init() {
beanList = new ArrayList<>();
mRectF = new RectF();
mPaint = new Paint();
mPaint.setAntiAlias(true);
mPaint.setDither(true); } @Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
//MeasureSpec封装了父View传递给子View的布局要求
//宽度测量模式
int wMode = MeasureSpec.getMode(widthMeasureSpec);
//宽度测量值
int wSize = MeasureSpec.getSize(widthMeasureSpec);
//高度测量模式
int hMode = MeasureSpec.getMode(heightMeasureSpec);
//高度测量值
int hSize = MeasureSpec.getSize(heightMeasureSpec);
switch (wMode) {
case MeasureSpec.EXACTLY:
//相当于match_parent或者一个具体值
mRWidth = wSize;
break;
case MeasureSpec.AT_MOST:
// 相当于wrap_content ,需要手动测量大小,这里先写死大小
mRWidth = (int) DpUtil.dp2px(mContext, 400f);
break;
case MeasureSpec.UNSPECIFIED:
//很少会用到
break;
default:
break;
}
switch (hMode) {
case MeasureSpec.EXACTLY:
//相当于match_parent或者一个具体值
mRHeight = hSize;
break;
case MeasureSpec.AT_MOST:
// 相当于wrap_content ,需要手动测量大小,这里先写死大小
mRHeight = (int) DpUtil.dp2px(mContext, 200f);
break;
case MeasureSpec.UNSPECIFIED:
//很少会用到
break;
default:
break;
}
//存储测量好的宽和高
setMeasuredDimension(wSize, hSize);
} @Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
diameter = Math.min(mRWidth, mRHeight);
} @Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
//设置圆形绘制的范围
mRectF.set(0, 0, diameter, diameter);
//画布中心X坐标向右移动(控件宽度-圆直径)之差的八分之一的距离
//画布中心Y坐标向下移动(控件宽度-圆直径)之差的二分之一的距离
canvas.translate((mRWidth - diameter) / 8, (mRHeight - diameter) / 2);
if (beanList.size() > 0 && Float.compare(sumValue, 0.0f) != 0) {
for (int i = 0; i < beanList.size(); i++) {
CakeBean bean = beanList.get(i);
//画圆弧
mPaint.setColor(bean.mColor);
canvas.drawArc(mRectF, rotateDegree, bean.degree, true, mPaint);
rotateDegree += bean.degree;
//画矩形和文字
drawRectAndText(canvas, bean);
}
}
} private void drawRectAndText(Canvas canvas, CakeBean bean) {
iRectF = new RectF();
//设置画矩形的范围
float left = diameter + mMargin;
float right = diameter + mMargin + mRectWidth;
float bottom = textY + mRectHeight;
iRectF.set(left, textY, right, bottom);
canvas.drawRect(iRectF, mPaint);
//设置颜色
mPaint.setColor(Color.BLACK);
//设置文字大小
mPaint.setTextSize(30);
//画文字
canvas.drawText(bean.name + "(" + new DecimalFormat(".00").format(bean.value / sumValue * 100) + "%)", right + 10, textY + 30, mPaint);
textY += mRectHeight;
} /**
* 饼状图添加数据
*
* @param beans CakeBean数据
*/
public void setData(List<CakeBean> beans) {
if (beans == null || beans.size() <= 0) return;
for (int i = 0; i < beans.size(); i++) {
CakeBean bean = beans.get(i);
sumValue += bean.value;
}
for (int i = 0; i < beans.size(); i++) {
CakeBean bean = beans.get(i);
bean.degree = bean.value / sumValue * 360;
beanList.add(bean);
}
invalidate();
} /**
* @param startDegree 设置起始角度
*/ public void setStartDegree(float startDegree) {
this.rotateDegree = startDegree;
invalidate();
}}
复制代码

自定义View的使用先到这里,自定义ViewGroup接下篇
android自定义View&自定义ViewGroup(下)

转载于:https://juejin.im/post/5a33e7e56fb9a044fe466e2f

android自定义View&自定义ViewGroup(上)的更多相关文章

  1. 【Android 应用开发】自定义View 和 ViewGroup

    一. 自定义View介绍 自定义View时, 继承View基类, 并实现其中的一些方法. (1) ~ (2) 方法与构造相关 (3) ~ (5) 方法与组件大小位置相关 (6) ~ (9) 方法与触摸 ...

  2. 自定义View 和 ViewGroup

    一. 自定义View介绍 自定义View时, 继承View基类, 并实现其中的一些方法. (1) ~ (2) 方法与构造相关 (3) ~ (5) 方法与组件大小位置相关 (6) ~ (9) 方法与触摸 ...

  3. Android 自定义View——自定义点击事件

    每个人手机上都有通讯录,这是毫无疑问的,我们通讯录上有一个控件,在通讯录的最左边有一列从”#”到”Z”的字母,我们通过滑动或点击指定的字母来确定联系人的位置,进而找到联系人.我们这一节就通过开发这个控 ...

  4. Android自定义View——自定义搜索框(SearchView)

    Android自定义View——自定义搜索框(SearchView) http://www.apkbus.com/android-142064-1-1.html

  5. android 自定义 view 和 ViewGroup

    ---恢复内容开始--- ViewGroup的职能为:给childView计算出建议的宽和高和测量模式 :决定childView的位置:为什么只是建议的宽和高,而不是直接确定呢,别忘了childVie ...

  6. android愤怒小鸟游戏、自定义View、掌上餐厅App、OpenGL自定义气泡、抖音电影滤镜效果等源码

    Android精选源码 精练的范围选择器,范围和单位可以自定义 自定义View做的小鸟游戏 android popwindow选择商品规格颜色尺寸效果源码 实现Android带有锯齿背景的优惠样式源码 ...

  7. 自定义View和ViewGroup

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

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

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

  9. 安卓自定义View实现图片上传进度显示(仿QQ)

    首先看下我们想要实现的效果如下图(qq聊天中发送图片时的效果): 再看下图我们实现的效果: 实现原理很简单,首先我们上传图片时需要一个进度值progress,这个不管是自己写的上传的方法还是使用第三方 ...

随机推荐

  1. 16个实例讲述如何写好App描述

    App描述很重要,很多人都知道,但你有没有亲自比较.研究过别人的app是如何描述的呢?   毫无疑问,app描述页面不仅仅是描述,它更多是一个销售页面,开发者不能面对面向用户营销自己的产品,因此app ...

  2. Jmeter 压力测试笔记(1)--服务器迁移失败

    近期,公司服务器因技术架构升级等原因需要迁移,在经过开发,运维DBA,测试多部门进行联合讨论后,制定出了迁移方案.迁移前也对APP应用进行了各种测试,并没有发现问题. 凌晨2点开始迁移,5点完成迁移. ...

  3. tp6源码解析-第二天,ThinkPHP6编译模板流程详解,ThinkPHP6模板源码详解

    TP6源码解析,ThinkPHP6模板编译流程详解 前言:刚开始写博客.如果觉得本篇文章对您有所帮助.点个赞再走也不迟 模板编译流程,大概是: 先获取到View类实例(依赖注入也好,通过助手函数也好) ...

  4. 2017蓝桥杯Excel地址(C++C组)

    题目:Excel地址Excel单元格的地址表示很有趣,它使用字母来表示列号.比如,A表示第1列,B表示第2列,Z表示第26列,AA表示第27列,AB表示第28列,BA表示第53列,....当然Exce ...

  5. String 对象-->大小比较

    1.定义和用法 规则:从左至右依次对比相同下标处的字符,当两个字符不相等时,哪个字符的ASCII值大,那就哪个字符串就大. 返回值为1,左边大于右边 返回值为-1,右边大于左边 返回值为0,则相等 举 ...

  6. 如何使用npt结合crontab实现集群之间的时间同步

    当我们每个机器的时间都不一致时,假如有一个定时任务,定的10点启动执行.结果namenode十点了启动任务,可是分配到的执行节点DataNode才九点五十导致任务执行失败怎么办?这就需要将机器之间的时 ...

  7. mount --bind绑定命令

    将目录或文件DirFile-1绑定到目录或文件DirFile-2上,所有对DirFile-2的访问就是对DirFile-1的访问 mount --bind [DirFile-1] [DirFile-2 ...

  8. 使用pandas读取csv文件和写入文件

    这是我的CSV文件 读取其中得tempo这一列 import pandas as pd #导入pandas包 data = pd.read_csv("E:\\毕设\\情感识别\\Music- ...

  9. Reward 杭电 2647

    Problem Description Dandelion's uncle is a boss of a factory. As the spring festival is coming , he ...

  10. search(6)- elastic4s-CRUD

    如果我们把ES作为某种数据库来使用的话,必须熟练掌握ES的CRUD操作.在这之前先更正一下上篇中关于检查索引是否存在的方法:elastic4s的具体调用如下: //删除索引 val rspExists ...