Custom Code
Introduction¶
What follows is a detailed tutorial that walks developers through the steps of writing custom code for an Android application using P2UX. Developers should be familiar with the Android Studio integrated development environment for MacOS and the Java programming languages for custom coding.
Topics include customizable View and control behaviors, Screen/Panels, and UI controls.
Terminology
Throughout the P2UX documentation the “developer” refers to you, the reader who is developing an app with P2UX. The “designer” refers to the individual whose principal job is to create the appearance and style of the app and its visual components. “PRL” refers to the P2UX Rendering Library for iOS, which controls the creation and workflow of content created for the P2UX platform.
Coding examples and code additions
In the coding examples below and throughout the documentation, changes and additions to be made by the developer are rendered with a blue highlight.
P2UX versus the standard Android Studio environment
P2UX based applications are easy to customize and extend for Android. The P2UX Android rendering library is based on the Android Widget package (android.widget) and other Android primitives. This means that writing custom business logic and integrating custom controls works much the same as it does when writing Android applications using Android Studio. With the P2UX platform, XML Layout files are no longer required for Android development. Instead, applications use P2UX Portable UX Bundles (PUB) either as embedded or dynamically accessed resources. Additionally, P2UX provides hooks to developers that allow them access to all of the components of P2UX as well as the ability to fully customize instances of the application.
Create a P2UX enabled project first
This document assumes a P2UX enabled Android Studio project has already been created. See Quick Start for steps to create a project.
P2UX and Android Studio¶
Java
P2UX native rendering libraries for Android can be extended using the Java programming language. The instructions below provide explicit reference to the proper use of Java coding in each applicable step that follows.
Adding Custom Behaviors¶
If desired, customizable behaviors can be added to the project code.
A subclass of the P2UXAppBehavior
class is the starting point for customizing the behavior of the application. From this class, all other parts of the application can be customized. (Common customized behaviors include creation of a Custom Control or overriding a particular View.)
In Android Studio, create a class to store the customized behaviors. The easiest way to do is by doing a right click on your project package and selecting New->Java Class. (This example uses the name SampleBehavior
.) In the Create New Class dialog, type in P2UXAppBehavior in the Superclass field.
This will create SampleBehavior
class. Put any customized behaviors within the implementation here. Below is a list of available customization methods for P2UXAppBehavior
:
Modifier and Type | Method and Description |
---|---|
P2UXFragment | createViewFragment(int type, Context context, P2UXDefinition def, P2UXFragment.UXFragmentDelegate fragmentDelegate, RectF rect, boolean cache, Object index, Object data, P2UXViewContainerDelegate viewDelegate) Creates a custom fragment |
P2UXScreen | createScreen(Context context, P2UXDefinition def, RectF rect, Object index, Object data, P2UXViewContainerDelegate viewDelegate) Creates a custom screen |
P2UXPanel | createPanel(Context context, P2UXDefinition def, RectF rect, Object index, Object data, P2UXViewContainerDelegate viewDelegate) Creates a custom panel |
View | createControl(String type, P2UXElementInstance elInstance, RectF rect, P2UXViewContainerDelegate viewDelegate, Object index, Object data) Creates a custom control |
In addition, update MainActivity
to include the custom behaviors.
To do this,MainActivity
must override the method createBehavior
. (This will override any default behaviors.) Edit MainActivity
to look like this:
@Override public void onCreate(Bundle savedInstanceState) { mAppKey = "xxxxx"; try { mResources = new JSONArray(); JSONObject resource = new JSONObject(); resource.put(P2UXAppTypes.P2UXApp_PackageAttrib_FormFactor, 0); resource.put(P2UXAppTypes.P2UXApp_PackageAttrib_Type, P2UXAppTypes.P2UXApp_PackageType_Static); resource.put(P2UXAppTypes.P2UXApp_PackageAttrib_Package, "shellui_phone"); resource.put(P2UXAppTypes.P2UXApp_PackageAttrib_Update, P2UXAppTypes.P2UXApp_PackageUpdate_None); mResources.put(resource); } catch (JSONException e) { P2UXLog.e(TAG, "onCreate - " + e.getMessage()); } if ((getApplicationInfo().flags & ApplicationInfo.FLAG_DEBUGGABLE) == ApplicationInfo.FLAG_DEBUGGABLE) { if (savedInstanceState == null) { savedInstanceState = new Bundle(); } savedInstanceState.putString(P2UXAppCreator.P2UXAppCreator_Opt_Env, P2UXAppCreator.P2UXAppCreator_Opt_Env_Prototype); savedInstanceState.putLong(P2UXAppCreator.P2UXAppCreator_Opt_LogLevel, P2UXLog.P2UXCoreLogFlagVerbose); } super.onCreate(savedInstanceState); applyTranslucentStatusBar(); } @Override public P2UXAppBehavior createBehavior(String appId) { return new SampleAppBehavior(); }
Adding Custom Fragment¶
Developers can override Fragment instances to provide any custom code needed for a fragment lifecycle/behavior and its hosted View
.
The PRL will request an instance from P2UXAppBehavior
when it needs to create a new fragment. If no instance is provided, P2UX will generate a standard instance of the Fragment.
To provide a custom Fragment instance, override the method createViewFragment
from a P2UXAppBehavior
subclass and return an instance of P2UXFragment
.
To handle button click action, override the method handleButtonClick
from P2UXAppFragment
. To access a control in the fragment, override the method viewCreated
from P2UXAppFragment
.
The name of a chosen Fragment is the value used to identify the screen when the PRL creates an instance of this object. The “def” parameter passed to this method contains the member variable systemType
that identifies the Screen being created. From this object, the developer determines the requested Screen and the specific P2UXFragment
subclass to instantiate.
Creating a custom Fragment instance in a P2UXAppBehavior
subclass
@Override public P2UXFragment createViewFragment(int type, Context context, P2UXDefinition def, P2UXFragment.UXFragmentDelegate fragmentDelegate, RectF rect, boolean cache, Object index, Object data, P2UXViewContainerDelegate viewDelegate) { if (def.getSystemType().equals("xxxxx")) { //return a fragment here, for example: return new SampleFragment(context, def, fragmentDelegate, rect, cache, index, data, viewDelegate); } return super.createViewFragment(type, context, def, fragmentDelegate, rect, cache, index, data, viewDelegate); } @Override public void viewCreated() { super.viewCreated(); P2UXButton btn = (P2UXButton) controlWithElementSystemTypeOrId("show_btn"); if (btn != null) { btn.setEnabled(false); } } @Override public boolean handleButtonClick(View ctrl) { P2UXElementInstance elementInstance = P2UXControlHelper.getElementInstance(ctrl); if (elInstance != null && elInstance.getSystemType.equals("button1")) { String elId = elInstance.getElId(); if (elId != null && elId.equals("button1")) { // take some custom action here for the "okbtn" // return true to indicate the event was handled and propagation // shouldn't continue return true; } } return super.handleButtonClick(ctrl); }
Adding Custom Screens/Panels¶
Developers can override Screen or Panel instances to provide any custom code needed for a hosted View
.
Since the P2UX UI is not based on XML Layout, connecting custom methods for each button click action is unnecessary. Instead, the developer can override the methods of the P2UXScreen
class to handle button click and other UI events. (In all other respects, writing code for customized Screen and Panel behavior is the same as for XML layout-based content.)
The PRL will request an instance from P2UXAppBehavior
when it needs to create a new Screen or Panel. If no instance is provided, P2UX will generate a standard instance of the Screen or Panel.
To provide a custom Screen instance, override the method createScreen
from a P2UXAppBehavior
subclass and return an instance of P2UXScreen
. To provide a custom Panel instance, override the method createPanel
and return an instance of P2UXPanel
. (Both P2UXScreen
and P2UXPanel
are View
based subclasses that add some additional helper methods. These methods make it easier to access screen components.)
The name of a chosen Screen or Panel is the value used to identify the item when the PRL creates an instance of this object. The “def” parameter passed to this method contains the member variable systemType
that identifies the Screen being created. From this object, the developer determines the requested Screen and the specific P2UXScreen
or P2UXPanel
subclass to instantiate.
Creating a custom Screen or Panel instance in a P2UXAppBehavior
subclass
@Override public P2UXScreen createScreen(Context context, P2UXDefinition def, RectF rect, Object index, Object data, P2UXViewContainerDelegate viewDelegate) { if (def.getSystemType().equals("xxxxx")) { //return some screen here, for example: return new SampleScreen(context, rect, def, viewDelegate, index, data); } return super.createScreen(context, def, rect, index, data, viewDelegate); } @Override public P2UXPanel createPanel(Context context, P2UXDefinition def, RectF rect, Object index, Object data, P2UXViewContainerDelegate viewDelegate) { if (def.getSystemType().equals("xxxxx")) { //return some screen here, for example: return new SamplePanel(context, rect, def, viewDelegate, index, data); } return super.createPanel(context, def, rect, index, data, viewDelegate); }
Accessing Control instances from P2UXPanel
or P2UXScreen
¶
To access instances of controls, P2UXScreen
provides a helper method, controlWithElementSystemTypeOrId
. This method takes the name or id of the control to be accessed and returns a control instance.
“System type” references
After creating a control, the developer can set a “system type” for the control in Builder. This effectively becomes the name for the new control. Referring to the control by its “system type” means that the developer does not need to change any existing custom code when choosing to create an alternate variation of the same control. (Otherwise, if the developer changes the name of the control in Builder, any custom code to referring to the new name would have to be changed as well.)
Accessing a control in a custom instance of P2UXScreen
looks like this:
@Override public void showView(boolean reload) { super.showView(reload); P2UXButton btn = (P2UXButton) controlWithElementSystemTypeOrId("show_btn"); if (btn != null) { btn.setEnabled(false); } }
Handling Events from P2UXAppController¶
End-users of a completed app will interact with controls in a Screen or Panel to generate events. These interactions will, in turn, trigger actions. Typically the PRL handles events directly through event interactions set up in Builder. In some cases, additional custom code may be needed to trigger actions not directly supported by P2UX.
The PRL provides a chain of event handlers that propagate an event through different components. Handling events at any point in the propagation chain provides a way to trigger custom code and also (optionally) to stop the propagation of the event at any specific handler.
Event propagation follows a set pattern.
First, events are relayed to the P2UXScreen
for the current Screen or Panel. Next, they pass to P2UXAppBehavior
. Finally, events are handled by the custom instance of the View
itself. If none of these components halt event propagation, the PRL will then handle any interactions defined for the event in Builder.
The P2UXScreen
contains helper methods that handle a variety of events when they occur. These methods give the developer a chance to integrate any custom code needed from the basic event level without triggering any custom actions.
Most standard control events are routed through the P2UXScreen
instance. Handling these events is a matter of overriding the appropriate event method in your P2UXScreen subclass. Event propagation can be allowed to continue or not depending on the return value from the event handler methods.
The example below illustrates the code that handles a Button press event.
Handling a Button press event in P2UXScreen subclass:
@Override public boolean handleButtonClick(View ctrl, P2UXElementInstance elInstance) { if (elInstance != null && elInstance.getSystemType.equals("button1")) { String elId = elInstance.getElId(); if (elId != null && elId.equals("button1")) { // take some custom action here for the "okbtn" // return true to indicate the event was handled and propagation // shouldn't continue return true; } } return super.handleButtonClick(ctrl); }
Adding Custom UI Controls¶
UI Controls are the visual components of a Screen or Panel created in the Builder tool. These can be interactive, like Buttons or Sliders, or non-interactive, like Shapes or Text. Although P2UX offers many different UI Controls directly creatable and usable in Builder, a specific application may require developer-defined UI controls. Builder provides a Custom Control component that can be added to a Screen or Panel and then replaced using custom code.
Any View
based control can be injected into your Screens and Panels via a Custom Control. This allows for existing custom controls or third party custom controls to be used with P2UX based applications.
The PRL manages the size and position of any custom controls defined using Builder. To provide an instance of a custom control, override the method createControl in your P2UXAppBehavior
subclass and return the View
subclassed instance of your control. The first parameter passed to this method, type, is the name of the control set when you created the custom control in Builder. P2UX will pass the initial size to use with your Custom Control, as well as a few other components that can be used to access any custom information created with your control in Builder.
The elementInstance parameter contains all the parameters specified using Builder for the control via its itemSpec member.
Create a custom control in P2UXAppBehavior
subclass like this:
@Override public View createControl(String type, P2UXElementInstance elemInstance, RectF rect, P2UXViewContainerDelegate viewDelegate, Object index, Object data) { if (type != null && !type.isEmpty()) { if (type.equals("calendar")) { // Below is an example of implementing a calendar using CalendarView View ctrl = new CalendarView(mContext); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams((int)rect.width(), (int)rect.height()); params.leftMargin = (int)rect.left; params.topMargin = (int)rect.top; ctrl.setLayoutParams(params); ((CalendarView) ctrl).setOnDateChangeListener(new CalendarView.OnDateChangeListener() { @Override public void onSelectedDayChange(@NonNull CalendarView view, int year, int month, int dayOfMonth) { Toast.makeText(mContext, month+1 + "/" + dayOfMonth + "/" + year, Toast.LENGTH_SHORT).show(); } }); return ctrl; } } return super.createControl(type, elemInstance, rect, viewDelegate, index, data); }