android view设置按钮颜色_Android 酷炫自定义 View:高仿 QQ 窗帘菜单

51edb1ce2ede4bdc7aeb9d2790e51269.png

作者:大公爵 链接:https://www.jianshu.com/p/cdb3d373fe37


介绍

不知道大家是否有印象,QQ 曾经有个版本用到了一种双向侧拉菜单,就像窗帘一样可以两边开合,并且伴有 3D 旋转效果,效果非常酷炫,吸引很多人模仿实现。

Android 系统提供了一个侧拉抽屉控件,叫 DrawerLayout,使用过的人都知道,效果不错并且有一定拓展性,基于 DrawerLayout 我们可以实现 QQ 的效果,但是今天我们要介绍的是另一个思路:自定义 HorizontalScrollView

这个思路非常简单,并且你可以很方便地拓展出任何你想要的效果,说不定做的比 QQ 更酷炫哦。

效果

首先来看下最终实现的效果(gif 版)

2D 模式
c6741de0bffba3d8d8ed3c08da226cf5.gif
3D 模式
d4568d3882802ff64e936f4cf1b59188.gif

自定义 View 基础

Android 自定义 View 是一个很大的主题,一篇文章肯定是讲不完的,GcsSloop 的自定义 View 系列文章写了十几篇都不能做到面面俱到,所以今天这篇文章我们就从一个小案例入手,讲讲如何实现双向侧拉菜单。

大家都知道 Android 自定义 View 分为两大类,一是自定义 View,二是自定义 ViewGroup,这篇文章要讲的显然是自定义 ViewGroup

自定义 View 和 ViewGroup 的区别就是 ViewGroup 除了负责自身的显示效果外,里面还要包含其它的子 View,这必然带来复杂性增加,表现在代码里就是自定义 View 通常只需要复写 onDraw 和 onTouch,而自定义 ViewGroup 还要考虑子 View 的测量、子 View 的布局、子 View 的事件分发等等,涉及到的方法了 onMeasureonLayoutdispatchTouchEventonInterceptTouchEvent等。

其中事件分发是一个重点,而在自定义 View 种很重要的 onDraw 反而不是最重要的。

// 自定义View

当然自定义 View 和自定义 ViewGroup 也有很多共通的,比如自定义属性,绘制函数等。那我们闲言少叙,开始动手实现吧。

实现思路

我们看上面的效果挺酷炫的,感觉无从下手,但是仔细观察你会发现,其实整个界面分为三部分:左边菜单、中间主布局、右边菜单。它们的位置关系是从左到右依次排列。再仔细观察菜单的切换你会发现,忽略缩放、透明度等动画,其实菜单切换的过程就是三部分滚动的过程,于是,我们就有了一个大体的思路:

「用一个 HorizontalScrollView 包裹三个部分的试图,通过控制 HorizontalScrollView 的滚动距离来实现展示不同的部分。」 (如下图)

420a58824e615a73122f5237d1cbf4ec.png

当然,这只是一个思路,距离最终效果还差一些,我们基于这个思路,要解决以下几个问题:

  • (1)初始的时候要展示中间主布局。
  • (2)左右菜单区域的宽度要客配置。
  • (3)松手后,不能停在菜单的一半处,要能自动收起或打开菜单。
  • (4)左右菜单要是可配置的,因为用户可能只需要左侧菜单或者只需要右侧菜单。
  • (5)复杂的事件分发。
  • (6)菜单切换时的 3D 效果。

自定义 HorizontalScrollView

有了思路,我们就有了方向,废话不多说,开始撸代码。

(1)首先新建一个类,集成自 HorizontalScrollView
public 

架子出来了,现在往架子里填内容,先来获取 3 个子 View

(2)获取子 View

通过上面的分析我们知道一共有三个子 View:左侧菜单、中间主体、右侧菜单,但是这三个子 View 不一定全有,如果用户只配置了左侧菜单,那右侧菜单子 View 就不存在。

if (左右菜单都有) {
第0个子View是左侧菜单
第1个子View是中间主体
第2个子View是右侧菜单
} else if (只有左侧菜单) {
第0个子View是左侧菜单
第1个子View是中间主体
} else if (只有右侧菜单) {
第0个子View是中间主体
第1个子View是中间主体
}

首先我们要定义三种菜单类型常量,代表上面三种菜单类型:

public 

然后根据菜单类型获取子 View:

@Override
(3)菜单宽度

获取到了三个子 View,下面就要设置子 View 的宽度。中间主体的宽度是屏幕宽度,这个没啥好说的。左右菜单的宽度是要窄一点的。

我们是这样定义的:左侧菜单是主菜单,显示的内容比较多,所有左侧菜单宽度我们是用屏幕宽度 - 右侧边距,而右侧菜单是次菜单,就显示一个按钮。所以右侧按钮宽度就由用户直接指定。

@Override
(4)初始展示中间主体布局

这个就很简单了,HorizontalScrollView 默认的滚动位置是 0,所以就会展示左侧菜单,我们只要把滚动位置设置到左侧菜单宽度就行。

@Override
(5)自动回弹

下面就是重点了,很重很重的点。我们在滚动时,松手后应该能自动根据当前滚动位置关闭或者打开菜单。通常就是以菜单的一半作为分界线。

