自定义可折叠按钮_折叠展开的悬浮按钮产品定义-程序员宅基地

技术标签: 折叠按钮  自定义控件  android  Android  

*本篇文章已授权微信公众号 guolin_blog (郭霖)独家发布

这几天无聊系列之——自定义可折叠按钮

效果如下:

注意看到没,有个浮动按钮,上滑到顶缩小,下滑到某个地方就展开。暂且不说上拉跟下滑的时候。主要时按钮伸缩效果如何实现呢?这个就是我们今天的主题了。

思路分析

一、按钮拉伸思路步骤详解

如下(绘画的很粗糙,忽略):

主要是由两个圆,然后中间是一个矩形。当按钮缩小时,就是右边这个圆向左边这个圆靠近,圆心不断向左移,中间矩形宽度不断减少,可以看到矩形的宽度就是右边圆圆心的x坐标减去左边圆心x坐标

开始动手撸代码。

1.创建一个叫StretchableFloatingButton的类并且继承ViewGroup,并实现相应的方法。

2.在onMeasure方法终测量控件的宽高度,以及圆心半径,矩形右边的x坐标。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
    if (width == 0){
        width = MeasureSpec.getSize(widthMeasureSpec);
        height = MeasureSpec.getSize(heightMeasureSpec);

        //获取圆的半径
        center = height/2;
        //矩形右边x坐标
        x = width -center;

    }
    super.onMeasure(widthMeasureSpec, heightMeasureSpec);
}

3.确定左边圆圆心,右边圆圆心,以及矩形范围,并绘制出来。(注意ViewGroup绘制在dispatchDraw方法里)

  • 左边圆心:(center,center)半径为center
  • 右边圆心:(width-center,center) 半径为center
  • 矩形范围(RectF)确定左上角和右下角坐标:(center,0,width-center,height)
@Override
protected void dispatchDraw(Canvas canvas) {
    //画左边圆
    canvas.drawCircle(center, center, center, paint);

    //画矩形
    rectF = new RectF(center, 0, width-center, height);
    canvas.drawRect(rectF, paint);

    //画右边圆
    canvas.drawCircle(width-center, center, center, paint);

    super.dispatchDraw(canvas);
}

4.在布局中引用,看效果

5.实现缩小和展开效果

在上面我们分析到,缩小(展开),是右边圆心不断向左边靠近(远离);矩形宽度缩小(变大),也就是右边圆心x坐标不断减小(增大),达到位置时动画就结束。

  • 改变的就是矩形范围(center,0,x,height),x 就是我们减小(增大)的值。
  • 右边圆(x,center),x跟矩形x一致。

如何不断减小(增大)x的值?

我们可以通过handler不断发送消息,减少(增加)x的值并重绘,当x到达左边圆心x坐标或到达矩形右边原本x坐标位置,停止发送。

定义一个方法让外部调用

看实现效果:

二、内部小圆放大缩小思路步骤详解

1.先把小圆绘制出来。

  • 确定圆心位置,跟左边的圆圆心一致
  • 小圆半径为centet-y,这里y指的是圆环宽度,设置初始值为20,等下我们要操作的圆环宽度y,来控制小圆的变化。

效果如下:

2.当整个控件放大缩小时控制圆的缩小和放大

这里要确定个比值:小圆最小时距离左边圆的宽度是固定 我们设为y,而控件减小或增大的距离也是确定的,我们设为x。当出现变化时,x减少多少,y就根据x/y这个比值减少多少。

y/x = 圆环最大宽度/控件拉伸总距离

  • 在onMesure中确定这个比值

  • 在控件拉伸活着放大时,的到具体的y值。

效果如下:

三、内部文字拉伸思路步骤详解

在考虑文字拉伸的时候如果用canvas 画文字,控制拉伸比较难控制,所以这个控件是继承ViewGroup。默认我在里面写了个TextView,通过onLayout去控制文本宽度。

1.创建一个TextView,并在onMesaure中测量宽高度。

