oxyplot-xamarin/Source/OxyPlot.Xamarin.Android/PlotView.cs

404 строки
12 KiB
C#
Исходник Ответственный История

Этот файл содержит неоднозначные символы Юникода!

Этот файл содержит неоднозначные символы Юникода, которые могут быть перепутаны с другими в текущей локали. Если это намеренно, можете спокойно проигнорировать это предупреждение. Используйте кнопку Экранировать, чтобы подсветить эти символы.

// --------------------------------------------------------------------------------------------------------------------
// <copyright file="PlotView.cs" company="OxyPlot">
// Copyright (c) 2014 OxyPlot contributors
// </copyright>
// <summary>
// Represents a view that can show a <see cref="PlotModel" />.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace OxyPlot.Xamarin.Android
{
using System;
using global::Android.Content;
using global::Android.Graphics;
using global::Android.Util;
using global::Android.Views;
using OxyPlot;
/// <summary>
/// Represents a view that can show a <see cref="PlotModel" />.
/// </summary>
public class PlotView : View, IPlotView
{
/// <summary>
/// The factor that scales from OxyPlot´s device independent pixels (96 dpi) to
/// Android´s density-independent pixels (160 dpi).
/// </summary>
/// <remarks>See <a href="http://developer.android.com/guide/practices/screens_support.html">Supporting multiple screens.</a>.</remarks>
public const double Scale = 160d / 96d;
/// <summary>
/// The rendering lock object.
/// </summary>
private readonly object renderingLock = new object();
/// <summary>
/// The invalidation lock object.
/// </summary>
private readonly object invalidateLock = new object();
/// <summary>
/// The touch points of the previous touch event.
/// </summary>
private ScreenPoint[] previousTouchPoints;
/// <summary>
/// The current model.
/// </summary>
private PlotModel model;
/// <summary>
/// The default controller
/// </summary>
private IPlotController defaultController;
/// <summary>
/// The current render context.
/// </summary>
private CanvasRenderContext rc;
/// <summary>
/// The model invalidated flag.
/// </summary>
private bool isModelInvalidated;
/// <summary>
/// The update data flag.
/// </summary>
private bool updateDataFlag = true;
/// <summary>
/// Initializes a new instance of the <see cref="PlotView" /> class.
/// </summary>
/// <param name="context">The context.</param>
/// <remarks>Use this constructor when creating the view from code.</remarks>
public PlotView(Context context) :
base(context)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PlotView" /> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="attrs">The attribute set.</param>
/// <remarks>This constructor is called when inflating the view from XML.</remarks>
public PlotView(Context context, IAttributeSet attrs) :
base(context, attrs)
{
}
/// <summary>
/// Initializes a new instance of the <see cref="PlotView" /> class.
/// </summary>
/// <param name="context">The context.</param>
/// <param name="attrs">The attribute set.</param>
/// <param name="defStyle">The definition style.</param>
/// <remarks>This constructor performs inflation from XML and applies a class-specific base style.</remarks>
public PlotView(Context context, IAttributeSet attrs, int defStyle) :
base(context, attrs, defStyle)
{
}
/// <summary>
/// Gets or sets the plot model.
/// </summary>
/// <value>The model.</value>
public PlotModel Model
{
get
{
return this.model;
}
set
{
if (this.model != value)
{
if (this.model != null)
{
((IPlotModel)this.model).AttachPlotView(null);
this.model = null;
}
if (value != null)
{
if (value.PlotView != null)
{
throw new InvalidOperationException("This PlotModel is already in use by some other PlotView control.");
}
((IPlotModel)value).AttachPlotView(this);
this.model = value;
}
this.InvalidatePlot();
}
}
}
/// <summary>
/// Gets or sets the plot controller.
/// </summary>
/// <value>The controller.</value>
public IPlotController Controller { get; set; }
/// <summary>
/// Gets the actual model in the view.
/// </summary>
/// <value>
/// The actual model.
/// </value>
Model IView.ActualModel
{
get
{
return this.Model;
}
}
/// <summary>
/// Gets the actual <see cref="PlotModel" /> of the control.
/// </summary>
public PlotModel ActualModel
{
get
{
return this.Model;
}
}
/// <summary>
/// Gets the actual controller.
/// </summary>
/// <value>
/// The actual <see cref="IController" />.
/// </value>
IController IView.ActualController
{
get
{
return this.ActualController;
}
}
/// <summary>
/// Gets the coordinates of the client area of the view.
/// </summary>
public OxyRect ClientArea
{
get
{
return new OxyRect(0, 0, this.Width, this.Height);
}
}
/// <summary>
/// Gets the actual <see cref="IPlotController" /> of the control.
/// </summary>
/// <value>The actual plot controller.</value>
public IPlotController ActualController
{
get
{
return this.Controller ?? (this.defaultController ?? (this.defaultController = new PlotController()));
}
}
/// <summary>
/// Hides the tracker.
/// </summary>
public void HideTracker()
{
}
/// <summary>
/// Hides the zoom rectangle.
/// </summary>
public void HideZoomRectangle()
{
}
/// <summary>
/// Invalidates the plot (not blocking the UI thread)
/// </summary>
/// <param name="updateData">if set to <c>true</c>, all data bindings will be updated.</param>
public void InvalidatePlot(bool updateData = true)
{
lock (this.invalidateLock)
{
this.isModelInvalidated = true;
this.updateDataFlag = this.updateDataFlag || updateData;
}
this.Invalidate();
}
/// <summary>
/// Sets the cursor type.
/// </summary>
/// <param name="cursorType">The cursor type.</param>
public void SetCursorType(CursorType cursorType)
{
}
/// <summary>
/// Shows the tracker.
/// </summary>
/// <param name="trackerHitResult">The tracker data.</param>
public void ShowTracker(TrackerHitResult trackerHitResult)
{
}
/// <summary>
/// Shows the zoom rectangle.
/// </summary>
/// <param name="rectangle">The rectangle.</param>
public void ShowZoomRectangle(OxyRect rectangle)
{
}
/// <summary>
/// Stores text on the clipboard.
/// </summary>
/// <param name="text">The text.</param>
public void SetClipboardText(string text)
{
}
/// <summary>
/// Handles key down events.
/// </summary>
/// <param name="keyCode">The key code.</param>
/// <param name="e">The event arguments.</param>
/// <returns><c>true</c> if the event was handled.</returns>
public override bool OnKeyDown(Keycode keyCode, KeyEvent e)
{
var handled = base.OnKeyDown(keyCode, e);
if (!handled)
{
handled = this.ActualController.HandleKeyDown(this, e.ToKeyEventArgs());
}
return handled;
}
/// <summary>
/// Handles touch screen motion events.
/// </summary>
/// <param name="e">The motion event arguments.</param>
/// <returns><c>true</c> if the event was handled.</returns>
public override bool OnTouchEvent(MotionEvent e)
{
var handled = base.OnTouchEvent(e);
if (!handled)
{
switch (e.Action)
{
case MotionEventActions.Down:
handled = this.OnTouchDownEvent(e);
break;
case MotionEventActions.Move:
handled = this.OnTouchMoveEvent(e);
break;
case MotionEventActions.Up:
handled = this.OnTouchUpEvent(e);
break;
}
}
return handled;
}
/// <summary>
/// Draws the content of the control.
/// </summary>
/// <param name="canvas">The canvas to draw on.</param>
protected override void OnDraw(Canvas canvas)
{
base.OnDraw(canvas);
var actualModel = this.ActualModel;
if (actualModel == null)
{
return;
}
if (!actualModel.Background.IsUndefined())
{
canvas.DrawColor(actualModel.Background.ToColor());
}
else
{
// Use white as default background color
canvas.DrawColor(Color.White);
}
lock (this.invalidateLock)
{
if (this.isModelInvalidated)
{
((IPlotModel)actualModel).Update(this.updateDataFlag);
this.updateDataFlag = false;
this.isModelInvalidated = false;
}
}
lock (this.renderingLock)
{
if (this.rc == null)
{
this.rc = new CanvasRenderContext(Scale);
}
this.rc.SetTarget(canvas);
using (var bounds = new Rect())
{
canvas.GetClipBounds(bounds);
var width = bounds.Right - bounds.Left;
var height = bounds.Bottom - bounds.Top;
((IPlotModel)actualModel).Render(this.rc, width / Scale, height / Scale);
}
}
}
/// <summary>
/// Handles touch down events.
/// </summary>
/// <param name="e">The motion event arguments.</param>
/// <returns><c>true</c> if the event was handled.</returns>
private bool OnTouchDownEvent(MotionEvent e)
{
var args = e.ToTouchEventArgs(Scale);
var handled = this.ActualController.HandleTouchStarted(this, args);
this.previousTouchPoints = e.GetTouchPoints(Scale);
return handled;
}
/// <summary>
/// Handles touch move events.
/// </summary>
/// <param name="e">The motion event arguments.</param>
/// <returns><c>true</c> if the event was handled.</returns>
private bool OnTouchMoveEvent(MotionEvent e)
{
var currentTouchPoints = e.GetTouchPoints(Scale);
var args = new OxyTouchEventArgs(currentTouchPoints, this.previousTouchPoints);
var handled = this.ActualController.HandleTouchDelta(this, args);
this.previousTouchPoints = currentTouchPoints;
return handled;
}
/// <summary>
/// Handles touch released events.
/// </summary>
/// <param name="e">The motion event arguments.</param>
/// <returns><c>true</c> if the event was handled.</returns>
private bool OnTouchUpEvent(MotionEvent e)
{
return this.ActualController.HandleTouchCompleted(this, e.ToTouchEventArgs(Scale));
}
}
}