热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

Android自定义TipView仿QQ长按后的提示窗口

这篇文章主要介绍了Android自定义TipView仿QQ长按后的提示窗口,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

自定义view--TipView

TipView其实就是类似QQ长按消息弹出来的横放的提示框。

通过看书和参考各位大神的博客(再次对大神表示恭敬),我用了一下午时间写完了这么一个view。

先来看图:


1 自定义TipView思路

1 首先我们考虑是继承View还是ViewGroup

其实TipView直观看更像是一个group,里面有子view。但其实我们并不需要继承ViewGroup,因为我们不用像LinearLayout那样在布局文件里面去添加子view,而且TipView的item我们用文字就好。如果继承于Group我们还要考虑onLayout的问题,为了简单我直接继承自View。

2 重写方法

TipView要像PopupWindow、Dialog一样显示在Activity上而不是添加到父容器中,原因是如果创建后添加到父容器中去托管的话,父容器的布局规则会影响我们TipView的显示效果。所以我们要使用WindowManager来把TipView添加到外层布局,并且要充满屏幕,i原因为我们要点击tem之外的地方使TipView消失。所以view大小是固定充满屏幕的,不需要重写onMeasure。
需要重写onDraw来绘制view。

3 显示位置

TipView主要分两部分,一部分是三角标,一部分是带有圆角的主体。

当我们点击后,三角标顶点始终在点击位置上方一定距离(如果顶点定位在点击位置,会导致手指挡住一部分三角,用户体验度不佳),并且主体不要与屏幕左右边界碰撞,当要遮挡ToolBar时向下绘制。

2 定义变量

public static final int TOP = 0;//从点击位置上面绘制
  public static final int DOWN = 1;//...下面...

  private int mItemWidth;//item宽
  private int mItemHeight;//item高
  private int mTriaHeight;//三角的高度
  private int mHalfTriaWidth;//三角的半宽
  private int mTriaAcme;//三角的顶点
  private int mTriaItemBorder;//三角的顶点
  private int realLeft;//窗口距左边的值
  private int marginSide;//窗口距左右边的值,防止出现的窗口紧贴边界
  private int mSeparateLineColor = Color.WHITE;
  private int mTextSize;//选项文字的大小
  private int mTextColor;//选项文字的颜色

  private int mItemSeparation;//分割线宽度;
  private int mRadius;//圆角
  private List items;//存放item的集合
  private List mItemRectList = new ArrayList<>(); // 存储每个方块
  private Paint mPaint;//画笔
  private Paint mSeparationPaint;//分割线画笔
  private Paint mSPaint;//三角的画笔
  private Path mPath;//路径
  private int x, y;//点击的位置
  private ViewGroup viewRoot;//父容器
  private int location = TOP;//绘制位置
  private int choose = -1;//点击的item
  private int mToolbarBottom;//Toolbar下边距屏幕上距离
  private WindowManager windowManager;
  private WindowManager.LayoutParams layoutParams;//windowManger布局管理器,为了像Dialog一样在Activity弹出,而不是依附于某个group
  private onItemCilckLinener itemCilckLinener;
  private Context cOntext= null;

3 构造函数以及初始化方法

