iis服务器助手广告广告
返回顶部
首页 > 资讯 > 移动开发 >Android菜单栏DIY实现效果详解
  • 492
分享到

Android菜单栏DIY实现效果详解

2024-04-02 19:04:59 492人浏览 独家记忆
摘要

目录前言实现的效果和思路1. 绘制底部布局2. 添加子view3. 处理事件分发4. 做个动画5. 小结前言 个人打算开发个视频编辑的APP,然后把一些用上的技术总结一下,这次主要是

前言

个人打算开发个视频编辑的APP,然后把一些用上的技术总结一下,这次主要是APP的底部菜单栏用到了一个自定义View去绘制实现的,所以这次主要想讲讲自定义View的一些用到的点和自己如何去DIY一个不一样的自定义布局。

实现的效果和思路

可以先看看实现的效果

两个页面的内容还没做,当前就是一个Demo,可以看到底部的菜单栏是一个绘制出来的不规则的一个布局,那要如何实现呢。可以先来看看它的一个绘制区域:

就是一个底部的布局和3个子view,底部的区域当然也是个规则的区域,只不过我们是在这块区域上去进行绘制。

可以把整个过程分为几个步骤:

1. 绘制底部布局

  • (1) 绘制矩形区域
  • (2) 绘制外圆形区域
  • (3) 绘制内圆形区域

2. 添加子view进行布局

3. 处理事件分发的区域 (底部菜单上边的白色区域不触发菜单的事件)

4. 写个动画意思意思

1. 绘制底部布局

这里做的话就没必要手动去添加view这些了,直接全部手动绘制就行。

compaNIOn object{
    const val DIMENS_64 = 64.0
    const val DIMENS_96 = 96.0
    const val DIMENS_50 = 50.0
    const val DIMENS_48 = 48.0
    interface OnChildClickListener{
        fun onClick(index : Int)
    }
}
private var paint : Paint ?= null  // 绘制蓝色区域的画笔
private var paint2 : Paint ?= null  // 绘制白色内圆的画笔
private var allHeight : Int = 0  // 总高度,就是绘制的范围
private var bgHeight : Int = 0  // 背景的高度,就是蓝色矩阵的范围
private var mRadius : Int = 0  // 外圆的高度
private var mChildSize : Int = 0
private var mChildCenterSize : Int = 0
private var mWidthZone1 : Int = 0
private var mWidthZone2 : Int = 0
private var mChildCentre : Int = 0
private var childViews : MutableList<View> = mutableListOf()
private var objectAnimation : ObjectAnimator ?= null
var onChildClickListener : OnChildClickListener ?= null
init {
    initView()
}
private fun initView(){
    val lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
        DimensionUtils.dp2px(context, DIMENS_64).toInt())
    layoutParams = lp
    allHeight = DimensionUtils.dp2px(context, DIMENS_96).toInt()
    bgHeight = DimensionUtils.dp2px(context, DIMENS_64).toInt()
    mRadius = DimensionUtils.dp2px(context, DIMENS_50).toInt()
    mChildSize = DimensionUtils.dp2px(context, DIMENS_48).toInt()
    mChildCenterSize = DimensionUtils.dp2px(context, DIMENS_64).toInt()
    setWillNotDraw(false)
    initPaint()
}
private fun initPaint(){
    paint = Paint()
    paint?.isAntiAlias = true
    paint?.color = context.resources.getColor(R.color.kylin_main_color)
    paint2 = Paint()
    paint2?.isAntiAlias = true
    paint2?.color = context.resources.getColor(R.color.kylin_third_color)
}

上边是先把一些尺寸给定义好(我这边是没有设计图,自己去直接调整的,所以可能有些视觉效果不太好,如果有设计师帮忙的话效果肯定会好些),绘制流程就是绘制3个形状,然后代码里也加了些注释哪个变量有什么用,这步应该不难,没什么可以多解释的。

