亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb

首頁 > 開發 > 綜合 > 正文

Kotlin自定義菜單控件

2024-07-21 23:03:48
字體:
來源:轉載
供稿:網友

本文實例為大家分享了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,這個插值器實現的效果就是在線性上先快速的到達終點然后超出然后仔慢慢回到終點,當然不想要這種效果自己可以自定義一個插值器。至于插值器如何用及如何自定義,這里就不在贅述,以后會專門寫一篇文章來介紹。

以上就是這個菜單控件的整體實現過程,是不是很簡單。


注:相關教程知識閱讀請移步到kotlin教程頻道。
發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
亚洲香蕉成人av网站在线观看_欧美精品成人91久久久久久久_久久久久久久久久久亚洲_热久久视久久精品18亚洲精品_国产精自产拍久久久久久_亚洲色图国产精品_91精品国产网站_中文字幕欧美日韩精品_国产精品久久久久久亚洲调教_国产精品久久一区_性夜试看影院91社区_97在线观看视频国产_68精品久久久久久欧美_欧美精品在线观看_国产精品一区二区久久精品_欧美老女人bb
欧美制服第一页| 国内精品久久久久久影视8| 亚洲精品视频网上网址在线观看| 成人国产精品免费视频| 欧美日韩亚洲精品内裤| 欧美成人免费全部| 日韩在线视频观看正片免费网站| 欧美精品日韩www.p站| 亲子乱一区二区三区电影| 美女少妇精品视频| 97超碰色婷婷| 日本精品一区二区三区在线播放视频| 成人淫片在线看| 日韩中文字在线| 亚洲美女自拍视频| 欧美大片在线影院| 久久久久久亚洲精品不卡| 国产美女91呻吟求| 国内揄拍国内精品| 深夜成人在线观看| 亚洲福利影片在线| 国产精品尤物福利片在线观看| 日韩av中文字幕在线免费观看| 国内精品视频在线| 中文字幕在线观看亚洲| 久久久国产91| 4k岛国日韩精品**专区| 欧美亚洲国产成人精品| 国产丝袜一区二区三区免费视频| 国产免费一区二区三区香蕉精| 国产网站欧美日韩免费精品在线观看| 日韩国产欧美精品在线| www.久久久久| 日韩美女视频中文字幕| 国产精品香蕉在线观看| 91国产美女在线观看| 成人午夜两性视频| 国产精品视频一区二区高潮| 成人激情免费在线| 91精品中文在线| 亚洲精品国产精品久久清纯直播| 日韩av日韩在线观看| 国产精品福利在线| 欧美性猛交xxxx乱大交| 亚洲老头老太hd| 77777亚洲午夜久久多人| 精品久久久久久中文字幕一区奶水| 久久人人爽人人| 亚洲天堂第一页| 久久精品男人天堂| 亚洲色图15p| 欧美日韩国产在线看| 91午夜理伦私人影院| 欧美一级视频免费在线观看| 国产97在线|日韩| 国产精品视频色| 欧美综合第一页| 中文字幕日韩综合av| 久久99国产综合精品女同| 久久综合五月天| 国产精品视频播放| 日本精品一区二区三区在线| 色999日韩欧美国产| 亚洲综合大片69999| 91精品视频免费看| 国产视频久久久久| 成人国产精品免费视频| 91日本视频在线| 国产亚洲欧美日韩精品| 久久久久久亚洲精品不卡| 久久天天躁狠狠躁夜夜爽蜜月| 亚洲精品福利免费在线观看| 欧美一级淫片播放口| 欧美精品一区二区免费| 亚洲精品成人免费| 伊人久久久久久久久久| 国产精品美女免费看| 美女啪啪无遮挡免费久久网站| 粗暴蹂躏中文一区二区三区| 91在线观看免费高清完整版在线观看| 欧美精品一本久久男人的天堂| 亚洲美女精品久久| 欧美性色视频在线| 色爱av美腿丝袜综合粉嫩av| 亚洲性日韩精品一区二区| 久热99视频在线观看| 国产精品jizz在线观看麻豆| 国产精品无码专区在线观看| 国产精品高清免费在线观看| 久久久精品久久| 久久久久久久一区二区| 国产精品视频公开费视频| 国色天香2019中文字幕在线观看| 久久久久www| 亚洲免费精彩视频| 久久久国产91| 欧美老少配视频| 亚洲色图在线观看| 国产精品夜间视频香蕉| 日韩av免费看网站| 精品在线欧美视频| 亚洲国产精品va在线看黑人| 久久久久这里只有精品| 最近中文字幕日韩精品| 中文字幕久久久av一区| 欧美精品久久久久久久久| 在线激情影院一区| 97精品久久久中文字幕免费| 日韩中文字幕免费视频| 国产精品久久二区| 91精品国产乱码久久久久久蜜臀| 91国产精品视频在线| 日韩欧美在线视频| 欧美一级淫片aaaaaaa视频| 日韩经典中文字幕| 97高清免费视频| 亚洲国产欧美在线成人app| 在线亚洲欧美视频| 国产自产女人91一区在线观看| 国产91在线高潮白浆在线观看| 欧美激情视频一区二区| 亚洲欧美精品一区二区| 成人黄色av免费在线观看| 一区二区三区日韩在线| 日韩成人中文字幕在线观看| 欧美日韩国产丝袜美女| 欧美大片在线看免费观看| 中文字幕欧美亚洲| 欧美大胆在线视频| 精品国产老师黑色丝袜高跟鞋| 亚洲日韩中文字幕在线播放| 欧美一区二区三区免费观看| 国产精品亚洲激情| 久久视频中文字幕| 国产成人精品最新| 欧美特级www| 美女扒开尿口让男人操亚洲视频网站| 成人xvideos免费视频| 91在线观看免费网站| 久久久久久亚洲精品中文字幕| 国产欧美亚洲视频| 亚洲精品国产精品国自产在线| 亚洲天堂精品在线| 日韩视频免费中文字幕| 国产精品高潮呻吟久久av黑人| 另类天堂视频在线观看| 在线日韩日本国产亚洲| 一区二区三区黄色| 国产精品久久9| 国模精品系列视频| 亚洲欧洲国产一区| 欧美另类极品videosbest最新版本| 国产精品久久久久影院日本| 欧美成aaa人片在线观看蜜臀| 亚洲免费精彩视频| 久久频这里精品99香蕉| 国产免费亚洲高清| 精品国产成人在线| 91在线观看免费观看| 在线观看亚洲视频| 日韩免费在线观看视频| 亚洲视频在线观看网站| 亚洲欧美国产精品| 俺去亚洲欧洲欧美日韩|