虚位以待(AD)
虚位以待(AD)
首页 > 软件编程 > Android编程 > Android 实现气泡布局/弹窗,可控制气泡尖角方向及偏移量

Android 实现气泡布局/弹窗,可控制气泡尖角方向及偏移量
类别:Android编程   作者:码皇   来源:互联网   点击:

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);
    }
    @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

然后自定义一个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

调用

    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

相关热词搜索: