1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167
|
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) {
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); }
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; }
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); } }
|