override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    val wSize = MeasureSpec.getSize(widthMeasureSpec)
    // 拿到子view做操作的,和这步无关,可以先不看
    if (childViews.size <= 0) {
        for (i in 0 until childCount) {
            val cView = getChildAt(i)
            initChildView(cView, i)
            childViews.add(cView)
            if (i == childCount/2){
                val ms: Int = MeasureSpec.makeMeasureSpec(mRadius, MeasureSpec.AT_MOST)
                measureChild(cView, ms, ms)
            }else {
                val ms: Int = MeasureSpec.makeMeasureSpec(mChildSize, MeasureSpec.AT_MOST)
                measureChild(cView, ms, ms)
            }
        }
    }
    setMeasuredDimension(wSize, allHeight)
}

这步其实也很简单,就是说给当前自定义view设置高度为allHeight

override fun onDraw(canvas: Canvas?) {
    super.onDraw(canvas)
    // 绘制长方形区域
    canvas?.drawRect(left.toFloat(), ((allHeight - bgHeight).toFloat()),
        right.toFloat(), bottom.toFloat(), paint!!)
    // 绘制圆形区域
    paint?.let {
        canvas?.drawCircle(
            (width/2).toFloat(), mRadius.toFloat(),
            mRadius.toFloat(),
            it
        )
    }
    // 绘制内圆区域
    paint2?.let {
        canvas?.drawCircle(
            (width/2).toFloat(), mRadius.toFloat(),
            (mRadius - 28).toFloat(),
            it
        )
    }
}

最后进行绘制, 就是上面说的绘制3个图形,代码里的注释也说得很清楚。

2. 添加子view

我这里是外面布局去加子view的,想弄得灵活点(但感觉也不太好,后面还是想改成里面定义一套规范来弄会好些,如果自由度太高的话去做自定义就很麻烦,而且实际开发中这种需求也没必要把扩展性做到这种地步,基本就是整个APP只有一个地方使用)

但是这边也只是一个Demo先做个演示。

<com.kylin.libkcommons.widget.BottomMenuBar
    Android:id="@+id/bv_content"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_alignParentBottom="true"
    >
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/home"
        />
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/video"
        />
    <ImageView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/more"
        />
</com.kylin.libkcommons.widget.BottomMenuBar>
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
    super.onMeasure(widthMeasureSpec, heightMeasureSpec)
    val wSize = MeasureSpec.getSize(widthMeasureSpec)
    if (childViews.size <= 0) {
        for (i in 0 until childCount) {
            val cView = getChildAt(i)
            initChildView(cView, i)
            childViews.add(cView)
            if (i == childCount/2){
                val ms: Int = MeasureSpec.makeMeasureSpec(mRadius, MeasureSpec.AT_MOST)
                measureChild(cView, ms, ms)
            }else {
                val ms: Int = MeasureSpec.makeMeasureSpec(mChildSize, MeasureSpec.AT_MOST)
                measureChild(cView, ms, ms)
            }
        }
    }
    setMeasuredDimension(wSize, allHeight)
}

拿到子view进行一个管理,做一些初始化的操作,主要是设点击事件这些,这里不是很重要。

override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
    if (mChildCentre == 0){
        mChildCentre = width / 6
    }
    // 辅助事件分发区域
    if (mWidthZone1 == 0 || mWidthZone2 == 0) {
        mWidthZone1 = width / 2 - mRadius / 2
        mWidthZone2 = width / 2 + mRadius / 2
    }
    // 设置每个子view的显示区域
    for (i in 0 until childViews.size) {
        if (i == childCount/2){
            childViews[i].layout(mChildCentre*(2*i+1) - mChildCenterSize/2 ,
                allHeight/2 - mChildCenterSize/2,
                mChildCentre*(2*i+1) + mChildCenterSize/2 ,
                allHeight/2 + mChildCenterSize/2)
        }else {
            childViews[i].layout(mChildCentre*(2*i+1) - mChildSize/2 ,
                allHeight - bgHeight/2 - mChildSize/2,
                mChildCentre*(2*i+1) + mChildSize/2 ,
                allHeight - bgHeight/2 + mChildSize/2)
        }
    }
}

