气泡框实现

  • 前言
  • 绘制
    • 要点
    • API
    • 实现
  • 成果

前言

遇到一个需要实现气泡框的组件,大概长这样:

里面的内容可以是单行文字,可以是菜单,或其他组件;

当时想了3种办法:

  • 使用已有的气泡框图做背景
  • 制作点九图做背景
  • 自己画背景

用现有的图始终难以控制边距,矩形内是有内容的;
点九图也有同样的问题,只有自己画的图才有可能计算出准确的边距。

于是开始自定义View。

绘制

因为最终目的是要在绘制好的组件中放置其他组件,所以优先选择CustomClipper<Path>,相当于剪切出一个可容纳子组件存在的空间出来;
理论上也可以使用CustomPainter画出背景后进行操作,但那样就和使用已有的图做背景区别不大了。

要点

如最开始的草图所示,其实气泡框就是一个矩形和三角形的组合,但除此之外还有一些可以扩展的操作,比如:

  1. 矩形的角和三角形的角可以增加一定弧度
  2. 三角形的顶角可以偏移成为斜角,接近于聊天的气泡框
  3. 矩形容纳子组件的边距能够先行计算
  4. 三角形朝向不同

除此之外,就是进行一些简单的几何计算,比如三角形朝向左右上下不同方向时,对应坐标参数的变化。

思路则是:先画三角,再画矩形,最后计算边距
三角需要先根据朝向计算画的起始点,矩形则根据三角的高度确定四角所在坐标,边距也根据三角的高度确定。

API

矩形比较容易实现,加圆角也容易:

RRect.fromRectAndRadius(Rect.fromLTWH(l, t, r ,b), radius);

三角形也简单,使用连线即可:

path.lineTo(x,y);

但lineTo只能画直线,如果要使角有弧度,可以考虑使用圆锥曲线:

path.conicTo(peakX, peakY, endX, endY, weight);

当weight>1后,曲线会越来越锐利,接近三角形的角,可以自行调节;

实现

先定义朝向

enum ArrowDirection { left, right, top, bottom, none }

朝向不同,就需要计算相应的坐标;

再确定三角形的高度宽度

  @overridePath getClip(Size size) {print("w=${size.width},h=${size.height}");final pathTriangle = Path();final pathRect = Path();//若有指定值,则宽高为指定值,//若无指定值,宽高以各自平行的矩形边作为基准final arrowW = arrowWidth == 0? (direction == ArrowDirection.left || direction == ArrowDirection.right? size.height: size.width) *widthWeight: arrowWidth;final arrowH = arrowHeight == 0? (direction == ArrowDirection.left || direction == ArrowDirection.right? size.width: size.height) *heightWeight: arrowHeight;print("arrowW=$arrowW,arrowH=$arrowH");
}

为了能够让三角形的宽高能随着父布局的宽高而变,在这里设置了两种选择,一种是直接传入宽高准确值,一种是传入与整个布局成比例的比例值
三角形始终以与矩形相邻的边作为宽(底边),当朝向为左右(水平)时,三角形实际上是“放倒了”,所以需要判断在水平方向时的取值

接着确定起点

    //箭头为水平方向(左右)时,三角形底边中心的纵坐标final basisPointY = arrowBasisOffset < -1 || arrowBasisOffset > 1? size.height / 2 + arrowBasisOffset: size.height / 2 * (1 + arrowBasisOffset);//箭头为水平方向(左右)时,三角形顶角顶点的纵坐标final peakPointY = basisPointY + arrowW * arrowPeakOffset;print("b=$arrowBasisOffset,p=$arrowPeakOffset");//箭头为垂直方向(上下)时,三角形底边中心的横坐标final basisPointX = arrowBasisOffset < -1 || arrowBasisOffset > 1? size.width / 2 + arrowBasisOffset: size.width / 2 * (1 + arrowBasisOffset);//箭头为垂直方向(上下)时,三角形顶角顶点的横坐标final peakPointX = basisPointX + arrowW * arrowPeakOffset;print("peakX=$peakPointX,basisX=$basisPointX");

这里将三角形底边中心点作为基准点(basisPoint),起点则为底边靠左或靠上的一个端点;
项角顶点(peakPoint)则用于偏移,当peakPoint与basisPoint相等时,说明是等腰三角;
同样分水平垂直方向,水平时基准点只算Y轴,垂直时基准点只算X轴;

然后开始画三角形
依据上图,A-B即为宽度,c-basisPoint的垂直高度即为高度,C点为圆锥曲线的控制点,从A到B绘制曲线,weight(权重)超过10就很接近三角形了;

  drawArrow(Path pathTriangle, double startX, double startY, double peakX,double peakY, double endX, double endY, double weight) {pathTriangle.moveTo(startX, startY);pathTriangle.conicTo(peakX, peakY, endX, endY, weight);pathTriangle.close();}

