Advertisement
Guest User

Untitled

a guest
Nov 26th, 2014
161
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 17.91 KB | None | 0 0
  1. namespace NovaCura.Flow.Client.Android.Views.Helpers
  2. {
  3.     using System;
  4.  
  5.     using Cirrious.MvvmCross.Binding.Droid.Views;
  6.  
  7.     using NovaCura.Flow.Client.Android.Helpers;
  8.  
  9.     using global::Android.Content;
  10.     using global::Android.Content.Res;
  11.     using global::Android.Graphics;
  12.     using global::Android.Util;
  13.     using global::Android.Views;
  14.  
  15.     class BubbleLayout : MvxLinearLayout
  16.     {
  17.         private static readonly Color enabledBackgroundColor;
  18.         private static readonly Color disabledBackgroundColor;
  19.         private static readonly Color pressedBackgroundColor;
  20.  
  21.         private static readonly Paint enabledBackgroundPaint;
  22.         private static readonly Paint disabledBackgroundPaint;
  23.         private static readonly Paint pressedBackgroundPaint;
  24.         private static readonly Paint borderPaint;
  25.  
  26.         static BubbleLayout()
  27.         {
  28.             enabledBackgroundColor = Color.White;
  29.             disabledBackgroundColor = Color.ParseColor("#eaeaea");
  30.             pressedBackgroundColor = Color.LightBlue;
  31.  
  32.             enabledBackgroundPaint = new Paint { Color = enabledBackgroundColor };
  33.             enabledBackgroundPaint.SetStyle(Paint.Style.Fill);
  34.             enabledBackgroundPaint.AntiAlias = true;
  35.             enabledBackgroundPaint.SetShadowLayer(4f, 1f, 1f, Color.ParseColor("#44000000"));
  36.  
  37.             disabledBackgroundPaint = new Paint { Color = disabledBackgroundColor };
  38.             disabledBackgroundPaint.SetStyle(Paint.Style.Fill);
  39.             disabledBackgroundPaint.AntiAlias = true;
  40.  
  41.             pressedBackgroundPaint = new Paint { Color = pressedBackgroundColor };
  42.             pressedBackgroundPaint.SetStyle(Paint.Style.Fill);
  43.             pressedBackgroundPaint.AntiAlias = true;
  44.             pressedBackgroundPaint.SetShadowLayer(4f, 1f, 1f, Color.ParseColor("#44000000"));
  45.  
  46.             borderPaint = new Paint { Color = Color.ParseColor("#bbbaba"), StrokeWidth = 1.5f, AntiAlias = true };
  47.             borderPaint.SetStyle(Paint.Style.Stroke);
  48.         }
  49.        
  50.         public BubbleLayout(Context context, IAttributeSet attrs, IMvxAdapterWithChangedEvent adapter)
  51.             : base(context, attrs, adapter ?? new MvxAdapterWithChangedEvent(context))
  52.         {
  53.             // Make the view clickable and set up the tap gesture listener
  54.            
  55.             var recognizer = new BubbleViewGestureListener(
  56.                 onShowPress: () => this.IndicatePressed(true),
  57.                 onTap: (didShowPress) =>
  58.                 {
  59.                     if (didShowPress)
  60.                         this.IndicatePressed(false);
  61.                     else
  62.                         this.FlashAsIfPressed();
  63.                 });
  64.  
  65.             this.gestureDetector = new GestureDetector(context, recognizer);
  66.  
  67.             this.SetPadding();
  68.  
  69.             TypedArray a = context.ObtainStyledAttributes(attrs, Resource.Styleable.BubbleLayout);
  70.  
  71.             this.arrowDirection = (ArrowDirection)a.GetInt(Resource.Styleable.BubbleLayout_arrowDirection, 0);
  72.             this.arrowType = (ArrowType)a.GetInt(Resource.Styleable.BubbleLayout_arrowType, 0);
  73.  
  74.             this.UpdateArrow();
  75.         }
  76.        
  77.         public BubbleLayout(Context context, IAttributeSet attrs)
  78.             : this(context, attrs, null)
  79.         {
  80.         }
  81.  
  82.         protected override void OnFinishInflate()
  83.         {
  84.             this.Orientation = global::Android.Widget.Orientation.Vertical;
  85.             this.SetWillNotDraw(false);
  86.             this.SetMinimumHeight(this.DpToPx(30));
  87.  
  88.             base.OnFinishInflate();
  89.         }
  90.  
  91.         private ArrowDirection arrowDirection = ArrowDirection.Down;
  92.         public ArrowDirection ADirection
  93.         {
  94.             get
  95.             {
  96.                 return this.arrowDirection;
  97.             }
  98.             set
  99.             {
  100.                 this.arrowDirection = value;
  101.                 this.UpdateArrow();
  102.             }
  103.         }
  104.  
  105.         private float arrowOffset = 0.0f;
  106.         public float AOffset
  107.         {
  108.             get
  109.             {
  110.                 return this.arrowOffset;
  111.             }
  112.             set
  113.             {
  114.                 this.arrowOffset = value;
  115.                 this.UpdateArrow();
  116.             }
  117.         }
  118.  
  119.         private ArrowType arrowType = ArrowType.None;
  120.         public ArrowType AType
  121.         {
  122.             get
  123.             {
  124.                 return this.arrowType;
  125.             }
  126.             set
  127.             {
  128.                 this.arrowType = value;
  129.                 this.UpdateArrow();
  130.             }
  131.         }
  132.  
  133.         public enum ArrowDirection
  134.         {
  135.             Left,
  136.             Right,
  137.             Up,
  138.             Down
  139.         }
  140.        
  141.         public enum ArrowType
  142.         {
  143.             None,
  144.             Incoming,
  145.             Outgoing
  146.         }
  147.  
  148.         private void UpdateArrow()
  149.         {
  150.             if (this.arrowDirection == ArrowDirection.Left)
  151.             {
  152.                 this.SetWestArrow(this.arrowType, this.arrowOffset);
  153.             }
  154.             else if (this.arrowDirection == ArrowDirection.Up)
  155.             {
  156.                 this.SetNorthArrow(this.arrowType, this.arrowOffset);
  157.             }
  158.             else if (this.arrowDirection == ArrowDirection.Right)
  159.             {
  160.                 this.SetEastArrow(this.arrowType, this.arrowOffset);
  161.             }
  162.             else if (this.arrowDirection == ArrowDirection.Down)
  163.             {
  164.                 this.SetSouthArrow(this.arrowType, this.arrowOffset);
  165.             }
  166.         }
  167.  
  168.         public void SetNorthArrow(ArrowType arrowType, float offset)
  169.         {
  170.             this.northEdgeArrow.ArrowType = arrowType;
  171.             this.northEdgeArrow.Offset = offset;
  172.             this.SetPadding();
  173.             this.InvalidateOutline();
  174.             this.Invalidate();
  175.  
  176.             this.arrowDirection = ArrowDirection.Up;
  177.             this.arrowType = arrowType;
  178.             this.arrowOffset = offset;
  179.         }
  180.  
  181.         public void SetEastArrow(ArrowType arrowType, float offset)
  182.         {
  183.             this.eastEdgeArrow.ArrowType = arrowType;
  184.             this.eastEdgeArrow.Offset = offset;
  185.             this.SetPadding();
  186.             this.InvalidateOutline();
  187.             this.Invalidate();
  188.  
  189.             this.arrowDirection = ArrowDirection.Right;
  190.             this.arrowType = arrowType;
  191.             this.arrowOffset = offset;
  192.         }
  193.  
  194.         public void SetSouthArrow(ArrowType arrowType, float offset)
  195.         {
  196.             this.southEdgeArrow.ArrowType = arrowType;
  197.             this.southEdgeArrow.Offset = offset;
  198.             this.SetPadding();
  199.             this.InvalidateOutline();
  200.             this.Invalidate();
  201.  
  202.             this.arrowDirection = ArrowDirection.Down;
  203.             this.arrowType = arrowType;
  204.             this.arrowOffset = offset;
  205.         }
  206.  
  207.         public void SetWestArrow(ArrowType arrowType, float offset)
  208.         {
  209.             this.westEdgeArrow.ArrowType = arrowType;
  210.             this.westEdgeArrow.Offset = offset;
  211.             this.SetPadding();
  212.             this.InvalidateOutline();
  213.             this.Invalidate();
  214.  
  215.             this.arrowDirection = ArrowDirection.Left;
  216.             this.arrowType = arrowType;
  217.             this.arrowOffset = offset;
  218.         }
  219.  
  220.         public event EventHandler SizeChanged;
  221.  
  222.         protected override void OnSizeChanged(int w, int h, int oldw, int oldh)
  223.         {
  224.             base.OnSizeChanged(w, h, oldw, oldh);
  225.  
  226.             if (this.SizeChanged != null)
  227.                 this.SizeChanged(this, null);
  228.  
  229.             this.InvalidateOutline();
  230.         }
  231.  
  232.         private void FlashAsIfPressed()
  233.         {
  234.             // TODO: figure out how to flash like a push button
  235.             // How is this actually done?
  236.  
  237.             //this.IndicatePressed(true);
  238.             //Thread.Sleep(500);
  239.             //this.IndicatePressed(false);
  240.         }
  241.  
  242.         /*public override bool DispatchTouchEvent(MotionEvent e)
  243.         {
  244.             // Stop disabled views from swallowing touch events
  245.            
  246.             if (this.Enabled)
  247.                 return base.DispatchTouchEvent(e);
  248.            
  249.         }*/
  250.  
  251.         private void IndicatePressed(bool pressed)
  252.         {
  253.             this.touchDown = pressed;
  254.             this.Invalidate();
  255.         }
  256.  
  257.         public void SetHeaderArea(Paint paint, int height, bool isHorizontal = false)
  258.         {
  259.             this.headerAreaPaint = paint;
  260.             this.headerAreaHeight = height;
  261.             this.headerIsHorizontal = isHorizontal;
  262.         }
  263.  
  264.         protected virtual Paint GetBackgroundPaint(bool enabled)
  265.         {
  266.             if (enabled)
  267.                 return enabledBackgroundPaint;
  268.             else
  269.                 return disabledBackgroundPaint;
  270.         }
  271.  
  272.         protected override void OnDraw(Canvas canvas)
  273.         {
  274.             if (this.outline == null)
  275.                 this.CreateOutline();
  276.  
  277.             var backgroundPaint = this.Pressed || this.touchDown ? pressedBackgroundPaint : this.GetBackgroundPaint(this.Enabled);
  278.  
  279.             canvas.DrawPath(this.outline, backgroundPaint);
  280.  
  281.             if (this.headerAreaHeight > 0)
  282.             {
  283.                 canvas.Save();
  284.                 canvas.ClipPath(this.outline);
  285.  
  286.                 this.headerAreaPaint.SetStyle(Paint.Style.Fill);
  287.  
  288.                 if (this.headerIsHorizontal)
  289.                     canvas.DrawRect(new Rect(0, 0, this.headerAreaHeight, this.Height), this.headerAreaPaint);
  290.                 else
  291.                     canvas.DrawRect(new Rect(0, 0, this.Width, this.headerAreaHeight), this.headerAreaPaint);
  292.                 canvas.Restore();
  293.             }
  294.  
  295.             canvas.DrawPath(this.outline, borderPaint);
  296.  
  297.             this.InvalidateOutline();
  298.         }
  299.  
  300.         protected override void OnFocusChanged(bool gainFocus, FocusSearchDirection direction, Rect previouslyFocusedRect)
  301.         {
  302.             base.OnFocusChanged(gainFocus, direction, previouslyFocusedRect);
  303.         }
  304.  
  305.         private void InvalidateOutline()
  306.         {
  307.             if (this.outline != null)
  308.             {
  309.                 this.outline.Dispose();
  310.                 this.outline = null;
  311.             }
  312.         }
  313.  
  314.         private void SetPadding()
  315.         {
  316.             // Initial padding values
  317.             var paddingLeft = 8 + this.boxMargin;
  318.             var paddingTop = 8 + this.boxMargin;
  319.             var paddingRight = 8 + this.boxMargin + this.arrowHeight;
  320.             var paddingBottom = 8 + this.boxMargin;
  321.  
  322.             // Initialize extra space (for arrow)
  323.             var topSpace = 0;
  324.             var bottomSpace = 0;
  325.  
  326.             // If there are outgoing arrows, add some extra space for them
  327.             if (this.northEdgeArrow.ArrowType == ArrowType.Outgoing)
  328.                 topSpace = this.arrowHeight;
  329.  
  330.             if (this.southEdgeArrow.ArrowType == ArrowType.Outgoing)
  331.                 bottomSpace += this.arrowHeight;
  332.  
  333.             this.SetPadding(paddingLeft, paddingTop + topSpace, paddingRight, paddingBottom + bottomSpace); // TODO: Convert to device independent pixels
  334.         }
  335.  
  336.         protected override void Dispose(bool disposing)
  337.         {
  338.             if (this.outline != null)
  339.             {
  340.                 this.outline.Dispose();
  341.                 this.outline = null;
  342.             }
  343.  
  344.             base.Dispose(disposing);
  345.         }
  346.  
  347.         private void CreateOutline()
  348.         {
  349.             // Initialize extra space (for arrow)
  350.             var topSpace = 0;
  351.             var bottomSpace = 0;
  352.  
  353.             // If there are outgoing arrows, add some extra space for them
  354.  
  355.             if (this.northEdgeArrow.ArrowType == ArrowType.Outgoing)
  356.                 topSpace = this.arrowHeight;
  357.  
  358.             if (this.southEdgeArrow.ArrowType == ArrowType.Outgoing)
  359.                 bottomSpace += this.arrowHeight;
  360.  
  361.             this.outline = new Path();
  362.  
  363.             // Top left rounded corner
  364.             this.outline.MoveTo(this.boxMargin, this.boxMargin + this.cornerSize + topSpace);
  365.             this.outline.QuadTo(this.boxMargin, this.boxMargin + topSpace, this.boxMargin + this.cornerSize, this.boxMargin + topSpace);
  366.  
  367.             if (this.northEdgeArrow.ArrowType != ArrowType.None)
  368.             {
  369.                 var arrowCenter = this.northEdgeArrow.Offset > 0f ? this.northEdgeArrow.Offset : (this.Width / 2);
  370.                 this.outline.LineTo(arrowCenter - (this.arrowWidth / 2), this.boxMargin + topSpace);
  371.  
  372.                 if (this.northEdgeArrow.ArrowType == ArrowType.Outgoing)
  373.                     this.outline.LineTo(arrowCenter, this.boxMargin);
  374.                 else
  375.                     this.outline.LineTo(arrowCenter, this.arrowHeight + this.boxMargin + topSpace);
  376.  
  377.                 this.outline.LineTo(arrowCenter + (this.arrowWidth / 2), this.boxMargin + topSpace);
  378.             }
  379.  
  380.             // Top right rounded corner
  381.             this.outline.LineTo(this.Width - (this.boxMargin + this.cornerSize + this.arrowHeight), this.boxMargin + topSpace); // top right corner
  382.             this.outline.QuadTo(this.Width - (this.boxMargin + this.arrowHeight), this.boxMargin + topSpace, this.Width - (this.boxMargin + this.arrowHeight), this.boxMargin + this.cornerSize + topSpace);
  383.  
  384.             if (this.eastEdgeArrow.ArrowType != ArrowType.None)
  385.             {
  386.                 var arrowCenter = this.eastEdgeArrow.Offset > 0f ? this.eastEdgeArrow.Offset : (this.Height / 2);
  387.                 this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight), arrowCenter - (this.arrowWidth / 2));
  388.  
  389.                 if (this.eastEdgeArrow.ArrowType == ArrowType.Outgoing)
  390.                     this.outline.LineTo(this.Width - this.boxMargin, arrowCenter);
  391.                 else
  392.                     this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight + this.arrowHeight), arrowCenter);
  393.  
  394.                 this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight), arrowCenter + (this.arrowWidth / 2));
  395.             }
  396.  
  397.             // Bottom right rounded corner
  398.             this.outline.LineTo(this.Width - (this.boxMargin + this.arrowHeight), this.Height - (this.boxMargin + bottomSpace + this.cornerSize));
  399.             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));
  400.  
  401.             if (this.southEdgeArrow.ArrowType != ArrowType.None)
  402.             {
  403.                 var arrowCenter = this.southEdgeArrow.Offset > 0f ? this.southEdgeArrow.Offset : (this.Width / 2);
  404.                 this.outline.LineTo(arrowCenter + (this.arrowWidth / 2), this.Height - (this.boxMargin + bottomSpace)); // arrow top right corner
  405.  
  406.                 if (this.southEdgeArrow.ArrowType == ArrowType.Outgoing)
  407.                     this.outline.LineTo(arrowCenter, this.Height - this.boxMargin); // arrow tip
  408.                 else
  409.                     this.outline.LineTo(arrowCenter, this.Height - (this.boxMargin + this.arrowHeight + bottomSpace));
  410.  
  411.                 this.outline.LineTo(arrowCenter - (this.arrowWidth / 2), this.Height - (this.boxMargin + bottomSpace)); // arrow top left corner
  412.             }
  413.  
  414.             // Bottom left rounded corner
  415.             this.outline.LineTo(this.boxMargin + this.cornerSize, this.Height - (this.boxMargin + bottomSpace));
  416.             this.outline.QuadTo(this.boxMargin, this.Height - (this.boxMargin + bottomSpace), this.boxMargin, this.Height - (this.boxMargin + bottomSpace + this.cornerSize));
  417.             this.outline.LineTo(this.boxMargin, this.boxMargin + this.cornerSize + topSpace); // back to start of top left corner curve
  418.             this.outline.Close();
  419.  
  420.         }
  421.  
  422.         public override bool OnTouchEvent(MotionEvent e)
  423.         {
  424.             if (this.Clickable)
  425.             {
  426.                 var result = this.gestureDetector.OnTouchEvent(e);
  427.  
  428. #warning Should indicate as non-pressed if the finger moves outside the bounds
  429.                 if (e.Action == MotionEventActions.Up || e.Action == MotionEventActions.Outside || e.Action == MotionEventActions.Cancel)
  430.                     this.IndicatePressed(false);
  431.             }
  432.             return base.OnTouchEvent(e);
  433.         }
  434.  
  435.         private class EdgeArrowType
  436.         {
  437.             public ArrowType ArrowType;
  438.             public float Offset;
  439.         }
  440.  
  441.         private EdgeArrowType northEdgeArrow = new EdgeArrowType();
  442.         private EdgeArrowType eastEdgeArrow = new EdgeArrowType();
  443.         private EdgeArrowType southEdgeArrow = new EdgeArrowType();
  444.         private EdgeArrowType westEdgeArrow = new EdgeArrowType();
  445.  
  446.         private bool touchDown;
  447.         private GestureDetector gestureDetector;
  448.  
  449.         private Path outline;
  450.         private readonly int arrowWidth = 24;
  451.         private readonly int arrowHeight = 12;
  452.         private readonly int boxMargin = 2;
  453.         private readonly int cornerSize = 6;
  454.  
  455.         private int headerAreaHeight;
  456.         private Paint headerAreaPaint;
  457.         private bool headerIsHorizontal;
  458.  
  459.         private class BubbleViewGestureListener : GestureDetector.SimpleOnGestureListener
  460.         {
  461.             public BubbleViewGestureListener(Action onShowPress, Action<bool> onTap)
  462.             {
  463.                 this.onShowPressAction = onShowPress;
  464.                 this.onTapAction = onTap;
  465.             }
  466.  
  467.             public override bool OnDown(MotionEvent e)
  468.             {
  469.                 this.hasShownPress = false;
  470.                 return true;
  471.             }
  472.  
  473.             public override void OnShowPress(MotionEvent e)
  474.             {
  475.                 this.hasShownPress = true;
  476.                 this.onShowPressAction();
  477.             }
  478.  
  479.             public override bool OnSingleTapUp(MotionEvent e)
  480.             {
  481.                 this.onTapAction(this.hasShownPress);
  482.                 return true;
  483.             }
  484.  
  485.             private bool hasShownPress;
  486.             private Action onShowPressAction;
  487.             private Action<bool> onTapAction;
  488.         }
  489.  
  490.     }
  491. }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement