Android 通用带箭头提示窗

这篇具有很好参考价值的文章主要介绍了Android 通用带箭头提示窗。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

简介

自定义PopupWindow, 适用于提示类弹窗。

使用自定义Drawable设置带箭头的背景,测试控件和弹窗的尺寸,自动设置弹窗的显示位置,让箭头指向锚点控件的中间位置,且根据锚点控件在屏幕的位置,自动适配弹窗显示位置。 

适用于描述、解释弹窗。

一、效果

带箭头弹窗显示在控件的左侧,箭头相对控件居中对齐 ,且与控件左边框挨着

Android 通用带箭头提示窗,androidAndroid 通用带箭头提示窗,android

 示例代码如下:

二、使用

        GuZhiExplainPupopWindow window = new GuZhiExplainPupopWindow(this);
        //设置弹窗显示文案
        window.setTips("1234567890-34567890-【qweqwertyuiop[sdfghjkl;zxcvbnm,.我们一起走向富强");
        //获取窗口的背景drawable对象
        ArrowsDrawable ad = window.getArrowsDrawable();
        //设置drawable箭头的大小
        ad.setArrowsHeight(ConvertUtils.dp2px(8));
        //设置drawable中箭头与边框距离
        ad.setArrowsPadding(ConvertUtils.dp2px(10));
        //设置drawable的padding, 实际是设置显示文案的TextView的padding
        //ad.setPadding(ConvertUtils.dp2px(10));
        window.setPadding(ConvertUtils.dp2px(10));
        //设置drawable背景色
        ad.setColor(Color.DKGRAY);
        findViewById(R.id.tv33).setOnClickListener(view -> {
            if (!window.isShowing()) {
                //设置窗口显示位置(AUTO_HORIZONTAL 是水平位置,在控件的左侧或右侧,根据控件中心在屏幕中的位置决定)
                window.setShowPosition(GuZhiExplainPupopWindow.AUTO_HORIZONTAL);
                //显示弹窗,偏移量是0,默认是箭头在控件居中位置
                window.show(view, 0, 0);
            }
        });

三、自定义布局

重写initView方法,并设置TipsTv, 同时需要注意的是,在设置弹窗布局时,根布局的宽高属性是wrap_content,设置其它是不生效的,如果需要指定textView的宽或高,或弹窗尺寸,根布局使用某ViewGroup控件,再设置其子控件的尺寸。文章来源地址https://www.toymoban.com/news/detail-611433.html

        GuZhiExplainPupopWindow window = new GuZhiExplainPupopWindow(this, R.layout.pupopwindow_view_guzhi_explain) {
            @Override
            public void initView(View contentView) {
                //自定义布局初始化控件
                super.initView(contentView);
                TextView customTv = contentView.findViewById(R.id.explain_tv);
                setTipsTv(customTv);
            }
        };

四、源码

package com.ttkx.deviceinfo.bkchart.popupwindow;

import android.content.Context;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.PopupWindow;
import android.widget.TextView;

import com.blankj.utilcode.util.ConvertUtils;
import com.blankj.utilcode.util.Utils;
import com.ttkx.deviceinfo.R;
import com.ttkx.deviceinfo.bkchart.ArrowsDrawable;
import com.ttkx.deviceinfo.bkchart.GuZhiActivity;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import androidx.annotation.IntDef;
import androidx.core.widget.PopupWindowCompat;

/**
 * 估值说明弹窗
 * Created by liuyu
 */
public class SimpleTipsPupopWindow extends PopupWindow {

    private ArrowsDrawable mBgDrawable;
    private TextView mTipsTv;
    private int mShowPosition = TOP;
    public static final int AUTO_VERTICAL = Gravity.CENTER_VERTICAL;
    public static final int AUTO_HORIZONTAL = Gravity.CENTER_HORIZONTAL;
    public static final int LEFT = Gravity.LEFT;
    public static final int TOP = Gravity.TOP;
    public static final int RIGHT = Gravity.RIGHT;
    public static final int BOTTOM = Gravity.BOTTOM;

    public SimpleTipsPupopWindow(GuZhiActivity context) {
        this(context, View.inflate(context, R.layout.pupopwindow_view_guzhi_explain, null));
    }

