使用android.graphics.Path类自绘制PopupWindow背景
PopupWindow简单介绍
PopupWindow是悬浮在当前activity上的一个容器,用它能够展示随意的内容。 
PopupWindow跟位置有关的API有以下几个:
- showAsDropDown(View anchor, int xoff, int yoff, int gravity) 
 显示在anchor的左下角,通过xoff,yoff调整距离。gravity是popup相对于anchor的对齐方式。假设popup超出屏幕,而且展示内容的根容器是滑动控件,将以滑动方式展示。假设展示内容根容器不是滑动控件,超出屏幕内容将不可见。
- showAsDropDown (View anchor, int xoff, int yoff) 
 同上
- showAsDropDown (View anchor) 
 同上
- showAtLocation (View parent, int gravity, int x, int y) 
 展示在屏幕的特定位置,假设内容超出屏幕将被裁剪。gravity 为NO_GRAVITY等同于 Gravity.LEFT | Gravity.TOP 
showAsDropDown 还是showAtLocation? 
 假设有anchor,能够使用showAsDropDown 方法。假设没有anchor能够使用showAtLocation 方法,注意使用showAtLocation 方法popup内容超出屏幕即使内容放到ScrollView里也不会滚动。
使用Path类自绘制PopupWindow背景
这里选择showAtLocation方法,使用Path类自绘制PopupWindow背景。
绘制规则例如以下: 
 
给定Popup锚点的x坐标,anchorX;y坐标,anchorYDown,anchorYUp,自己定义view会自己主动计算三角绘制位置。以及显示在anchor下方还是上方。默认显示在下方,下方显示不下再显示在上方。
不足是内容太长无法滚动显示。
实现
package com.xxx;
import com.xxx.utils.log.LogUtils;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Path;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ShapeDrawable;
import android.graphics.drawable.shapes.PathShape;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout.LayoutParams;
import android.widget.PopupWindow;
import android.widget.TextView;
/**
 * TextView with popup style background (has triangle on top or bottom). The
 * anchor triangle will show accurately below or above the anchor position.
 *
 * @author wangwenping
 * @date 2015-6-27
 */
