Android 自定义布局实现气泡弹窗,可控制气泡尖角方向及偏移量。

效果图

实现

首先自定义一个气泡布局。

/** * 气泡布局 */public class BubbleRelativeLayout extends RelativeLayout { /**  * 气泡尖角方向  */ public enum BubbleLegOrientation {  TOP, LEFT, RIGHT, BOTTOM, NONE } public static int PADDING = 30; public static int LEG_HALF_BASE = 30; public static float STROKE_WIDTH = 2.0f; public static float CORNER_RADIUS = 8.0f; public static int SHADOW_COLOR = Color.argb(100, 0, 0, 0); public static float MIN_LEG_DISTANCE = PADDING + LEG_HALF_BASE; private Paint mFillPaint = null; private final Path mPath = new Path(); private final Path mBubbleLegPrototype = new Path(); private final Paint mPaint = new Paint(Paint.DITHER_FLAG); private float mBubbleLegOffset = 0.75f; private BubbleLegOrientation mBubbleOrientation = BubbleLegOrientation.LEFT; public BubbleRelativeLayout(Context context) {  this(context, null); } public BubbleRelativeLayout(Context context, AttributeSet attrs) {  this(context, attrs, 0); } public BubbleRelativeLayout(Context context, AttributeSet attrs, int defStyle) {  super(context, attrs, defStyle);  init(context, attrs); } private void init(final Context context, final AttributeSet attrs) {  //setGravity(Gravity.CENTER);  ViewGroup.LayoutParams params = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT);  setLayoutParams(params);  if (attrs != null) {   TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.bubble);   try {    PADDING = a.getDimensionPixelSize(R.styleable.bubble_padding, PADDING);    SHADOW_COLOR = a.getInt(R.styleable.bubble_shadowColor, SHADOW_COLOR);    LEG_HALF_BASE = a.getDimensionPixelSize(R.styleable.bubble_halfBaseOfLeg, LEG_HALF_BASE);    MIN_LEG_DISTANCE = PADDING + LEG_HALF_BASE;    STROKE_WIDTH = a.getFloat(R.styleable.bubble_strokeWidth, STROKE_WIDTH);    CORNER_RADIUS = a.getFloat(R.styleable.bubble_cornerRadius, CORNER_RADIUS);   } finally {    if (a != null) {     a.recycle();    }   }  }  mPaint.setColor(SHADOW_COLOR);  mPaint.setStyle(Style.FILL);  mPaint.setStrokeCap(Cap.BUTT);  mPaint.setAntiAlias(true);  mPaint.setStrokeWidth(STROKE_WIDTH);  mPaint.setStrokeJoin(Paint.Join.MITER);  mPaint.setPathEffect(new CornerPathEffect(CORNER_RADIUS));  if (Build.VERSION.SDK_INT >= 11) {   setLayerType(LAYER_TYPE_SOFTWARE, mPaint);  }  mFillPaint = new Paint(mPaint);  mFillPaint.setColor(Color.WHITE);  mFillPaint.setShader(new LinearGradient(100f, 0f, 100f, 200f, Color.WHITE, Color.WHITE, TileMode.CLAMP));  if (Build.VERSION.SDK_INT >= 11) {   setLayerType(LAYER_TYPE_SOFTWARE, mFillPaint);  }  mPaint.setShadowLayer(2f, 2F, 5F, SHADOW_COLOR);  renderBubbleLegPrototype();  setPadding(PADDING, PADDING, PADDING, PADDING); } @Override protected void onConfigurationChanged(Configuration newConfig) {  super.onConfigurationChanged(newConfig); } /**  * 尖角path  */ private void renderBubbleLegPrototype() {  mBubbleLegPrototype.moveTo(0, 0);  mBubbleLegPrototype.lineTo(PADDING * 1.5f, -PADDING / 1.5f);  mBubbleLegPrototype.lineTo(PADDING * 1.5f, PADDING / 1.5f);  mBubbleLegPrototype.close(); } public void setBubbleParams(final BubbleLegOrientation bubbleOrientation, final float bubbleOffset) {  mBubbleLegOffset = bubbleOffset;  mBubbleOrientation = bubbleOrientation; } /**  * 根据显示方向,获取尖角位置矩阵  * @param width  * @param height  * @return  */ private Matrix renderBubbleLegMatrix(final float width, final float height) {  final float offset = Math.max(mBubbleLegOffset, MIN_LEG_DISTANCE);  float dstX = 0;  float dstY = Math.min(offset, height - MIN_LEG_DISTANCE);  final Matrix matrix = new Matrix();  switch (mBubbleOrientation) {   case TOP:    dstX = Math.min(offset, width - MIN_LEG_DISTANCE);    dstY = 0;    matrix.postRotate(90);    break;   case RIGHT:    dstX = width;    dstY = Math.min(offset, height - MIN_LEG_DISTANCE);    matrix.postRotate(180);    break;   case BOTTOM:    dstX = Math.min(offset, width - MIN_LEG_DISTANCE);    dstY = height;    matrix.postRotate(270);    break;  }  matrix.postTranslate(dstX, dstY);  return matrix; } @Override protected void onDraw(Canvas canvas) {  final float width = canvas.getWidth();  final float height = canvas.getHeight();  mPath.rewind();  mPath.addRoundRect(new RectF(PADDING, PADDING, width - PADDING, height - PADDING), CORNER_RADIUS, CORNER_RADIUS, Direction.CW);  mPath.addPath(mBubbleLegPrototype, renderBubbleLegMatrix(width, height));  canvas.drawPath(mPath, mPaint);  canvas.scale((width - STROKE_WIDTH) / width, (height - STROKE_WIDTH) / height, width / 2f, height / 2f);  canvas.drawPath(mPath, mFillPaint); }}

样式 attrs.xml

<?xml version="1.0" encoding="utf-8"?>            

然后自定义一个PopupWindow,用于显示气泡。

public class BubblePopupWindow extends PopupWindow { private BubbleRelativeLayout bubbleView; private Context context; public BubblePopupWindow(Context context) {  this.context = context;  setWidth(ViewGroup.LayoutParams.WRAP_CONTENT);  setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);  setFocusable(true);  setOutsideTouchable(false);  setClippingEnabled(false);  ColorDrawable dw = new ColorDrawable(0);  setBackgroundDrawable(dw); } public void setBubbleView(View view) {  bubbleView = new BubbleRelativeLayout(context);  bubbleView.setBackgroundColor(Color.TRANSPARENT);  bubbleView.addView(view);  setContentView(bubbleView); } public void setParam(int width, int height) {  setWidth(width);  setHeight(height); } public void show(View parent) {  show(parent, Gravity.TOP, getMeasuredWidth() / 2); } public void show(View parent, int gravity) {  show(parent, gravity, getMeasuredWidth() / 2); } /**  * 显示弹窗  *  * @param parent  * @param gravity  * @param bubbleOffset 气泡尖角位置偏移量。默认位于中间  */ public void show(View parent, int gravity, float bubbleOffset) {  BubbleRelativeLayout.BubbleLegOrientation orientation = BubbleRelativeLayout.BubbleLegOrientation.LEFT;  if (!this.isShowing()) {   switch (gravity) {    case Gravity.BOTTOM:     orientation = BubbleRelativeLayout.BubbleLegOrientation.TOP;     break;    case Gravity.TOP:     orientation = BubbleRelativeLayout.BubbleLegOrientation.BOTTOM;     break;    case Gravity.RIGHT:     orientation = BubbleRelativeLayout.BubbleLegOrientation.LEFT;     break;    case Gravity.LEFT:     orientation = BubbleRelativeLayout.BubbleLegOrientation.RIGHT;     break;    default:     break;   }   bubbleView.setBubbleParams(orientation, bubbleOffset); // 设置气泡布局方向及尖角偏移   int[] location = new int[2];   parent.getLocationOnScreen(location);   switch (gravity) {    case Gravity.BOTTOM:     showAsDropDown(parent);     break;    case Gravity.TOP:     showAtLocation(parent, Gravity.NO_GRAVITY, location[0], location[1] - getMeasureHeight());     break;    case Gravity.RIGHT:     showAtLocation(parent, Gravity.NO_GRAVITY, location[0] + parent.getWidth(), location[1] - (parent.getHeight() / 2));     break;    case Gravity.LEFT:     showAtLocation(parent, Gravity.NO_GRAVITY, location[0] - getMeasuredWidth(), location[1] - (parent.getHeight() / 2));     break;    default:     break;   }  } else {   this.dismiss();  } } /**  * 测量高度  *   * @return  */ public int getMeasureHeight() {  getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);  int popHeight = getContentView().getMeasuredHeight();  return popHeight; } /**  * 测量宽度  *   * @return  */ public int getMeasuredWidth() {  getContentView().measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);  int popWidth = getContentView().getMeasuredWidth();  return popWidth; }}

view_popup_window.xml

<?xml version="1.0" encoding="utf-8"?>

调用

BubblePopupWindow leftTopWindow = new BubblePopupWindow(MainActivity.this);View bubbleView = inflater.inflate(R.layout.layout_popup_view, null);TextView tvContent = (TextView) bubbleView.findViewById(R.id.tvContent);tvContent.setText("HelloWorld");leftTopWindow.setBubbleView(bubbleView); // 设置气泡内容leftTopWindow.show(view, Gravity.BOTTOM, 0); // 显示弹窗

依赖

dependencies { compile 'com.yuyh.bubble:library:1.0.0'}

项目地址:https://github.com/smuyyh/BubblePopupWindow

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

更多相关文章

  1. Android(安卓)自定义View (一)
  2. Android(安卓)Studio 中Kotlinx开发
  3. 如何实现Android(安卓)布局背景模糊化处理
  4. View高度动画
  5. Android(安卓)自定义左滑删除列表
  6. Android(安卓)popupwindow 示例程序一
  7. android布局全屏显示,状态栏和导航栏透明设置
  8. 使用setContentView的方式更换布局文件从而更换界面
  9. Android(安卓)NavigationDrawer(侧滑导航栏 DrawerLayout + Navi

随机推荐

  1. android 应用层开发
  2. Android 使用 lambda 表达式
  3. Android(Java):Android的状态栏通知(Noti
  4. Funambol android eclipse上的配置及说明
  5. android 、java优质资料合集
  6. Android(安卓)4.0的图形硬件加速及绘制技
  7. 解决Android Studio Gradle DSL method n
  8. Android访问WebService
  9. Android开发之SlidingDrawer(一)
  10. 使用Android提供的模拟任意地理位置,报jav