2.通过onLayout摆放TextView位置。确定两个坐标左上角,右下角坐标

  • 左上角(ceter*2 +5,center-tHeight/2),+5的目的是设置间距

  • 右下角 (tWidth + center*2+10,(center+tHeight/2)) +10设置间距

注:tX 在后面代码中来控制文本宽度。默认初始为 tX = tWidth + center*2+10

效果如下:

3.设置TextView文本宽度变化比,其实就是TextView右下角的x坐标值的大小,达到拉伸目的。

TextView最大拉伸距离就是自身,控件拉伸距离在上面我们也已经确定是矩形的宽度。当控件拉伸多少根据这个比值,TextView对应拉伸的距离,即我我们所要求的tX。

确定比值:TextView最大宽度/控件拉伸总距离

根据比值得到tX,因为是改变子控件位置,需要调用requestLayout方法

效果如下:

四、小圆中图片旋转思路步骤详解

最终到了我们这个控件的最后一个思路环节了。就是小图标的旋转以及变化,在原效果中,当控件拉伸时,小图标跟着旋转,并且在效果结束之后变换图片。

这里涉及到了canvas的drawBitamp方法,以及canvas的roatate旋转方法。

1.先把小图标画出来。

  • 确定小图标宽高度,这里我设置了固定值:小圆的半径 - 5,-5设置间距。

  • 确定小图标左上角坐标位置。(center - iconWidth / 2,center - iconWidth / 2)

效果如下:

2.处理旋转,这里我设置默认旋转90度。因为原效果折叠时是逆时针旋转所以是-90度。

同样的我们也是要求出旋转比。根据旋转比得出旋转值。

旋转比: 总旋转角度/控件拉伸距离

3.根据旋转比求出旋转角度

最终效果如下:

优化

一、设置自定义属性

我们写了这么多,里面的角度,图标,文字,颜色等等都是固定的,应该可以在xml代码中动态去设置它。

这里需要写自定义属性,在valuse文件夹创建一个attrs的XML文件,编写自定义属性

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <declare-styleable name="FlodAbleButton">
        <!--字体大小-->
        <attr name="text_size" format="float"/>
        <!--折叠速度-->
        <attr name="speed" format="float"/>
        <!--控件背景颜色-->
        <attr name="bac_color" format="color"/>
        <!--字体颜色-->
        <attr name="text_color" format="color"/>
        <!--小圆颜色-->
        <attr name="inner_circle_color" format="color"/>
        <!--文本-->
        <attr name="text" format="string"/>
        <!--展开图标-->
        <attr name="open_icon" format="reference"/>
        <!--挂壁图标-->
        <attr name="close_icon" format="reference"/>
        <!--图标旋转角度-->
        <attr name="degrees" format="float"/>
    </declare-styleable>
</resources>

在代码中获取自定义属性值

在xml中使用

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/colorPrimary"
    tools:context="com.roy.stretchablefloatingbutton.MainActivity">

    <com.roy.library.FlodableButton
        android:id="@+id/fb"
        android:layout_width="match_parent"
        android:layout_height="60dp"
        android:layout_margin="10dp"
        app:bac_color="#ff0"
        app:close_icon="@mipmap/icon_2"
        app:inner_circle_color="@color/black"
        app:open_icon="@mipmap/icon"
        app:speed="60"
        app:text="地铁/景区/商圈/城市"
        app:text_color="@color/black"
        app:text_size="16"
        app:degrees="90"/>

</LinearLayout>

二、设置折叠监听事件

当控件折叠或者展开成功时,设置一个监听事件,把折叠状态以及控件自身回调出去

public void setFoldListener(FoldListener foldListener) {
    this.foldListener = foldListener;
}

//折叠展开监听
public interface FoldListener{
    void onFold(boolean isIncrease,FlodableButton sfb);
}

使用

sfb.setFoldListener(new FlodableButton.FoldListener() {
    @Override
    public void onFold(boolean isIncrease, FlodableButton sfb) {
        String text = isIncrease? "展开了":"折叠了";
        Toast.makeText(MainActivity.this,text,Toast.LENGTH_SHORT).show();
    }
});

