Kotlin自定义菜单控件

本文实例为大家分享了Kotlin自定义菜单控件的具体代码,供大家参考,具体内容如下

首先贴一下效果图

思路:菜单控件分两部分,一是点击的子按钮(RecordButton),二是包裹着子按钮的容器(RecordMenu)。

子按钮负责显示文字及背景颜色和点击事件,父容器主要控制子控件的位置和动画显示。

实现:

子按钮,先贴代码

class RecordButton : RelativeLayout {
 /** 控件显示的文本*/
 lateinit var textValue: String
 /** 控件显示的文本字体大小*/
 private var textSize: Float = 18f
 /** 控件显示的文本字体颜色*/
 private var textColor: Int = Color.BLACK
 /** 控件按下时显示的文本字体颜色*/
 private var textColorPress: Int = Color.WHITE
 /** 控件显示的背景颜色*/
 private var backColorNormal: Int = R.drawable.bg_menu_item
 /** 控件按下时显示的背景颜色*/
 private var backColorPress: Int = R.drawable.bg_menu_item_press
 /** 控件是否是主按钮*/
 var isSwitchMain: Boolean = false
 /** 按钮按下时的时间*/
 var pressBtnTime: Long = 0L
 /** 按钮抬起时的时间*/
 var upBtnTime: Long = 0L
 /** 事件是否是点击事件*/
 var isClick: Boolean = false
 /** 点击事件是否打开*/
 var isOpen: Boolean = false
 /** 文本控件*/
 private lateinit var textView: TextView
 /** 监听事件*/
 var onRecordItemClickListener: OnRecordItemClickListener? = null

 constructor(context: Context,
    textValue: String,
    textSize: Float,
    textColor: Int,
    backColorNormal: Int,
    textColorPress: Int,
    backColorPress: Int) : this(context) {
  this.textValue = textValue
  this.textSize = textSize
  this.textColor = textColor
  this.backColorNormal = backColorNormal
  this.isSwitchMain = isSwitchMain
  this.textColorPress = textColorPress
  this.backColorPress = backColorPress
  setBackgroundResource(backColorNormal)

  textView = TextView(context)
  textView.text = textValue
  textView.gravity = CENTER
  textView.setTextColor(textColor)
  textView.textSize = textSize
  var ll = LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT)
  ll.addRule(CENTER_IN_PARENT)
  addView(textView, ll)
 }

 constructor(context: Context) : this(context, null) {

 }

 constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0) {
 }

 constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {

 }

 override fun onTouchEvent(event: MotionEvent?): Boolean {
  when (event?.action) {
   MotionEvent.ACTION_DOWN -> {
    pressBtnTime = System.currentTimeMillis()
    setBackgroundResource(backColorPress)
    textView.setTextColor(textColorPress)
    return true
   }
   MotionEvent.ACTION_MOVE -> {
   }
   MotionEvent.ACTION_UP -> {
    upBtnTime = System.currentTimeMillis()
    setBackgroundResource(backColorNormal)
    textView.setTextColor(textColor)
    isClick = (upBtnTime - pressBtnTime) / 1000 < 0.5
   }
  }
  if (isClick) {
   onRecordItemClickListener?.onClick(isSwitchMain, textValue,isOpen)
   isOpen = !isOpen
  }
  return true
 }
}

这里主要用一个RelativeLayout包裹着一个TextView,这么写是为了防止以后扩展,需要添加图片什么的,关于这个样式和显示没什么好说的,主要的就是点击事件,在触摸事件中判断按下和抬起的时间差,如果时间差小于0.5秒则断定为点击。

包裹容器

class RecordMenu : RelativeLayout{
 /** 子按钮半径*/
 private var itemRadius: Int = 0
 /*** 按钮间距*/
 private var itemMargin: Int = 0
 /** 动画时间*/
 private var duration: Long = 0
 /** 字体大小*/
 private var itemFontSize = 18f
 /** 字体正常颜色*/
 private var itemFontColorN = Color.BLACK
 /** 点击时字体颜色*/
 private var itemFontColorP = Color.WHITE
 /** 按钮正常背景*/
 private var itemBackDrawableN = R.drawable.bg_menu_item
 /** 按钮点击背景*/
 private var itemBackDrawableP = R.drawable.bg_menu_item_press
 /** 是否是展开状态*/
 private var isOpen: Boolean = false
 /** 动画是否正在运行*/
 private var isRun: Boolean = false
 /** 子控件监听*/
 private var recordListener = RecordListener()
 /** 上一级的监听事件*/
 var onRecordItemClickListener: OnRecordItemClickListener? = null