@SuppressLint("DrawAllocation")
public class PopupTextView extends TextView
{
    private static final String TAG = "PopupTextView";
    private static final boolean IS_DEBUG = false;
    /**
     * x of anchor triangle in the popup
     */
    private float mTriangleX;
    /**
     * border color
     */
    private int mBorderColor = 0xff1fc38f;
    /**
     * border width
     */
    private int mBorderWidth = 2;
    /**
     * background color
     */
    private int mBgColor = 0xffffffff;
    /**
     * background color in dark mode
     */
    private int mBgColorDark = 0xff999999;
    /**
     * anchor height
     */
    private float mAnchorHeight = 20;
    /**
     * anchor width
     */
    private float mAnchorWidth = 30;
    /**
     * If content under anchor
     */
    private boolean mShowDown = true;
    /**
     * Below items for draw
     */
    private ShapeDrawable mBorderDrawable;
    private Path mBorderPath;
    private ShapeDrawable mBgDrawable;
    private Path mBgPath;
    private int mWidth;
    private int mHeight;
    /**
     * Keep a record of original padding.
     */
    private int mPadding;
    /**
     * Is night mode.
     */
    private boolean mIsNightMode;
    /**
     * anchor x, y in screen
     */
    private int mAnchorYUp;
    private int mAnchorYDown;
    private int mAnchorX;
    /**
     * screen height & width
     */
    private int mScreenHeight;
    private int mScreenWidth;
    private float mDensity;
    private PopupWindow mPopupWindow;
    private Context mCtx;
    /**
     * Touch listener
     */
    private OnTouchListener mOnTouchListener;
    private boolean mDismissAfterTouch = true;
    /**
     * The minimum margin to left or right.
     */
    private int TRIANGLE_MINIMUM_MARGIN = 10;
    public PopupTextView(Context context)
    {
        super(context);
        setFocusable(true);
        init(context);
    }
    public PopupTextView(Context context, AttributeSet attrs, int defStyle)
    {
        super(context, attrs, defStyle);
        init(context);
    }
    public PopupTextView(Context context, AttributeSet attrs)
    {
        super(context, attrs);
        init(context);
    }
    private void init(Context c)
    {
        mCtx = c;
        mPadding = getPaddingBottom();
        DisplayMetrics dm = c.getResources().getDisplayMetrics();
        mScreenHeight = dm.heightPixels;
        mScreenWidth = dm.widthPixels;
        mDensity = dm.scaledDensity;
    }
    /**
     * Show as pop up window
     */
    public void show()
    {
        if (mPopupWindow != null)
        {
            mPopupWindow.dismiss();
        }
        if (IS_DEBUG)
        {
            LogUtils.d(TAG, "mAnchorX=" + mAnchorX + " mWidth=" + mWidth + " mHeight=" + mHeight);
        }
        mPopupWindow = new PopupWindow(this, LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
        if (mOnTouchListener != null)
        {
            mPopupWindow.setTouchInterceptor(new OnTouchListener()
            {
                @Override
                public boolean onTouch(View arg0, MotionEvent arg1)
                {
                    mOnTouchListener.onTouch(arg0, arg1);
                    if (mDismissAfterTouch && arg1.getAction() == MotionEvent.ACTION_UP)
                    {
                        mPopupWindow.dismiss();
                    }
                    return false;
                }
            });
        }
        mPopupWindow.setFocusable(true);
        mPopupWindow.setTouchable(true);
        mPopupWindow.setBackgroundDrawable(new BitmapDrawable());
        int popX = 0, popY = 0;
        if (mWidth <= 0 || mHeight <= 0)
        {
            // The first time we showthe pop up window out of the screen to get
            // the size of itself.
            popX = mScreenWidth;
            popY = mScreenHeight;
        }
        else
        {
            // The second time we calculate the pop up window's right position.
            Point pos = getLayoutValue();
            popX = pos.x;
            popY = pos.y;
            mTriangleX = mAnchorX - pos.x;
            mTriangleX = Math.max(mTriangleX, TRIANGLE_MINIMUM_MARGIN);
            mTriangleX = Math.min(mTriangleX, mWidth - TRIANGLE_MINIMUM_MARGIN - mAnchorWidth);
        }
        mPopupWindow.showAtLocation(this, Gravity.LEFT | Gravity.TOP, popX, popY);
    }
    /**
     * Calculate the pop up window's right position.
     *
     * @return
     */
    private Point getLayoutValue()
    {
        int x = mAnchorX - mWidth / 2;
        if (x < 10 * mDensity)
        {
            x = (int) (10 * mDensity);
        }
        else if (x + mWidth > mScreenWidth - 10 * mDensity)
        {
            x = (int) (mScreenWidth - mWidth - 10 * mDensity);
        }
        boolean showDown = mAnchorYDown + mHeight < mScreenHeight || mAnchorYDown <= mScreenHeight / 2;
        setShowDown(showDown);
        int y = showDown ? mAnchorYDown : mAnchorYUp - mHeight;
        return new Point(x, y);
    }
    /**
     * Init drawble path.
     *
     * @param width
     * @param height
     */
    private void initPath(int width, int height)
    {
        mBorderPath = new Path();
        mBgPath = new Path();
        if (mShowDown)
        {
            /**
             * ....|<----------------width-------->|<br>
             * ....|<--archorX------>|<br>
             * ....................2<br>
             * ..................../\ (anchor)<br>
             * ....0/7-------------1 3-----------4...........----<br>
             * ....|...............................|.............|<br>
             * ....|...............................|.............height<br>
             * ....|...............................|.............|<br>
             * ....6-------------------------------5............---<br>
             */
            PointF[] borderPoints = new PointF[] { new PointF(0, mAnchorHeight),
                    new PointF(mTriangleX - mAnchorWidth / 2, mAnchorHeight), new PointF(mTriangleX, 0),
                    new PointF(mTriangleX + mAnchorWidth / 2, mAnchorHeight), new PointF(width, mAnchorHeight),
                    new PointF(width, height), new PointF(0, height), new PointF(0, mAnchorHeight), };
            mBorderPath = createLIneToPath(borderPoints);
            PointF[] bgPoints = new PointF[] {
                    new PointF(borderPoints[0].x + mBorderWidth, borderPoints[0].y + mBorderWidth),
                    new PointF(borderPoints[1].x + mBorderWidth, borderPoints[1].y + mBorderWidth),
                    new PointF(borderPoints[2].x, borderPoints[2].y + mBorderWidth),
                    new PointF(borderPoints[3].x - mBorderWidth, borderPoints[3].y + mBorderWidth),
                    new PointF(borderPoints[4].x - mBorderWidth, borderPoints[4].y + mBorderWidth),
                    new PointF(borderPoints[5].x - mBorderWidth, borderPoints[5].y - mBorderWidth),
                    new PointF(borderPoints[6].x + mBorderWidth, borderPoints[6].y - mBorderWidth),
                    new PointF(borderPoints[7].x + mBorderWidth, borderPoints[7].y + mBorderWidth), };
            mBgPath = createLIneToPath(bgPoints);
        }
        else
        {
            /**
             * 0/7-----------------------------1<br>
             * |...............................|<br>
             * |...............................|<br>
             * 6------------------5..3---------2<br>
             * ....................\/<br>
             * ....................4<br>
             */
            PointF[] borderPoints = new PointF[] { new PointF(0, 0), new PointF(width, 0),
                    new PointF(width, height - mAnchorHeight),
                    new PointF(mTriangleX + mAnchorWidth / 2, height - mAnchorHeight), new PointF(mTriangleX, height),
                    new PointF(mTriangleX - mAnchorWidth / 2, height - mAnchorHeight),
                    new PointF(0, height - mAnchorHeight), new PointF(0, 0), };
            mBorderPath = createLIneToPath(borderPoints);
            PointF[] bgPoints = new PointF[] {
                    new PointF(borderPoints[0].x + mBorderWidth, borderPoints[0].y + mBorderWidth),
                    new PointF(borderPoints[1].x - mBorderWidth, borderPoints[1].y + mBorderWidth),
                    new PointF(borderPoints[2].x - mBorderWidth, borderPoints[2].y - mBorderWidth),
                    new PointF(borderPoints[3].x - mBorderWidth, borderPoints[3].y - mBorderWidth),
                    new PointF(borderPoints[4].x, borderPoints[4].y - mBorderWidth),
                    new PointF(borderPoints[5].x + mBorderWidth, borderPoints[5].y - mBorderWidth),
                    new PointF(borderPoints[6].x + mBorderWidth, borderPoints[6].y - mBorderWidth),
                    new PointF(borderPoints[7].x + mBorderWidth, borderPoints[7].y + mBorderWidth), };
            mBgPath = createLIneToPath(bgPoints);
        }
    }
    private Path createLIneToPath(PointF[] points)
    {
        Path path = new Path();
        if (points != null && points.length > 1)
        {
            path.moveTo(points[0].x, points[0].y);
            for (int i = 1; i < points.length; i++)
            {
                path.lineTo(points[i].x, points[i].y);
            }
        }
        path.close();
        return path;
    }
    public int getAnchorYUp()
    {
        return mAnchorYUp;
    }
    public void setAnchorYUp(int mAnchorYUp)
    {
        this.mAnchorYUp = mAnchorYUp;
    }
    public int getAnchorYDown()
    {
        return mAnchorYDown;
    }
    public void setAnchorYDown(int mAnchorYDown)
    {
        this.mAnchorYDown = mAnchorYDown;
    }
    public int getAnchorX()
    {
        return mAnchorX;
    }
    public void setAnchorX(int anchorX)
    {
        this.mAnchorX = anchorX;
    }
    public void setOnTouchListener(OnTouchListener l)
    {
        mOnTouchListener = l;
    }
    public void setDismissAfterTouch(boolean dismissAfterTouch)
    {
        mDismissAfterTouch = dismissAfterTouch;
    }
    public boolean getDismissAfterTouch()
    {
        return mDismissAfterTouch;
    }
    public void setShowDown(boolean showDown)
    {
        mShowDown = showDown;
        if (mShowDown)
        {
            setPadding(getPaddingLeft(), (int) mAnchorHeight + mPadding, getPaddingRight(), mPadding);
        }
        else
        {
            setPadding(getPaddingLeft(), mPadding, getPaddingRight(), (int) mAnchorHeight + mPadding);
        }
    }
    public void setNightMode(boolean isNightMode)
    {
        mIsNightMode = isNightMode;
    }
    @Override
    protected void onSizeChanged(int w, int h, int oldw, int oldh)
    {
        super.onSizeChanged(w, h, oldw, oldh);
        if (IS_DEBUG)
        {
            LogUtils.d(TAG, "w=" + w + " h=" + h + " oldw=" + oldw + " oldh=" + oldh);
        }
        mWidth = w;
        mHeight = h;
        show();
    }
    @Override
    protected void onDraw(Canvas canvas)
    {
        initPath(mWidth, mHeight);
        mBorderDrawable = new ShapeDrawable(new PathShape(mBorderPath, mWidth, mHeight));
        mBorderDrawable.getPaint().setColor(mBorderColor);
        mBgDrawable = new ShapeDrawable(new PathShape(mBgPath, mWidth, mHeight));
        int bgColor = mBgColor;
        if (mIsNightMode)
        {
            bgColor = mBgColorDark;
        }
        mBgDrawable.getPaint().setColor(bgColor);
        int x = 0;
        int y = 0;
        mBorderDrawable.setBounds(x, y, x + mWidth, y + mHeight);
        mBorderDrawable.draw(canvas);
        mBgDrawable.setBounds(x, y, x + mWidth, y + mHeight);
        mBgDrawable.draw(canvas);
        super.onDraw(canvas);
    }
}
4 下载
使用android.graphics.Path类自绘制PopupWindow背景的更多相关文章
- Android中Path类的lineTo方法和quadTo方法画线的区别
		转载:http://blog.csdn.net/stevenhu_223/article/details/9229337 当我们需要在屏幕上形成画线时,Path类的应用是必不可少的,而Path类的li ... 
- Android中贝塞尔曲线的绘制方法
		贝塞尔曲线,很多人可能不太了解,什么叫做贝塞尔曲线呢?这里先做一下简单介绍:贝塞尔曲线也可以叫做贝济埃曲线或者贝兹曲线,它由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋.一般的矢量图形软件常 ... 
- android 利用Path.cubicTo 画 贝塞尔曲线
		Path.cubicTo void android.graphics.Path.cubicTo(float x1, float y1, float x2, float y2, float x3, fl ... 
- android Graphics(一):概述及基本几何图形绘制
		前言:我最近想抽空研究研究android的各种特效,android的特效真是其它平台无法比拟的,而且一个漂亮的UI交互,会给APP增色不少,而学习特效之前,有关graphics绘图的基础知识是必不可少 ... 
- Android -- 自定义View小Demo,关于Path类的使用(一)
		1,在我们知道自定义view中onDraw()方法是用于绘制图形的,而Path类则是其中的一个重要的类,如下图效果: 代码也没有什么难度,直接贴出来吧 @Override protected void ... 
- Android开发之Path类使用详解,自绘各种各样的图形!
		玩过自定义View的小伙伴都知道,在View的绘制过程中,有一个类叫做Path,Path可以帮助我们实现很多自定义形状的View,特别是配合xfermode属性来使用的时候.OK,那我们今天就来看看P ... 
- android.graphics(2) - Path, drawPath, moveTo, lineTo, addRect, addCircle, addOval, addArc, drawText, drawTextOnPath
		一.创建路径 canvas中绘制路径利用: void drawPath (Path path, Paint paint) 1.直线路径 void moveTo (float x1, float y1) ... 
- Android之使用AchartEngineActivity引擎绘制柱状图、曲线图
		1.简介 AChartEngine(简称ACE)是Google的一个开源图表库(for Android).它功能强大,支持散点图.折线 .关于里面类的具体使用,请下载响应的文档说明(主页上有). 2. ... 
- android Graphics(三):区域(Range)
		前言:最近几天对画图的研究有些缓慢,项目开始写代码了,只能在晚上空闲的时候捯饬一下自己的东西,今天给大家讲讲区域的相关知识,已经想好后面两篇的内容了,这几天有时间赶紧写出来给大家.有关界面开发的东东内 ... 
随机推荐
- Java访问HTTPS时证书验证问题
			为了尽可能避免安全问题,公司的很多系统服务都逐步https化,虽然开始过程会遇到各种问题,但趋势不改.最完美的https应用是能实现双向认证,客户端用私钥签名用服务端公钥加密,服务端用私钥签名客户端都 ... 
- 函数和指针 C++
			一.用函数指针变量调用函数. 指针变量也可以指向一个函数,一个函数在编译时被分配给一个入口地址.这个函数入口地址就称为函数的指针.可以用一个指针变量指向函数,然后通过该指针变量调用此函数. 定义指向函 ... 
- Cracking the Coding Interview 5.2
			Given a(decimal -e.g. 3.72)number that is passed in as a string, print the binary representation. If ... 
- webpack入门 --初级压缩
			1.新建一个文件夹,再初始化npm: npm init 2.安装webpack,首先要全局安装,再本地安装: npm install webpack -g // 全局安装 npm install we ... 
- WebStorm2018.2 破解 激活
			1.进入http://idea.lanyus.com/,如图: 2.下载http://idea.lanyus.com/jar/JetbrainsCrack-3.1-release-enc.jar . ... 
- 开源作品-PHP写的在线文件管理工具(单文件绿色版)-SuExplorer_PHP_3_0
			前言:项目开发过程中,网站一般部署到远程服务器,所以文件管理就不能和本机操作一样方便.通常文件管理是用ftp下载到本地,修改后再上传,或者远程登录到服务器进行修改.但是这些操作都依赖于复杂的第三方软件 ... 
- MVC 接收文件
			[HttpPost] public ActionResult Layedit() { var files = Request.Files; //获得所上传的所有文件 ) { HttpPostedFil ... 
- Linux date命令的用法(转)
			1.命令:date 2.命令功能:date 可以用来显示或设定系统的日期与时间. 3.命令参数 -d<字符串>:显示字符串所指的日期与时间.字符串前后必须加上双引号: -s<字符串& ... 
- java 如何将异常Exception的信息转换为String
			一般情况下,我们是通过log4j封装的api将异常打印到日志当中. logger.error("error", e); 如果我们想在程序中获得该异常的详细信息,并输出到数据库中,我 ... 
- python网络编程part1
			1. 网络架构 单机 单机游戏 以下两个基于网络的 CS架构 客户端游戏 cs--->client客户/server服务 你自己是客户端(消费)--->服务端(收钱) 服务端(应用程序)一 ... 
