Android自定义View(一)
来源:互联网 发布:八个字网络流行语 编辑:程序博客网 时间:2024/06/05 08:59
此文章是转载笔者鸿祥大神的文章,加上自己的见解而成,本文的大多内容可在鸿祥大神的这篇博客找到【Android自定义View(一)】
其实这篇文章的也很棒【Android中将布局文件/View添加至窗口过程分析 ---- 从setContentView()谈起】
接下来我们进入正题哈[在这个项目中,笔者SDK的version是22]
很多的Android入门程序猿来说对于android自定义View,可能都是比较恐惧的,但是这又是高手进阶的必经之路,所有准备在自定义View上面花一些功夫,多写一些文章。先总结下自定义View的步骤:
1、自定义View的属性
2、在View的构造方法中获得我们自定义的属性
[3、重写onMesure]
[4、重写onLayout ]
5、重写onDraw
我把3和4用[]标出了,所以说3和4不一定是必须的,当然了大部分情况下还是需要重写的。
1、自定义View的属性,首先在res/values/ 下建立一个attribute.xml ,在里面定义我们的属性和声明我们的整个样式。
<?xml version="1.0" encoding="utf-8"?><!-- 文本的属性文件--><resources> <attr name="titleTextContent" format="string"/> <attr name="titleTextColor" format="color"/> <attr name="titleTextSize" format="dimension"/> <!-- 以上面的属性映射在一起,外界通过name="CustomTitleView"访问attribute.xml这个文件,相当于我们常说的接口吧"--> <declare-styleable name="CustomTitleView"> <attr name="titleTextContent"/> <attr name="titleTextColor"/> <attr name="titleTextSize"/> </declare-styleable></resources>
我们定义了字体,字体颜色,字体大小3个属性,format是值该属性的取值类型:
一共有:string[字符串],color[颜色],demension[尺寸],integer[整形],enum[枚举],reference[引用],float[浮点],boolean[布尔],fraction[分数],flag[信号[型]]
然后在布局文件[res/layout/activity_main.xml]中声明我们的自定义View
<?xml version="1.0" encoding="utf-8"?><!-- xmlns:custom="http://schemas.android.com/apk/res/com.wnyx.customview"--><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" xmlns:custom="http://schemas.android.com/apk/res-auto" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:paddingBottom="@dimen/activity_vertical_margin" android:paddingLeft="@dimen/activity_horizontal_margin" android:paddingRight="@dimen/activity_horizontal_margin" android:paddingTop="@dimen/activity_vertical_margin" tools:context="com.wnyx.customview.MainActivity" tools:ignore="ResAuto"> <com.wnyx.customview.CustomTitleView android:layout_width="wrap_content" android:layout_height="wrap_content" custom:titleTextContent="3712" android:padding="10dp" custom:titleTextColor="#ff0000" android:layout_centerInParent="true" custom:titleTextSize="40sp"/></RelativeLayout>
其中笔者注释了【xmlns:custom="http://schemas.android.com/apk/res/com.wnyx.customview】[后面的包路径com.wnyx.customview指的是项目的package],
了解xml文件的读者就会知道xmlns其实就是xml文件的命名空间,不了解的读者可以看下面这两篇文章
【Android 自定义view中的属性,命名空间,以及tools标签】
【android 布局文件中xmlns:android="http://schemas.android.com/apk/res/android"】
2、在View的构造方法中,获得我们的自定义的样式public class CustomTitleView extends View { //文本字体的内容 private String titleTextContent ; //文本字体的颜色 private int titleTextColor ; //文本字体的大小 private int titleTextSize ; //文本的绘制范围[即规定矩形的宽高] //在计算机中,我们通过上、下、左、右四个点坐标来确定举行的大小[范围]。当然,两个点坐标也是可以确定矩形的大小[范围、尺寸] private Rect rect ; //画笔[Paint][Paint类保存有关如何绘制的样式[形状]和颜色信息几何,文本和位图] //像我们日常生活中的笔芯一样,有的写的字笔画粗点,有的写字笔画细点[笔芯的属性:字笔画的粗细,字笔画的颜色] //字的粗细和字的大小是两个概念,不要搞混,例如一根细笔芯能写出一个很小的字,也能写出一个很大的字 private Paint paint ; public CustomTitleView(Context context) { //super(context); //调用本类含两个构造参数的构造函数 this(context,null); } public CustomTitleView(Context context, AttributeSet attrs) { //super(context, attrs); //默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造方法调用我们的三个参数的构造方法,我们在三个参数的构造方法中获得自定义属性。 //调用本类含三个构造参数的构造函数 this(context,attrs,0); } public CustomTitleView(Context context, AttributeSet attrs, int defStyleAttr) { //调用父类的含三个参数的构造函数 super(context, attrs, defStyleAttr); //Read customView's attribute from res/values/attribute.xml file . TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomTitleView,0,0); int count = typedArray.getIndexCount(); for (int i=0;i<count;i++) { int attributeId = typedArray.getIndex(i); switch (attributeId) { case R.styleable.CustomTitleView_titleTextContent: titleTextContent = typedArray.getString(attributeId); break; case R.styleable.CustomTitleView_titleTextColor: //默认设置颜色位灰色 titleTextColor = typedArray.getColor(attributeId, Color.BLUE); break; case R.styleable.CustomTitleView_titleTextSize: // 默认设置为16sp,TypeValue也可以把sp转化为px titleTextSize = typedArray.getDimensionPixelSize(attributeId, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,14,getResources().getDisplayMetrics())); break; } } //用完记得回收 typedArray.recycle(); typedArray = null; //获得绘制文本的宽和高[范围、边界] paint = new Paint(); paint.setTextSize(titleTextSize); rect = new Rect(); paint.getTextBounds(titleTextContent,0,titleTextContent.length(),rect); }}我们重写了3个构造方法,默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造调用我们的三个参数的构造,我们在三个参数的构造中获得自定义属性。
3、我们重写onMeasure()方法
//1:在画图之前,我们先要测量[度量Measure]图的大小; //2:知道图的大小尺寸后,我们要在画板上寻找能放置该图的空间,即放置[布局Layout]图在画板上的位置 //3:当然,上述的第一第二步只是我们在正式画图之前的准备工作,当准备工作做好之后,我们进入正式的画图[绘制Draw]阶段 //测量[度量Measure] @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //super.onMeasure(widthMeasureSpec, heightMeasureSpec); //规范[规格]:常指生产的成品或所使用的原材料等规定的质量标准,常用在制造学和物理学中。同一品种或同一型号产品的不同尺寸[具体参考互动百科] //我们这样来理解规格,其实规范相当模式的集合,但是每个模式都有一个范围,范围里面的某个具体的值我们称为size[大小] //就像数学中的数轴划分区间的方法一样 //规范[规格]:0-1为一个规范; //模式:把 0-1分为2份[0-0.5(模式一)和0.5到1(模式2)] //大小:0.4为[0,0.5]这个区间的一个具体值,我们称之为具体的大小 //笔者花这么多文字来描述,只想阐述一个道理:"计算机中的概念大部分来自现实生活,在现实生活中都可以找到对应的模板"。 //通过规范[spec]得到得到模式[mode] int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //通过规范[spec]得到得到大小[size] int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width ; int height ; if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { paint.setTextSize(titleTextSize); paint.getTextBounds(titleTextContent, 0, titleTextContent.length(), rect); float textWidth = rect.width(); //desired[期望] int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight()); width = desired; } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { paint.setTextSize(titleTextSize); paint.getTextBounds(titleTextContent, 0, titleTextContent.length(), rect); float textHeight = rect.height(); int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom()); height = desired; } setMeasuredDimension(width, height); }
想要具体了解onMeasure()方法的读者请看这篇文章【Android开发之自定义控件(一)---onMeasure详解】,里面涉及到源代码
//放置[布局Layout],确定View的位置 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { //super.onLayout(changed, left, top, right, bottom); }想要具体了解onLayout()方法的读者请看这篇文章【Android开发之自定义控件(二)---onLayout详解】,里面涉及到源代码
5、我们重写onDraw()方法
//画图[绘制Draw] canvas[画布,即我们现实生活中画板、绘画纸] @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘画前先把需要画文本的位置的规划出来,即Rect充当容器,容器里面放置文本这个图 paint.setColor(Color.YELLOW); canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint); //真正绘制文本这个图了 paint.setColor(titleTextColor); canvas.drawText(titleTextContent, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + rect.height(),paint); }想要具体了解onDraw()方法的读者请看这篇文章【Android 自定义 view(三)—— onDraw 方法理解】
下面是完整的CustomTitleView.java的代码
package com.wnyx.customview;import android.content.Context;import android.content.res.TypedArray;import android.graphics.Canvas;import android.graphics.Color;import android.graphics.Paint;import android.graphics.Rect;import android.util.AttributeSet;import android.util.TypedValue;import android.view.View;/** * Created by wn on 2017/7/5. * CustomView:自定义View */public class CustomTitleView extends View { //文本字体的内容 private String titleTextContent ; //文本字体的颜色 private int titleTextColor ; //文本字体的大小 private int titleTextSize ; //文本的绘制范围[即规定矩形的宽高] //在计算机中,我们通过上、下、左、右四个点坐标来确定举行的大小[范围]。当然,两个点坐标也是可以确定矩形的大小[范围、尺寸] private Rect rect ; //画笔[Paint][Paint类保存有关如何绘制的样式[形状]和颜色信息几何,文本和位图] //像我们日常生活中的笔芯一样,有的写的字笔画粗点,有的写字笔画细点[笔芯的属性:字笔画的粗细,字笔画的颜色] //字的粗细和字的大小是两个概念,不要搞混,例如一根细笔芯能写出一个很小的字,也能写出一个很大的字 private Paint paint ; public CustomTitleView(Context context) { //super(context); //调用本类含两个构造参数的构造函数 this(context,null); } public CustomTitleView(Context context, AttributeSet attrs) { //super(context, attrs); //默认的布局文件调用的是两个参数的构造方法,所以记得让所有的构造方法调用我们的三个参数的构造方法,我们在三个参数的构造方法中获得自定义属性。 //调用本类含三个构造参数的构造函数 this(context,attrs,0); } public CustomTitleView(Context context, AttributeSet attrs, int defStyleAttr) { //调用父类的含三个参数的构造函数 super(context, attrs, defStyleAttr); //Read customView's attribute from res/values/attribute.xml file . TypedArray typedArray = context.getTheme().obtainStyledAttributes(attrs,R.styleable.CustomTitleView,0,0); int count = typedArray.getIndexCount(); for (int i=0;i<count;i++) { int attributeId = typedArray.getIndex(i); switch (attributeId) { case R.styleable.CustomTitleView_titleTextContent: titleTextContent = typedArray.getString(attributeId); break; case R.styleable.CustomTitleView_titleTextColor: //默认设置颜色位灰色 titleTextColor = typedArray.getColor(attributeId, Color.BLUE); break; case R.styleable.CustomTitleView_titleTextSize: // 默认设置为16sp,TypeValue也可以把sp转化为px titleTextSize = typedArray.getDimensionPixelSize(attributeId, (int)TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,14,getResources().getDisplayMetrics())); break; } } //用完记得回收 typedArray.recycle(); typedArray = null; //获得绘制文本的宽和高[范围、边界] paint = new Paint(); paint.setTextSize(titleTextSize); rect = new Rect(); paint.getTextBounds(titleTextContent,0,titleTextContent.length(),rect); } //1:在画图之前,我们先要测量[度量Measure]图的大小; //2:知道图的大小尺寸后,我们要在画板上寻找能放置该图的空间,即放置[布局Layout]图在画板上的位置 //3:当然,上述的第一第二步只是我们在正式画图之前的准备工作,当准备工作做好之后,我们进入正式的画图[绘制Draw]阶段 //测量[度量Measure] @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { //super.onMeasure(widthMeasureSpec, heightMeasureSpec); //规范[规格]:常指生产的成品或所使用的原材料等规定的质量标准,常用在制造学和物理学中。同一品种或同一型号产品的不同尺寸[具体参考互动百科] //我们这样来理解规格,其实规范相当模式的集合,但是每个模式都有一个范围,范围里面的某个具体的值我们称为size[大小] //就像数学中的数轴划分区间的方法一样 //规范[规格]:0-1为一个规范; //模式:把 0-1分为2份[0-0.5(模式一)和0.5到1(模式2)] //大小:0.4为[0,0.5]这个区间的一个具体值,我们称之为具体的大小 //笔者花这么多文字来描述,只想阐述一个道理:"计算机中的概念大部分来自现实生活,在现实生活中都可以找到对应的模板"。 //通过规范[spec]得到得到模式[mode] int widthMode = MeasureSpec.getMode(widthMeasureSpec); int heightMode = MeasureSpec.getMode(heightMeasureSpec); //通过规范[spec]得到得到大小[size] int widthSize = MeasureSpec.getSize(widthMeasureSpec); int heightSize = MeasureSpec.getSize(heightMeasureSpec); int width ; int height ; if (widthMode == MeasureSpec.EXACTLY) { width = widthSize; } else { paint.setTextSize(titleTextSize); paint.getTextBounds(titleTextContent, 0, titleTextContent.length(), rect); float textWidth = rect.width(); //desired[期望] int desired = (int) (getPaddingLeft() + textWidth + getPaddingRight()); width = desired; } if (heightMode == MeasureSpec.EXACTLY) { height = heightSize; } else { paint.setTextSize(titleTextSize); paint.getTextBounds(titleTextContent, 0, titleTextContent.length(), rect); float textHeight = rect.height(); int desired = (int) (getPaddingTop() + textHeight + getPaddingBottom()); height = desired; } setMeasuredDimension(width, height); } //放置[布局Layout],确定View的位置 @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { //super.onLayout(changed, left, top, right, bottom); } //画图[绘制Draw] canvas[画布,即我们现实生活中画板、绘画纸] @Override protected void onDraw(Canvas canvas) { super.onDraw(canvas); //绘画前先把需要画文本的位置的规划出来,即Rect充当容器,容器里面放置文本这个图 paint.setColor(Color.YELLOW); canvas.drawRect(0,0,getMeasuredWidth(),getMeasuredHeight(),paint); //真正绘制文本这个图了 paint.setColor(titleTextColor); canvas.drawText(titleTextContent, getWidth() / 2 - rect.width() / 2, getHeight() / 2 + rect.height(),paint); }}
下面是MainActivity.java的代码
package com.wnyx.customview;import android.app.Activity;import android.os.Bundle;//注释:AppCompatActivity和Activity的setContentView(int layoutId)的实现方法不一样public class MainActivity extends Activity { @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); }}
最后,上一张程序运行截图吧
最后,可能设计到高版本的SDK转到低版本的SDK,读者课参考这篇文章【修改Android Studio默认的API Level(SDK版本)】
当然,要修改的文件看你项目情况了,一般都是修改values文件下的xml文件
关于AndroidStudio中AndroidStudio中make Project、clean Project、Rebuild Project的区别?
1.Make Project:编译Project下所有Module,一般是自上次编译后Project下有更新的文件,不生成apk。
2.Make Selected Modules:编译指定的Module,一般是自上次编译后Module下有更新的文件,不生成apk。
3.Clean Project:删除之前编译后的编译文件,并重新编译整个Project,比较花费时间,不生成apk。
4.Rebuild Project:先执行Clean操作,删除之前编译的编译文件和可执行文件,然后重新编译新的编译文件,不生成apk,
这里效果其实跟Clean Project是一致的,这个不知道Google搞什么鬼~~
5.Build APK:前面4个选项都是编译,没有生成apk文件,如果想生成apk,需要点击Build APK。
6.Generate Signed APK:生成有签名的apk。
- 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 (一)
- Android 自定义View (一)
- Android 自定义View (一)
- Android 自定义View (一)
- UIControl 控件简介
- bootstrap底部导航栏固定
- [Delphi]获取机器的Mac地址
- UITextField小结
- iOS系统高德地图的使用
- Android自定义View(一)
- 关于php在通信协议中的使用研究
- 高斯混合背景建模的改进方案
- Effective C++第五章-实现
- iphone 线程 NSCondition NSThread
- 微信浏览器禁止页面下拉查看网址(不影响页面内部scroll)
- Cocoa多线程编程之 block 与 dispatch quene
- C# DataTable中Compute方法用法集锦(数值/字符串/运算符/表等操作)(转载)
- UITableViewCell的4种显示格式