Android -- 浮动组建

简介:

在开发Android应用时,加新功能是必不可少的,我们加入了新的功能,有的一看界面就可以看出来,但是有的新功能就比较隐蔽,也就是用户很难知道你添加了这个新功能,这个时候就需要用户在打开我们的应用时给出一些提示,说明我们在哪里添加了新功能,点击哪里可以看到这个新功能。这时我们第一时间想到的可能是Toast,因为它用法简单,又不影响用户操作,但是它有个缺点,就是不能明确的指示是哪里添加了新功能,除非你用文字描述出来。

基本思路                                                                                      

  • 首先你要有一个处理好的9 PNG的图片,用于自适应文字显示,关于9 PNG处理可以参考Android Doc
  • 要显示在哪个View的下面,就要知道这个目标View的位置
  • 把要显示的文本放在一个TextView里,使用Toast的setView方法设置Toast要显示的View。
  • 根据得到的位置,最后就是使用Toast的setGravity方法把要显示的内容放到正确的位置显示出来即可。
  • 总的来说首先就是要知道目标View,根据targetView计算出要显示提示的位置,然后根据位置使用Toast把提示的文本显示出来。但是这里有几个难点,下面就一一解决

    Activity加载完成时获取targetVIew的宽高和位置属性                     

    我们加入了新的功能提示,自然会在用户打开这个界面的时候就提示,但是在UI没有渲染完成绑定倒Window上的时候,是不能获取倒targetView的width、height和position的,那么我们怎么才能知道targetView的这些属性呢?Activity的onAttachedToWindow回调方法是不能用的,况且它是在API 5加上的,以前的API中并没有。不过我们还有一种方法,那就是在显示提示的时候获取targetView的属性,如果获取不到(为0)就一直获取,直到获取到为止,这其实是一个轮询。为了达到这一目的,我们在开发者调用FloatTextToast.show()的时候使用Android的Message机制轮询获取一个targetView的属性,如果获取到,就会显示提示文字了。在此之前先看下FloatTextToast构造函数,可以对它有个大概的了解,防止后面的代码中出现的成员变量不认识。

    复制代码
    /**
         * 私有的构造函数
         * 
         * @param context
         *            上下文
         * @param targetView
         *            目标view
         * @param content
         *            内容
         * @param time
         *            显示时间
         */
        private MyToast(Context context, View targetView, String content, int time) {
            this.targetView = targetView;
            this.context = context;
            this.content = content;
            this.time = time;
            // 初始化Toast
            toast = new Toast(this.context);
            textView = new TextView(this.context);
            textView.setTextColor(Color.BLACK);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
            textView.setBackgroundResource(R.drawable.float_text_toast_bg);
            textView.setText(this.content);
            toast.setView(textView);// 设置背景
            
            toast.setDuration(time);// 设置时间
            // 开始handlerthread
            handlerThread = new HandlerThread("MyToast");
            handlerThread.start();
            // 构造一个自己的looper
            handler = new MyHandler(handlerThread.getLooper());
        }
    复制代码

    自定义自己的消息循环机制                                                              

    要想在一个自定义的组件中使用Message机制,一定要有自己的Looper机制,我们不能使用Activity的Looper,因为主Looper可能会有其他的Message需要处理,这就会导致我们的show方法会延迟调用,这样效果就不好了,所以要有一个专门的Looper来处理此Message。要声明自己的Looper,就需要HandlerThread这个类的配合了,这可是个好东西,使用它你会很容易的创建一个自己的线程用于处理你Message。使用方法很简单,如下代码:

    // 开始handlerthread
            handlerThread = new HandlerThread("MyToast");
            handlerThread.start();
            // 构造一个自己的looper
            handler = new MyHandler(handlerThread.getLooper());

    这样就声明了一个HandlerThread并且让它运行,运行之后我们就可以获取一个属于该Thread的Looper,然后把Message发送给这个Looper,那么这个线程就可以处理你发送的消息了。。看看我们的自定义Handler

    复制代码
    /**
         * 自定义的Handler
         * 
         * @author sansung
         * 
         */
        class MyHandler extends Handler {
    
            public MyHandler(Looper looper) {
                super(looper);
                // TODO 自动生成的构造函数存根
            }
    
            @Override
            public void handleMessage(Message msg) {
                // TODO 自动生成的方法存根
                super.handleMessage(msg);
                switch (msg.what) {
                case WHAT_SHOW:
                    showInHandler();
                    break;
                default:
                    break;
                }
            }
    
        }
    复制代码

    它需要传递一个Looper作为构造参数声明,意思就是使用这个Looper处理我发send的Message的意思。上面的代码

    handler = new MyHandler(handlerThread.getLooper());

    正是我们使用自己开启的线程处理我们的Message的意思。下面看下我们的showInHandler()方法是怎么处理的。

    复制代码
    /**
         * Handler调用的show方法,主要为了等待targetView的位置
         * 如果targetView的位置没有得到,handler looper继续循环获取
         */
        private void showInHandler() {
            int[] targetPos = getTargetViewPos();
            if(targetPos[0]==0&&targetPos[1]==0){
                handler.sendEmptyMessageDelayed(WHAT_SHOW, 100);
            }else{
                final Rect contentPos=getSize(targetPos);
                toast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);
                toast.show();
            }
        }
    复制代码

    该方法其实就是在获取targetVIew的位置,如果获取不到,则向自定义的Looper里发送一个Message重新调用该函数,如果得到了位置,那么就调用Toast的setGravity方法设置好要显示文本的位置,然后显示即可。

    获取要显示文本的位置                                                                    

    要获取显示的位置,就要知道targetVIew的位置以及它的宽、高,这样就能计算要显示文本的位置了。View组件都有一个函数,可以把自己在Window里的坐标转换为一个数组。

    复制代码
    /**
         * 得到目标View的位置
         * @return
         */
        private int[] getTargetViewPos() {
            final int[] targetPos = new int[2];
            targetView.getLocationInWindow(targetPos);
            return targetPos;
        }
    复制代码

    这样,就返回了targetView的xy坐标了。光有targetView的坐标还不够,还要有contentView最终要显示的位置。

    复制代码
    /**
         * 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处
         * 
         * @param targetPos
         * @return 一个包含top和left的Rect
         */
        private Rect getSize(int[] targetPos) {
            final Rect windowVisibleRect = new Rect();
            final View targetView = this.targetView;
            final TextView contentView = textView;
            // 状态栏高度
            targetView.getWindowVisibleDisplayFrame(windowVisibleRect);
            int statusBarHeight = windowVisibleRect.top;
            // 背景图那个三角箭头的位置
            final TextPaint textPaint = contentView.getPaint();
            int contentW = (int) textPaint.measureText((String) contentView
                    .getText());
            int arrowPos = (int) (contentW * (30.0 / 160));
    
            final Rect rect = new Rect();
            rect.left = targetPos[0] + targetView.getWidth() / 2 - arrowPos;
            rect.top = targetPos[1] - statusBarHeight + targetView.getHeight();
            return rect;
        }
    复制代码

    这个函数的功能就是让文本显示在targetView的下方的横向中间的位置,也就是文本的背景尖角三角要指向targetView横向中间的位置,这样才好看些。为了这个才需要使用Paint测量文本的宽度,所以这也是该组件的一个缺陷,不能显示String格式之外的字符,比如SpannableString。

    完整的组件代码                                                                            

    上面是对组件代码的拆分讲解,是为了说明我们当时实现这个组件的想法以及步骤,下面就整体把代码列出来,明了的看一下。

    复制代码
    public class MyToast {
        private static final int WHAT_SHOW = 1;
        private View targetView = null;// 目标view
        private Context context = null;// Toast的上下文
        private Toast toast = null;// 显示的土司
        private String content = null;// 土司显示内容
        private TextView textView = null;// 土司中的textview
        private int time = 0;// 土司显示时间
        private HandlerThread handlerThread = null;// handlerThread
        private static MyToast myToast = null;
        private Handler handler = null;
    
        /**
         * 私有的构造函数
         * 
         * @param context
         *            上下文
         * @param targetView
         *            目标view
         * @param content
         *            内容
         * @param time
         *            显示时间
         */
        private MyToast(Context context, View targetView, String content, int time) {
            this.targetView = targetView;
            this.context = context;
            this.content = content;
            this.time = time;
            // 初始化Toast
            toast = new Toast(this.context);
            textView = new TextView(this.context);
            textView.setTextColor(Color.BLACK);
            textView.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 16);
            textView.setBackgroundResource(R.drawable.float_text_toast_bg);
            textView.setText(this.content);
            toast.setView(textView);// 设置背景
            
            toast.setDuration(time);// 设置时间
            // 开始handlerthread
            handlerThread = new HandlerThread("MyToast");
            handlerThread.start();
            // 构造一个自己的looper
            handler = new MyHandler(handlerThread.getLooper());
        }
    
        /**
         * 显示Toast
         * 
         * @param context
         *            上下文
         * @param targetView
         *            目标view
         * @param content
         *            内容
         * @param time
         *            显示时间
         */
        public static MyToast makeText(Context context, View targetView,
                String content, int time) {
            myToast = new MyToast(context, targetView, content, time);
            return myToast;
    
        }
    
        public void show() {
            handler.sendEmptyMessage(WHAT_SHOW);
        }
    
        /**
         * 计算获取浮动文本显示的位置,把浮动文本放在targetView的中心处
         * 
         * @param targetPos
         * @return 一个包含top和left的Rect
         */
        private Rect getSize(int[] targetPos) {
            final Rect windowVisibleRect = new Rect();
            final View targetView = this.targetView;
            final TextView contentView = textView;
            // 状态栏高度
            targetView.getWindowVisibleDisplayFrame(windowVisibleRect);
            int statusBarHeight = windowVisibleRect.top;
            // 背景图那个三角箭头的位置
            final TextPaint textPaint = contentView.getPaint();
            int contentW = (int) textPaint.measureText((String) contentView
                    .getText());
            int arrowPos = (int) (contentW * (30.0 / 160));
    
            final Rect rect = new Rect();
            rect.left = targetPos[0] + targetView.getWidth() / 2 - arrowPos;
            rect.top = targetPos[1] - statusBarHeight + targetView.getHeight();
            return rect;
        }
        /**
         * Handler调用的show方法,主要为了等待targetView的位置
         * 如果targetView的位置没有得到,handler looper继续循环获取
         */
        private void showInHandler() {
            int[] targetPos = getTargetViewPos();
            if(targetPos[0]==0&&targetPos[1]==0){
                handler.sendEmptyMessageDelayed(WHAT_SHOW, 100);
            }else{
                final Rect contentPos=getSize(targetPos);
                toast.setGravity(Gravity.LEFT|Gravity.TOP, contentPos.left, contentPos.top);
                toast.show();
            }
        }
        /**
         * 得到目标View的位置
         * @return
         */
        private int[] getTargetViewPos() {
            final int[] targetPos = new int[2];
            targetView.getLocationInWindow(targetPos);
            return targetPos;
        }
    
        /**
         * 自定义的Handler
         * 
         * @author sansung
         * 
         */
        class MyHandler extends Handler {
    
            public MyHandler(Looper looper) {
                super(looper);
                // TODO 自动生成的构造函数存根
            }
    
            @Override
            public void handleMessage(Message msg) {
                // TODO 自动生成的方法存根
                super.handleMessage(msg);
                switch (msg.what) {
                case WHAT_SHOW:
                    showInHandler();
                    break;
                default:
                    break;
                }
            }
    
        }
    
    }
    复制代码

    此组件和Toast的实现方法一样,所以上手不难,只需使用makeText静态方法生成一个即可

    MyToast.makeText(MainActivity.this, v, "12222222222222221111111111", 1).show();

    我是天王盖地虎的分割线                             




    本文转自我爱物联网博客园博客,原文链接:http://www.cnblogs.com/yydcdut/p/3888889.html,如需转载请自行联系原作者