    public SimpleTipsPupopWindow(GuZhiActivity context, int layoutId) {
        this(context, View.inflate(context, layoutId, null));
    }

    public SimpleTipsPupopWindow(GuZhiActivity context, View contentView) {
        super(context);
        setContentView(contentView);
        setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
        setOutsideTouchable(true);
        init(contentView);
    }

    private void init(View contentView) {
        mBgDrawable = new ArrowsDrawable(ArrowsDrawable.TOP, ConvertUtils.dp2px(5));
        mBgDrawable.setCornerRadius(ConvertUtils.dp2px(4));
        mBgDrawable.setArrowsPadding(ConvertUtils.dp2px(10));
        mBgDrawable.setArrowsHeight(ConvertUtils.dp2px(5));
        boolean redMode = true;
        mBgDrawable.setColor(Color.parseColor(redMode ? "#e6292F3C" : "#f22b3346"));
//        mBgDrawable.setPadding(ConvertUtils.dp2px(10));
        mTipsTv = contentView.findViewById(R.id.explain_tv);
        initView(contentView);
    }

    /**
     * 用于自定义布局 初始化
     * @param contentView
     */
    public void initView(View contentView) {
    }

    /**
     * 设置tips TextView
     * @param tv
     */
    public void setTipsTv(TextView tv) {
        mTipsTv = tv;
    }

    private int makeDropDownMeasureSpec(int measureSpec) {
        int mode;
        if (measureSpec == ViewGroup.LayoutParams.WRAP_CONTENT) {
            mode = View.MeasureSpec.UNSPECIFIED;
        } else {
            mode = View.MeasureSpec.EXACTLY;
        }
        return View.MeasureSpec.makeMeasureSpec(View.MeasureSpec.getSize(measureSpec), mode);
    }

    public void setTips(String tips) {
        if (mTipsTv != null) {
            mTipsTv.setText(tips);
        }
    }

    public void show(View anchor, int os, int oy) {
        if (mBgDrawable == null) {
            return;
        }
        int showPosition = mShowPosition;


        int offsetX = 0;


        int offsetY = 0;
        int locationX = getLocationOnScreen(anchor)[0];
        int locationY = getLocationOnScreen(anchor)[1];

        if (showPosition == LEFT || showPosition == RIGHT || showPosition == AUTO_HORIZONTAL) {
            mBgDrawable.setArrowsPosition(ArrowsDrawable.LEFT, mTipsTv);
            getContentView().measure(makeDropDownMeasureSpec(getWidth()), makeDropDownMeasureSpec(getHeight()));
            int windowWidth = this.getContentView().getMeasuredWidth();
            os += anchor.getWidth();
            offsetY = (int) -(anchor.getHeight() / 2 + mBgDrawable.getArrowsCenterDistance());
            if (showPosition == LEFT) {
                boolean showLeft = locationX >= windowWidth;
                offsetX = disHor(showLeft, windowWidth, anchor, os);
            } else if (showPosition == RIGHT) {
                boolean showLeft = !(getAppScreenWidth() - (locationX + anchor.getWidth()) > windowWidth);
                offsetX = disHor(showLeft, windowWidth, anchor, os);
            } else if (showPosition == AUTO_HORIZONTAL) {
                int screenWidth = getAppScreenWidth();
                boolean showLeft = locationX + anchor.getWidth() / 2 >= screenWidth / 2;
                offsetX = disHor(showLeft, windowWidth, anchor, os);
            }
        } else {
            mBgDrawable.setArrowsPosition(ArrowsDrawable.TOP, mTipsTv);//先设置箭头drawable方向为垂直方向的,因箭头尺寸会影响到计算窗口的高度
            getContentView().measure(makeDropDownMeasureSpec(getWidth()), makeDropDownMeasureSpec(getHeight()));
            int windowHeight = this.getContentView().getMeasuredHeight();
            os += anchor.getWidth() / 2;
            offsetX = (int) (os - mBgDrawable.getArrowsCenterDistance());
            if (showPosition == TOP) {
                int distanceTop = locationY - getStatusBarHeight();//锚点控件距离顶部距离
                //计算锚点控件在屏幕中的位置
                offsetY = disVer(distanceTop >= windowHeight, windowHeight, anchor, oy);
            } else if (showPosition == BOTTOM) {
                int distanceBottom = getLocationOnScreen(anchor)[1] - anchor.getHeight() - getNavBarHeight();//锚点控件距离底部距离
                offsetY = disVer(distanceBottom < windowHeight, windowHeight, anchor, oy);
            } else if (showPosition == AUTO_VERTICAL) {
                int appScreenHeight = getAppScreenHeight();
                int anchorCenterY = locationY + anchor.getHeight() / 2;
                offsetY = disVer(appScreenHeight / 2 < anchorCenterY, windowHeight, anchor, oy);
            }
        }
        //设置textView的padding,防止设置drawable背景不生效
        Rect padding = mBgDrawable.getPadding();
        mTipsTv.setPadding(padding.left, padding.top, padding.right, padding.bottom);
        PopupWindowCompat.showAsDropDown(this, anchor, offsetX, offsetY, Gravity.START);
    }

