使用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背景。


绘制规则例如以下:
使用android.graphics.Path类自绘制PopupWindow背景_第1张图片
给定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-------->|
* ....|<--archorX------>|
* ....................2
* ..................../\ (anchor)
* ....0/7-------------1 3-----------4...........----
* ....|...............................|.............|
* ....|...............................|.............height
* ....|...............................|.............|
* ....6-------------------------------5............---
*/
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
* |...............................|
* |...............................|
* 6------------------5..3---------2
* ....................\/
* ....................4
*/
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 下载

Github

csdn下载

你可能感兴趣的:(移动开发)