Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- namespace NovaCura.Flow.Client.Android.Views.Helpers
- {
- using System;
- using Cirrious.MvvmCross.Binding.Droid.Views;
- using NovaCura.Flow.Client.Android.Helpers;
- using global::Android.Content;
- using global::Android.Content.Res;
- using global::Android.Graphics;
- using global::Android.Util;
- using global::Android.Views;
- class BubbleLayout : MvxLinearLayout
- {
- private static readonly Color enabledBackgroundColor;
- private static readonly Color disabledBackgroundColor;
- private static readonly Color pressedBackgroundColor;
- private static readonly Paint enabledBackgroundPaint;
- private static readonly Paint disabledBackgroundPaint;
- private static readonly Paint pressedBackgroundPaint;
- private static readonly Paint borderPaint;
- static BubbleLayout()
- {
- enabledBackgroundColor = Color.White;
- disabledBackgroundColor = Color.ParseColor("#eaeaea");
- pressedBackgroundColor = Color.LightBlue;
- enabledBackgroundPaint = new Paint { Color = enabledBackgroundColor };
- enabledBackgroundPaint.SetStyle(Paint.Style.Fill);
- enabledBackgroundPaint.AntiAlias = true;
- enabledBackgroundPaint.SetShadowLayer(4f, 1f, 1f, Color.ParseColor("#44000000"));
- disabledBackgroundPaint = new Paint { Color = disabledBackgroundColor };
- disabledBackgroundPaint.SetStyle(Paint.Style.Fill);
- disabledBackgroundPaint.AntiAlias = true;
- pressedBackgroundPaint = new Paint { Color = pressedBackgroundColor };
- pressedBackgroundPaint.SetStyle(Paint.Style.Fill);
- pressedBackgroundPaint.AntiAlias = true;
- pressedBackgroundPaint.SetShadowLayer(4f, 1f, 1f, Color.ParseColor("#44000000"));
- borderPaint = new Paint { Color = Color.ParseColor("#bbbaba"), StrokeWidth = 1.5f, AntiAlias = true };
- borderPaint.SetStyle(Paint.Style.Stroke);
- }
- public BubbleLayout(Context context, IAttributeSet attrs, IMvxAdapterWithChangedEvent adapter)
- : base(context, attrs, adapter ?? new MvxAdapterWithChangedEvent(context))
- {
- // Make the view clickable and set up the tap gesture listener
- var recognizer = new BubbleViewGestureListener(
- onShowPress: () => this.IndicatePressed(true),
- onTap: (didShowPress) =>
- {
- if (didShowPress)
- this.IndicatePressed(false);
- else
- this.FlashAsIfPressed();
- });
- this.gestureDetector = new GestureDetector(context, recognizer);
- this.SetPadding();
- TypedArray a = context.ObtainStyledAttributes(attrs, Resource.Styleable.BubbleLayout);
- this.arrowDirection = (ArrowDirection)a.GetInt(Resource.Styleable.BubbleLayout_arrowDirection, 0);
- this.arrowType = (ArrowType)a.GetInt(Resource.Styleable.BubbleLayout_arrowType, 0);
- this.UpdateArrow();
- }
- public BubbleLayout(Context context, IAttributeSet attrs)
- : this(context, attrs, null)
- {
- }
- protected override void OnFinishInflate()
- {
- this.Orientation = global::Android.Widget.Orientation.Vertical;
- this.SetWillNotDraw(false);
- this.SetMinimumHeight(this.DpToPx(30));
- base.OnFinishInflate();
- }
- private ArrowDirection arrowDirection = ArrowDirection.Down;
- public ArrowDirection ADirection
- {
- get
- {
- return this.arrowDirection;
- }
- set
- {
- this.arrowDirection = value;
- this.UpdateArrow();
- }
- }
- private float arrowOffset = 0.0f;
- public float AOffset
- {
- get
- {
- return this.arrowOffset;
- }
- set
- {
- this.arrowOffset = value;
- this.UpdateArrow();
- }
- }
- private ArrowType arrowType = ArrowType.None;
- public ArrowType AType
- {
- get
- {
- return this.arrowType;
- }
- set
- {
- this.arrowType = value;
- this.UpdateArrow();
- }
- }
- public enum ArrowDirection
- {
- Left,
- Right,
- Up,
- Down
- }
- public enum ArrowType
- {
- None,
- Incoming,
- Outgoing
- }
- private void UpdateArrow()
- {
- if (this.arrowDirection == ArrowDirection.Left)
- {
- this.SetWestArrow(this.arrowType, this.arrowOffset);
- }
- else if (this.arrowDirection == ArrowDirection.Up)
- {
- this.SetNorthArrow(this.arrowType, this.arrowOffset);
- }
- else if (this.arrowDirection == ArrowDirection.Right)
- {
- this.SetEastArrow(this.arrowType, this.arrowOffset);
- }
- else if (this.arrowDirection == ArrowDirection.Down)
- {
- this.SetSouthArrow(this.arrowType, this.arrowOffset);
- }
- }
- public void SetNorthArrow(ArrowType arrowType, float offset)
- {
- this.northEdgeArrow.ArrowType = arrowType;
- this.northEdgeArrow.Offset = offset;
- this.SetPadding();
- this.InvalidateOutline();
- this.Invalidate();
- this.arrowDirection = ArrowDirection.Up;
- this.arrowType = arrowType;
- this.arrowOffset = offset;
- }
- public void SetEastArrow(ArrowType arrowType, float offset)
- {
- this.eastEdgeArrow.ArrowType = arrowType;
- this.eastEdgeArrow.Offset = offset;
- this.SetPadding();
- this.InvalidateOutline();
- this.Invalidate();
- this.arrowDirection = ArrowDirection.Right;
- this.arrowType = arrowType;
- this.arrowOffset = offset;
- }
- public void SetSouthArrow(ArrowType arrowType, float offset)
- {
- this.southEdgeArrow.ArrowType = arrowType;
- this.southEdgeArrow.Offset = offset;
- this.SetPadding();
- this.InvalidateOutline();
- this.Invalidate();
- this.arrowDirection = ArrowDirection.Down;
- this.arrowType = arrowType;
- this.arrowOffset = offset;
- }
- public void SetWestArrow(ArrowType arrowType, float offset)
- {
- this.westEdgeArrow.ArrowType = arrowType;
- this.westEdgeArrow.Offset = offset;
- this.SetPadding();
- this.InvalidateOutline();
- this.Invalidate();
- this.arrowDirection = ArrowDirection.Left;
- this.arrowType = arrowType;
- this.arrowOffset = offset;
- }
- public event EventHandler SizeChanged;
- protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
- {
- base.OnSizeChanged(w, h, oldw, oldh);
- if (this.SizeChanged != null)
- this.SizeChanged(this, null);
- this.InvalidateOutline();
- }
- private void FlashAsIfPressed()
- {
- // TODO: figure out how to flash like a push button
- // How is this actually done?
- //this.IndicatePressed(true);
- //Thread.Sleep(500);
- //this.IndicatePressed(false);
- }
- /*public override bool DispatchTouchEvent(MotionEvent e)
- {
- // Stop disabled views from swallowing touch events
- if (this.Enabled)
- return base.DispatchTouchEvent(e);
- }*/
- private void IndicatePressed(bool pressed)
- {
- this.touchDown = pressed;
- this.Invalidate();
- }
- public void SetHeaderArea(Paint paint, int height, bool isHorizontal = false)
- {
- this.headerAreaPaint = paint;
- this.headerAreaHeight = height;
- this.headerIsHorizontal = isHorizontal;
- }
- protected virtual Paint GetBackgroundPaint(bool enabled)
- {
- if (enabled)
- return enabledBackgroundPaint;
- else
- return disabledBackgroundPaint;
- }
- protected override void OnDraw(Canvas canvas)
- {
- if (this.outline == null)
- this.CreateOutline();
- var backgroundPaint = this.Pressed || this.touchDown ? pressedBackgroundPaint : this.GetBackgroundPaint(this.Enabled);
- canvas.DrawPath(this.outline, backgroundPaint);
- if (this.headerAreaHeight > 0)
- {
- canvas.Save();
- canvas.ClipPath(this.outline);
- this.headerAreaPaint.SetStyle(Paint.Style.Fill);
- if (this.headerIsHorizontal)
- canvas.DrawRect(new Rect(0, 0, this.headerAreaHeight, this.Height), this.headerAreaPaint);
- else
- canvas.DrawRect(new Rect(0, 0, this.Width, this.headerAreaHeight), this.headerAreaPaint);
- canvas.Restore();
- }
- canvas.DrawPath(this.outline, borderPaint);
- this.InvalidateOutline();
- }
- protected override void OnFocusChanged(bool gainFocus, FocusSearchDirection direction, Rect previouslyFocusedRect)
- {
- base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
- }
- private void InvalidateOutline()
- {
- if (this.outline != null)
- {
- this.outline.Dispose();
- this.outline = null;
- }
- }
- private void SetPadding()
- {
- // Initial padding values
- var paddingLeft = 8 + this.boxMargin;
- var paddingTop = 8 + this.boxMargin;
- var paddingRight = 8 + this.boxMargin + this.arrowHeight;
- var paddingBottom = 8 + this.boxMargin;
- // Initialize extra space (for arrow)
- var topSpace = 0;
- var bottomSpace = 0;
- // If there are outgoing arrows, add some extra space for them
- if (this.northEdgeArrow.ArrowType == ArrowType.Outgoing)
- topSpace = this.arrowHeight;
- if (this.southEdgeArrow.ArrowType == ArrowType.Outgoing)
- bottomSpace += this.arrowHeight;
- this.SetPadding(paddingLeft, paddingTop + topSpace, paddingRight, paddingBottom + bottomSpace); // TODO: Convert to device independent pixels
- }
- protected override void Dispose(bool disposing)
- {
- if (this.outline != null)
- {
- this.outline.Dispose();
- this.outline = null;
- }
- base.Dispose(disposing);
- }
- private void CreateOutline()
- {
- // Initialize extra space (for arrow)
- var topSpace = 0;
- var bottomSpace = 0;
- // If there are outgoing arrows, add some extra space for them
- if (this.northEdgeArrow.ArrowType == ArrowType.Outgoing)
- topSpace = this.arrowHeight;
- if (this.southEdgeArrow.ArrowType == ArrowType.Outgoing)
- bottomSpace += this.arrowHeight;
- this.outline = new Path();
- // Top left rounded corner
- this.outline.MoveTo(this.boxMargin, this.boxMargin + this.cornerSize + topSpace);
- this.outline.QuadTo(this.boxMargin, this.boxMargin + topSpace, this.boxMargin + this.cornerSize, this.boxMargin + topSpace);
- if (this.northEdgeArrow.ArrowType != ArrowType.None)
- {
- var arrowCenter = this.northEdgeArrow.Offset > 0f ? this.northEdgeArrow.Offset : (this.Width / 2);
- this.outline.LineTo(arrowCenter - (this.arrowWidth / 2), this.boxMargin + topSpace);
- if (this.northEdgeArrow.ArrowType == ArrowType.Outgoing)
- this.outline.LineTo(arrowCenter, this.boxMargin);
- else
- this.outline.LineTo(arrowCenter, this.arrowHeight + this.boxMargin + topSpace);
- this.outline.LineTo(arrowCenter + (this.arrowWidth / 2), this.boxMargin + topSpace);
- }
- // Top right rounded corner
- this.outline.LineTo(this.Width - (this.boxMargin + this.cornerSize + this.arrowHeight), this.boxMargin + topSpace); // top right corner
- this.outline.QuadTo(this.Width - (this.boxMargin + this.arrowHeight), this.boxMargin + topSpace, this.Width - (this.boxMargin + this.arrowHeight), this.boxMargin + this.cornerSize + topSpace);
- if (this.eastEdgeArrow.ArrowType != ArrowType.None)
- {
- var arrowCenter = this.eastEdgeArrow.Offset > 0f ? this.eastEdgeArrow.Offset : (this.Height / 2);
- this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight), arrowCenter - (this.arrowWidth / 2));
- if (this.eastEdgeArrow.ArrowType == ArrowType.Outgoing)
- this.outline.LineTo(this.Width - this.boxMargin, arrowCenter);
- else
- this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight + this.arrowHeight), arrowCenter);
- this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight), arrowCenter + (this.arrowWidth / 2));
- }
- // Bottom right rounded corner
- this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight), this.Height - (this.boxMargin + bottomSpace + this.cornerSize));
- this.outline.QuadTo(this.Width - (this.boxMargin + this.arrowHeight), this.Height - (this.boxMargin + bottomSpace), this.Width - (this.boxMargin + this.cornerSize + this.arrowHeight), this.Height - (this.boxMargin + bottomSpace));
- if (this.southEdgeArrow.ArrowType != ArrowType.None)
- {
- var arrowCenter = this.southEdgeArrow.Offset > 0f ? this.southEdgeArrow.Offset : (this.Width / 2);
- this.outline.LineTo(arrowCenter + (this.arrowWidth / 2), this.Height - (this.boxMargin + bottomSpace)); // arrow top right corner
- if (this.southEdgeArrow.ArrowType == ArrowType.Outgoing)
- this.outline.LineTo(arrowCenter, this.Height - this.boxMargin); // arrow tip
- else
- this.outline.LineTo(arrowCenter, this.Height - (this.boxMargin + this.arrowHeight + bottomSpace));
- this.outline.LineTo(arrowCenter - (this.arrowWidth / 2), this.Height - (this.boxMargin + bottomSpace)); // arrow top left corner
- }
- // Bottom left rounded corner
- this.outline.LineTo(this.boxMargin + this.cornerSize, this.Height - (this.boxMargin + bottomSpace));
- this.outline.QuadTo(this.boxMargin, this.Height - (this.boxMargin + bottomSpace), this.boxMargin, this.Height - (this.boxMargin + bottomSpace + this.cornerSize));
- this.outline.LineTo(this.boxMargin, this.boxMargin + this.cornerSize + topSpace); // back to start of top left corner curve
- this.outline.Close();
- }
- public override bool OnTouchEvent(MotionEvent e)
- {
- if (this.Clickable)
- {
- var result = this.gestureDetector.OnTouchEvent(e);
- #warning Should indicate as non-pressed if the finger moves outside the bounds
- if (e.Action == MotionEventActions.Up || e.Action == MotionEventActions.Outside || e.Action == MotionEventActions.Cancel)
- this.IndicatePressed(false);
- }
- return base.OnTouchEvent(e);
- }
- private class EdgeArrowType
- {
- public ArrowType ArrowType;
- public float Offset;
- }
- private EdgeArrowType northEdgeArrow = new EdgeArrowType();
- private EdgeArrowType eastEdgeArrow = new EdgeArrowType();
- private EdgeArrowType southEdgeArrow = new EdgeArrowType();
- private EdgeArrowType westEdgeArrow = new EdgeArrowType();
- private bool touchDown;
- private GestureDetector gestureDetector;
- private Path outline;
- private readonly int arrowWidth = 24;
- private readonly int arrowHeight = 12;
- private readonly int boxMargin = 2;
- private readonly int cornerSize = 6;
- private int headerAreaHeight;
- private Paint headerAreaPaint;
- private bool headerIsHorizontal;
- private class BubbleViewGestureListener : GestureDetector.SimpleOnGestureListener
- {
- public BubbleViewGestureListener(Action onShowPress, Action<bool> onTap)
- {
- this.onShowPressAction = onShowPress;
- this.onTapAction = onTap;
- }
- public override bool OnDown(MotionEvent e)
- {
- this.hasShownPress = false;
- return true;
- }
- public override void OnShowPress(MotionEvent e)
- {
- this.hasShownPress = true;
- this.onShowPressAction();
- }
- public override bool OnSingleTapUp(MotionEvent e)
- {
- this.onTapAction(this.hasShownPress);
- return true;
- }
- private bool hasShownPress;
- private Action onShowPressAction;
- private Action<bool> onTapAction;
- }
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement