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 下载

Github

csdn下载

使用android.graphics.Path类自绘制PopupWindow背景的更多相关文章

  1. Android中Path类的lineTo方法和quadTo方法画线的区别

    转载:http://blog.csdn.net/stevenhu_223/article/details/9229337 当我们需要在屏幕上形成画线时,Path类的应用是必不可少的,而Path类的li ...

  2. Android中贝塞尔曲线的绘制方法

    贝塞尔曲线,很多人可能不太了解,什么叫做贝塞尔曲线呢?这里先做一下简单介绍:贝塞尔曲线也可以叫做贝济埃曲线或者贝兹曲线,它由线段与节点组成,节点是可拖动的支点,线段像可伸缩的皮筋.一般的矢量图形软件常 ...

  3. android 利用Path.cubicTo 画 贝塞尔曲线

    Path.cubicTo void android.graphics.Path.cubicTo(float x1, float y1, float x2, float y2, float x3, fl ...

  4. android Graphics(一):概述及基本几何图形绘制

    前言:我最近想抽空研究研究android的各种特效,android的特效真是其它平台无法比拟的,而且一个漂亮的UI交互,会给APP增色不少,而学习特效之前,有关graphics绘图的基础知识是必不可少 ...

  5. Android -- 自定义View小Demo,关于Path类的使用(一)

    1,在我们知道自定义view中onDraw()方法是用于绘制图形的,而Path类则是其中的一个重要的类,如下图效果: 代码也没有什么难度,直接贴出来吧 @Override protected void ...

  6. Android开发之Path类使用详解,自绘各种各样的图形!

    玩过自定义View的小伙伴都知道,在View的绘制过程中,有一个类叫做Path,Path可以帮助我们实现很多自定义形状的View,特别是配合xfermode属性来使用的时候.OK,那我们今天就来看看P ...

  7. 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) ...

  8. Android之使用AchartEngineActivity引擎绘制柱状图、曲线图

    1.简介 AChartEngine(简称ACE)是Google的一个开源图表库(for Android).它功能强大,支持散点图.折线 .关于里面类的具体使用,请下载响应的文档说明(主页上有). 2. ...

  9. android Graphics(三):区域(Range)

    前言:最近几天对画图的研究有些缓慢,项目开始写代码了,只能在晚上空闲的时候捯饬一下自己的东西,今天给大家讲讲区域的相关知识,已经想好后面两篇的内容了,这几天有时间赶紧写出来给大家.有关界面开发的东东内 ...

随机推荐

  1. poj 3259-- Wormholes(SPFA)

                                                                                                         ...

  2. Node.js:路由

    ylbtech-Node.js:路由 1.返回顶部 1. Node.js 路由 我们要为路由提供请求的 URL 和其他需要的 GET 及 POST 参数,随后路由需要根据这些数据来执行相应的代码. 因 ...

  3. 【转】In ASP.NET using jQuery Uploadify upload attachment

    Upload Uploadify is a JQuery plug-in, achieve the effect is very good, with progress display. Upload ...

  4. BZOJ 2002 LCT板子题

    思路: LCT啊... (分块也行) 不过YOUSIKI出了一道“弹飞大爷” 就不能用分块水过去了 //By SiriusRen #include <cstdio> #include &l ...

  5. DropDownListFor

  6. WEB笔记-1、HTML 标记与文档结构

    1.HTML 标记与文档结构   1.1 块级(block)和行内(inline)标签   块级标签 <h1>-<h6> : 6级标签,h1表示最重要(h1 不仅仅是最大最突出 ...

  7. 编译OpenCV遇到Qmake问题

    1.Ubuntu安装OpenCv,出现:qmake: could not exec '/usr/lib/x86_64-linux-gnu/qt4/bin/qmake': No such file or ...

  8. 『转』The Beginning of your Design Career

    想想,如果明天我开始学日语,坚持到毕业,其实也可以日语入门了.所以机会都是抓住,当初,也就是去年的时候,我那个时候就开始坚持日语入门,想想现在应该可以开始N2了吧-所以...过去不去理会,现在开始继续 ...

  9. 跳出语句 break continue

    break 使用场景:终止switch或者循环 在选择结构switch语句中 在循环语句中 离开使用场景的存在是没有意义的 public static void main(String[] args) ...

  10. eclipse中的maven项目部署到tomcat中

    http://www.cnblogs.com/guodefu909/p/4874549.html