Skip to content

Commit 8734389

Browse files
authored
Merge pull request #1170 from t-arn/android_scrollcontainer
Implementation of ScrollContainer for Android
2 parents 3c130c5 + 29de34a commit 8734389

File tree

5 files changed

+112
-13
lines changed

5 files changed

+112
-13
lines changed

examples/scrollcontainer/scrollcontainer/app.py

+26-13
Original file line numberDiff line numberDiff line change
@@ -1,39 +1,52 @@
11
import toga
2-
from toga.constants import COLUMN
2+
from toga.constants import COLUMN, ROW
33
from toga.style import Pack
44

55

66
class Item(toga.Box):
77
def __init__(self, text):
88
super().__init__(style=Pack(direction=COLUMN))
99

10-
label = toga.Label(text)
11-
12-
hline = toga.Divider()
13-
hline.style.padding_top = 5
14-
hline.style.padding_bottom = 5
15-
16-
self.add(label, hline)
10+
row = toga.Box(style=Pack(direction=ROW))
11+
for x in range(10):
12+
label = toga.Label(text+", "+str(x), style=Pack(padding_right=10))
13+
row.add(label)
14+
self.add(row)
1715

1816

1917
class ScrollContainerApp(toga.App):
20-
def startup(self):
18+
vscrolling = True
19+
hscrolling = False
20+
scroller = None
2121

22+
def startup(self):
2223
box = toga.Box()
2324
box.style.direction = COLUMN
2425
box.style.padding = 10
26+
self.scroller = toga.ScrollContainer(horizontal=self.hscrolling, vertical=self.vscrolling)
27+
switch_box = toga.Box(style=Pack(direction=ROW))
28+
switch_box.add(toga.Switch('vertical scrolling', is_on=self.vscrolling, on_toggle=self.handle_vscrolling))
29+
switch_box.add(toga.Switch('horizontal scrolling', is_on=self.hscrolling, on_toggle=self.handle_hscrolling))
30+
box.add(switch_box)
2531

2632
for x in range(100):
2733
label_text = 'Label {}'.format(x)
2834
box.add(Item(label_text))
2935

30-
scroller = toga.ScrollContainer(horizontal=False)
31-
scroller.content = box
36+
self.scroller.content = box
3237

33-
self.main_window = toga.MainWindow(self.name)
34-
self.main_window.content = scroller
38+
self.main_window = toga.MainWindow(self.name, size=(400, 700))
39+
self.main_window.content = self.scroller
3540
self.main_window.show()
3641

42+
def handle_hscrolling(self, widget):
43+
self.hscrolling = widget.is_on
44+
self.scroller.horizontal = self.hscrolling
45+
46+
def handle_vscrolling(self, widget):
47+
self.vscrolling = widget.is_on
48+
self.scroller.vertical = self.vscrolling
49+
3750

3851
def main():
3952
return ScrollContainerApp('ScrollContainer', 'org.beeware.widgets.scrollcontainer')

src/android/toga_android/factory.py

+2
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
from .widgets.multilinetextinput import MultilineTextInput
1313
from .widgets.passwordinput import PasswordInput
1414
from .widgets.selection import Selection
15+
from .widgets.scrollcontainer import ScrollContainer
1516
from .widgets.slider import Slider
1617
from .widgets.switch import Switch
1718
from .widgets.textinput import TextInput
@@ -38,6 +39,7 @@ def not_implemented(feature):
3839
"PasswordInput",
3940
"Selection",
4041
"Slider",
42+
"ScrollContainer",
4143
"Switch",
4244
"TextInput",
4345
"WebView",
+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from rubicon.java import JavaClass, JavaInterface
2+
3+
MotionEvent = JavaClass("android/view/MotionEvent")
4+
View__OnTouchListener = JavaInterface("android/view/View$OnTouchListener")

src/android/toga_android/libs/android_widgets.py

