CustomView
PUBLISHED
You can create custom views with NUI, following some general guidelines:
- Derive your view from the
Tizen.NUI.BaseComponents.CustomView
class, which provides common functionality required by all views. - Use properties as much as possible, as views must be data-driven.
Custom views are used through JavaScript and JSON files.
- The view can be updated when the properties (such as styles) change.
Ensure that the view handles property changes gracefully, on both the first and subsequent changes.
- Use visuals, instead of creating multiple child views, to make the rendering pipeline more efficient.
- Use events to make the application react to view state changes.
- Use of gestures instead of analyzing raw touch events.
The Tizen.NUI.BaseComponents.CustomView
class is derived from the Tizen.NUI.ViewWrapper [1] class, which in turn is derived from the Tizen.NUI.BaseComponents.View [2] class:
public class CustomView : ViewWrapper public class ViewWrapper : View
NUI contains predefined custom controls already derived from CustomView
objects, including:
- Tizen.NUI.Spin [3] control, which is used for continuously changing values when the user can easily predict a set of values.
- Tizen.NUI.BaseComponents.VisualView [4] control, which enables you to add any visual. For more information, see Visuals [5].
Creating a Custom View
To create a custom view:
- Create a view with the
new
operator:contactView = new ContactView();
- Define a static constructor for the view.
Each custom view must have its static constructor called before any JSON file is loaded. Static constructors for a class are only run once (they are run per view, not per instance). The view must register its type inside the static constructor.
The Type Registry is used to register your custom view. Type registration allows the creation of the view through a JSON file, as well as registering properties, signals, actions, transitions, and animation effects. Use the
Register()
method of the Tizen.NUI.CustomViewRegistry [6] class to register the views and any scriptable properties they have:static ContactView() { CustomViewRegistry.Instance.Register(CreateInstance, typeof(ContactView)); }
- Define a
CreateInstance()
method for the custom view.Each custom view must provide a
CreateInstance()
method, which is passed to theRegister()
method as a parameter. TheCreateInstance()
method is called if the view is in a JSON file:static CustomView CreateInstance() { return new ContactView(); }
- Override the
OnInitialize()
method, if necessary. It is called after the view has been initialized.
public override void OnInitialize() { /// Create a container for the star images _container = new FlexContainer(); _container.FlexDirection = FlexContainer.FlexDirectionType.Row; _container.WidthResizePolicy = ResizePolicyType.FillToParent; _container.HeightResizePolicy = ResizePolicyType.FillToParent; this.Add(_container); }
The following table lists other important custom view methods that you can use to manage the view.
Table: Custom view methods
Name Description SetBackground()
Set the background with a property map. EnableGestureDetection()
Allow deriving classes to enable any of the gesture detectors that are available. RegisterVisual()
Register a visual by a property index, linking a view to a visual, when required. CreateTransition()
Create a transition effect on the view for animations. RelayoutRequest()
Request a re-layout, which means performing a size negotiation on the view, its parent, and children (and potentially whole scene). OnStageConnection()
If a notification is required when a custom view is connected to a stage default window, override the OnStageConnection()
method. You can use theOnStageDisconnection()
method similarly to react to a view getting disconnected from the window.
You can manage the general behavior of your custom view by defining a value for the CustomViewBehaviour
enumeration of the Tizen.NUI.BaseComponents.CustomView [7] class during object construction. You can determine how the custom view reacts to size negotiation, style changes, event callbacks, and keyboard navigation:
public VisualView() : base(typeof(VisualView).Name, CustomViewBehaviour.ViewBehaviourDefault) { } public ContactView() : base(typeof(ContactView).Name, CustomViewBehaviour.RequiresKeyboardNavigationSupport) { }
Rendering Content
To render content, use or reuse visuals [5]. You can also render content by creating and adding more views to the control itself as its children. However, this solution is not fully optimized and means extra views are added, requiring additional processing.
The following example shows how you can create and register an image visual:
- Define a scriptable property (in this case,
ImageURL
) which creates the visual when being set. A scriptable property automatically generates indices. - Create the visual with a property map. For more information on the property maps that can be used for each visual type, see Visuals [5].
- The
RegisterVisual()
method of the Tizen.NUI.BaseComponents.CustomView [7] class registers a visual by a property index, linking a view to a visual when required. - The
GetPropertyIndex()
method of the Tizen.NUI.Animatable [8] class gets the generated index corresponding to the property name.A range of property indices are provided for
ImageVisualPropertyIndex
, 0 by default.
/// ContactView.cs private VisualBase _imageVisual; [ScriptableProperty()] public string ImageURL { get { return _imageURL; } set { _imageURL = value; /// Create and register an image visual PropertyMap imageVisual = new PropertyMap(); imageVisual.Add(Visual.Property.Type, new PropertyValue((int)Visual.Type.Image)) .Add(ImageVisualProperty.URL, new PropertyValue(_imageURL)) .Add(ImageVisualProperty.AlphaMaskURL, new PropertyValue(_maskURL)); _imageVisual = VisualFactory.Get().CreateVisual(imageVisual); RegisterVisual(GetPropertyIndex("ImageURL"), _imageVisual); /// Set the depth index for the image visual _imageVisual.DepthIndex = ImageVisualPropertyIndex; } }
Managing Properties
Properties can be animatable or non-animatable. Examples of animatable Tizen.NUI.BaseComponents.View [2] class properties are Position
, Orientation
, and Scale
. For more information on the NUI animation framework, see Animation [9].
Properties can be accessed through a unique index. The index can be set manually in code (hard-coded), or calculated automatically. The ContactView.cs
file example (in Rendering Content) shows both indexing methods: fixed for depth index, and automatic for registering visuals. The NUI code base is currently being modified (as of July 2017) to utilize property registration based solely on automatic generation of indices.
Property indices are generated automatically in the Tizen.NUI.ScriptableProperty [10] class. With it, you can register a property with the Type Registry. To obtain a unique index for each property, use the GetPropertyIndex()
method of the Tizen.NUI.Animatable [8] class, with the name of the property as a parameter.
Add ScriptableProperty
to any view-related property that you want to script from JSON:
internal class ScriptableProperty : System.Attribute
Styling Custom Views
The NUI property system allows custom views to be easily styled, changing the look and feel of the view without any code changes. The styling is implemented with JSON stylesheets.
The following table shows an example of a customized style.
Table: Normal and customized style
Normal style | Customized style |
---|---|
The format of the JSON stylesheets is under development:
- The following example (including a visual) shows the current (as of July 2017) JSON stylesheet format:
"styles": { "TextField": { "pointSize": 18, "primaryCursorColor": [0.0,0.72,0.9,1.0], "secondaryCursorColor": [0.0,0.72,0.9,1.0], "cursorWidth": 3, "selectionHighlightColor": [0.75,0.96,1.0,1.0], "grabHandleImage": "{DALI_STYLE_IMAGE_DIR}cursor_handler_drop_center.png", "selectionHandleImageLeft": {"filename": "{DALI_STYLE_IMAGE_DIR}selection_handle_drop_left.png"}, "selectionHandleImageRight": {"filename": "{DALI_STYLE_IMAGE_DIR}selection_handle_drop_right.png"}, "enableSelection": true }, "TextFieldFontSize0": { "pointSize": 10 }, "TextSelectionPopup": { "popupMaxSize": [656,72], "optionDividerSize": [2,0], "popupDividerColor": [0.23,0.72,0.8,0.11], "popupIconColor": [1.0,1.0,1.0,1.0], "popupPressedColor": [0.24,0.72,0.8,0.11], "background": { "visualType": "IMAGE", "url": "{DALI_IMAGE_DIR}selection-popup-background.9.png" }, "backgroundBorder": { "visualType": "IMAGE", "url": "{DALI_IMAGE_DIR}selection-popup-border.9.png", "mixColor": [0.24,0.72,0.8,1.0] }, "popupFadeInDuration": 0.25, "popupFadeOutDuration": 0.25 } }
- The following example (including a visual) shows the new stylesheet format being developed:
"states": { "NORMAL": { "states": { "UNSELECTED": { "visuals": { "backgroundVisual": { "visualType": "IMAGE", "url": "backgroundUnSelected.png" } } }, "SELECTED": { "visuals": { "backgroundVisual": { "visualType": "IMAGE", "url": "backgroundSelected.png" } } } } } }
For more information on building up visuals for various button states using JSON stylesheets and transitioning between the various button states, see Styling Controls with JSON [11].
Creating Transitions
Controls change states based on user interaction. All controls can move between the NORMAL
, FOCUSED
, and DISABLED
states. Whilst in those states, a button has the SELECTED
and UNSELECTED
substates. As a control moves between states and substates, transition animations can be used to show visually how the control state changes.
You can create a specific entry and exit animation for each state and substate, or a more common transition animation that is run when a control moves between specific states. You can also use a predefined effect during the transition. Currently, only a CROSSFADE
effect is available, animating the opacity of visuals fading in and out.
You can implement transition effects in 2 ways:
- Using a JSON stylesheet [11]
The following example uses the
CROSSFADE
effect:"transitions": [ { "from": "UNSELECTED", "to": "SELECTED", "visualName": "*", "effect": "CROSSFADE", "animator": { "alphaFunction": "EASE_OUT", "duration": 0.2, "delay": 0 } } ]
- Using the
CreateTransition()
method of the Tizen.NUI.BaseComponents.CustomView [7] classYou can animate scriptable properties by using the
CreateTransition()
method fromTizen.NUI.BaseComponents.CustomView
-derived classes. The method creates a transition effect on the view. ThetransitionData
parameter describes the effect to create, and the return value is a handle to an animation defined with the given effect, or an empty handle if no properties match.protected Animation CreateTransition(TransitionData transitionData);
The following example is taken from the
AnimateVisual()
method in the Tizen.NUI.BaseComponents.VisualView [4] class, which is aCustomView
-derived class:_alphaFunction = "EASE_IN_OUT_SINE"; PropertyMap _animator = new PropertyMap(); if (_alphaFunction != null) { _animator.Add("alphaFunction", new PropertyValue(_alphaFunction)); } PropertyMap _timePeriod = new PropertyMap(); _timePeriod.Add("duration", new PropertyValue((endTime - startTime) / 1000.0f)); _timePeriod.Add("delay", new PropertyValue(startTime / 1000.0f)); _animator.Add("timePeriod", new PropertyValue(_timePeriod)); string _str1 = property.Substring(0, 1); string _str2 = property.Substring(1); string _str = _str1.ToLower() + _str2; if (_str == "position") { _str = "offset"; } PropertyValue destVal = PropertyValue.CreateFromObject(destinationValue); PropertyMap _transition = new PropertyMap(); _transition.Add("target", new PropertyValue(target.Name)); _transition.Add("property", new PropertyValue(_str)); if (initialValue != null) { PropertyValue initVal = PropertyValue.CreateFromObject(initialValue); _transition.Add("initialValue", new PropertyValue(initVal)); } _transition.Add("targetValue", destVal); _transition.Add("animator", new PropertyValue(_animator)); TransitionData _transitionData = new TransitionData(_transition); return this.CreateTransition(_transitionData);
Handling Events and Gestures
You can monitor the following Tizen.NUI.BaseComponents.View [2] class events for your custom view:
TouchEvent
is triggered when any touch occurs within the bounds of the custom view.HoverEvent
is triggered when a pointer moves within the bounds of the custom view (for example, mouse pointer or hover pointer).WheelEvent
is triggered when the mouse wheel (or similar) is moved while hovering over the custom view (through a mouse pointer or hover pointer).
NUI has a gesture system which analyses a stream of touch events and attempts to determine the intention of the user. Detectors are provided for the following gestures:
- Pan: When the user starts panning (or dragging) 1 or more fingers.
The panning must start from within the bounds of the view.
- Pinch: Detects when 2 touch points move towards or away from each other.
The center point of the pinch must be within the bounds of the view.
- Tap: When the user taps within the bounds of the view.
- Long press: When the user presses and holds on a certain point within the bounds of the view.
To use gesture detectors:
- Specify a gesture detector in the
OnInitialize()
method:public override void OnInitialize() { /// Enable the tap gesture EnableGestureDetection(Gesture.GestureType.Tap); }
The
EnableGestureDetection()
method of the Tizen.NUI.BaseComponents.CustomView [7] class allows deriving classes to enable any available gesture detectors. The above example only enables the default gesture detection for each type. If customization is required for the gesture detection, the gesture detector can be retrieved and set up accordingly in the same method:PanGestureDetector panGestureDetector = GetPanGestureDetector(); panGestureDetector.AddDirection(PanGestureDetector.DIRECTION_VERTICAL);
-
Override the appropriate method for handling the gesture:
OnPan(PanGesture& pan)
for handling the pan gestureOnPinch(PinchGesture& pinch)
for handling the pinch gestureOnTap(TapGesture& tap)
for handling the tap gestureOnLongPress(LongPressGesture& longPress)
for handling the long-press gesture
public override void OnTap(TapGesture tap) { /// Change the color visual to a random color Random random = new Random(); float nextRed = (random.Next(0, 256) / 255.0f); float nextGreen = (random.Next(0, 256) / 255.0f); float nextBlue = (random.Next(0, 256) / 255.0f); Animation anim = AnimateBackgroundColor(new Color(nextRed, nextGreen, nextBlue, 1.0f), 0, 2000); anim.Play(); }
Managing Size Negotiation
Size negotiation controls the view sizes in a container, based on dependency rules between the views. The Tizen.NUI.ResizePolicyType [12] enumeration specifies a range of options for controlling the way views resize. These options enable automatic resizing.
Table: Resize policy types
ResizePolicyType enumerator |
Description |
---|---|
DimensionDependency |
Use this option to make one dimension depend on another. This option covers width-for-height and height-for-width rules. |
FillToParent |
Use this option to maintain a size similar to the parent's size. Aspect ratio is not maintained. |
FitToChildren |
Use this option to scale the size of the view around the size of its children. For example, the height of a pop-up can be resized to fit its content. |
Fixed |
Use this option to maintain a specific size as set by the Size2D property of the Tizen.NUI.BaseComponents.View [2] class. This is the default for all views. |
SizeFixedOffsetFromParent |
Use this option to maintain a size is similar to the parent's size with a fixed offset. Use the SetSizeModeFactor() method of the Tizen.NUI.BaseComponents.View class to specify the offset. |
SizeRelativeToParent |
Use this option to maintain a size is similar to the parent's size with a relative scale. Use the SetSizeModeFactor() method of the Tizen.NUI.BaseComponents.View class to specify the ratio. |
UseAssignedSize |
Use this option to make the view use a size assigned to it. THis option is not an actual resize policy, but more of an implementation detail. |
UseNaturalSize |
Use this option for objects, such as images or text, to get their natural size. This can mean the dimensions of an image or the size of text with no wrapping. You can also use this option with table views when the size of the table depends on its children. |
To set a resize policy for a custom view:
contactView = new ContactView(); contactView.WidthResizePolicy = ResizePolicyType.FillToParent; contactView.HeightResizePolicy = ResizePolicyType.FillToParent;
The view is positioned and resized (relaid out) automatically when a view property or stage hierarchy changes. Although you do not usually need to request for relayouting manually, you can use the RelayoutRequest()
method of the Tizen.NUI.BaseComponents.CustomView [7] class for deriving views when the derived view wants to be relaid out.
The following overridable methods of the Tizen.NUI.BaseComponents.CustomView
class provide customization points for the size negotiation algorithm:
- The
GetNaturalSize()
method returns the natural size of the view. - The
GetHeightForWidth()
method returns the height for a given width. It is invoked by the size negotiation algorithm if the width is fixed. - The
GetWidthForHeight()
method returns the width for a given height. It is invoked by the size negotiation algorithm if the height is fixed. - The
OnRelayout()
method is called during the relayout process at the end of the frame, immediately after size negotiation is complete and the new size has been set on the view. The method can be overridden to position and resize the view. - The
OnSetResizePolicy()
method is called when a resize policy is set on a view, and it allows deriving views to respond to changes in the resize policy. The method can be overridden to receive notice that the resize policy has changed on the view and action can be taken.
Size negotiation is enabled on views by default. To disable size negotiation, pass the DisableSizeNegotiation
behavior flag into the view constructor.