首页 > 系统 > Android > 正文

Android实现气泡布局/弹窗效果 气泡尖角方向及偏移量可控

2019-12-12 05:32:18
字体:
来源:转载
供稿:网友

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"?><resources> <declare-styleable name="bubble">  <attr name="shadowColor" format="color" />  <attr name="padding" format="dimension" />  <attr name="strokeWidth" format="float" />  <attr name="cornerRadius" format="float" />  <attr name="halfBaseOfLeg" format="dimension" /> </declare-styleable></resources>

然后自定义一个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"?><com.yuyh.library.BubbleRelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:app="http://schemas.android.com/apk/res-auto" android:id="@+id/brlBackground" android:layout_width="wrap_content" android:layout_height="wrap_content" android:background="@android:color/transparent" app:cornerRadius="10" app:halfBaseOfLeg="18dp" app:padding="18dp" app:shadowColor="#64000000" app:strokeWidth="5"></com.yuyh.library.BubbleRelativeLayout>

调用

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

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

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表