+1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
DialogInterface__OnClickListener = JavaInterface("android/content/DialogInterface$OnClickListener")
99
EditText = JavaClass("android/widget/EditText")
1010
Gravity = JavaClass("android/view/Gravity")
11+
HorizontalScrollView = JavaClass("android/widget/HorizontalScrollView")
1112
ImageView = JavaClass("android/widget/ImageView")
1213
ImageView__ScaleType = JavaClass("android/widget/ImageView$ScaleType")
1314
InputType = JavaClass("android/text/InputType")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
from travertino.size import at_least
2+
3+
from ..libs import android_widgets, android
4+
from .base import Widget
5+
from toga_android.window import AndroidViewport
6+
7+
8+
class TogaOnTouchListener(android.View__OnTouchListener):
9+
is_scrolling_enabled = True
10+
11+
def __init__(self):
12+
super().__init__()
13+
14+
def onTouch(self, view, motion_event):
15+
if self.is_scrolling_enabled:
16+
return view.onTouchEvent(motion_event)
17+
else:
18+
return True
19+
20+
21+
class ScrollContainer(Widget):
22+
vScrollListener = None
23+
hScrollView = None
24+
hScrollListener = None
25+
26+
def create(self):
27+
vScrollView = android_widgets.ScrollView(self._native_activity)
28+
vScrollView_layout_params = android_widgets.LinearLayout__LayoutParams(
29+
android_widgets.LinearLayout__LayoutParams.MATCH_PARENT,
30+
android_widgets.LinearLayout__LayoutParams.MATCH_PARENT
31+
)
32+
vScrollView_layout_params.gravity = android_widgets.Gravity.TOP
33+
vScrollView.setLayoutParams(vScrollView_layout_params)
34+
self.vScrollListener = TogaOnTouchListener()
35+
self.vScrollListener.is_scrolling_enabled = self.interface.vertical
36+
vScrollView.setOnTouchListener(self.vScrollListener)
37+
self.native = vScrollView
38+
self.hScrollView = android_widgets.HorizontalScrollView(self._native_activity)
39+
hScrollView_layout_params = android_widgets.LinearLayout__LayoutParams(
40+
android_widgets.LinearLayout__LayoutParams.MATCH_PARENT,
41+
android_widgets.LinearLayout__LayoutParams.MATCH_PARENT
42+
)
43+
hScrollView_layout_params.gravity = android_widgets.Gravity.LEFT
44+
self.hScrollListener = TogaOnTouchListener()
45+
self.hScrollListener.is_scrolling_enabled = self.interface.horizontal
46+
self.hScrollView.setOnTouchListener(self.hScrollListener)
47+
vScrollView.addView(self.hScrollView, hScrollView_layout_params)
48+
if self.interface.content is not None:
49+
self.set_content(self.interface.content)
50+
51+
def set_content(self, widget):
52+
widget.viewport = AndroidViewport(widget.native)
53+
content_view_params = android_widgets.LinearLayout__LayoutParams(
54+
android_widgets.LinearLayout__LayoutParams.MATCH_PARENT,
55+
android_widgets.LinearLayout__LayoutParams.MATCH_PARENT
56+
)
57+
widget_parent = widget.native.getParent()
58+
if widget_parent is not None:
59+
widget_parent.removeView(widget.native)
60+
self.hScrollView.addView(widget.native, content_view_params)
61+
for child in widget.interface.children:
62+
child._impl.container = widget
63+
64+
def set_vertical(self, value):
65+
self.vScrollListener.is_scrolling_enabled = value
66+
67+
def set_horizontal(self, value):
68+
self.hScrollListener.is_scrolling_enabled = value
69+
70+
def rehint(self):
71+
# Android can crash when rendering some widgets until they have their layout params set. Guard for that case.
72+
if self.native.getLayoutParams() is None:
73+
return
74+
self.native.measure(
75+
android_widgets.View__MeasureSpec.UNSPECIFIED,
76+
android_widgets.View__MeasureSpec.UNSPECIFIED,
77+
)
78+
self.interface.intrinsic.width = at_least(self.native.getMeasuredWidth())
79+
self.interface.intrinsic.height = self.native.getMeasuredHeight()

0 commit comments

Comments
 (0)