    private int disHor(boolean showLeft, int windowWidth, View anchor, int ox) {
        int offsetX;
        if (showLeft) {//锚点控件在屏幕中上方,反之在屏幕中下方
            mBgDrawable.setArrowsPosition(ArrowsDrawable.RIGHT);
            offsetX = -windowWidth + ox - anchor.getWidth();
        } else {
            mBgDrawable.setArrowsPosition(ArrowsDrawable.LEFT);
            offsetX = ox;
        }
        return offsetX;
    }

    private int disVer(boolean showTop, int windowHeight, View anchor, int oy) {
        int offsetY = 0;
        if (showTop) {//锚点控件在屏幕中上方,反之在屏幕中下方
            mBgDrawable.setArrowsPosition(ArrowsDrawable.BOTTOM);
            offsetY = -(windowHeight + anchor.getHeight() + oy);
        } else {
            mBgDrawable.setArrowsPosition(ArrowsDrawable.TOP);
            offsetY = oy;
        }
        return offsetY;
    }

    public ArrowsDrawable getArrowsDrawable() {
        return mBgDrawable;
    }

    public void setPadding(int padding) {
        mBgDrawable.setPadding(padding);
    }

    @IntDef({LEFT, RIGHT, TOP, BOTTOM, AUTO_VERTICAL, AUTO_HORIZONTAL})
    @Retention(RetentionPolicy.SOURCE)
    public @interface ShowPosition {

    }

    /**
     * 设置显示位置(相对于锚点控件 左边、上方、右边、下面)
     * 注意:窗口相对控件的方向,与箭头方向是相反的。
     * LEFT, RIGHT, TOP, BOTTOM
     *
     * @param showPosition
     */
    public void setShowPosition(@ShowPosition int showPosition) {
        mShowPosition = showPosition;
    }

    private static int[] getLocationOnScreen(View view) {
        int[] location = new int[2];
        view.getLocationOnScreen(location);
        return location;
    }

    private static int getStatusBarHeight() {
        // 获得状态栏高度
        Resources resources = Resources.getSystem();
        int resourceId = resources.getIdentifier("status_bar_height", "dimen", "android");
        return resources.getDimensionPixelSize(resourceId);
    }

    private static int getNavBarHeight() {
        Resources res = Resources.getSystem();
        int resourceId = res.getIdentifier("navigation_bar_height", "dimen", "android");
        if (resourceId != 0) {
            return res.getDimensionPixelSize(resourceId);
        } else {
            return 0;
        }
    }

    private static int getAppScreenHeight() {
        WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);
        if (wm == null) return -1;
        Point point = new Point();
        wm.getDefaultDisplay().getSize(point);
        return point.y;
    }

    private static int getAppScreenWidth() {
        WindowManager wm = (WindowManager) Utils.getApp().getSystemService(Context.WINDOW_SERVICE);
        if (wm == null) return -1;
        Point point = new Point();
        wm.getDefaultDisplay().getSize(point);
        return point.x;
    }
}