进行布局,这里比较重要,因为能看出,中间的图标会更大一些,所以要做一些适配。其实这里就是把宽度分为6块,然后3个view分别在1,3,5这三个左边点,y的话就是除中间那个,其它两个都是bgHeight绘制高度的的一半,中间那个是allHeight总高度的一半,这样3个view的x和y坐标都能拿到了,再根据宽高就能算出l,t,r,b四个点,然后布局。

3. 处理事件分发

可以看出我们的区域是一个不规则的区域,按照我们用抽象的角度去思考,我们希望这个菜单栏的区域只是显示蓝色的那个区域,所以蓝色区域上面的白色区域就算是我们自定义view的范围,他触发的事件也应该是后面的view的事件(Demo中后面的View是一个ViewPager),而不是菜单栏。

// 辅助事件分发区域
if (mWidthZone1 == 0 || mWidthZone2 == 0) {
    mWidthZone1 = width / 2 - mRadius / 2
    mWidthZone2 = width / 2 + mRadius / 2
}

这两块是圆外的x的区域。


private fun isshowZone(x : Float, y : Float) : Boolean{
    if (y >= allHeight - bgHeight){
        return true
    }
    if (x >= mWidthZone1 && x <= mWidthZone2){
        // 在圆内
        val relativeX = abs(x - width/2)
        val squareYZone = mRadius.toDouble().pow(2.0) - relativeX.toDouble().pow(2.0)
        return y >= mRadius - sqrt(squareYZone)
    }
    return false
}

先判断y如果在背景的矩阵中(上面说了自定义view分成矩阵,外圆,内圆),那肯定是菜单的区域。如果不在,那就要判断y在不在圆内,这里就必须用勾股定理去判断。

override fun onTouchEvent(event: MotionEvent?): Boolean {
    // 点击区域进行拦截
    if (event?.action == MotionEvent.ACTION_DOWN && isShowZone(event.x, event.y)){
        return true
    }
    return super.onTouchEvent(event)
}

最后做一个事件分发的拦截。除了计算区域那可能需要去想想,其它地方我觉得都挺好理解的吧。

4. 做个动画

给子view设点击事件让外部处理,然后给中间的按钮做个动画效果。

private fun initChildView(cView : View?, index : Int) {
    cView?.setOnClickListener {
        if (index == childViews.size/2) {
            startAnim(cView)
        }else {
            onChildClickListener?.onClick(index)
        }
    }
}
private fun startAnim(view : View){
    if (objectAnimation == null) {
        objectAnimation = ObjectAnimator.ofFloat(view,
            "rotation", 0f, -15f, 180f, 0f)
        objectAnimation?.addListener(object : Animator.AnimatorListener {
            override fun onAnimationStart(p0: Animator) {
            }
            override fun onAnimationEnd(p0: Animator) {
                onChildClickListener?.onClick(childViews.size / 2)
            }
            override fun onAnimationCancel(p0: Animator) {
                onChildClickListener?.onClick(childViews.size / 2)
            }
            override fun onAnimationRepeat(p0: Animator) {
            }
        })
        objectAnimation?.duration = 1000
        objectAnimation?.interpolator = AccelerateDecelerateInterpolator()
    }
    objectAnimation?.start()
}

注意做释放操作。

fun onDestroy(){
    try {
        objectAnimation?.cancel()
        objectAnimation?.removeAllListeners()
    }catch (e : Exception){
        e.printStackTrace()
    }finally {
        objectAnimation = null
    }
}

5. 小结

其实代码都挺简单的,关键是你要去想出一个方法来实现这个场景,然后感觉这个自定义viewgroup也是比较经典的,涉及到measure、layout、draw,涉及到动画,涉及到点击冲突。