相关文章
|
Android开发 数据格式 XML
Android开发之浮动Activity
场景 在使用App时,曾经看到这样一个场景,如下图所示,点击顶部菜单按钮,有一个类似的对话框的列表显示出来,让用户选择其中的一个快递选项,然后选中的快递信息就会填充到底部的Activity中。
869 0
|
XML Java Android开发
Android任意窗口添加固定/浮动窗体:音乐播放器底部/顶部常驻播放窗体
 Android任意窗口添加固定/浮动窗体:音乐播放器底部/顶部常驻播放窗体 在Android窗口添加一个固定或者浮动位置的窗体,这在一些常见的Android APP中经常遇到,比如音乐播放器类APP,这些APP通常在窗口主要位置放置歌曲列表之类,但会在底部或者顶部放置一个“条形”壮的微型播放器常驻窗口。
1555 0
|
C# Android开发 Java
用Xamarin 实现园友的 :Android浮动小球与开机自启动
原文:用Xamarin 实现园友的 :Android浮动小球与开机自启动 前两天看园子里有筒子写了个 Android浮动小球与开机自启动  , 感觉这种被 360 玩烂的功能原来是如此的简单啊。。。 我不会 Java, 当然也不懂如何 用 ADT 开发 Android App. 但是我看过几天 Xamarin 啊,C# 我还是会的。
1567 0
|
Android开发
Android基础 - 对话框和浮动Activity -- 转
在之前的学习过程中我们已经知道,Android应用中最常用的屏幕显示开发是基于Activity的,但是,在很多情况下我们需要显示一个对话框或浮动窗体来完成一些简单的任务,比如需要让用户输入一些内容,或让用户确认一些信息。
722 0
|
3天前
|
JavaScript 搜索推荐 Android开发
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
23 8
【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
|
15天前
|
前端开发 Java Shell
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
117 20
【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
13天前
|
Dart 前端开发 Android开发
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
37 4
【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
|
29天前
|
缓存 前端开发 Android开发
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
79 12
【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
|
1月前
|
Dart 前端开发 Android开发
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈
36 1
【02】写一个注册页面以及配置打包选项打包安卓apk测试—开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草央千澈

