记录自定义view的基本使用
使用一个自定义view记录思想
效果如下,实现方式有很多种,以此来回顾一下自定义view
使用Android Studio快速创建
删除一些无用的
(1)首先思考一下这个图片,共有三个一样,这时候我们只要实现抽出公共的,我们看到总共分三个模块,1个是矩形背景,2是倒计时文字,3是右边的文字显示,相应的,我们对每个模块进行颜色,文字大小等划分
(2)对应的attrs.xml
<!--倒计时--><declare-styleable name="TimeView"><!--矩形背景--><attr name="re_color" format="color" /><attr name="re_width" format="dimension" /><attr name="re_height" format="dimension" /><!--倒计时数字--><attr name="time_string" format="string" /><attr name="time_color" format="color" /><attr name="time_size" format="dimension" /><!--日期文字--><attr name="date_text_string" format="string" /><attr name="date_text_color" format="color" /><attr name="date_text_size" format="dimension" /></declare-styleable>
(3)我们再去java文件中将这些属性获取出来,都是一些固定操作
// 矩形背景private var _rectangleColor: Int = Color.BLACK // 矩形背景颜色private var _rectangleWidth: Float = 0f // 矩形宽度private var _rectangleHeight: Float = 0f // 矩形高度// 倒计时数字private var _timeString: String = ""private var _timeTextColor: Int = Color.WHITE // 倒计时颜色private var _timeTextSize: Float = 13f // 倒计时数字大小// 日期数字private var _textString: String = ""private var _textColor: Int = Color.BLACK // 倒计时颜色private var _textSize: Float = 13f // 倒计时数字大小// init中,最后别忘了a.recycle()// Load attributesval a = context.obtainStyledAttributes(attrs, R.styleable.TimeView, defStyle, 0)// 矩形背景_rectangleColor = a.getColor(R.styleable.TimeView_re_color,_rectangleColor)_rectangleWidth = a.getDimension(R.styleable.TimeView_re_width,_rectangleWidth)_rectangleHeight = a.getDimension(R.styleable.TimeView_re_height,_rectangleHeight)// 倒计时_timeString = a.getString(R.styleable.TimeView_time_string).toString()_timeTextSize = a.getDimension(R.styleable.TimeView_time_size,_timeTextSize)_timeTextColor = a.getColor(R.styleable.TimeView_time_color,_timeTextColor)// 文字_textString = a.getString(R.styleable.TimeView_date_text_string).toString()_textSize = a.getDimension(R.styleable.TimeView_date_text_size,_textSize)_textColor = a.getColor(R.styleable.TimeView_date_text_color,_textColor)a.recycle()
(4)这时候我们再分析图片,三个模块都需要一个画笔吧,然后我们再各自定义画笔和初始化画笔
private var rectanglePaint: Paint? = null // 矩形画笔private var timePaint: TextPaint? = null // 时间画笔private var textPaint: TextPaint? = null // 文字画笔// 初始化它们// Set up a default TextPaint objectrectanglePaint = Paint().apply {flags = Paint.ANTI_ALIAS_FLAGcolor = _rectangleColorstyle = Paint.Style.FILL}// timetimePaint = TextPaint().apply {flags = Paint.ANTI_ALIAS_FLAGtextAlign = Paint.Align.CENTERtextSize = _timeTextSizecolor = _timeTextColor}// texttextPaint = TextPaint().apply {flags = Paint.ANTI_ALIAS_FLAGtextAlign = Paint.Align.CENTERtextSize = _textSizecolor = _textColor}
(5)然后我们来进行绘制,可以自己动手画个草图,边调试边改
@SuppressLint("DrawAllocation")override fun onDraw(canvas: Canvas) {super.onDraw(canvas)// allocations per draw cycle.val paddingLeft = paddingLeftval paddingTop = paddingTopval paddingRight = paddingRightval paddingBottom = paddingBottomval contentWidth = width - paddingLeft - paddingRightval contentHeight = height - paddingTop - paddingBottomvar timeWidth = getTextWidth(_timeString, timePaint!!)val timeHeight = getTextHeight(_timeString, timePaint!!)val textWidth = getTextWidth(_textString, textPaint!!)val textHeight = getTextHeight(_textString, textPaint!!)val leftWidth = (contentWidth - (_rectangleWidth + 10 + textWidth)) / 2with(canvas) {drawRoundRect(RectF(leftWidth , (contentHeight - _rectangleHeight) / 2, leftWidth + _rectangleWidth, _rectangleHeight + (contentHeight - _rectangleHeight) / 2), 10f, 10f, rectanglePaint!!)drawText(_timeString, leftWidth + _rectangleWidth / 2, (contentHeight / 2 + timeHeight / 2).toFloat(), timePaint!!)drawText(_textString, leftWidth + _rectangleWidth + textWidth / 2 + 10, (contentHeight / 2 + textHeight / 2).toFloat(), textPaint!!)}}private fun getTextWidth(text: String, paint: Paint): Float {val rect = Rect() // 文字所在区域的矩形paint.getTextBounds(text, 0, text.length, rect)return rect.width().toFloat()}private fun getTextHeight(text: String, paint: Paint): Float {val rect = Rect() // 文字所在区域的矩形paint.getTextBounds(text, 0, text.length, rect)return rect.height().toFloat()}
(6)然后我们看下效果,接下来就是再LinearLayout布局中,水平放置三个就可以了
(7)代码改变各个属性的状态也别落下
/*** 矩形背景*/var rectangleColor: Intget() = _rectangleColorset(value) {_rectangleColor = valueinvalidate()}var rectangleWidth: Floatget() = _rectangleWidthset(value) {_rectangleWidth = valueinvalidate()}var rectangleHeight: Floatget() = _rectangleHeightset(value) {_rectangleHeight = valueinvalidate()}/*** 倒计时*/var timeString: Stringget() = _timeStringset(value) {_timeString = valueinvalidate()}var timeTextColor: Intget() = _timeTextColorset(value) {_timeTextColor = valueinvalidate()}var timeTextSize: Floatget() = _timeTextSizeset(value) {_timeTextSize = valueinvalidate()}/*** 日期*/var textString: Stringget() = _textStringset(value) {_textString = valueinvalidate()}var textColor: Intget() = _textColorset(value) {_textColor = valueinvalidate()}var textSize: Floatget() = _textSizeset(value) {_textSize = valueinvalidate()}
- 在我们感觉快大功告成时候发现,宽度怎么只能设置具体值,比如100,200,设置android:layout_width=“wrap_content” android:layout_width="match_parent"效果是一样的,我们百度一下,发现父类View中onMeasure()中默认的使用方式
/*** Utility to return a default size. Uses the supplied size if the* MeasureSpec imposed no constraints. Will get larger if allowed* by the MeasureSpec.** @param size Default size for this view* @param measureSpec Constraints imposed by the parent* @return The size this view should be.*/public static int getDefaultSize(int size, int measureSpec) {int result = size;int specMode = MeasureSpec.getMode(measureSpec);int specSize = MeasureSpec.getSize(measureSpec);switch (specMode) {case MeasureSpec.UNSPECIFIED:result = size;break;case MeasureSpec.AT_MOST:case MeasureSpec.EXACTLY:result = specSize;break;}return result;}
case MeasureSpec.AT_MOST:
case MeasureSpec.EXACTLY:使用的是一样的,这时候我们需要自己来重写,进行区分
// 给个默认值private val DEFAULT_WITH = 140private val DEFAULT_Height = 140override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val widthMode = MeasureSpec.getMode(widthMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)val heightMode = MeasureSpec.getMode(heightMeasureSpec)//处理wrap_contentde情况if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WITH, DEFAULT_Height)} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WITH, heightSize)} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, DEFAULT_Height)}}
我们再去试一下使用wrap_content,这次正常了,然后再在项目中使用
LinearLayout水平放置
源码
attrs.xml
<!--倒计时--><declare-styleable name="TimeView"><!--矩形背景--><attr name="re_color" format="color" /><attr name="re_width" format="dimension" /><attr name="re_height" format="dimension" /><!--倒计时数字--><attr name="time_string" format="string" /><attr name="time_color" format="color" /><attr name="time_size" format="dimension" /><!--日期文字--><attr name="date_text_string" format="string" /><attr name="date_text_color" format="color" /><attr name="date_text_size" format="dimension" /></declare-styleable>
TimeView
class TimeView : View {// 矩形背景private var _rectangleColor: Int = Color.BLACK // 矩形背景颜色private var _rectangleWidth: Float = 0f // 矩形宽度private var _rectangleHeight: Float = 0f // 矩形高度private var rectanglePaint: Paint? = null // 矩形画笔// 倒计时数字private var _timeString: String = ""private var _timeTextColor: Int = Color.WHITE // 倒计时颜色private var _timeTextSize: Float = 13f // 倒计时数字大小private var timePaint: TextPaint? = null// 日期数字private var _textString: String = ""private var _textColor: Int = Color.BLACK // 倒计时颜色private var _textSize: Float = 13f // 倒计时数字大小private var textPaint: TextPaint? = nullprivate val DEFAULT_WITH = 140private val DEFAULT_Height = 140/*** 矩形背景*/var rectangleColor: Intget() = _rectangleColorset(value) {_rectangleColor = valueinvalidate()}var rectangleWidth: Floatget() = _rectangleWidthset(value) {_rectangleWidth = valueinvalidate()}var rectangleHeight: Floatget() = _rectangleHeightset(value) {_rectangleHeight = valueinvalidate()}/*** 倒计时*/var timeString: Stringget() = _timeStringset(value) {_timeString = valueinvalidate()}var timeTextColor: Intget() = _timeTextColorset(value) {_timeTextColor = valueinvalidate()}var timeTextSize: Floatget() = _timeTextSizeset(value) {_timeTextSize = valueinvalidate()}/*** 日期*/var textString: Stringget() = _textStringset(value) {_textString = valueinvalidate()}var textColor: Intget() = _textColorset(value) {_textColor = valueinvalidate()}var textSize: Floatget() = _textSizeset(value) {_textSize = valueinvalidate()}constructor(context: Context) : super(context) {init(null, 0)}constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {init(attrs, 0)}constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context,attrs,defStyle) {init(attrs, defStyle)}private fun init(attrs: AttributeSet?, defStyle: Int) {// Load attributesval a = context.obtainStyledAttributes(attrs, R.styleable.TimeView, defStyle, 0)// 矩形背景_rectangleColor = a.getColor(R.styleable.TimeView_re_color,_rectangleColor)_rectangleWidth = a.getDimension(R.styleable.TimeView_re_width,_rectangleWidth)_rectangleHeight = a.getDimension(R.styleable.TimeView_re_height,_rectangleHeight)// 倒计时_timeString = a.getString(R.styleable.TimeView_time_string).toString()_timeTextSize = a.getDimension(R.styleable.TimeView_time_size,_timeTextSize)_timeTextColor = a.getColor(R.styleable.TimeView_time_color,_timeTextColor)// 文字_textString = a.getString(R.styleable.TimeView_date_text_string).toString()_textSize = a.getDimension(R.styleable.TimeView_date_text_size,_textSize)_textColor = a.getColor(R.styleable.TimeView_date_text_color,_textColor)a.recycle()// Set up a default TextPaint objectrectanglePaint = Paint().apply {flags = Paint.ANTI_ALIAS_FLAGcolor = _rectangleColorstyle = Paint.Style.FILL}// timetimePaint = TextPaint().apply {flags = Paint.ANTI_ALIAS_FLAGtextAlign = Paint.Align.CENTERtextSize = _timeTextSizecolor = _timeTextColor}// texttextPaint = TextPaint().apply {flags = Paint.ANTI_ALIAS_FLAGtextAlign = Paint.Align.CENTERtextSize = _textSizecolor = _textColor}}override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {super.onMeasure(widthMeasureSpec, heightMeasureSpec)val widthSize = MeasureSpec.getSize(widthMeasureSpec)val widthMode = MeasureSpec.getMode(widthMeasureSpec)val heightSize = MeasureSpec.getSize(heightMeasureSpec)val heightMode = MeasureSpec.getMode(heightMeasureSpec)//处理wrap_contentde情况if (widthMode == MeasureSpec.AT_MOST && heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WITH, DEFAULT_Height)} else if (widthMode == MeasureSpec.AT_MOST) {setMeasuredDimension(DEFAULT_WITH, heightSize)} else if (heightMode == MeasureSpec.AT_MOST) {setMeasuredDimension(widthSize, DEFAULT_Height)}}@SuppressLint("DrawAllocation")override fun onDraw(canvas: Canvas) {super.onDraw(canvas)// allocations per draw cycle.val paddingLeft = paddingLeftval paddingTop = paddingTopval paddingRight = paddingRightval paddingBottom = paddingBottomval contentWidth = width - paddingLeft - paddingRightval contentHeight = height - paddingTop - paddingBottomvar timeWidth = getTextWidth(_timeString, timePaint!!)val timeHeight = getTextHeight(_timeString, timePaint!!)val textWidth = getTextWidth(_textString, textPaint!!)val textHeight = getTextHeight(_textString, textPaint!!)val leftWidth = (contentWidth - (_rectangleWidth + 10 + textWidth)) / 2with(canvas) {drawRoundRect(RectF(leftWidth , (contentHeight - _rectangleHeight) / 2, leftWidth + _rectangleWidth, _rectangleHeight + (contentHeight - _rectangleHeight) / 2), 10f, 10f, rectanglePaint!!)drawText(_timeString, leftWidth + _rectangleWidth / 2, (contentHeight / 2 + timeHeight / 2).toFloat(), timePaint!!)drawText(_textString, leftWidth + _rectangleWidth + textWidth / 2 + 10, (contentHeight / 2 + textHeight / 2).toFloat(), textPaint!!)}}private fun getTextWidth(text: String, paint: Paint): Float {val rect = Rect() // 文字所在区域的矩形paint.getTextBounds(text, 0, text.length, rect)return rect.width().toFloat()}private fun getTextHeight(text: String, paint: Paint): Float {val rect = Rect() // 文字所在区域的矩形paint.getTextBounds(text, 0, text.length, rect)return rect.height().toFloat()}}
记录自定义view的基本使用相关推荐
- Android自定义View之圆形头像
记录贴 现在制作圆形头像的第三方工具已经很多了,本帖只为记录自定义view学习过程. 1.主体代码部分 public class CirclePhotoView extends View {priva ...
- Android自定义View全解
目录 目录.png 1. 自定义View基础 1.1 分类 自定义View的实现方式有以下几种 类型 定义 自定义组合控件 多个控件组合成为一个新的控件,方便多处复用 继承系统View控件 继承自Te ...
- 自定义View开发时踩的坑
在这篇博客中,我希望按照日记的方式来记录自己在android的自定义视图开发中所遇到的一些问题: 1.首先是在自定义view中获取对应的子视图,在自定义视图中定义了很多成员变量,按照个人的代码编写习惯 ...
- Android中实现Bitmap在自定义View中的放大与拖动
一基本实现思路: 基于View类实现自定义View –MyImageView类.在使用View的Activity类中完成OnTouchListener接口,实现对MotionEvent事件的监听与处理 ...
- android 单选框 icon,Android中的普通对话框、单选对话框、多选对话框、带Icon的对话框、以及自定义Adapter和自定义View对话框详解...
标签: 对话框就是一个AlertDialog,但是一个简单的AlertDialog,我们却可以将它玩出许多花样来,下面我们就来一起总结一下AlertDialog的用法.看看各位童鞋在平时的工作中否都用 ...
- 自定义View:测量measure,布局layout,绘制draw
1. 什么是View 在Android的官方文档中是这样描述的:表示了用户界面的基本构建模块.一个View占用了屏幕上的一个矩形区域并且负责界面绘制和事件处理. 手机屏幕上所有看得见摸得着的都是Vie ...
- 《Android开发艺术探索》自定义View中关于“HorizontalScrollViewEx”的改进
在<Android开发艺术探索>一书中自定义View一节中提到了关于一个类似横向滑动List的自定义ViewGroup:HorizontalScrollViewEx.如果你使用过的话就会发 ...
- 手把手带你画一个漂亮蜂窝view Android自定义view
上一篇做了一个水波纹view 不知道大家有没有动手试试呢点击打开链接 这个效果做起来好像没什么意义,如果不加监听回调 图片就能直接替代.写这篇博客的目的是锻炼一下思维能力,以更好的面多各种自定义vi ...
- android标尺自定义view,android尺子的自定义view——RulerView详解
项目中用到自定义尺子的样式: 原效果为 因为跟自己要使用的view稍有不同 所以做了一些修改,修改的注释都放在代码中了,特此记录一下. 首先是一个自定义View: public class RuleV ...
- 自定义View -- 刻度尺
[图片] 这次在自定义View时主要通过以下几个步骤: 1.准备阶段(在构造方法处) 初始化各种默认的Paint,图片资源.( 其中NinePatch资源需要通过Bitmap生成,绘制时调用nineP ...
最新文章
- Swift使用iconfont图标
- 阿里云专家手把手教你重塑 IT 架构!
- leetcode239. 滑动窗口最大值(思路+详解)
- resteasy_RESTEasy教程第2部分:Spring集成
- 查询数据库中有多少个数据表_您的数据中有多少汁?
- oracle如何检查是否rac,Oracle RAC 状态检查
- java war包混淆_使用proguard混淆java web项目代码
- Ubuntu 10.10用LibreOffice替换OpenOffice
- Gadgets 侧边栏小工具 跟踪调试方法
- JavaScript中的数据类型及数据类型转换(附实例)
- mysql授权数据库(或表)给用户
- Python实现高斯滤波
- SPSS 探索性因素分析与验证性因素分析的区别【SPSS 034期】
- 360浏览器插件360抢票王火车票刷票技巧
- 关于电子科技大学学生用餐情况的一些调查
- linux内核函数 ffs,linux内核中的宏ffs(x)【转】
- 云服务器如何计算宽带
- 自发和诱发电生理活动之间的动态关系
- Android 网络请求库Retrofit简单使用
- 医学图像的CT值与像素值总结及转换代码
热门文章
- 一个复杂的nf_conntrack实例全景解析
- 1.2.1 Milking Cows 挤牛奶
- 【数论】四则运算的取模处理
- 用户环境变量_linux 初级3 环境变量命令env、set、export、declare的区别
- python设计查询余额程序_使用Python调取任意数字资产钱包余额功能
- ajax与easyui树节点,easyUI 树形列表 想实现 类似于according 的效果 怎么办?
- mysql的db.opt文件_MySQL数据库的db.opt文件
- 图:出场顺序号码随机抽取及公开展示,并行随机抽取多个题目号码及公开展示-软件原型设计
- PowerPoint的巧妙使用就可造就一场经济而又专业的知识竞赛场面
- 如何分辨高仿和原装VGA线(图文教程)_对比赛打分进行现场硬件连线产品参考_[评委计分系统-双屏版]使用参考