if(滚动距离 < 左侧菜单宽度一半) {

上面这段逻辑如果不明白的可以多看几遍,明白这个逻辑后才能看下面的代码实现。

@Override

霍,怎么这么多代码?原因是我们要考虑三种菜单类型,每种类型关闭菜单的滚动距离是不一样的。所以实现起来要分开考虑,代码自然就多了。

59be82770cc9b44d82e5ac8a2fcdb41c.gif
(6)事件分发

啊,终究逃不过这一关,自定义 ViewGroup 是一定要面对事件分发的。

我们的预期是这样的:

  • a、当菜单关闭(左右菜单都关闭,中间主体全屏展示)的时候,不拦截事件,用户可以点击页面元素,滑动列表。

  • b、当菜单打开(左右菜单都一样)的时候,点击中间主体区域时拦截事件,点击其它地方不拦截事件。也就是说当菜单打开时,主体区域的页面元素不可点击,列表也不可滑动,但是菜单区域的元素可以点击。

这里需要两个判断条件:菜单是否打开、是否点击在中间主体区域。

菜单是否打开很简单,我们设置一个变量 isOpen,每次打开菜单置为 true,关闭菜单置为 false

是否点击在中间主体区域稍微复杂一点,我们首先要获取手指点击相对于屏幕的坐标值。

int rawX = (

然后我们要获取中间主体 View 所占的区域:

int[] location = 

这里为什么要乘以一个 SCALE_CONTENT 呢?这个值是主体区域在动画过程中的缩放比例,乘以这个缩放比例就可以得到缩放后的宽高。

有了这两步,判断是否点击在中间主体区域就很简单了

rect.contains(rawX, rawY);

完整代码:

@Override
(7)3D 动画

这个菜单的效果全靠这个动画撑起来的,看似复杂,其实动画是最简单的。

我们根据左右菜单拉出的百分比计算各个 View 的平移缩放alpha 动画值,如图在 3D 模式下,再加上一个旋转。旋转我们只针对左侧菜单和中间主体,右侧菜单不旋转。

@Override

自定义属性

好了,整个窗帘菜单基本已经实现了,但是要完善一下自定义属性,方便用户配置。

// attr.xml
<?xml version="1.0" encoding="utf-8"?>
<resources>
// 左侧菜单的右边距
<attr name="rightPadding" format="dimension" />
// 菜单类型
<attr name="menuType" format="enum">
<enum name="leftMenu" value="0x1" />
<enum name="rightMenu" value="0x2" />
<enum name="doubleMenu" value="0x3" />
attr>
// 是否打开3D模式
<attr name="with3D" format="boolean" />
// 3D模式下菜单的旋转角度
<attr name="menuRotate" format="integer" />
// 3D模式下内容区域的旋转角度
<attr name="contentRotate" format="integer" />

<declare-styleable name="CurtainsLayout">
<attr name="rightPadding" />
<attr name="menuType" />
<attr name="with3D" />
<attr name="menuRotate" />
<attr name="contentRotate" />
declare-styleable>
resources>

使用

自定义封装好了,当然就要给别人用啦,使用很简单。

    xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:curtains="http://schemas.android.com/apk/res-auto"
android:id="@+id/id_menu"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="@drawable/menu_bg"
android:overScrollMode="never"
android:scrollbars="none"
curtains:rightPadding="100dp"
curtains:menuType="doubleMenu"
curtains:with3D="true"
curtains:contentRotate="15"
curtains:menuRotate="20">

android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >

"@layout/layout_left_menu" />"@layout/layout_content" />"@layout/layout_right_menu" />

总结

至此,自定义窗帘菜单我们就讲完了,看完你可能还是觉得一脸懵逼。很正常,上面讲的是思路和主要方法实现,除此之外还有很多边缘性的东西,要想看完整的实现请移步源码。如有错误或者疑问,请在讨论区提出。

码云 git:https://gitee.com/makeunion/CurtainsLayout

---END---

推荐阅读:
ByRecyclerView:真·万能分割线 (线性/宫格/瀑布流)
【译】一文带你了解Android中23个关于Canvas绘制的方法
一文形象生动地解释什么是MVP架构模式?
【超级实用】Iterm2 + oh-my-zsh 打造强大的终端编辑器
3bbf34582d10d8b5661b62d97d529454.png
每一个“在看”,我都当成真的喜欢2b94b2a5e90f88965d98e2652fe2f5b3.gif
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
牙科就诊管理系统利用当下成熟完善的SSM框架,使用跨平台的可开发大型商业网站的Java语言,以及最受欢迎的RDBMS应用软件之一的Mysql数据库进行程序开发。实现了用户在线查看数据。管理员管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等功能。牙科就诊管理系统的开发根据操作人员需要设计的界面简洁美观,在功能模块布局上跟同类型网站保持一致,程序在实现基本要求功能时,也为数据信息面临的安全问题提供了一些实用的解决方案。可以说该程序在帮助管理者高效率地处理工作事务的同时,也实现了数据信息的整体化,规范化与自动化。 管理员在后台主要管理病例管理、字典管理、公告管理、药单管理、药品管理、药品收藏管理、药品评价管理、药品订单管理、牙医管理、牙医收藏管理、牙医评价管理、牙医挂号管理、用户管理、管理员管理等。 牙医列表页面,此页面提供给管理员的功能有:查看牙医、新增牙医、修改牙医、删除牙医等。公告信息管理页面提供的功能操作有:新增公告,修改公告,删除公告操作。公告类型管理页面显示所有公告类型,在此页面既可以让管理员添加新的公告信息类型,也能对已有的公告类型信息执行编辑更新,失效的公告类型信息也能让管理员快速删除。药品管理页面,此页面提供给管理员的功能有:新增药品,修改药品,删除药品。药品类型管理页面,此页面提供给管理员的功能有:新增药品类型,修改药品类型,删除药品类型。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值