 constructor(context: Context):this(context,null){

 }

 constructor(context: Context, attrs: AttributeSet?) : this(context,attrs,0) {

 }

 constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs,defStyleAttr) {
  init(context, attrs)
 }

 override fun onLayout(change: Boolean, l: Int, t: Int, r: Int, b: Int) {
  /** 画出每个子控件的位置*/
  for (i in 0 until childCount) {
   var recordButton = getChildAt(i) as RecordButton
   var left: Int = 0
   var right: Int = itemRadius * 2
   var top: Int = (childCount - 1) * (itemRadius * 2 + itemMargin) + itemRadius
   var bottom: Int = top + itemRadius * 2
   recordButton.layout(left, top, right, bottom)

  }
 }

 override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
  var width = itemRadius * 2
  var height = (childCount - 1) * (itemRadius * 2 + itemMargin) + itemRadius * 2 + itemRadius
  width += paddingLeft + paddingRight
  height += paddingTop + paddingBottom
  val count = childCount
  for (i in 0 until count) {
   getChildAt(i).measure(width, width)
   if(i == count-1){
    var recordButton = getChildAt(i) as RecordButton
    recordButton.isSwitchMain = true
   }
  }
  setMeasuredDimension(width, height)
 }

 private fun init(context: Context, attrs: AttributeSet?) {
  val typedArray = context.obtainStyledAttributes(attrs, R.styleable.RecordMenu)
  itemRadius = typedArray.getDimension(R.styleable.RecordMenu_itemRadius, 30f).toInt()
  itemMargin = typedArray.getDimension(R.styleable.RecordMenu_itemMargin, 10f).toInt()
  duration = typedArray.getInteger(R.styleable.RecordMenu_animDuration, 2000).toLong()
  itemFontSize = typedArray.getDimension(R.styleable.RecordMenu_itemFontSize,18f)
  itemFontColorN = typedArray.getColor(R.styleable.RecordMenu_itemFontColorN,Color.BLACK)
  itemFontColorP = typedArray.getColor(R.styleable.RecordMenu_itemFontColorP,Color.WHITE)
  itemBackDrawableN = typedArray.getResourceId(R.styleable.RecordMenu_itemBackDrawableN,R.drawable.bg_menu_item)
  itemBackDrawableP = typedArray.getResourceId(R.styleable.RecordMenu_itemBackDrawableP,R.drawable.bg_menu_item_press)
 }

 fun addItemView(textValue: String){
  var recordButton = RecordButton(context,textValue,itemFontSize,itemFontColorN,itemBackDrawableN,itemFontColorP,itemBackDrawableP)
  var l1 = LayoutParams(itemRadius * 2, itemRadius * 2)
  addView(recordButton, l1)
  recordButton.onRecordItemClickListener = recordListener
 }
 fun addItemView(textValue: String,itemBackDrawableN:Int,itemBackDrawableP:Int){
  var recordButton = RecordButton(context,textValue,itemFontSize,itemFontColorN,itemBackDrawableN,itemFontColorP,itemBackDrawableP)
  var l1 = LayoutParams(itemRadius * 2, itemRadius * 2)
  addView(recordButton, l1)
  recordButton.onRecordItemClickListener = recordListener
 }
 inner class RecordListener : OnRecordItemClickListener {
  override fun onClick(isSwitch: Boolean, textValue: String,isOpen1:Boolean) {
   if (!isRun) {
    if (!isOpen) {
     openMenu()
    } else {
     closeMenu()
    }
   }
   onRecordItemClickListener?.onClick(isSwitch,textValue,isOpen1)
  }
 }

 /**
  * 展开控件
  */
 fun openMenu() {
  isOpen = true
  isRun = true
  for (i in 0 until childCount) {
   buttonItemOpenAnimation(i, getChildAt(i) as RecordButton)
  }
 }

 /**
  * 关闭控件
  */
 fun closeMenu() {
  isRun = true
  isOpen = false
  for (i in 0 until childCount) {
   buttonItemCloseAnimation(i, getChildAt(i) as RecordButton)
  }
 }

 /**
  * 展开动画
  */
 private fun buttonItemOpenAnimation(index: Int, view: RecordButton) {
  if (!view.isSwitchMain) {
   val propertyAnimator = view.animate().alpha(1f).setInterpolator(OvershootInterpolator()).setDuration(duration / 3)
   propertyAnimator.y((itemRadius * 2 * index + itemMargin * index + itemRadius).toFloat())
   if (isOpen) {
    view.visibility = View.VISIBLE
   }

   propertyAnimator.setListener(object : Animator.AnimatorListener {
    override fun onAnimationRepeat(p0: Animator?) {

    }

    override fun onAnimationCancel(p0: Animator?) {

    }

    override fun onAnimationEnd(p0: Animator?) {
     if (index == childCount - 2) {
      isRun = false
     }
    }

    override fun onAnimationStart(p0: Animator?) {

    }
   })
   propertyAnimator.start()
  }
 }

 /**
  * 关闭动画
  */
 private fun buttonItemCloseAnimation(index: Int, view: RecordButton) {
  if (!view.isSwitchMain) {
   val propertyAnimator = view.animate().alpha(0f).setDuration(duration / 3)
   propertyAnimator.y(((itemRadius * 2 + itemMargin) * (childCount - 1) + itemRadius).toFloat())

   propertyAnimator.setListener(object : Animator.AnimatorListener {
    override fun onAnimationStart(animation: Animator) {}

    override fun onAnimationEnd(animation: Animator) {
     if (index == childCount - 2) {
      isRun = false
     }
     if (!isOpen) {
      view.visibility = View.GONE
     }
    }

    override fun onAnimationCancel(animation: Animator) {}

    override fun onAnimationRepeat(animation: Animator) {}
   })

   propertyAnimator.start()
  }
 }
}