热门文章

最新文章

  • 1
    如何修复 Android 和 Windows 不支持视频编解码器的问题?
  • 2
    Android历史版本与APK文件结构
  • 3
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 4
    【04】flutter补打包流程的签名过程-APP安卓调试配置-结构化项目目录-完善注册相关页面-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程
  • 5
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
  • 8
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
  • 9
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
  • 10
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
  • 1
    Cellebrite UFED 4PC 7.71 (Windows) - Android 和 iOS 移动设备取证软件
    24
  • 2
    【03】仿站技术之python技术,看完学会再也不用去购买收费工具了-修改整体页面做好安卓下载发给客户-并且开始提交网站公安备案-作为APP下载落地页文娱产品一定要备案-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    32
  • 3
    Android历史版本与APK文件结构
    119
  • 4
    【02】仿站技术之python技术,看完学会再也不用去购买收费工具了-本次找了小影-感觉页面很好看-本次是爬取vue需要用到Puppeteer库用node.js扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-优雅草卓伊凡
    27
  • 5
    【01】仿站技术之python技术,看完学会再也不用去购买收费工具了-用python扒一个app下载落地页-包括安卓android下载(简单)-ios苹果plist下载(稍微麻烦一丢丢)-客户的麻将软件需要下载落地页并且要做搜索引擎推广-本文用python语言快速开发爬取落地页下载-优雅草卓伊凡
    23
  • 6
    APP-国内主流安卓商店-应用市场-鸿蒙商店上架之必备前提·全国公安安全信息评估报告如何申请-需要安全评估报告的资料是哪些-优雅草卓伊凡全程操作
    55
  • 7
    【09】flutter首页进行了完善-采用android studio 进行真机调试开发-增加了直播间列表和短视频人物列表-增加了用户中心-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    37
  • 8
    当flutter react native 等混开框架-并且用vscode-idea等编译器无法打包apk,打包安卓不成功怎么办-直接用android studio如何打包安卓apk -重要-优雅草卓伊凡
    71
  • 9
    【08】flutter完成屏幕适配-重建Android,增加GetX路由,屏幕适配,基础导航栏-多版本SDK以及gradle造成的关于fvm的使用(flutter version manage)-卓伊凡换人优雅草Alex-开发完整的社交APP-前端客户端开发+数据联调|以优雅草商业项目为例做开发-flutter开发-全流程-商业应用级实战开发-优雅草Alex
    117
  • 10
    Android经典面试题之Kotlin中Lambda表达式和匿名函数的区别
    29