private MyTipView(Context context, int x, int y, ViewGroup viewRoot, List items) {
    super(context);
    this.viewRoot = viewRoot;
    this.cOntext= context;
    this.x = x;
    this.y = y;
    this.items = items;
    windowManager = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
    layoutParams = new WindowManager.LayoutParams();
    layoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;//窗口的宽
    layoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;//窗口的高
    //设置LayoutParams的属性
    layoutParams.type = WindowManager.LayoutParams.TYPE_APPLICATION_PANEL;//该Type描述的是形成的窗口的层级关系,下面会详细列出它的属性
    layoutParams.format = PixelFormat.TRANSLUCENT;//不设置这个弹出框的透明遮罩显示为黑色
    //layoutParams.token = viewRoot.getWindowToken();//设置Token
    int[] location = new int[2];
    viewRoot.getLocationInWindow(location);//获取在当前窗口内的绝对坐标
    viewRoot.getLocationOnScreen(location);//获取在整个屏幕内的绝对坐标
    mToolbarBottom = location[1];//[0]是x轴坐标,[1]y轴
    windowManager.addView(this, layoutParams);
    init();
    initView();
  }

  //初始化画笔
  private void init() {
    mPaint = new Paint();
    mSPaint = new Paint();
    mPath = new Path();
    mSeparatiOnPaint= new Paint();
    mSeparationPaint.setStyle(Paint.Style.FILL);
    mPaint.setAntiAlias(true);
    mPaint.setStyle(Paint.Style.FILL);
    mPaint.setTextSize(Sp2Px(14));
    mPaint.setColor(Color.BLACK);
    mSPaint.setAntiAlias(true);
    mSPaint.setStyle(Paint.Style.FILL);
    mSPaint.setColor(Color.BLACK);
    //初始变量
    mItemWidth = Dp2Px(50);
    mItemHeight = Dp2Px(48);
    mTriaHeight = Dp2Px(10);//三角的高度
    mHalfTriaWidth = Dp2Px(6);//三角的半宽
    mTriaAcme = Dp2Px(6);//三角的顶点
    marginSide = Dp2Px(4);//左右边距
    mItemSeparation = Dp2Px(1);//分割线宽度;
    mRadius = Dp2Px(6);//圆角
    mTextColor = Color.WHITE;
    mTextSize = Sp2Px(14);
  }

4 计算三角顶点位置

private void initView() {
    int count = items.size();
    int width = count * mItemWidth + mItemSeparation * (count - 1);
    int mScreenWidth = getResources().getDisplayMetrics().widthPixels;
    if (y - mToolbarBottom <(mItemHeight + mTriaHeight + mTriaAcme)) {
      location = DOWN;//下方显示
      mTriaAcme += y;//设置三角顶点y轴值;
      mTriaItemBorder = mTriaAcme + mTriaHeight;//计算三角方块交界y
    } else {
      location = TOP;
      mTriaAcme = y - mTriaAcme;//计算顶点位置y轴值
      mTriaItemBorder = mTriaAcme - mTriaHeight;//计算三角方块交界y值
    }
    if (x <(width / 2 + marginSide)) {
      realLeft = marginSide;//计算最左侧距离屏幕左边距离,左边撑不下
    } else if ((mScreenWidth - x) <(width / 2 + marginSide)) {
      realLeft = mScreenWidth - marginSide - width;//计算最左侧距离屏幕左边距离,右边撑不下
    } else {
      realLeft = x - width / 2;//计算最左侧距离屏幕左边距离,触碰不到边界
    }
  }

5 设置背景为透明

private void drawBackground(Canvas canvas) {
    canvas.drawColor(Color.TRANSPARENT);
  }

6 绘制三角

private void drawTop(Canvas canvas) {
    //绘制三角
    mPath.reset();
    mPath.moveTo(x, mTriaAcme);
    mPath.lineTo(x - mHalfTriaWidth, mTriaAcme - mTriaHeight);
    mPath.lineTo(x + mHalfTriaWidth, mTriaAcme - mTriaHeight);
    canvas.drawPath(mPath, mSPaint);
    MyDraw(canvas, mTriaItemBorder - mItemHeight);
  }

  private void drawDown(Canvas canvas) {
    //绘制三角
    mPath.reset();//清理路径
    mPath.moveTo(x, mTriaAcme);
    mPath.lineTo(x - mHalfTriaWidth, mTriaAcme + mTriaHeight);
    mPath.lineTo(x + mHalfTriaWidth, mTriaAcme + mTriaHeight);
    canvas.drawPath(mPath, mSPaint);
    //绘制方块
    MyDraw(canvas, mTriaItemBorder);
  }

7 绘制方块

绘制时因为第一个和最后一个方块带有圆角,单独绘制