这里面主要就是控制子视图的大小,位置,动画。在onLayout方法中遍历每个子视图,通过layout设置视图位置,这里设置每个子视图都在容器的底部。然后在OnMeasure中设置整个视图的大小,这个根据子视图的大小和个数来计算同时加上内边距。

最后就是通过子视图的点击事件来执行动画,这里用到的是属性动画,用的是系统自带的一个插值器OvershootInterpolator,这个插值器实现的效果就是在线性上先快速的到达终点然后超出然后仔慢慢回到终点,当然不想要这种效果自己可以自定义一个插值器。至于插值器如何用及如何自定义,这里就不在赘述,以后会专门写一篇文章来介绍。

以上就是这个菜单控件的整体实现过程,是不是很简单。

(0)

相关推荐

  • Kotlin自定义View系列教程之标尺控件(选择身高、体重等)的实现

    前言 本篇文章讲的是Kotlin 自定义view之实现标尺控件Ruler,以选择身高.体重等.开发中,当我们需要获取用户的身高和体重等信息时,如果直接让他们输入,显然体验不够好.像类似于唯品会.好轻等APP都是使用了类似于刻度尺的控件让用户滑动选择身高体重,觉得很棒.网上已有人使用Java语言实现这样的功能,但不影响我对其的学习.和往常一样,主要还是想总结一下自定义view之实现标尺控件的开发过程以及一些需要注意的地方. 按照惯例,我们先来看看效果图 一.先总结下自定义View的步骤: 1.自定

  • 自定义View系列之kotlin绘制手势设置温度控件的方法

    引言 最近公司接了一个车联网的项目,主要是新能源汽车的一些控制功能,其中涉及到一个是温度的调节功能,产品的意思是做一个手势滑动调节温度,大概意思我是明白的.就是要手势调节呗,没办法,谁让我是搬砖的呢,人为刀俎,我为鱼肉,只有搞了: 最后搞出来的效果大概如下,不过还没确定, 思路 在这里我先说下自己的实现思路,这个控件的难点主要是手势控制,其他的都很简单,没有什么好说的,控制的一些具体的数值我是写死的,没有做自定义拓展,主要是闲麻烦,如果有需要可以自己的实现: 具体的实现步奏 首先绘制圆盘,刻度,

  • Kotlin如何直接使用控件ID原理详析

    前言 最近断断续续地把项目的界面部分的代码由JAva改成了Kotlin编写,并且如果应用了kotlin-android-extensions插件,一个显而易见的好处是再也不用写 findViewById()来实例化你的控件对象了,直接操作你在布局文件里的id即可,这一点我感觉比butterknife做的还简洁友好. Activity import android.support.v7.app.AppCompatActivity import android.os.Bundle import ko

  • Kotlin自定义菜单控件

    本文实例为大家分享了Kotlin自定义菜单控件的具体代码,供大家参考,具体内容如下 首先贴一下效果图 思路:菜单控件分两部分,一是点击的子按钮(RecordButton),二是包裹着子按钮的容器(RecordMenu). 子按钮负责显示文字及背景颜色和点击事件,父容器主要控制子控件的位置和动画显示. 实现: 子按钮,先贴代码 class RecordButton : RelativeLayout { /** 控件显示的文本*/ lateinit var textValue: String /**

  • Android自定义组合控件之自定义下拉刷新和左滑删除实例代码

    绪论 最近项目里面用到了下拉刷新和左滑删除,网上找了找并没有可以用的,有比较好的左滑删除,但是并没有和下拉刷新上拉加载结合到一起,要不就是一些比较水的结合,并不能在项目里面使用,小编一着急自己组合了一个,做完了和QQ的对比了一下,并没有太大区别,今天分享给大家,其实并不难,但是不知道为什么网上没有比较好的Demo,当你的项目真的很急的时候,又没有比较好的Demo,那么"那条友谊的小船儿真是说翻就翻啊",好了,下面先来具体看一下实现后的效果吧: 代码已经上传到Github上了,小伙伴们记

  • iOS开发中使用Quartz2D绘图及自定义UIImageView控件

    绘制基本图形 一.简单说明 图形上下文(Graphics Context):是一个CGContextRef类型的数据 图形上下文的作用:保存绘图信息.绘图状态 决定绘制的输出目标(绘制到什么地方去?)(输出目标可以是PDF文件.Bitmap或者显示器的窗口上) 相同的一套绘图序列,指定不同的Graphics Context,就可将相同的图像绘制到不同的目标上. Quartz2D提供了以下几种类型的Graphics Context: Bitmap Graphics Context PDF Grap

  • 自定义ExtJS控件之下拉树和下拉表格附源码

    简介 在Ext官方的例子中只有下拉列表控件,但是在实际业务中只有下拉列表无法满足需求的,像下拉树和下拉表格都是很常见的控件,对于刚使用Ext的人来说,自定义一个控件好难,其实多读官方的源码有些事情就不会那么难了.下面是下拉树的代码: 复制代码 代码如下: Ext.define('ComboTreeBox',{ extend : 'Ext.form.field.ComboBox', multiSelect : true, createPicker : function(){ var me = th

  • android之视频播放系统VideoView和自定义VideoView控件的应用

    Android播放视频,包含系统自带VideoView控件,和自定义VideoView控件,可全屏播放,案例包含了本地视频和网络视频. 1:自定义VideoView控件 2:布局代码 3:Activity代码: 4:网络权限 5:效果图 小结:其中的Uri mUri = Uri.parse("android.resource://" + getPackageName() +"/"+ R.raw.qiche);//本地视频 是加载的本地视频,可以下载一个视频,在res

  • 使用CustomValidator自定义验证控件检查是否有对ListBox控件选择

    在前网页前端处,我们放置ListBox控件,在数据提交前,检查用户是否有对此控件进行选择? Insus.NET的方法是使用Javascript与CustomValidator自定义验证控件来检查. 可以看到最终结果:  你也想参此例子,可以参考下面数据与准备方法,写一个对象,它将用来产生十个天干. HeavenlyStem.cs 复制代码 代码如下: using System; using System.Collections.Generic; using System.Linq; using

  • asp.net webform自定义分页控件

    做web开发一直用到分页控件,自己也动手实现了个,使用用户自定义控件. 翻页后数据加载使用委托,将具体实现放在在使用分页控件的页面进行注册. 有图有真相,给个直观的认识: 自定义分页控件前台代码: <style type="text/css"> .pager-m-l { margin-left: 10px; } .pager { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; fo

  • asp.net web页面自定义分页控件使用详解

    这几天学习了一下自定义分页控件,现将实现方法记录下来,亲测可以实现: 1.首先创建一个.ascx文件,命名为TurnPage,然后在里面写控件前台展示的界面: 2.然后在TurnPage.ascx.cs里面写相应的后台代码,代码如下: namespace Web { public delegate void GoToPage(int PageNum); public partial class TurnPage : System.Web.UI.UserControl { private GoTo

  • Asp.net 菜单控件简洁版

    本文介绍的菜单控件采用的css 和ul list来显示菜单,生成的html小,无需javascript支持,对大部分的浏览器都支持,除ie6要单独修改css也可以使其支持. 通过本文可以了解asp.net 控件的开发,及Composite设计模式的实际运用. 采用Composite设计模式设计菜单类: MenuCompositeitem类 复制代码 代码如下: namespace Ruinet.Controls { [Serializable()] public class MenuCompos

  • android自定义倒计时控件示例

    自定义TextView控件TimeTextView代码: 复制代码 代码如下: import android.content.Context;import android.content.res.TypedArray;import android.graphics.Paint;import android.text.Html;import android.util.AttributeSet;import android.widget.TextView; import com.new0315.R;

随机推荐