Kivy. Xamarin. React Native. Three frameworks - one experiment
Greetings to all! As soon as the smoke from the hot discussion in the comments to my Kivy article - a framework for cross-platform development №1 donkey, and among others a worthy commentcame through , we ( Mirimon , SeOd ) thought that it would be interesting for us and our readers to compare Kivy, Xamarin.Forms and React Native, having written the same application on them, accompany it with a corresponding article on Habré, turnips on GitHub and honestly tell who faced what difficulties during implementation. Having gathered in the Telegram and having discussed the details, we set to work.
For this comparison, we decided to write a simple task scheduler with three screens. To make a certain cut of the state of these three platforms today on the example of something more voluminous than the example that we have chosen, in view of the employment of each in our projects / at work / at home, for too long. Despite the simplicity of our application, it will allow you to visually show the principles of application development in each environment, work with data, UI, etc.
Notes can be added by different people, so the author of the note should be listed.
Notes within the project should be added / deleted / edited.
Notes must be content-sized, but not more than 150 pixels.
Deleting notes should be both through the context menu of the note itself, and through the swipe
An approximate UI should look something like this:
Before starting, a little Kivy help:
Kivy is a cross-platform graphical framework written in the Python / Cython programming language, based on OpenGL ES 2, aimed at creating modern user interfaces, more focused on working with touch devices.Kivy apps run on platforms such as Linux, OS X, Windows, Android, iOS, and Rapberry Pi.In development, you have access to a wide range of Python libraries ranging from Requests to NumPy and OpenCV.Kivy has access to almost all native mobile APIs (GPS, Camera, Accelerometer, Google APIs for Android) through PyJNIus (Android) and PyOBJus (iOS), which automatically wrap Java / Objective-C code into Python interface .
Kivy is fastThis applies to both application development and application execution speed.All critical functions are implemented at the C level.Kivy also uses the GPU wherever it makes sense.The GPU does most of the work, thereby greatly increasing performance.
Kivy is very flexible.This means that the rapidly developing Kivy development allows you to instantly adapt to new technologies.Kivy developers have repeatedly added support for new external devices and software protocols, sometimes even before their release.Kivy can be used in combination with a large number of different third-party solutions.For example, in Windows, Kivy supports WM_TOUCH, which means that any device with Windows 7 Pen & Touch drivers will work with Kivy.In OS X, you can use Apple Multi-Touch devices, such as trackpads and mice.In Linux, you can use HID input input events.In addition, Kivy supports TUIO (Tangible User Interface Objects) and several other input sources.
You can write a simple application with a few lines of code.Kivy programs are created using the Python programming language, which is incredibly versatile and powerful, yet easy to use.In addition, Kivy developers have created their own GUI markup language to create complex custom GUIs.This language allows you to quickly customize, connect and organize the elements of the application.
And yes, Kivy is completely free.You can use it everywhere!In a commercial product or in Open Source.
I will give all the application code and show in some detail how certain elements are implemented when developing for mobile platforms. As an IDE, I always use PyCharm , which perfectly supports the syntax Kv Language - a special DSL language in which the UI representation of your application is written. The application skeleton is created using the console tool CreatorKivyProject , which provides basic screens using the MVVM template.
The baseclass folder contains the logic of widgets and controllers implemented in the Python programming language, and kv contains the interface description files in the Kv Language language . The applibs directory is used for third-party libraries, the data folder contains media content, databases and other data. The main.py file is the entry point of the application. He doesn’t do anything except launch the UI renderer TodoList (). Run () , catch an error if it occurs and display a window for sending a report bug, created automatically by the CreatorKivyProject utility, has nothing to do with writing our application, and therefore is not considered.
The todolist.py file with program code implements the TodoList class, which loads the interface layouts, initializes their instances, monitors the hard keys of the device, and returns our first screen, which are listed in the Activity Manager. After TodoList (). Run () , the build function is called and returns the widget that will be displayed on the screen.
For example, a simple program code that displays one screen with an image will look like this:
And here is the diagram of our application class:
todolist.py:
# -*- coding: utf-8 -*- import os from kivy.app import App from kivy.lang import Builder from kivy.core.window import Window from kivy.factory import Factory from libs.applibs.kivymd.theming import ThemeManager from libs.dataBase import DataBase class TodoList(App, DataBase): title = 'Todo List' icon = 'icon.png' theme_cls = ThemeManager() theme_cls.primary_palette = 'BlueGrey' def __init__(self, **kvargs): super(TodoList, self).__init__(**kvargs) Window.bind(on_keyboard=self.eventsProgram) Window.softinput_mode = 'below_target' self.Window = Window self.pathToBase = '%s/data/dataProjects.json' % self.directory self.nameAuthor = u' ' def build(self): self.setDataProjects() self.loadAllKvFiles(os.path.join(self.directory, 'libs', 'uix', 'kv')) self.rootScreen = Factory.RootScreen() # # Activity. self.activityManager = self.rootScreen.ids.activityManager self.listProjectsActivity = self.rootScreen.ids.listProjectsActivity self.listNotesActivity = self.rootScreen.ids.listNotesActivity self.addNewNoteActivity = self.rootScreen.ids.addNewNoteActivity return self.rootScreen def loadAllKvFiles(self, directory_kv_files): for kv_file in os.listdir(directory_kv_files): kv_file = os.path.join(directory_kv_files, kv_file) if os.path.isfile(kv_file): Builder.load_file(kv_file) def on_start(self): self.listProjectsActivity.setListProjects(self) def eventsProgram(self, instance, keyboard, keycode, text, modifiers): if keyboard in (1001, 27): if self.activityManager.current == 'add new note activity': self.activityManager.backActivity( 'list notes activity', self.addNewNoteActivity.ids.floatingButton) if self.activityManager.current == 'list notes activity': self.activityManager.current = 'list project activity' return True
Our application consists of only three Activities , which are switched by the screen manager ( ScreenMenager ), which we returned to the build function:
When the application starts, the Activity that is specified first in the ActivityManager will be installed. In our case, this is ListProjectsActivity . In the application for lists of projects and tasks I used ScrollView . Although it was more correct - RecycleView . Because the first, if posts and projects will be over a hundred, will not cope. More precisely, it will be very long to render lists. RecycleView allows you to display lists of any length almost instantly. But since in any case with large lists, one would have to use either dynamic loading of data into the list or pagination, and this was not discussed in the TOR, I used the ScrollView . The second reason is that I was too lazy to redo the lists under RecycleView (and it is completely different in use from ScrollView ), and there wasn’t much time, because the entire application was written in four hours in smoke breaks and coffee breaks.
The start screen with a list of projects (ListProjectsActivity.kv and ListProjectsActivity.py) looks like this:
Since the screen layout of the ListProjectsActivity is already shown in the screenshot, I’ll show you what its control class looks like:
# -*- coding: utf-8 -*- from kivy.app import App from kivy.uix.screenmanager import Screen as Activity from libs.uix.baseclass.InputDialog import InputDialog from . ProjectItem import ProjectItem class ListProjectsActivity(Activity): objApp = App.get_running_app() def setListProjects(self, objApp): for nameProject in objApp.dataProjects.keys(): self.ids.layoutContainer.add_widget(ProjectItem(projectName=nameProject)) def createNewProject(self, projectName): if projectName andnot projectName.isspace(): self.ids.layoutContainer.add_widget(ProjectItem(projectName=projectName)) self.objApp.addProjectInBase(projectName) def deleteProject(self, instance): for projectName in self.objApp.dataProjects: if instance.projectName == projectName: self.objApp.deleteProjectFromBase(projectName) break def showDialogCreateProject(self, *args): InputDialog( title=' ', hintText=' ', textButtonCancel='', textTuttonOk='', eventsCallback=self.createNewProject).show()
Call diology window:
In operation, the window call and the creation of a new project will look like this:
On the question of application data, I will not stop, because the data is a regular dictionary of the form
and which is stored in the data directory as a simple json file.
Let's see what is the item with the name of the project and how in Kivy to use deleting an item from the list by swipe? To do this, we must inherit the behavior of the widget in the list from the SwipeBehavior class of the SwipeToDelete library:
In general, every widget in Kivy has an on_touch method with which you can catch any events occurring on the screen. Here is a small part of the list of available events:
There were no problems here either, it’s just a standard DropDown widget. Thanks to the fact that you can customize all the widgets and controllers in Kivy as much as your imagination allows you, I easily got a nice menu. Left base dropdown, right is mine:
from kivy.app import App from kivy.clock import Clock from libs.applibs.animdropdown import AnimMenuDropDown classContextMenuAndroidActivity(AnimMenuDropDown):deftapOnItem(self, textItem): objApp = App.get_running_app() if textItem == '': objApp.listActivity.deletePost() else: objApp.activityManager.current = 'add new note activity' Clock.schedule_once(objApp.addNewNoteActivity.editNote, .5)
Next, we import the MenuDropDown button from the animdropdown library, pass it the object of our context menu as a parameter, and after that we add this button to the screen we need. In our application, this is the button on the right in the note card:
Activity layout cards notes:
Base class NoteActivity :
from kivy.app import App from kivy.properties import StringProperty from kivy.uix.boxlayout import BoxLayout from libs.applibs.animdropdown import MenuButton from libs.applibs.swipetodelete import SwipeBehavior from . ContextMenu import ContextMenu classNoteActivity(SwipeBehavior, BoxLayout): nameDate = StringProperty() textNote = StringProperty() pathToAvatar = StringProperty() def__init__(self, **kwargs): super(NoteActivity, self).__init__(**kwargs) self.objApp = App.get_running_app() menuButton = MenuButton( dropdown_cls=ContextMenu, icon='dots-vertical', _on_dropdown_fnc=self.setCurrentPost) self.ids.titleBox.add_widget(menuButton) defsetCurrentPost(self, *args): self.objApp.listNotesActivity.checkCurentPost = self
Software implementation of ListNotesActivity :
# -*- coding: utf-8 -*- from kivy.app import App from kivy.uix.screenmanager import Screen as Activity from kivy.properties import ObjectProperty from . NoteActivity import NoteActivity class ListNotesActivity(Activity): checkCurentPost = ObjectProperty() objApp = App.get_running_app() def clearList(self): if self.objApp.activityManager.current == 'list project activity': self.ids.layoutContainer.clear_widgets() def addNewNote(self, objApp): objApp.activityManager.current = 'add new note activity' def setDefaultcheckCurentPost(self): self.checkCurentPost = lambda x: None def setNotesProject(self, nameProject): self.ids.toolBar.title = nameProject for dataProject in self.objApp.dataProjects[nameProject][1]: self.ids.layoutContainer.add_widget(NoteActivity( textNote=dataProject['textNote'], nameDate=dataProject['nameDate'], pathToAvatar=dataProject['pathToAvatar'])) def deletePost(self, instance=None): # . if not self.checkCurentPost: checkCurentPost = instance else: checkCurentPost = self.checkCurentPost self.ids.layoutContainer.remove_widget(self.checkCurentPost) nameProject = self.ids.toolBar.title self.objApp.deleteNoteFromBase(nameProject, checkCurentPost.textNote) def checkScroll(self): if self.checkCurentPost and type(self.checkCurentPost) is not NoteActivity: self.checkCurentPost(self)
How to manage Activity apps? In order to switch from one Activity to another, we must specify the name of the new Activity to the screen manager:
classListNotesActivity(Activity): ... defaddNewNote(self, *args): self.objApp.activityManager.current = 'add new note activity'
... where 'add new note activity' is the name of the Activity to add a new note.
Unlike Xamarin.Forms, the UI in Kivy will look the same everywhere. So, if you are writing an application for two platforms (Android and iOS), you should take this into account when marking up the interface and specifying properties for widgets. Or do two markup for the two platforms (the logic remains unchanged). This is a plus, since the UI render and events do not depend on the platform features, you do not use native APIs to manage these actions, which allows your application to run smoothly on almost any platform. All graphics are rendered using native OpenGL and SDL2 calls on the GPU, which allows you to very quickly draw menus, buttons and other graphical user interface features including 2D and 3D graphics.
This application uses Android UI MaterialDesign. For example, my last project had an adaptive interface:
But a demonstration of the possibilities in the style of Material Design
As I said before, Kivy does not use native APIs for rendering UI, so it allows you to emulate various models of devices and platforms using the screen module. It is enough to launch your project with the necessary parameters, so that the application's test window opens on your computer as if it were running on a real device. It sounds weird, but since Kivy is abstracted from the platform in the UI drawing, this makes it possible not to use heavy and slow emulators for tests. This only applies to UI. For example, the test application described in this article was tested with the parameters -m screen: droid2, portrait, scale = .75 that one-to-one corresponds to my real device.
What can be said in conclusion? Is Kivy good? Undoubtedly good! If you own a wonderful programming language Python, you can easily make applications for mobile (and not only) platforms with a no less remarkable Kivy framework.
Advantages of application development using the Kivy framework:
Since we are dealing with Python, application development speed is much faster than development speed in any other programming language or framework.
Megatons of ready-made Python libraries that you can use in your projects: OpenCV, Django, Flask, NumPy, ffmpeg, sqlite3, lxml and thousands of others.
Since Kivy uses OpenGL and GPU for drawing graphics, as well as its own widgets and controllers, the UI render speed is very high and you are completely relieved of the headache that is present in other frameworks that need to be used for the implementation of some parts of the interface.
You use the native only where you need access to specific platform functions, which simply cannot be in a truly cross-platform framework: for example, access to geolocation, access to a camera, BlueTooth technology ...
Implementing access to the native Android API for IMEI and device models using PyJnius:
You can use third-party jar libraries in your projects when it comes to Android.
You fully own all the events occurring on the screen: touch, multitouch, svayp, pushing and other events without leaving the native so it is an integral part of Kivy.
Kivy features in Touch devices:
Despite all the advantages, Kivy has several disadvantages:
The speed of “cold start”, that is, the first launch of the application from the moment when all the libraries will be deployed, is quite long. The subsequent ones are ordinary, but longer than the native depends on the load of the processor of the mobile device.
Work with lists. It is possible to display a list of 100,000 points in half a second (for example, user cards, shop windows, quotes), but with one condition - all cards must be of the same height. If you display a list, for example, quotes, with an unknown amount of text, but in its entirety, you cannot display more than ten points at a time, as this will take about 10-15 seconds. In this case, you will have to load 10-15 items each while scrolling through the list.
It is impossible to display text whose size exceeds 6500 characters (3.5 pages of printed text) - we get a black screen. This is solved by splitting the text and then gluing it, which still seems to be a crutch. However, it is not clear who might have thought of displaying such amount of text at a time. Especially when it comes to mobile platforms.