这个Demo表示你要实现怎样的效果都可以,只要是draw能画出来的,你都能实现,我这个是中间凸出来,你可以实现凹进去,你可以实现波浪的样子,可以实现复杂的曲线,都行,你用各种基础图形去做拼接,或者画贝塞尔等等,其实都不难,主要是要有个计算和调试的过程。但是你的形状要和点击区域关联起来,你设计的图案越复杂,你要适配的点击区域计算量就越大。

甚至我还能做得效果更屌的是,那3个子view的图标,我都能画出来,就不用ImagerView,直接手动画出来,这样做的好处是什么呢?我对子view的图标能做各种炫酷的属性动画,我在切换viewpager时对图标做属性动画,那不得逼格再上一层。 为什么我没做呢,因为没有设计,我自己做的话要花大量的时间去调,要是有设计的话他告诉我尺寸啊位置啊这些信息,做起来就很快。我的APP主要是打算实现视频的编辑为主,所以这些支线就没打算花太多时间去处理。

以上就是Android 菜单栏DIY实现效果详解的详细内容,更多关于Android 菜单栏DIY的资料请关注编程网其它相关文章!

--结束END--

本文标题: Android菜单栏DIY实现效果详解

本文链接: https://www.lsjlt.com/news/167528.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • Android菜单栏DIY实现效果详解
    目录前言实现的效果和思路1. 绘制底部布局2. 添加子view3. 处理事件分发4. 做个动画5. 小结前言 个人打算开发个视频编辑的APP,然后把一些用上的技术总结一下,这次主要是...
    99+
    2024-04-02
  • Android studio实现菜单效果
    本文实例为大家分享了Android studio实现菜单效果的具体代码,供大家参考,具体内容如下 一、onCreateOptionsMenu() 首先在res目录下新建一个menu文...
    99+
    2024-04-02
  • Android仿微信底部菜单栏效果
    前言在市面上,大多数的APP都需要通过底部菜单栏来将程序的功能进行分类整理,通常都是分为3-5个大模块,从而正确有效地引导用户去使用我们的APP。实现底部菜单栏的方法也有很多种。1.仿微信底部菜单栏(ViewPager+ImagerView...
    99+
    2023-05-30
    android 菜单
  • Android Drawerlayout实现侧滑菜单效果
    本文实例为大家分享了Drawerlayout侧滑菜单的具体代码,供大家参考,具体内容如下Drawerlayout的xml布局<?xml version="1.0" encoding="utf-8"?><Re...
    99+
    2023-05-30
    drawerlayout 侧滑菜单 roi
  • Android实现微博菜单弹出效果
    先上Android仿微博菜单弹出效果图,这个截图不是很流畅,大家可以下载apk试一下。 说一下实现思路: 截取当前窗口,对图片做高斯模糊处理,将处理后的图片做popupwindow的背景图片; 2、创建popupwindow,完成...
    99+
    2023-05-30
    android 菜单弹出
  • Android使用DrawerLayout实现侧滑菜单效果
    一、概述DrawerLayout是一个可以方便的实现Android侧滑菜单的组件,我最近开发的项目中也有一个侧滑菜单的功能,于是DrawerLayout就派上用场了。如果你从未使用过DrawerLayout,那么本篇博客将使用一个简单的案例...
    99+
    2023-05-30
    android drawerlayout 侧滑菜单
  • Android卫星菜单效果的实现方法
    Android小白第一次写博客,心情无比激动。下面给大家展示一下卫星菜单的实现。1.简单介绍卫星菜单在应用程序中,有很多展示菜单的方式,但其功能都是大同小异,这样一来,菜单的美观以及展示方式就显的尤为重要,卫星菜单就是很不错的一种。下面是本...
    99+
    2023-05-31
    android 卫星菜单 roi
  • Android中DrawerLayout如何实现侧滑菜单效果
    这篇文章主要为大家展示了“Android中DrawerLayout如何实现侧滑菜单效果”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“Android中DrawerLayout如何实现侧滑菜单效果”...
    99+
    2023-05-30
    drawerlayout android
  • Android实现毛玻璃效果弹出菜单动画
    本文实例为大家分享了Android实现毛玻璃效果弹出菜单动画的具体代码,供大家参考,具体内容如下 仿ios上屏幕下方向上滑出来的一个模糊菜单,效果如下 原理很简单,页面上原来有...
    99+
    2022-11-13
    Android毛玻璃效果 Android毛玻璃菜单 Android弹出菜单
  • android怎么实现底部菜单栏功能
    在Android中,可以通过使用BottomNavigationView控件来实现底部菜单栏功能。以下是实现底部菜单栏功能的步骤: ...
    99+
    2024-03-11
    android
  • iOS实现简单分栏效果
    本文实例为大家分享了iOS实现简单分栏效果的具体代码,供大家参考,具体内容如下 直接贴代码喽 GMSubfieldViiew.h #import <UIKit/UIKit.h&...
    99+
    2024-04-02
  • vue实现下拉菜单效果
    本文实例为大家分享了vue实现下拉菜单效果的具体代码,供大家参考,具体内容如下 下拉菜单主要运用了hover显示与隐藏以及定位的问题 效果图: 可能出现的问题 定位后菜单的div无...
    99+
    2024-04-02
  • 纯CSS实现菜单导航栏的悬浮效果的实现步骤
    随着Web设计的不断进步,用户对于网站的需求也越来越高。为了提供更好的用户体验,悬浮效果在网站设计中得到了广泛应用。本文将介绍如何使用纯CSS来实现菜单导航栏的悬浮效果,以提升网站的可用性和美观性。创建基本菜单结构首先,我们需要在HTML文...
    99+
    2023-10-21
    菜单 导航栏 实现步骤:CSS 悬浮效果。
  • vue实现多级菜单效果
    本文实例为大家分享了vue实现多级菜单效果的具体代码,供大家参考,具体内容如下 效果图: 效果说明: 点击父菜单,如果有子菜单就在其左侧显示出子菜单 思路: 通过递归的方式。 代码...
    99+
    2024-04-02
  • javascript实现简单下拉菜单效果
    本文实例为大家分享了 javascript实现简单下拉菜单的具体代码,供大家参考,具体内容如下 效果: 思路:比较复杂的是样式的编写,首先是ul里面含有3个li,每个li...
    99+
    2022-11-13
    js 下拉菜单
  • jquery实现简单下拉菜单效果
    本文用简单的几行jquery代码实现简单的下拉菜单效果,供大家参考,具体内容如下 看效果 html <ul>     <li>       主题市场    ...
    99+
    2024-04-02
  • CSS3 实现分类菜单效果
    这篇文章给大家介绍CSS3 实现分类菜单效果,内容非常详细,感兴趣的小伙伴们可以参考借鉴,希望对大家能有所帮助。HTML<html><head>    <title>...
    99+
    2023-06-08
  • 怎么使用DrawerLayout和NavigationView实现侧滑菜单栏的效果
    小编给大家分享一下怎么使用DrawerLayout和NavigationView实现侧滑菜单栏的效果,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!效果如下:Layout布局<android.support.v4.wi...
    99+
    2023-05-30
    material design
  • 基于Android实现可滚动的环形菜单效果
    效果 首先看一下实现的效果: 可以看出,环形菜单的实现有点类似于滚轮效果,滚轮效果比较常见,比如在设置时间的时候就经常会用到滚轮的效果。那么其实通过环形菜单的表现可以将其看作是一个...
    99+
    2024-04-02
  • 纯CSS实现带阴影效果的菜单导航栏的实现步骤
    在网页设计中,菜单导航栏是一个非常常见的元素。通过给菜单导航栏添加阴影效果,不仅可以增加其美观度,还可以提升用户体验。在本文中,我们将使用纯CSS来实现一个带阴影效果的菜单导航栏,并提供具体的代码示例供参考。实现步骤如下:创建HTML结构首...
    99+
    2023-10-21
    菜单 CSS 导航栏
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作