到了这里,关于Android 通用带箭头提示窗的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • android aidl进程间通信封装通用实现

    接上一篇的分析,今天继续 aidl复杂流程封装-CSDN博客 今天的任务就是将代码梳理下放进来 1 项目gradle配置:    需要将对应的代码放到各自的目录下,这里仅贴下关键内容,细节可以下载代码慢慢看     sourceSets {         main {             manifest.srcFile \\\'src/main/AndroidManife

    2024年02月22日
    浏览(37)
  • android aidl进程间通信封装通用实现-用法说明

    接上一篇:android aidl进程间通信封装通用实现-CSDN博客 该aar包的使用还是比较方便的 一先看客户端 1 初始化 2 发送事件:  就是通过json的形式将参数包裹进来,另一端统一解析  唯一标志位:KEY_CLIENT_REQUEST_AUTHOR 是必须要加的,不然区分不出来是那个客户端 二再看服务端

    2024年02月20日
    浏览(34)
  • UI系列一Android多子view嵌套通用解决方案

    百度App在17年的版本中实现2个子view嵌套滚动,用于Feed落地页(webview呈现文章详情 + recycle呈现Native评论)。原理是在外层提供一个UI容器(我们称之为”联动容器”)处理WebView和Recyclerview连贯嵌套滚动。 当时的联动容器对子view限制比较大,仅支持WebView和Recyclerview进行联动滚动

    2024年04月16日
    浏览(42)
  • Android Studio xml文件代码自动提示失效/Android Studio 代码不自动提示/怎样设置才能不手动敲Android Studio 代码

    下图为我成功解决Android不自动提示代码之后的样子(这个过程耗时两天,把相关博客都看了一遍,尝试了各种各样的方法都没有解决。但是用这个方法的话几分钟就解决掉了) 我的电脑一直都是很奇怪的状态,因为用别的软件也总是会出现各种bug,但解决这个问题找到合适

    2024年02月03日
    浏览(61)
  • [5 种有效方法] 适用于 Android 的通用解锁图案/密码

    在当今世界,保护您的密码对于您的文件和数据的安全至关重要,尤其是在第三方应用程序盛行的情况下。为这些应用程序注册帐户不是问题,就像记住它们一样。但是,如果您不知何故忘记了手机密码,您仍然可以在不丢失宝贵数据的情况下找到解决方法。 一些工具在互联

    2024年02月11日
    浏览(44)
  • 通用(任何android机型)Root教程(完整版附砖机自救方法)

    一台android终端(可能是手机、可能是平板,也可能是其它),很多功能是要取得Root权限后才能使用的。那我们就来Root它。 取得root权限只要在机器里放两个文件就可以了 /system/bin/su(取得root权限的命令) /system/xbin/su(同上,只不过是有些软件只认这个路径) /system/app/Supe

    2024年02月09日
    浏览(59)
  • Delphi 开发手持机(android)打印机通用开发流程(举一反三)

       目录 一、场景说明 二、厂家应提供的SDK文件 三、操作步骤: 1. 导出Delphi需要且能使用的接口文件: 2. 创建FMX Delphi项目,将上一步生成的接口文件(V510.Interfaces.pas)引入: 3. 将jarsdk.jar 包加入到 libs中:  4. Delphi中调用: 四、完整源代码下载 五、总结: 一、场景说明

    2024年02月11日
    浏览(48)
  • Android studio不自动提示代码

    可以修改一下sdk版本,改成30(Android 11.0(R)),我之前用的33不行,具体操作如下 File-Project Structure选择要修改的模块将Compile Sdk Version改成30  将Target SDK Version改成30  然后应该就可以了,重启android studio  

    2024年02月07日
    浏览(41)
  • VB一个可以改变箭头方向的气泡提示

    \\\'新建一个类名。名称为clsTip Option Explicit \\\'* 模块名称:clsTip.cls \\\'* 功能:一个可以改变箭头方向的气泡提示类 Private Type TOOLINFO cbSize As Long dwFlags As Long hwnd As Long dwID As Long rtRect(3) As Long hInst As Long lpszText As String lParam As Long End Type Private Declare Sub InitCommonControls Lib “comctl32” () P

    2024年02月07日
    浏览(32)
  • Android问题解决方案(一):Android 打空包提交开发者后台提示没有”android:exported“的属性设置

    今日了解到了一个新内容,意指打一个新的空包提审,来实现同个应用二次提审的效果;通过一系列操作后,成功打出了所谓aab格式的安装包。(至于为什么是aab格式,相信有所了解的朋友自然也知道什么原因,还未曾了解的朋友可以先了解相关的信息,这里就不赘述了,自

    2023年04月11日
    浏览(54)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包