Приветствую всех! Как только дым от жаркой дискуссии в комментариях к моей статье Kivy — фреймворк для кроссплатформенной разработки №1 осел, и среди прочих пробился достойный внимания комментарий, мы (Mirimon, SeOd), подумали, что было бы интересно и нам и читателям самостоятельно сравнить Kivy, Xamarin.Forms и React Native, написав на них одно и тоже приложение, сопроводить его соответствующей статьей на Хабре, репой на GitHub и честно рассказать, кто с какими трудностями столкнулся при реализации. Собравшись в Телеграмме и обсудив детали, мы принялись за работу.
Для этого сравнения мы решили написать простой планировщик задач с тремя экранами. Делать некий срез состояния этих трех платформ на сегодняшний день на примере чего-то более объемного, чем пример, который мы выбрали, ввиду занятости каждого в своих проектах/на работе/дома, слишком долго. Несмотря на простоту нашего приложения, оно позволит наглядно показать принципы разработки приложения в каждой среде, работу с данными, UI и пр.
Три фреймворка — один эксперимент. Xamarin.Forms. Часть 2
Три фреймворка — один эксперимент. React Native. Часть 3
Данная статья является первой в цикле, поэтому начать хочется с ТЗ, который мы для себя набросали, чтобы результат получился похожий.
Вариант ТЗ:
- Заметки должны быть структурированы по проектам
- Заметки могут добавляться разными людьми, так что должен быть указан автор заметки
- Заметки внутри проекта должны добавляться/удаляться/редактироваться
- Заметки должны быть размером по контенту, но не более 150 пикселей
- Удаление заметок должно быть как через контекстное меню самой заметки, так и через свайп
Примерный UI должен выглядеть как-то так:
Kivy быстр. Это относится как к разработке приложений, так и к скорости выполнения приложений. Все критически важные функции реализованы на уровне Cи. Также Kivy использует GPU везде, где это имеет смысл. GPU выполяет бОльшую часть работы, тем самым значительно увеличивая производительность.
Kivy очень гибкок. Это означает, что быстро развивающаяся разработка Kivy позволяет мгновенно адаптироваться к новым технологиям. Разработчики Kivy не раз добавляли поддержку новых внешних устройств и программных протоколов, иногда даже до их выпуска. Kivy можно использовать в сочетании с большим количеством различных сторонних решений. Например, в Windows Kivy поддерживает WM_TOUCH, что означает, что любое устройство с драйверами Windows 7 Pen & Touch будет работать с Kivy. В OS X вы можете использовать устройства Apple с поддержкой Multi-Touch, такие как трекпады и мыши. В Linux вы можете использовать входные события ввода HID. В дополнение к этому Kivy поддерживает TUIO (Tangible User Interface Objects) и ряд других источников ввода.
Вы можете написать простое приложение с несколькими строками кода. Программы с Kivy создаются с использованием языка программирования Python, который является невероятно универсальным и мощным, но простым в использовании. Кроме того, разработчики Kivy создали собственный язык разметки графических интерфейсов, для создания сложных пользовательских GUI. Этот язык позволяет быстро настраивать, подключать и упорядочивать элементы приложения.
И, да, Kivy абсолютно бесплатен. Вы можете использовать его везде! В коммерческом продукте либо в Open Source.
Я приведу весь код приложения и покажу достаточно подробно, как реализуются те или иные элементы при разработке под мобильные платформы. В качестве IDE я всегда использую PyCharm, который отлично поддерживает синтаксис Kv Language — специальный DSL язык, на котором пишется UI представление вашего приложения. Скелет приложения создан с помощью консольной утилиты CreatorKivyProject, которая предоставляет базовые экраны с использованием шаблона MVVM.
В папке baseclass содержится логика виджетов и контроллов, реализованная на языке программирования Python, в kv — файлы описания интерфейса на языке Kv Language. Директория applibs используется для сторонних библиотек, в папке data находятся медиаконтент, базы данных и прочие данные. Файл main.py — это точка входа приложения. Ничем он не занимается, кроме как запуском рендера UI TodoList().run(), отловом ошибки в случае ее возникновения и выводом окна для отправки баг репорта, создан автоматически утилитой CreatorKivyProject, не имеет отношения к написанию нашего приложения, а потому не рассматривается.
Файл todolist.py с программным кодом реализует класс TodoList, который загружает макеты интерфейса, инициализирует их инстансы, следит за событиями хард клавиш устройства и возвращает наш первый экран, которые перечислены в Activity менеджере. После TodoList().run() вызывается функция build и возвращает виджет, который будет отображен на экране.
Для примера, код простой программы, которая выводит один экран с изображением, будет выглядеть так:
А вот схема нашего класса приложения:
# -*- 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
Наше приложение состоит всего из трех Activity, переключением которых занимается менеджер экранов (ScreenMenager), который мы вернули в функции build:
#:import ListProjectsActivity libs.uix.baseclass.ListProjectsActivity.ListProjectsActivity
#:import ListNotesActivity libs.uix.baseclass.ListNotesActivity.ListNotesActivity
#:import AddNewNoteActivity libs.uix.baseclass.AddNewNoteActivity.AddNewNoteActivity
#:import ActivityManager libs.uix.baseclass.ActivityManager.ActivityManager
<RootScreen@BoxLayout>:
orientation: 'vertical'
spacing: dp(2)
ActivityManager:
id: activityManager
ListProjectsActivity:
id: listProjectsActivity
ListNotesActivity:
id: listNotesActivity
AddNewNoteActivity:
id: addNewNoteActivity
При старте приложения будет установлено то Activity, которое указано в ActivityManager первым. В нашем случае — это ListProjectsActivity. В приложении для списков проектов и задач я использовал ScrollView. Хотя правильнее было — RecycleView. Потому что первый, если постов и проектов будет за сотню, не справится. Точнее, будет очень долго рендерить списки. RecycleView позволяет вывести списки любой длины практически мгновенно. Но так как в любом случае при больших списках пришлось бы использовать либо динамическую подгрузку данных в список, либо разбиение на страницы, а в ТЗ это не обсуждалось, я использовал имеено ScrollView. Вторая причина состоит в том, что мне было лень переделывать списки под RecycleView (а он координально отличается в использовании от ScrollView), да и времени особо не было, потому что все приложение написано за четыре часа в перекурах и кофе брейках.
Стартовый экран со списком проектов (ListProjectsActivity.kv и ListProjectsActivity.py) выглядит следующим образом:
Поскольку разметка экрана ListProjectsActivity уже приведена на скрине, покажу, как выглядит его управляющий класс:
# -*- 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 and not 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()
Вызов диологового окна:
В работе вызов окна и создание нового проекта будет выглядеть так:
На вопросе о данных приложения я не буду останавливаться, потому что данные представляют из себя обычный словарь вида
{"Name Project": [{"pathToAvatar": "", "nameDate": "", "nameAuthor": "", "textNote": ""}]}
и который хранится в директории data в виде простого json файла.
Посмотрим, что представляет из себя пункт с названием проекта и как в Kivy использовать удаление пункта из списка путем свайпа? Для этого мы должны наследовать поведение виджета в списке от класса SwipeBehavior библиотеки SwipeToDelete:
ProjectItemActivity.py
from kivy.properties import StringProperty
from kivy.uix.boxlayout import BoxLayout
from libs.applibs.swipetodelete import SwipeBehavior
class ProjectItemActivity(SwipeBehavior, BoxLayout):
projectName = StringProperty()
def on_touch_down(self, touch):
if self.collide_point(touch.x, touch.y):
self.move_to = self.x, self.y
return super(ProjectItemActivity, self).on_touch_down(touch)
def on_touch_move(self, touch):
if self.collide_point(touch.x, touch.y):
self.reduce_opacity()
return super(ProjectItemActivity, self).on_touch_move(touch)
def on_touch_up(self, touch):
if self.collide_point(touch.x, touch.y):
self.check_for_left()
self.check_for_right()
return super(ProjectItemActivity, self).on_touch_up(touch)
И описание пункта проекта в Kv разметке:
ProjectItemActivity.kv
<ProjectItemActivity>:
swipe_rectangle: self.x, self.y , self.width, self.height
swipe_timeout: 1000000
swipe_distance: 1
event_after_swipe: app.listActivity.deleteProject
OneLineListItem:
text: root.projectName
on_press: app.listActivity.setNotesProject(root.projectName)
Вообще, у каждого виджета в Kivy есть метод on_touch с помощью которого вы можете отловить любые события, происходящие на экране. Вот небольшая часть списка доступных событий:
['double_tap_time', 'grab_state', 'is_double_tap', 'is_mouse_scrolling', 'is_touch', 'is_triple_tap', 'move', 'push', 'push_attrs', 'push_attrs_stack', 'scale_for_screen', 'time_end', 'time_start', 'time_update', 'triple_tap_time', 'ungrab', 'update_time_end']
Реализация контекстного меню для Android…
Здесь тоже не возникло никаких проблем, так это всего лишь стандартный виджет DropDown. Благодоря тому, что все виджеты и контроллы в Kivy вы можете кастомизировать настолько, насколько вам позволяет ваша фантазия, я с легкостью получил симпотичную менюшку. Слева базовый DropDown, справа — мой:
Разметка списка контекстного меню:
ContextMenuAndroidActivity.kv
#:import MDSeparator libs.applibs.kivymd.card.MDSeparator
#:import MenuItem libs.applibs.animdropdown.MenuItem
<ContextMenuAndroidActivity>:
MenuItem:
text: 'Редактировать'
menu: root
on_press: root.tapOnItem(self.text)
MDSeparator:
MenuItem:
text: 'Удалить'
menu: root
on_press: root.tapOnItem(self.text)
Программная часть контекстного меню:
ContextMenuAndroidActivity.kv
from kivy.app import App
from kivy.clock import Clock
from libs.applibs.animdropdown import AnimMenuDropDown
class ContextMenuAndroidActivity(AnimMenuDropDown):
def tapOnItem(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)
Далее мы импортируем кнопку MenuDropDown из библиотеки animdropdown, передаем ей в качестве параметра объект нашего контекстного меню и уже после добавляем эту кнопку в нужное нам месте нам экране. В нашем приложении это кнопка справа в карточке заметки:
Разметка Activity карточки заметки:
Базовый класс 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
class NoteActivity(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)
def setCurrentPost(self, *args):
self.objApp.listNotesActivity.checkCurentPost = self
Программная реализация 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)
Как управлять Activity приложения? Для того чтобы переключится с одного Activity на другое, мы должны указать менеджеру экранов имя нового Activity:
class ListNotesActivity(Activity):
...
def addNewNote(self, *args):
self.objApp.activityManager.current = 'add new note activity'
… где 'add new note activity' имя Activity для добавления новой заметки.
Экран и разметка Activity AddNewNoteActivity:
Базовый класс:
from kivy.app import App
from kivy.animation import Animation
from kivy.uix.screenmanager import Screen as Activity
from kivy.metrics import dp
from libs.uix.baseclass.NoteActivity import NoteActivity
class AddNewNoteActivity(Activity):
objApp = None
edit = False
oldTextNote = ''
def animationButton(self):
self.objApp = App.get_running_app()
self.ids.toolBar.title = self.objApp.listNotesActivity.ids.toolBar.title
Animation(size=(dp(56), dp(56)), d=.5, t='in_out_cubic').start(self.ids.floatingButton)
def addNewNotes(self, textNote):
if self.edit:
nameProject = self.ids.toolBar.title
self.objApp.addEditNoteInBase(nameProject, textNote, self.oldTextNote)
self.objApp.activityManager.backActivity('list notes activity', self.ids.floatingButton)
self.objApp.listNotesActivity.checkCurentPost.textNote = textNote
self.edit = False
return
self.objApp.listNotesActivity.ids.layoutContainer.add_widget(
NoteActivity(
textNote=textNote, nameDate='%s\n%s' % (
self.objApp.nameAuthor, self.objApp.getDate()),
pathToAvatar='data/images/avatar.png'))
self.objApp.addNoteInBase(self.ids.toolBar.title, textNote, 'data/images/avatar.png')
def editNote(self, interval):
self.edit = True
self.ids.textInput.text = self.objApp.listNotesActivity.checkCurentPost.textNote
self.oldTextNote = self.ids.textInput.text
Для анимации кнопки я использовал событие on_enter, которое вызывается в момент установки Activity на экран:
В разметке:
<AddNewNoteActivity>
on_enter: root.animationButton()
В Python коде:
class AddNewNoteActivity(Activity):
def animationButton(self):
Animation(size=(dp(56), dp(56)), d=.5, t='in_out_cubic').start(self.ids.floatingButton)
В отличие от Xamarin.Forms UI в Kivy будет выглядеть везде одинаково. Так что, если вы пишите приложение для двух платформ (Android и iOS), вы должны учитывать это при разметке интерфейса и указании свойств виджетам. Или же делать две разметки для двух платформ (логика остается неизменной). Это плюс, так как рендер UI и события не зависят от особенностей платформы, вы не используете нативные API для управления этими действиями, что позволяет вашему приложению безболезнено выполнятся практически на любой платформе. Вся графика рендерится с помощью нативных вызовов OpenGL и SDL2 на GPU, что позволяет очень быстро рисовать менюшки, кнопки и прочие прелести графического интерфейса включая 2D и 3D графику.
Данное приложение использует Android UI MaterialDesign. Например, последний мой проект имел адаптивный интерфес:
А вот демонстрация возможностей в стиле Material Design:
Как я уже говорил, Kivy не использует нативные API для рендера UI, поэтому позволяет эмулировать различные модели устройств и платформ с помощью модуля screen. Достаточно запустить ваш проект с нужными параметрами, чтобы на компьютере открылось окно тестируемого приложения так, как если бы оно было запущено на реальном устройстве. Звучит странно, но поскольку Kivy абстрагируется от платформы в отрисовке UI, это позволяет не использовать тяжелые и медленные эмуляторы для тестов. Это касается только UI. Например, тестовое приложение, описываемое в этой статье тестировалось с параметрами -m screen:droid2, portrait, scale=.75 что один в один соответствует моему реальному устройству.
devices = {
# device: (name, width, height, dpi, density)
'onex': ('HTC One X', 1280, 720, 312, 2),
'one': ('HTC One', 1920, 1080, 468, 3),
'onesv': ('HTC One SV', 800, 480, 216, 1.5),
's3': ('Galaxy SIII', 1280, 720, 306, 2),
'note2': ('Galaxy Note II', 1280, 720, 267, 2),
'droid2': ('Motorola Droid 2', 854, 480, 240, 1.5),
'xoom': ('Motorola Xoom', 1280, 800, 149, 1),
'ipad': ('iPad (1 and 2)', 1024, 768, 132, 1),
'ipad3': ('iPad 3', 2048, 1536, 264, 2),
'iphone4': ('iPhone 4', 960, 640, 326, 2),
'iphone5': ('iPhone 5', 1136, 640, 326, 2),
'xperiae': ('Xperia E', 480, 320, 166, 1),
'nexus4': ('Nexus 4', 1280, 768, 320, 2),
'nexus7': ('Nexus 7 (2012 version)', 1280, 800, 216, 1.325),
'nexus7.2': ('Nexus 7 (2013 version)', 1920, 1200, 323, 2),
# taken from design.google.com/devices
# please consider using another data instead of
# a dict for autocompletion to work
# these are all in landscape
'phone_android_one': ('Android One', 854, 480, 218, 1.5),
'phone_htc_one_m8': ('HTC One M8', 1920, 1080, 432, 3.0),
'phone_htc_one_m9': ('HTC One M9', 1920, 1080, 432, 3.0),
'phone_iphone': ('iPhone', 480, 320, 168, 1.0),
'phone_iphone_4': ('iPhone 4', 960, 640, 320, 2.0),
'phone_iphone_5': ('iPhone 5', 1136, 640, 320, 2.0),
'phone_iphone_6': ('iPhone 6', 1334, 750, 326, 2.0),
'phone_iphone_6_plus': ('iPhone 6 Plus', 1920, 1080, 400, 3.0),
'phone_lg_g2': ('LG G2', 1920, 1080, 432, 3.0),
'phone_lg_g3': ('LG G3', 2560, 1440, 533, 3.0),
'phone_moto_g': ('Moto G', 1280, 720, 327, 2.0),
'phone_moto_x': ('Moto X', 1280, 720, 313, 2.0),
'phone_moto_x_2nd_gen': ('Moto X 2nd Gen', 1920, 1080, 432, 3.0),
'phone_nexus_4': ('Nexus 4', 1280, 768, 240, 2.0),
'phone_nexus_5': ('Nexus 5', 1920, 1080, 450, 3.0),
'phone_nexus_5x': ('Nexus 5X', 1920, 1080, 432, 2.6),
'phone_nexus_6': ('Nexus 6', 2560, 1440, 496, 3.5),
'phone_nexus_6p': ('Nexus 6P', 2560, 1440, 514, 3.5),
'phone_samsung_galaxy_note_4': ('Samsung Galaxy Note 4',
2560, 1440, 514, 3.0),
'phone_samsung_galaxy_s5': ('Samsung Galaxy S5', 1920, 1080, 372, 3.0),
'phone_samsung_galaxy_s6': ('Samsung Galaxy S6', 2560, 1440, 576, 4.0),
'phone_sony_xperia_c4': ('Sony Xperia C4', 1920, 1080, 400, 2.0),
'phone_sony_xperia_z_ultra': ('Sony Xperia Z Ultra', 1920, 1080, 348, 2.0),
'phone_sony_xperia_z1_compact': ('Sony Xperia Z1 Compact',
1280, 720, 342, 2.0),
'phone_sony_xperia_z2z3': ('Sony Xperia Z2/Z3', 1920, 1080, 432, 3.0),
'phone_sony_xperia_z3_compact': ('Sony Xperia Z3 Compact',
1280, 720, 313, 2.0),
'tablet_dell_venue_8': ('Dell Venue 8', 2560, 1600, 355, 2.0),
'tablet_ipad': ('iPad', 1024, 768, 132, 1.0),
'tablet_ipad_mini': ('iPad Mini', 1024, 768, 163, 1.0),
'tablet_ipad_mini_retina': ('iPad Mini Retina', 2048, 1536, 326, 2.0),
'tablet_ipad_pro': ('iPad Pro', 2732, 2048, 265, 2.0),
'tablet_ipad_retina': ('iPad Retina', 2048, 1536, 264, 2.0),
'tablet_nexus_10': ('Nexus 10', 2560, 1600, 297, 2.0),
'tablet_nexus_7_12': ('Nexus 7 12', 1280, 800, 216, 1.3),
'tablet_nexus_7_13': ('Nexus 7 13', 1920, 1200, 324, 2.0),
'tablet_nexus_9': ('Nexus 9', 2048, 1536, 288, 2.0),
'tablet_samsung_galaxy_tab_10': ('Samsung Galaxy Tab 10',
1280, 800, 148, 1.0),
'tablet_sony_xperia_z3_tablet': ('Sony Xperia Z3 Tablet',
1920, 1200, 282, 2.0),
'tablet_sony_xperia_z4_tablet': ('Sony Xperia Z4 Tablet',
2560, 1600, 297, 2.0)TodoList()
app.run()
}
Что можно сказать в заключение? Хорош ли Kivy? Бесспорно хорош! Если вы владеете замечательным языком программирования Python, вы без труда сможете делать приложения под мобильные (и не только) платформы с не менее замечательным фреймворком Kivy.
Плюсы разработки приложений с использованием фреймворка Kivy:
- Поскольку мы имеем дело с Python, скорость разработки приложений в разы превышает скорость разработки на любом другом языке программирования или фреймворке.
- Мегатонны уже готовых библиотек Python, которые вы можете использовать в своих проектах: OpenCV, Django, Flask, NumPy, ffmpeg, sqlite3, lxml и тысячи других.
- Поскольку Kivy для отрисовки графики использует OpenGL и GPU, а также собственные виджеты и контроллы, скорость рендера UI очень высока и вы полностью избавлены от головной боли, что присутствует в других фреймворках, которым нужно для реализации некоторых частей интерфейса лезть в нативную часть.
- Вы используете натив только там, где нужен доступ к специфическим функциям платформы, которых просто не может быть в действительно кроссплатформенном фреймворке: например, доступ к геолокации, доступ к камере, технологии BlueTooth…
Реализация доступа к нативному API Android для получения IMEI и модели устройства с помощью PyJnius:
def _get_model_android():
from jnius import autoclass
Build = autoclass('android.os.Build')
return str(Build.DEVICE)
def _get_imei_android():
from jnius import autoclass
Service = autoclass('org.renpy.android.PythonActivity').mActivity
Context = autoclass('android.content.Context')
TelephonyManager = Service.getSystemService(Context.TELEPHONY_SERVICE)
return str(TelephonyManager.getDeviceId())
Для примера — реализация нативного получения IMEI устройства на Java:
import android.content.Context;
import android.telephony.TelephonyManager;
public class GetImeiAndroid {
public String getImeiAndroid()
{
TelephonyManager tm = (TelephonyManager)getSystemService(Context.TELEPHONY_SERVICE);
String IMEINumber = tm.getDeviceId();
return IMEINumber;
}
}
- Вы можете использовать сторонние jar библиотеки в своих проектах, если речь идет об Android.
- Вы полностью владеете всеми событиями происходящими на экране: тач, мультитач, свайп, продавливание и др. событиями без ухода в натив так это является неотъемлемой частью Kivy.
Несмотря на все плюсы, Kivy имеет и ряд недостатков:
- Скорость «холодного старта», то есть, первый запуск приложение от момента, когда все библиотеки будут развернуты, довольно долгий. Последующие — обычные, но дольше, чем натив, зависит от нагрузки процессора мобильного устройства.
- Работа со списками. Можно за пол секунды вывести список размером в 100 000 пунктов (например, карточки пользователей, витрина, цитаты), но с одним условием — все карточки должны быть одинаковой высоты. Если выводить список, например, цитат, с заранее неизвестным количеством текста, но целиком, то за один раз больше десяти пунктов нельзя показывать, так как это займет около 10-15 секунд. В этом случае придется подгружать по 10-15 элементов при прокрутке списка.
- Нельзя вывести текст размер которого превышает 6500 символов (3.5 страницы печатного текста) — получим черный экран. Это решается разбиением текста с последующим его склеиванием, что все равно кажется костыльным. Однако не понятно, кому может прийти в голову выводить такое количество текста за раз. Особенно если речь идет о мобильных платформах.
> Больше статей о Kivy
Виртуальная машина (первое сообщение от ZenCODE) от разработчиков Kivy готовая и настроеная для сборки проектов под обе ветки Python.
Комментарии (33)
Neikist
27.08.2018 12:26После того как разочаровался в мобильной 1с ради интереса стал посматривать в сторону нативной и кроссплатформенной разработки под мобилки.
Посматривал на киви, ибо питон нравится своей выразительностью, но все же фреймворк мне чем то не нравился. Язык разметки, слабый набор стандартных виджетов, скорость (особенно старта, да и в целом), начал больше смотреть в сторону нативной разработки.
А вот недавно наткнулся на довольно интересную вещь: flutter+dart. Пока только изучаю, но выглядит довольно вкусно и на мой взгляд перспективно.
Язык довольно приятен и выразителен, компилируемый в натив, типы, интересная система создания виджетов, много стандартных виджетов выглядящих нативно, по заявлениям авторов лишен некоторых проблем, что в теории позволит выиграть по скорости анимаций и подобного у разработки на java под андроид, вроде как будет основным фреймворком под фуксию, и в последнее время заметны телодвижения гугла по продвижению.
Пока единственный минус что вижу — малое коммьюнити.HeaTTheatR Автор
27.08.2018 12:36Язык разметки, слабый набор стандартных виджетов
Скажите, в чём сильные стороны других языков разметки? Помимо того, что Kv Language решает все существующие задачи в плане UI, помимо этого данных язык разметки всё ещё является Python и поддерживает конструкции этого языка прямо в разметке. В чём слабость? Каких виджетов вам не хватило?Neikist
27.08.2018 12:37Мне в принципе не нравится наличие языков разметки)
HeaTTheatR Автор
27.08.2018 12:46То есть, слабость языка разметки в том, что он является языком разметки?
Neikist
27.08.2018 12:50Язык разметки, слабый набор стандартных виджетов
Про слабость я не говорил, да и неправильно будет, не изучал. Я просто его наличие в минусы записал. А слабость это про стандартные виджеты. Флаттер подкупил наличием нативно выглядящих виджетов из коробки. И работой с виджетами в целом. Довольно интересная система. Хотя конечно это все вопрос вкуса.HeaTTheatR Автор
27.08.2018 17:02А слабость это про стандартные виджеты.
Kivy потому и кроссплатформенный на 99%, что у него нет привязки к конкретной ОС. Разработчики даже отказывались от некоторых решений, например, с работой аудио, только потому, что предлагаемое решение было заявлено под одну конкретную платформу. Это касается и библиотеки KivyMD, которая предоставляет набор виджетов в стиле Material. Да, она хороша, но это не кроссплатформенное решение. Поэтому она не включена в стандартную комплектацию Kivy. Kivy — работает практически везде. Поэтому никакие Material-ы или Flat дизайном не для него. Хотите Material — пишите приложение в этом стиле. Хотите Flat — верстайте в стиле Flat. И то и другое — пожалуйста. Но из коробки есть только страшная серая тема. А дальше вы делаете всё, на что хватит вашей фантазии.
saag
27.08.2018 13:05А вот недавно наткнулся на довольно интересную вещь: flutter+dart. Пока только изучаю, но выглядит довольно вкусно и на мой взгляд перспективно
Это вы навторое пришествие ХристаFuchsi'yu намекаете?:-) Меня вот тоже занимает этот вопрос.Neikist
27.08.2018 13:21В части перспективности да, ходят в сети всякие слухи и подозрения. А вот вкусно и без этого все выглядит) По крайней мере точно хотя бы один-два проектика для себя сделать попробую, и дарт понравился как язык, и архитектура заложенная во фреймворк.
valis
27.08.2018 13:15Спасибо за обзор. Мы сейчас тоже находимся на стадии выбора инструмента кроссплатформеной разработки. Пока остановились на Xamarin (т.к у нас в основном .net разработчики) но и периодически поглядываем в сторону других инструментов.
Вот первое что бы хотелось слышать это примеры успешных крупных проектов, в которых применили данный инструмент, а уж потом сравнение кода.HeaTTheatR Автор
27.08.2018 13:35В конце статьи под спойлером есть
некоторые ссылки на видео проектов, разработаных с помощью Kivy. Их довольно много. В прошлой статье также есть ссылки на видео и приложения. Можете ознакомиться.
aknew
27.08.2018 13:37Для сборки приложения под iOS нужна macOS.
А разве этим не практически любая кроссплатформа страдает? Не думаю, что Kivy тут исключение, во всяком случае, такая проблема была с тем что доводилось пощупать (а довелось пощупать Qt, React, PhoneGap и Intel MOE, последние два реально только пощупать)HeaTTheatR Автор
27.08.2018 13:59Не знаю, не щупал. Завтра должна выйти статья Kivy. Xamarin. React Native. Xamarin. Часть вторая. Думаю, там автор расскажет об этом.
Mirimon
27.08.2018 14:23*спойлер
Xamarin позволяет iOS под виндой собирать.aknew
27.08.2018 14:45А вот это действительно интересно и, возможно, будет для кого-то серьезным плюсом. Не знал. Подождем статьи )
ZAMnoTEX
28.08.2018 12:49Подробнее, пожалуйста. А то приходится работать вот с этой не очень удобной штукой. И до сих пор считал, что по-другому не получится.
HeaTTheatR Автор
28.08.2018 13:00Об этом лучше спрашивать у автора второй части статьи
ZAMnoTEX
29.08.2018 10:41Как подтвердили в комментариях второй части статьи, сборка приложения под iOS невозможна без MacOS и XCode. Это часть политики Apple.
ivan_kov
28.08.2018 21:35Интересно, как обстаят дела у kivy c Rx?
HeaTTheatR Автор
28.08.2018 21:43У всех классов Kivy реализован метод on_. Вам вообще не надо об этом думать, потому что
class MyWidget(Widget): name = StringProperty()
Теперь вам доступен метод on_name, который будет вызываться при каждом изменении поля name
class MyWidget(Widget): ... def on_name(instance_name, property_name): ...
ivan_kov
28.08.2018 22:41Отслеживание изменений значения поля — только одино из полезных использований Rx. Еще упрощается асинхронная работа с операциями ввода-вывода, с потоками (thread), ну и с цепью асинхронных событий.
Да, забыл самое главное: спасибо за интересную статью!HeaTTheatR Автор
28.08.2018 23:49Еще упрощается асинхронная работа с операциями ввода-вывода, с потоками (thread), ну и с цепью асинхронных событий.
Все события в Kivy асинхронны. Ну, почти все. То есть, если вы вызовите диалоговое окно, основной поток приложения спокойно продолжает выполнятся дальше. В начале это было дико, но теперь я настолько к этому привык, что трудно представить, что какое-то событие в приложении может блокировать основной поток.
ivan_kov
28.08.2018 22:43Позволю себе еще один вопрос.
У меня начинается новый проект под Android, рассматриваю возможность спрыгнуть с Java.
Очень интересно узнать вызывает ли затруднения реализация операций в фоне (работа Service, JobService) и обработка каких-либо Intent-ов в BroadcastReceiver?HeaTTheatR Автор
28.08.2018 23:54С работой в фоне нет проблем. Но есть одно "Но". Если вы перезагрузите смартфон, при его последующем включении сервис вашего приложения не запустится. То есть нужно будет хотя бы раз запустить ваше приложение, чтобы дать старт сервису. Потом можете его убивать — сервис продолжает жить. До следующей перезагрузки. В этой статье автор утверждает обратное: можно запускать сервис Kivy со стартом смартфона. Но мне это проверить не удалось, потому что я так и не смог собрать пакет по этим гайдам.
Shatten55
29.08.2018 23:43Тоже пишем свои приложения на Kivy. Очень понравился именно своими возможности из «конструктора» собрать все, что хочется самому.
Можно небольшой вопрос, столкнулся с проблемой мультипроцессности (библиотека multiprocessing, не threading) после сборки PyInstaller-ом в Windows. Через некоторое время зависает главный процесс с Kivy. Долго искал решение, так и не нашел. Решил проблему написанием приложения на threading. Может быть слышали о таких проблемах?HeaTTheatR Автор
29.08.2018 23:46Нет, с такой проблемой не сталкивался. Нужно знать архитектуру вашего приложения и выполняемые им задачи.
Lietto
На волне хайпа могли бы и Flutter захватить
HeaTTheatR Автор
Ну, кто хотел, тот присоединился. Хотя мы не офишировали данное предприятие.