private void MyDraw(Canvas canvas, int t) {
    //绘制item
    int count = items.size();
    int width = (count - 1) * mItemSeparation + count * mItemWidth;
    int l = realLeft + mItemWidth + mItemSeparation;
    mItemRectList.clear();
    for (int i = 0; i 

最后一行代码

代码如下:
mItemRectList.add(new Rect(realLeft + i * (mItemSeparation + mItemWidth), t, realLeft + i * (mItemSeparation + mItemWidth) + mItemWidth, t + mItemHeight));

用一个List来存放Rect(矩形),这些矩形对应的是每一个item的方块,但是并没有绘制出来,只是存放起来,矩形是为了在绘制文字的时候提供文字居中时用到的。

8 绘制文字

private void drawTitle(Canvas canvas) {
    for (int i = 0; i 

9 点击变色,以及点击事件实现

@Override
  public boolean onTouchEvent(MotionEvent event) {
    switch (event.getAction()) {
      case MotionEvent.ACTION_DOWN:
        for (int i = 0; i  targetRect.right) {
      return false;
    }
    if (pointF.y  targetRect.bottom) {
      return false;
    }
    return true;
  }

10 Builder模式创建

 

public static class Builder {
    private List items = new ArrayList<>();
    private int x = 0, y = 0;
    private Context context;
    private ViewGroup viewRoot;
    private onItemCilckLinener itemCilckLinener;
    private int mRadius;

    public Builder(Context context, ViewGroup viewRoot) {
      this.cOntext= context;
      this.viewRoot = viewRoot;
    }
    public Builder addItem(TextItem item) {
      items.add(item);
      return this;
    }
    public Builder setmRadius(int radius) {
      mRadius = radius;
      return this;
    }
    public Builder setxAndy(int x, int y) {
      this.x = x;
      this.y = y;
      return this;
    }
    public Builder setOnItemClickLinener(onItemCilckLinener itemClickLinener) {
      this.itemCilckLinener = itemClickLinener;
      return this;
    }
    public MyTipView create() {
      if (items.size() == 0) {
        try {
          throw new Exception("item count is 0");
        } catch (Exception e) {
          e.printStackTrace();
        }
      }
      MyTipView myTipView = new MyTipView(context, x, y, viewRoot, items);
      myTipView.setItemCilckLinener(itemCilckLinener);
      if (mRadius != 0)
        myTipView.setRadius(mRadius);
      return myTipView;
    }
  }

11 item

//TipView的item
  public static class TextItem {
    private String title;
    private int textSize;
    private int textColor = Color.WHITE;

    public TextItem(String title) {
      this.title = title;
    }
    public TextItem(String title, int textSize) {
      this.title = title;
      this.textSize = textSize;
    }
    public TextItem(String title, int textSize, int textColor) {
      this.title = title;
      this.textSize = textSize;
      this.textColor = textColor;
    }
    public String getTitle() {
      return title;
    }
    public void setTitle(String title) {
      this.title = title;
    }
    public int getTextSize() {
      return textSize;
    }
    public void setTextSize(int textSize) {
      this.textSize = textSize;
    }
    public int getTextColor() {
      return textColor;
    }
    public void setTextColor(int textColor) {
      this.textColor = textColor;
    }
  }

12 使用示例

MyTipView.Builder builder = new MyTipView.Builder(this, linearLayout);
    builder.addItem(new MyTipView.TextItem("1"))
        .addItem(new MyTipView.TextItem("2"))
        .addItem(new MyTipView.TextItem("3"))
        .addItem(new MyTipView.TextItem("4"))
        .setxAndy((int) x, (int) y)
        .setOnItemClickLinener(new MyTipView.onItemCilckLinener() {
          @Override
          public void onItemCilck(String title, int i) {
            Toast.makeText(MainActivity.this, title, Toast.LENGTH_SHORT).show();
          }
        })
        .create();

13 源码

https://github.com/liujiakuoyx/learn/blob/master/MyTipView.java

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。


推荐阅读
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • 在Docker中,将主机目录挂载到容器中作为volume使用时,常常会遇到文件权限问题。这是因为容器内外的UID不同所导致的。本文介绍了解决这个问题的方法,包括使用gosu和suexec工具以及在Dockerfile中配置volume的权限。通过这些方法,可以避免在使用Docker时出现无写权限的情况。 ... [详细]
  • 打开文件管理器_【教程】模组管理器3.1食用指南
    文编:byakko最近有部分小伙伴反应还不会使用unity模组管理器,现在我就给大家讲一下unity模组管理器——从下载到使用。完整视频版以下是无WiF ... [详细]
  • 阿里Treebased Deep Match(TDM) 学习笔记及技术发展回顾
    本文介绍了阿里Treebased Deep Match(TDM)的学习笔记,同时回顾了工业界技术发展的几代演进。从基于统计的启发式规则方法到基于内积模型的向量检索方法,再到引入复杂深度学习模型的下一代匹配技术。文章详细解释了基于统计的启发式规则方法和基于内积模型的向量检索方法的原理和应用,并介绍了TDM的背景和优势。最后,文章提到了向量距离和基于向量聚类的索引结构对于加速匹配效率的作用。本文对于理解TDM的学习过程和了解匹配技术的发展具有重要意义。 ... [详细]
  • EPICS Archiver Appliance存储waveform记录的尝试及资源需求分析
    本文介绍了EPICS Archiver Appliance存储waveform记录的尝试过程,并分析了其所需的资源容量。通过解决错误提示和调整内存大小,成功存储了波形数据。然后,讨论了储存环逐束团信号的意义,以及通过记录多圈的束团信号进行参数分析的可能性。波形数据的存储需求巨大,每天需要近250G,一年需要90T。然而,储存环逐束团信号具有重要意义,可以揭示出每个束团的纵向振荡频率和模式。 ... [详细]
  • 在Android开发中,使用Picasso库可以实现对网络图片的等比例缩放。本文介绍了使用Picasso库进行图片缩放的方法,并提供了具体的代码实现。通过获取图片的宽高,计算目标宽度和高度,并创建新图实现等比例缩放。 ... [详细]
  • 学习笔记(34):第三阶段4.2.6:SpringCloud Config配置中心的应用与原理第三阶段4.2.6SpringCloud Config配置中心的应用与原理
    立即学习:https:edu.csdn.netcourseplay29983432482?utm_sourceblogtoedu配置中心得核心逻辑springcloudconfi ... [详细]
  • 目录实现效果:实现环境实现方法一:基本思路主要代码JavaScript代码总结方法二主要代码总结方法三基本思路主要代码JavaScriptHTML总结实 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • Centos7.6安装Gitlab教程及注意事项
    本文介绍了在Centos7.6系统下安装Gitlab的详细教程,并提供了一些注意事项。教程包括查看系统版本、安装必要的软件包、配置防火墙等步骤。同时,还强调了使用阿里云服务器时的特殊配置需求,以及建议至少4GB的可用RAM来运行GitLab。 ... [详细]
  • 生成对抗式网络GAN及其衍生CGAN、DCGAN、WGAN、LSGAN、BEGAN介绍
    一、GAN原理介绍学习GAN的第一篇论文当然由是IanGoodfellow于2014年发表的GenerativeAdversarialNetworks(论文下载链接arxiv:[h ... [详细]
  • [译]技术公司十年经验的职场生涯回顾
    本文是一位在技术公司工作十年的职场人士对自己职业生涯的总结回顾。她的职业规划与众不同,令人深思又有趣。其中涉及到的内容有机器学习、创新创业以及引用了女性主义者在TED演讲中的部分讲义。文章表达了对职业生涯的愿望和希望,认为人类有能力不断改善自己。 ... [详细]
  • baresip android编译、运行教程1语音通话
    本文介绍了如何在安卓平台上编译和运行baresip android,包括下载相关的sdk和ndk,修改ndk路径和输出目录,以及创建一个c++的安卓工程并将目录考到cpp下。详细步骤可参考给出的链接和文档。 ... [详细]
  • 20211101CleverTap参与度和分析工具功能平台学习/实践
    1.应用场景主要用于学习CleverTap的使用,该平台主要用于客户保留与参与平台.为客户提供价值.这里接触到的原因,是目前公司用到该平台的服务~2.学习操作 ... [详细]
  • 如何用UE4制作2D游戏文档——计算篇
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了如何用UE4制作2D游戏文档——计算篇相关的知识,希望对你有一定的参考价值。 ... [详细]
author-avatar
亦涵Doris
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有