android--自定义view
来源:互联网 发布:中国投资咨询公司 知乎 编辑:程序博客网 时间:2024/06/06 17:05
我们为什么要自定义View ?
已有的UI控件不能满足业务的需求。
自定义View的主要实现方式:
1、直接继承已有控件
2、继承ViewGroup实现组合控件
3、继承View实现完全自定义的控件
自定义View的重要方法:
1、onMeasure 控件的尺寸(需要对控件尺寸进行设置时实现)
2、onLayout 对控件进行排版,排列子控件(继承ViewGroup的方式必须实 现)
3、onDraw 在控件上进行图形绘制(需要自定义控件的显示效果时实现)
在这篇文章中,我主要解压介绍onMeasure和onDraw方法,在下一篇中,利用流式布局介绍onLayout方法。
在继承View时,会重写构造方法,我们一般选择两个构造方法。
构造方法的规则:
自定义View必须实现构造方法:
控件类名(Context context) 使用代码创建控件对象时候使用
控件类名(Context context,AttributeSet attrs) 在布局中使用时调用
onDraw方法 实现图形的绘制,
通过Canvas对象实现
Canvas(画布)的主要方法:
drawLine 画线
drawRect 画矩形
drawCircle 画圆形
drawText 画文字 .....
Paint(画笔)的主要方法:
setColor 设置颜色
setStrokeWidth 设置描线的宽度
注意:尽量不要在onDraw中创建对象,onDraw会被频繁调用(500ms左右
这里写一个倒计时的例子。我们一步一步来完成。
先写出一个圆中有数数字的样式。
布局文件 activity_main.xml
<?xml version="1.0" encoding="utf-8"?><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent" android:layout_height="match_parent" tools:context="com.example.my_view.MainActivity"><com.example.my_view.Counter android:layout_width="match_parent" android:layout_height="wrap_content" /></LinearLayout>Counter.java
package com.example.my_view;import android.content.Context;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.util.AttributeSet;import android.view.View;/** * 自定义控件 */public class Counter extends View { private Paint paint; public Counter(Context context) { super(context); } public Counter(Context context, AttributeSet attrs) { super(context, attrs); //因为自定义布局是写在xml文件中 ,所有会调用该方法。 //初始化画笔 init(); } private void init(){ //创建画笔 paint = new Paint(); //设置粗细 paint.setStrokeWidth(2); //设置平滑度 paint.setAntiAlias(true); //设置文字大小 paint.setTextSize(40); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //画笔颜色 paint.setColor(Color.RED); canvas.drawCircle(100,100,50,paint); //重置颜色 paint.setColor(Color.GREEN); canvas.drawText("10",75,110,paint); }}这里MainActivity.java中我什么都没有写,所有这里就不去看代码了。
效果图:
我们发现,画笔的颜色和一些基本设置都在自定义布局中来设置的。我们都知道,我们这些数据,都是在xml布局文件中设置的。那应该怎么去自定义属性呢?这里分几步去完成。
自定义属性
1、在values下添加attrs.xml自定义属性的资源文件
2、在attrs.xml中添加属性:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<!--自定义属性的集合 name为自定义控件的类名-->
<declare-styleable name="Notepad">
<!--自定义属性 name是属性名,format为属性值类型-->
<attr name="lineColor" format="color"/>
<attr name="lineHeight" format="integer"/>
</declare-styleable>
</resources>
3、在布局中的控件上添加属性:
在根布局上添加appNs
在控件上使用自定义属性:app:属性名 = "值"
4、在自定义View中读取自定义属性
//从布局文件中读取自定义属性
//obtainStyledAttributes获得属性集合,
//第一个参数是AttributeSet属性集合,第二个参数是attrs中定义的属性集 合名称
TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.Notepad);
//再从TypedArray中获得属性值
int color = typedArray.getColor(R.styleable.Notepad_lineColor, Color.BLUE);
int line = typedArray.getInteger(R.styleable.Notepad_lineHeight, 3);
//将TypedArray回收
typedArray.recycle();
实现用户触摸的监听的方式:
1、重写onTouchEvent方法 (自定义视图一般使用这种)
2、调用setOnTouchEventListener方法 (调用者去实现)
实现代码:
改变布局文件:
资源文件:attrs.xml文件
<?xml version="1.0" encoding="utf-8"?><resources> <declare-styleable name="Counter"> <attr name="bgColor" format="color"/> <attr name="textColor" format="color"/> <attr name="number" format="integer"/> </declare-styleable></resources>Counter.java
package com.example.my_view;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.os.SystemClock;import android.util.AttributeSet;import android.view.MotionEvent;import android.view.View;/** * 自定义控件 */public class Counter extends View { private Paint paint; private int bgColor; private int number; private int textColor; private int count; //该变量用来倒计时 private Thread thread; //利用线程来让数字跑起来 public Counter(Context context) { super(context); } public Counter(Context context, AttributeSet attrs) { super(context, attrs); //因为自定义布局是写在xml文件中 ,所有会调用该方法。 //初始化画笔 init(attrs); } private void init(AttributeSet attrs) { if (attrs != null) { //获取属性文件中 属性的集合 TypedArray typedArray = getContext().obtainStyledAttributes(attrs, R.styleable.Counter); //获取背景颜色 默认值:Color.YELLOW bgColor = typedArray.getColor(R.styleable.Counter_bgColor, Color.YELLOW); //获取字体颜色 默认值: Color.RED textColor = typedArray.getColor(R.styleable.Counter_textColor, Color.RED); //获取倒计时的数据 默认值:5 number = typedArray.getInteger(R.styleable.Counter_number, 5); count = number; //回收集合 typedArray.recycle(); } //创建画笔 paint = new Paint(); //设置粗细 paint.setStrokeWidth(2); //设置平滑度 paint.setAntiAlias(true); //设置文字大小 paint.setTextSize(40); } @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //画笔颜色 paint.setColor(bgColor); canvas.drawCircle(100, 100, 50, paint); //重置颜色 paint.setColor(textColor); canvas.drawText("" + count, 75, 110, paint); } /** * 实现触摸事件,让数字开始动起来 * @param event * @return */ @Override public boolean onTouchEvent(MotionEvent event) { if (event.getAction() == MotionEvent.ACTION_DOWN) { //启动线程倒计时 if (thread == null) { thread = new Thread(new Runnable() { @Override public void run() { while (true) { //重绘 postInvalidate(); if (count > 0) { count--; } else { break; } //让线程沉睡一秒, //这里没有用线程的sleep方法,因为用线程的sleep方法,还要处理异常 SystemClock.sleep(1000); } } }); //启动线程 thread.start(); } } return super.onTouchEvent(event); }}
效果图:
通过这个例子,应该可以了解onDraw方法的使用,下面看下onMeasure方法介绍。
我们在布局文件中添加一个控件:
运行后,你会发现,这个控件根本就没有显示出来,在上面应该也可以看到,我自定义控件设置的高是wrap_content,利用的布局是LinearLayout,那为什么没有显示出该控件呢?
因为我们在自定义控件时,根本就没有给控件设置大小,现在我们可以把onMeasure方法加上,让TextView控件显示出来。
在Counter.java添加如下代码:
@Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); //设置宽和高,默认值:200 this.setMeasuredDimension(getSize(widthMeasureSpec,200), getSize(heightMeasureSpec,200)); } /** * 获取 控件的大小 * @param spec 实际值 * @param defaultSize 默认值 */ private int getSize(int spec, int defaultSize){ /*//获取尺寸模式 模式有三种模式 EXACTLY:一般是设置了明确的值或者是MATCH_PARENT AT_MOST:表示子布局限制在一个最大值内,一般为WARP_CONTENT UNSPECIFIED:表示子布局想要多大就多大,很少使用*/ int mode = MeasureSpec.getMode(spec); //获取尺寸大小 int size = MeasureSpec.getSize(spec); // n 表示最后控件的大小 先等于默认值 int n = defaultSize; //如果是精准模式,则大小与size相同 if(mode == MeasureSpec.EXACTLY){ n = size; }else{ //吐过是AT_MOST模式,就选默认值和size中小的哪一个 if(mode == MeasureSpec.AT_MOST){ n = Math.min(size,defaultSize); } } //返回尺寸大小 return n; }
运行后:
onDraw方法和onMeasure两个方法就介绍到这里。下次利用流布局来介绍一下onLayout方法的使用。
- Android View---自定义View
- Android View---自定义View
- Android 自定义View 之 自定义View属性
- 【自定义View系列】android自定义View概述
- Android自定义view自定义属性
- Android自定义控件 -- 自定义View
- android自定义view(自定义数字键盘)
- Android自定义View-自定义属性
- Android自定义View-自定义属性
- Android 自定义View
- Android 自定义 View
- android自定义View
- Android 中自定义 view
- android 自定义view组件
- Android 自定义 View
- android 自定义view
- Android:如何自定义View
- android 自定义View
- 后台:nodejs 前台:vue 全栈开发 完整功能的外卖平台系统
- 自学Java之Java编程(读写RSS Feed)(019day)
- 图片选择器--AndroidImagePicker
- ORACLE 12C 安装过程相关报错及解决方案
- 图片轮播--AndroidImageSlider
- android--自定义view
- 为什么不能直接在BroadCastReceiver中开一个线程执行耗时任务
- LintCode 解题记录 Array 17.6.5
- 更改nginx网站根目录
- ACM:F: ACM小组的成绩排名
- 自学Java之Java编程(XML Web服务)(020day)
- Linux 内核源码(kernel source)
- 位运算实现加减乘除
- 【MyEclipse】设置作者信息等