A点(startX,startY);
B点(endX,endY);
C点(peakX,peakY);

然后根据朝向画矩形

    switch (direction) {case ArrowDirection.left://绘制位于左边的三角形箭头,即画一个顶角朝左的三角形drawArrow(pathTriangle, arrowH, basisPointY - arrowW / 2, 0, peakPointY,arrowH, basisPointY + arrowW / 2, conicWeight);//绘制位于右方的矩形pathRect.addRRect(RRect.fromRectAndRadius(Rect.fromLTWH(arrowH, 0, (size.width - arrowH), size.height),radius));break;case ArrowDirection.right://绘制位于右边的三角形箭头,画一个顶角朝右的三角形drawArrow(pathTriangle,size.width - arrowH,basisPointY - arrowW / 2,size.width,peakPointY,size.width - arrowH,basisPointY + arrowW / 2,conicWeight);//绘制位于左边的矩形pathRect.addRRect(RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, (size.width - arrowH), size.height), radius));break;case ArrowDirection.top://绘制位于顶部的三角形箭头,画一个顶角朝上的三角形drawArrow(pathTriangle, basisPointX - arrowW / 2, arrowH, peakPointX, 0,basisPointX + arrowW / 2, arrowH, conicWeight);//绘制位于下边的矩形pathRect.addRRect(RRect.fromRectAndRadius(Rect.fromLTWH(0, arrowH, size.width, size.height - arrowH),radius));break;case ArrowDirection.bottom://绘制位于底部的三角形箭头,画一个顶角朝下的三角形drawArrow(pathTriangle,basisPointX - arrowW / 2,size.height - arrowH,peakPointX,size.height,basisPointX + arrowW / 2,size.height - arrowH,conicWeight);// 绘制位于下边的矩形pathRect.addRRect(RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, size.width, size.height - arrowH), radius));break;default:pathRect.addRRect(RRect.fromRectAndRadius(Rect.fromLTWH(0, 0, size.width, size.height), radius));break;}

合并返回:

    pathTriangle.addPath(pathRect, const Offset(0, 0));return pathTriangle;

至此CustomClipper的工作完成,计算边距需要在自定义的容器中实现;
指定了准确的三角形高度,那么可以直接使用高度作为padding,考虑朝向即可:

  _padding() {switch (direction) {case ArrowDirection.bottom:return EdgeInsets.only(bottom: arrowHeight).add(padding!);case ArrowDirection.left:return EdgeInsets.only(left: arrowHeight).add(padding!);case ArrowDirection.right:return EdgeInsets.only(right: arrowHeight).add(padding!);case ArrowDirection.top:return EdgeInsets.only(top: arrowHeight).add(padding!);case ArrowDirection.none:return padding;}}

如果高度是指定比例,则使用FractionallySizedBox变相实现padding:

  _box() {switch (direction) {case ArrowDirection.left:case ArrowDirection.right:return FractionallySizedBox(widthFactor: 1 - arrowHeightWeight,child: child,);case ArrowDirection.top:case ArrowDirection.bottom:return FractionallySizedBox(heightFactor: 1 - arrowHeightWeight,child: child,);case ArrowDirection.none:return FractionallySizedBox(child: child,);}}

最后统合组件即可,源码见末尾。

成果

示例1,传入准确宽高:

ChatBubble(direction: ArrowDirection.bottom,arrowWidth: 30,arrowHeight: 30,child: Container(alignment: Alignment.centerLeft,child: Text("图来",style: TextStyle(color: Colors.black, inherit: false, fontSize: 18),),),)


示例2,加上偏移成为斜三角:

ChatBubble(direction: ArrowDirection.left,arrowWidth: 20,arrowHeight: 20,arrowPeakOffset: -0.8,child: Container(alignment: Alignment.centerLeft,child: Text("图来",style: TextStyle(color: Colors.black, inherit: false, fontSize: 18),),),)


示例3,改变权重使角更为平滑:

ChatBubble(direction: ArrowDirection.top,arrowWidthWeight: 0.1,arrowHeightWeight: 0.2,conicWeight: 1.5,child: Container(alignment: Alignment.centerLeft,child: Text("图来",style: TextStyle(color: Colors.black, inherit: false, fontSize: 18),),),)

源码在此。
以上。

Flutter气泡框实现相关推荐

  1. CSS自定义鼠标样式。JS获取鼠标坐标,实现提示气泡框跟随鼠标移动

    cursor: url(//img.58cdn.com.cn/resource/xxzl/captcha/pencil.png), default; ​​​​​​​ /*获取鼠标位置,让提示气泡框跟随 ...

  2. DocumentManager 在标签位置显示气泡框 z

    关于DevExpress DockManager下的DocumentManager头部标签如何显示气泡框,类似Visual studio那样显示文件的路径,如下图所示,------- 方式很简单,从工 ...

  3. [css] 使用css实现气泡框的效果

    [css] 使用css实现气泡框的效果 <!DOCTYPE html> <html lang="en"><head> <meta char ...

  4. html 文本框 初始化,Flutter 文本框初始化时显示默认值

    刚开始作Flutter文本框时候,使用的是TextField.彷佛大多数状况下都没有问题.代码形式以下:html class _FooState extends State { TextEditing ...

  5. android高德地图气泡,[置顶] Android-高德地图-显示气泡框

    现在的聊天框大多都是气泡框,气泡框长相可爱,有良好的用户体验. 如何把气泡框应用于地图上呢? 步骤一:首先要定义我们的气泡框布局,也就是所谓的layout. popup.xml: android:ba ...

  6. css制作tips提示框,气泡框,制作三角形

    有时候我们的页面会需要这样的一些提示框或者叫气泡框,运用css,我们可以实现这样的效果. 为了实现上面的效果,我们首先要理解如何制作三角形. 当我们给一个DIV不同颜色的边框的时候,我们可以得到下面的 ...

  7. linux视频对话框,抖音对话框视频怎么做?如何在视频画面上添加对话气泡框?视频加对话气泡的方法...

    小编家里有个快要两周岁的小侄女,平日里总是喜欢做一些人小鬼大的事情,这不,前段时间她妈妈发了一个小视频,她拉着一个跟她差不多大的行李箱一脸严肃地说她要去上班~那么小一个小不点,居然很认真地对她妈妈说要 ...

  8. UI设计中聊天气泡框的设计技巧

    聊天气泡我们很熟悉,每天都要和他打交道.早上醒来第一-件事你是不是打开微信看看.那在和朋友聊天时,你有没有这样的疑问,为什么输入文字多少不同,气泡的展示也不同?为什么发送不同比例的图片,其缩略图的展示 ...

  9. html怎么制作气泡,制作CSS气泡框

    气泡状文本框,是一种很生动的网页设计手段. 它可以用来表示用户的发言. 也可以用来作为特定信息的提示符. DVD租借网站Netflix,还用它显示碟片的详细信息. ================== ...

  10. php鼠标滑过跳出别的,鼠标经过出现气泡框实现方法

    鼠标特效现在也层出不穷,本文主要为大家带来一篇鼠标经过出现气泡框的简单实例,小编觉得挺不错的,现在就分享给大家,也给大家做个参考.一起跟随小编过来看看吧,希望能让更好的掌握鼠标特效. 今天看到一个cs ...

最新文章

  1. ajax实现异步校验
  2. BZOJ.1109.[POI2007]堆积木Klo(DP LIS)
  3. inputstream 初始化_MyBatis初始化之加载初始化
  4. MacBookPro 关机花屏解决
  5. Expression Blend 定义RadioButton行为动画
  6. 解读2014之前端篇:任性的颠覆式改变
  7. ffmpeg的内部Video Buffer管理和传送机制
  8. mysql中in和exists的应用场景_mysql中关于in和exists的使用
  9. VLOOKUP模糊匹配的妙用
  10. 2017.4.26-morning
  11. 小红书编辑器_为什么我建议你用小红书打造个人IP?
  12. c语言分桃分题设计思路,C语言实现的猴子分桃问题算法解决方案
  13. class redefinition faild: attempted to add a method
  14. 阿里云服务器ECS搭建后台(Windows Server 2012R2)
  15. vue下利用canvas实现在线图片标注
  16. python小数乘法计算_小数乘法100道
  17. 读Mybatis源码
  18. 查看LINUX放开端口,linux如何查看端口是否开放?
  19. 罗克韦尔AB PLC RSLogix模拟量IO模块基本介绍
  20. python解压文件中哪个是安装包_python解压文件格式的基本方法

热门文章

  1. 权重确定方法五:CRITIC权重法
  2. 录制电脑游戏视频的方法
  3. php 条件求和,Excel条件求和公式是什么
  4. 网络上的计算机无权限访问权限,权限,教您怎么解决无internet访问权限
  5. java 数学库_数学库
  6. 一个MySQL优化案例的初步思路(r8笔记第87天)
  7. 解决“各种”软件图标显示错误问题
  8. Backlog Order
  9. python google 搜索结果爬取_对于 Python 抓取 Google 搜索结果的一些了解
  10. 我奋斗了十八年不是为了和你一起喝杯咖啡