效果展示

三、设置自己的点击监听事件

本来有自己的点击事件,为什么也要设置自己的点击事件呢。原因如下:

在效果可以看到,当我们折叠时,点击空白处,还有点击事件响应,这是我们不需要的。
这个效果出现的原因是我们的控件设置宽度是match_parent,所以一般设置点击事件还是会响应。

如何解决这个问题呢?

我们可以在onTouchEvent事件中做拦截,这里我们要区分两种状况:

  • 折叠状态时,可点击区域是个圆,需要判断点击的时的 x坐标和y坐标是否在圆内。在圆内响应点击事件,不在圆内不做拦截处理。
  • 伸展状态时,可点击区域时整个控件。

    代码如下:

在Activity中使用

sfb.setOnClickListener(new FlodableButton.OnClickListener() {
    @Override
    public void onClick(FlodableButton sfb) {
        //点击事件处理
    }
}

效果展示

最后展示下最终的完成效果

最后附上源码地址

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/VRoymond/article/details/76472551

智能推荐

nmap 目标说明_nmap 扫描文件中的目标-程序员宅基地

文章浏览阅读1.9k次。1. 什么是目标说明除了 选项,所有出现在 Nmap 命令行上的命令字符串 都被视为对目标主机的说明。 最简单的目标说明是指定一个目标 IP 地址或主机名。2. 指定目标 IP 地址2.1. 普通格式 IP 地址指定普通的 IP 地址很简单,比如在 nmap 命令中跟随这样 192.168.0.1 格式的 IP 地址即可,这样每次只能指定一个 IP 地址。但是这样指定一个 IP 地址也有它的不足或是不够灵活的地方,比如需要同时扫描多个 IP地址,而且这几个 IP 地址的主机号是连续的也就是这几_nmap 扫描文件中的目标

.Net程序员如何快速成长之我见(转)-程序员宅基地

文章浏览阅读151次。在面试一些新程序员的时候,当问他为什么选择.net方向时,经常听到一些这样的回答:记得曾经我的老师对我们说,当你在开发语言和工具方面不知道怎么选择的时候,那么你只要跟着微软走就行了.所以最终他选择了.net.我想大家都曾有过类似的经历.当经历过数年之后,一些人在.net的某方面成为了专家,有些人精通数据库方面,有些人精通web界面设计,有些人精通系统架构.还有一些人是什么都会,但不管哪..._.net程序员到新公司怎么快速熟悉一个业余

linux性能调优汇总(一)cpu_linux性能调优工具-程序员宅基地

文章浏览阅读144次。本部分主要介绍日常开发中,经常使用的程序性能调试命令及思路,主要分为CPU、内存、磁盘IO三部分本章主要介绍CPU部分_linux性能调优工具

CSS 在全屏iphonex(刘海屏)中的适配_padding-top: env(safe-area-inset-top);-程序员宅基地

文章浏览阅读5.1k次,点赞3次,收藏6次。iphonex 已经上线有一段时间了,作为业界刘海屏幕第一款机型,导致全屏不能正常的全屏显示了,,所以需要对iphonx 适配,下面就详细说说如何适配先看一张适配前后的图:iphonex 提供的 meta 头<meta name="viewport" content="viewport-fit=contain"><meta name="vie..._padding-top: env(safe-area-inset-top);

Flume与实际项目中用到的_flume在实际中的应用-程序员宅基地

文章浏览阅读488次,点赞4次,收藏3次。http://flume.apache.org/安装1、上传2、解压3、修改conf/flume-env.sh 文件中的JDK目录 注意:JAVA_OPTS 配置 如果我们传输文件过大 报内存溢出时 需要修改这个配置项4、验证安装是否成功 ./flume-ng version5、配置环境变量 export FLUME_HOME=/home/apache-flume-1..._flume在实际中的应用

虚幻引擎图文笔记:自定义GameInstance(5月30日 更新)_虚幻获取mygameinstance-程序员宅基地

文章浏览阅读3k次,点赞3次,收藏8次。自定义一个 GameInstance类,这个流程很像 Godot中的 AutoLoad_虚幻获取mygameinstance

随便推点

THREE.js渲染顺序_threejs 多个几何体展示顺序-程序员宅基地

文章浏览阅读1.1k次。本文主要讲述了THREE.js中的不透明物体和透明物体的渲染顺序,主要涉及THREE.js的以下内容:Materialblending及blending相关的一系列属性Object3Ddepth原文:https://segmentfault.com/a/1190000041221932。_threejs 多个几何体展示顺序

SQL Server 数据库查询速度慢的原因有很多常见的有那些呢_mssql server 响应变慢了-程序员宅基地

文章浏览阅读428次,点赞9次,收藏8次。例如耽搁查询的排序、连接、扫描和GROUP BY字句同时执行,SQL SERVER根据系统的负载情况决定最优的并行等级,复杂的需要消耗大量的CPU的查询最适合并行处理。13、在查询Select语句中用Where字句限制返回的行数,避免表扫描,如果返回不必要的数据,浪费了服务器的I/O资源,加重了网络的负担降低性能。如果表很大,在表扫描的期间将表锁住,禁止其他的联接访问表,后果严重。其次,看一下SQL SERVER的数据存放的结构:一个页面的大小为8K(8060)字节,8个页面为一个盘区,按照B树存放。_mssql server 响应变慢了

Linux 学习路线图_linux学习路径-程序员宅基地

文章浏览阅读2.4w次,点赞62次,收藏559次。1.应用场景更加高效地学习并达到运用Linux 2.学习/操作 linux运维学习需要分为四个阶段:初级入门、中级进阶、高级提升、资深方向细化。 第一阶段:初级入门 初级阶段需要把linux学习路线搞清楚,任何学习都是循序渐进的,所以学linux也是需要有一定的路线。 初级入门: Linux基础知识、基本命令(起源、组成、常用命令如cp、ls、fi..._linux学习路径

量子计算与通信技术:实现无线信息传递的新方法-程序员宅基地

文章浏览阅读893次,点赞19次,收藏19次。1.背景介绍量子计算与通信技术是当今科技界最热门的研究领域之一。随着计算机科学、物理学、信息论等多个领域的发展,量子计算与通信技术已经从理论研究阶段迈出了实际应用的第一步。在这篇文章中,我们将深入探讨量子计算与通信技术的核心概念、算法原理、具体操作步骤以及数学模型公式。同时,我们还将分析其在未来发展趋势与挑战方面的展望。1.1 量子计算与通信技术的发展背景量子计算与通信技术的研究起源于..._量子计算辅助无线移动网络

QPainter绘制图片填充方式(正常大小、剪切大小、自适应大小、平铺)-程序员宅基地

文章浏览阅读976次。Qt中QPainter提供了绘制图像的API,极大地方便了我们对图像的绘制。Qt中提供了QPixmap, QBitmap,QBitMapQImage,QPicture等图像绘图设备,它们的类关系如下图所示:QPixmap继承了QPaintDevice,您可用以建立QPainter并于上进行绘图,您也可以直接指定图案加载Qt所支持的图档,像是BMP、GIF、JPG、JPEG、PNG等,并..._qt paint 绘制图片不指定大小

主线程和子线程的区别_进程主线程子线程-程序员宅基地

文章浏览阅读4.2k次。主线程和子线程的区别每个线程都有一个唯一标示符,来区分线程中的主次关系的说法。 线程唯一标示符:Thread.CurrentThread.ManagedThreadID;UI界面和Main函数均为主线程。被Thread包含的“方法体”或者“委托”均为子线程。委托可以包含多个方法体,利用this.Invoke去执行。也可以定义多种方法体,放在Thread里面去执行。则此方法体均为子线程。注意_进程主线程子线程