Android 实现气泡布局/弹窗,可控制气泡尖角方向及偏移量
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);}@Overrideprotected 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;}@Overrideprotected 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
<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
<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
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
