1. 使用一个自定义view记录思想

  2. 效果如下,实现方式有很多种,以此来回顾一下自定义view

  3. 使用Android Studio快速创建

  4. 删除一些无用的

(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()}
  1. 在我们感觉快大功告成时候发现,宽度怎么只能设置具体值,比如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)}}
  1. 我们再去试一下使用wrap_content,这次正常了,然后再在项目中使用
    LinearLayout水平放置

  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>

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的基本使用相关推荐

  1. Android自定义View之圆形头像

    记录贴 现在制作圆形头像的第三方工具已经很多了,本帖只为记录自定义view学习过程. 1.主体代码部分 public class CirclePhotoView extends View {priva ...

  2. Android自定义View全解

    目录 目录.png 1. 自定义View基础 1.1 分类 自定义View的实现方式有以下几种 类型 定义 自定义组合控件 多个控件组合成为一个新的控件,方便多处复用 继承系统View控件 继承自Te ...

  3. 自定义View开发时踩的坑

    在这篇博客中,我希望按照日记的方式来记录自己在android的自定义视图开发中所遇到的一些问题: 1.首先是在自定义view中获取对应的子视图,在自定义视图中定义了很多成员变量,按照个人的代码编写习惯 ...

  4. Android中实现Bitmap在自定义View中的放大与拖动

    一基本实现思路: 基于View类实现自定义View –MyImageView类.在使用View的Activity类中完成OnTouchListener接口,实现对MotionEvent事件的监听与处理 ...

  5. android 单选框 icon,Android中的普通对话框、单选对话框、多选对话框、带Icon的对话框、以及自定义Adapter和自定义View对话框详解...

    标签: 对话框就是一个AlertDialog,但是一个简单的AlertDialog,我们却可以将它玩出许多花样来,下面我们就来一起总结一下AlertDialog的用法.看看各位童鞋在平时的工作中否都用 ...

  6. 自定义View:测量measure,布局layout,绘制draw

    1. 什么是View 在Android的官方文档中是这样描述的:表示了用户界面的基本构建模块.一个View占用了屏幕上的一个矩形区域并且负责界面绘制和事件处理. 手机屏幕上所有看得见摸得着的都是Vie ...

  7. 《Android开发艺术探索》自定义View中关于“HorizontalScrollViewEx”的改进

    在<Android开发艺术探索>一书中自定义View一节中提到了关于一个类似横向滑动List的自定义ViewGroup:HorizontalScrollViewEx.如果你使用过的话就会发 ...

  8. 手把手带你画一个漂亮蜂窝view Android自定义view

    上一篇做了一个水波纹view  不知道大家有没有动手试试呢点击打开链接 这个效果做起来好像没什么意义,如果不加监听回调 图片就能直接替代.写这篇博客的目的是锻炼一下思维能力,以更好的面多各种自定义vi ...

  9. android标尺自定义view,android尺子的自定义view——RulerView详解

    项目中用到自定义尺子的样式: 原效果为 因为跟自己要使用的view稍有不同 所以做了一些修改,修改的注释都放在代码中了,特此记录一下. 首先是一个自定义View: public class RuleV ...

  10. 自定义View -- 刻度尺

    [图片] 这次在自定义View时主要通过以下几个步骤: 1.准备阶段(在构造方法处) 初始化各种默认的Paint,图片资源.( 其中NinePatch资源需要通过Bitmap生成,绘制时调用nineP ...

最新文章

  1. Swift使用iconfont图标
  2. 阿里云专家手把手教你重塑 IT 架构!
  3. leetcode239. 滑动窗口最大值(思路+详解)
  4. resteasy_RESTEasy教程第2部分:Spring集成
  5. 查询数据库中有多少个数据表_您的数据中有多少汁?
  6. oracle如何检查是否rac,Oracle RAC 状态检查
  7. java war包混淆_使用proguard混淆java web项目代码
  8. Ubuntu 10.10用LibreOffice替换OpenOffice
  9. Gadgets 侧边栏小工具 跟踪调试方法
  10. JavaScript中的数据类型及数据类型转换(附实例)
  11. mysql授权数据库(或表)给用户
  12. Python实现高斯滤波
  13. SPSS 探索性因素分析与验证性因素分析的区别【SPSS 034期】
  14. 360浏览器插件360抢票王火车票刷票技巧
  15. 关于电子科技大学学生用餐情况的一些调查
  16. linux内核函数 ffs,linux内核中的宏ffs(x)【转】
  17. 云服务器如何计算宽带
  18. 自发和诱发电生理活动之间的动态关系
  19. Android 网络请求库Retrofit简单使用
  20. 医学图像的CT值与像素值总结及转换代码

热门文章

  1. 一个复杂的nf_conntrack实例全景解析
  2. 1.2.1 Milking Cows 挤牛奶
  3. 【数论】四则运算的取模处理
  4. 用户环境变量_linux 初级3 环境变量命令env、set、export、declare的区别
  5. python设计查询余额程序_使用Python调取任意数字资产钱包余额功能
  6. ajax与easyui树节点,easyUI 树形列表 想实现 类似于according 的效果 怎么办?
  7. mysql的db.opt文件_MySQL数据库的db.opt文件
  8. 图:出场顺序号码随机抽取及公开展示,并行随机抽取多个题目号码及公开展示-软件原型设计
  9. PowerPoint的巧妙使用就可造就一场经济而又专业的知识竞赛场面
  10. 如何分辨高仿和原装VGA线(图文教程)_对比赛打分进行现场硬件连线产品参考_[评委计分系统-双屏版]使用参考