Android实现气泡布局/弹窗效果 气泡尖角方向及偏移量可控
2019年06月30日
作者:佚名
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); 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,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,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); } @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,width - PADDING,height - PADDING),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,location[0] + parent.getWidth(),location[1] - (parent.getHeight() / 2)); break; case Gravity.LEFT: showAtLocation(parent,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
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持编程小技巧。
以上是来客网为你收集整理的Android实现气泡布局/弹窗效果 气泡尖角方向及偏移量可控全部内容,希望文章能够帮你解决Android实现气泡布局/弹窗效果 气泡尖角方向及偏移量可控所遇到的程序开发问题。
如果觉得来客网网站内容还不错,欢迎将来客网网站推荐给程序员好友。