浏览代码

Merge pull request #187 from Unity-Technologies/handle_touches_on_mobile

Handle touches on mobile
/main
GitHub 3 年前
当前提交
6f52260c
共有 14 个文件被更改,包括 243 次插入323 次删除
  1. 38
      com.unity.uiwidgets/Runtime/Plugins/x86_64/libUIWidgets.dll.meta
  2. 8
      com.unity.uiwidgets/Runtime/engine/UIWidgetsPanelWrapper.cs
  3. 2
      engine/Scripts/lib_build.py
  4. 179
      engine/src/shell/platform/unity/android/uiwidgets_panel.cc
  5. 43
      engine/src/shell/platform/unity/android/uiwidgets_panel.h
  6. 40
      engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.h
  7. 178
      engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.mm
  8. 3
      engine/src/shell/platform/unity/darwin/macos/uiwidgets_panel.mm
  9. 3
      engine/src/shell/platform/unity/windows/uiwidgets_panel.cc
  10. 72
      Samples/UIWidgetsSamples_2019_4/Assets/WidgetsSample/MobileTouchSample.cs

38
com.unity.uiwidgets/Runtime/Plugins/x86_64/libUIWidgets.dll.meta


validateReferences: 1
platformData:
- first:
: Any
second:
enabled: 0
settings:
Exclude Android: 1
Exclude Editor: 0
Exclude Linux64: 0
Exclude OSXUniversal: 0
Exclude Win: 0
Exclude Win64: 0
Exclude iOS: 1
- first:
Android: Android
second:
enabled: 0
settings:
CPU: ARMv7
- first:
enabled: 1
enabled: 0
enabled: 0
enabled: 1
OS: AnyOS
CPU: x86_64
CPU: AnyCPU
enabled: 0
enabled: 1
enabled: 0
enabled: 1
settings:
CPU: None
- first:

settings:
CPU: AnyCPU
- first:
iPhone: iOS
second:
enabled: 0
settings:
AddToEmbeddedBinaries: false
CPU: AnyCPU
CompileFlags:
FrameworkDependencies:
userData:
assetBundleName:
assetBundleVariant:

8
com.unity.uiwidgets/Runtime/engine/UIWidgetsPanelWrapper.cs


if (pos == null) {
return;
}
UIWidgetsPanel_onMouseMove(ptr: _ptr, x: pos.Value.x, y: pos.Value.y);
UIWidgetsPanel_onMouseMove(ptr: _ptr, x: pos.Value.x, y: pos.Value.y, -1);
}
public void OnMouseScroll(Vector2 delta, Vector2? pos) {

// mouse event
if (pointerId < 0) {
UIWidgetsPanel_onMouseMove(ptr: _ptr, x: pos.Value.x, y: pos.Value.y);
UIWidgetsPanel_onMouseMove(ptr: _ptr, x: pos.Value.x, y: pos.Value.y, pointerId);
}
}

static extern void UIWidgetsPanel_onMouseUp(IntPtr ptr, float x, float y, int button);
[DllImport(dllName: NativeBindings.dllName)]
static extern void UIWidgetsPanel_onMouseMove(IntPtr ptr, float x, float y);
static extern void UIWidgetsPanel_onMouseMove(IntPtr ptr, float x, float y, int button);
[DllImport(dllName: NativeBindings.dllName)]
static extern void UIWidgetsPanel_onMouseLeave(IntPtr ptr);

2
engine/Scripts/lib_build.py


global flutter_root_path
flutter_root_path = os.getenv('FLUTTER_ROOT_PATH', 'null')
if flutter_root_path == 'null':
os.environ["FLUTTER_ROOT_PATH"] = engine_path + "/engine/src"
os.environ["FLUTTER_ROOT_PATH"] = os.path.join(engine_path, "engine","src")
flutter_root_path = os.getenv('FLUTTER_ROOT_PATH')
else:
print("This environment variable has been set, skip")

179
engine/src/shell/platform/unity/android/uiwidgets_panel.cc


vsync_batons_.push_back(baton);
}
void UIWidgetsPanel::SetEventPhaseFromCursorButtonState(
UIWidgetsPointerEvent *event_data)
{
MouseState state = GetMouseState();
event_data->phase = state.buttons == 0
? state.state_is_down ? UIWidgetsPointerPhase::kUp
: UIWidgetsPointerPhase::kHover
: state.state_is_down ? UIWidgetsPointerPhase::kMove
: UIWidgetsPointerPhase::kDown;
}
void UIWidgetsPanel::dispatchTouches(float x, float y, int button, UIWidgetsTouchPhase phase)
{
PointerData pointer_data;
pointer_data.Clear();
void UIWidgetsPanel::SendMouseMove(float x, float y)
{
UIWidgetsPointerEvent event = {};
event.x = x;
event.y = y;
SetEventPhaseFromCursorButtonState(&event);
SendPointerEventWithData(event);
}
auto packet = std::make_unique<PointerDataPacket>(1);
void UIWidgetsPanel::SendMouseDown(float x, float y)
{
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
SetMouseStateDown(true);
}
pointer_data.time_stamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
pointer_data.change = UIWidgetsPanel::PointerDataChangeFromUITouchPhase(phase);
pointer_data.kind = UIWidgetsPanel::DeviceKindFromTouchType();
pointer_data.device = -button;
pointer_data.pointer_identifier = 0;
pointer_data.physical_x = x;
pointer_data.physical_y = y;
pointer_data.physical_delta_x = 0.0;
pointer_data.physical_delta_y = 0.0;
pointer_data.pressure = 1.0;
pointer_data.pressure_max = 1.0;
pointer_data.signal_kind = PointerData::SignalKind::kNone;
packet->SetPointerData(0, pointer_data);
void UIWidgetsPanel::SendMouseUp(float x, float y)
{
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
if (event.phase == UIWidgetsPointerPhase::kUp)
{
SetMouseStateDown(false);
}
}
reinterpret_cast<EmbedderEngine*>(engine_)->DispatchPointerDataPacket(
std::move(packet));
}
void UIWidgetsPanel::SendMouseLeave()
{
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kRemove;
SendPointerEventWithData(event);
PointerData::Change UIWidgetsPanel::PointerDataChangeFromUITouchPhase(UIWidgetsTouchPhase phase)
{
switch(phase) {
case TouchBegan:
return PointerData::Change::kDown;
case TouchMoved:
return PointerData::Change::kMove;
case TouchEnded:
return PointerData::Change::kUp;
case TouchCancelled:
return PointerData::Change::kCancel;
default:
std::cerr << "Unhandled touch phase: " << phase << std::endl;
break;
void UIWidgetsPanel::SendPointerEventWithData(
const UIWidgetsPointerEvent &event_data)
{
MouseState mouse_state = GetMouseState();
// If sending anything other than an add, and the pointer isn't already added,
// synthesize an add to satisfy Flutter's expectations about events.
if (!mouse_state.state_is_added &&
event_data.phase != UIWidgetsPointerPhase::kAdd)
{
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kAdd;
event.x = event_data.x;
event.y = event_data.y;
event.buttons = 0;
SendPointerEventWithData(event);
}
// Don't double-add (e.g., if events are delivered out of order, so an add has
// already been synthesized).
if (mouse_state.state_is_added &&
event_data.phase == UIWidgetsPointerPhase::kAdd)
{
return;
}
return PointerData::Change::kCancel;
}
UIWidgetsPointerEvent event = event_data;
event.device_kind = kUIWidgetsPointerDeviceKindMouse;
event.buttons = mouse_state.buttons;
// Set metadata that's always the same regardless of the event.
event.struct_size = sizeof(event);
event.timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
UIWidgetsEngineSendPointerEvent(engine_, &event, 1);
if (event_data.phase == UIWidgetsPointerPhase::kAdd)
{
SetMouseStateAdded(true);
}
else if (event_data.phase == UIWidgetsPointerPhase::kRemove)
{
SetMouseStateAdded(false);
ResetMouseState();
}
}
PointerData::DeviceKind UIWidgetsPanel::DeviceKindFromTouchType()
{
return PointerData::DeviceKind::kTouch;
}
void UIWidgetsPanel::OnKeyDown(int keyCode, bool isKeyDown)
{

}
}
void UIWidgetsPanel::OnMouseMove(float x, float y)
void UIWidgetsPanel::OnMouseMove(float x, float y, int button)
SendMouseMove(x, y);
}
}
static uint64_t ConvertToUIWidgetsButton(int button)
{
switch (button)
{
case -1:
return kUIWidgetsPointerButtonMousePrimary;
case -2:
return kUIWidgetsPointerButtonMouseSecondary;
case -3:
return kUIWidgetsPointerButtonMouseMiddle;
dispatchTouches(x, y, button, TouchMoved);
std::cerr << "Mouse button not recognized: " << button << std::endl;
return 0;
}
void UIWidgetsPanel::OnMouseDown(float x, float y, int button)

uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0)
{
uint64_t mouse_buttons = GetMouseState().buttons | uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseDown(x, y);
}
dispatchTouches(x, y, button, TouchBegan);
}
}

{
uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0)
{
uint64_t mouse_buttons = GetMouseState().buttons & ~uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseUp(x, y);
}
dispatchTouches(x, y, button, TouchEnded);
if (process_events_)
{
SendMouseLeave();
}
//there should not be mouse leave events on iPhone!
std::cerr << "Invalid input on iPhone: mouse leave" << std::endl;
}
UIWIDGETS_API(UIWidgetsPanel *)

}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseMove(UIWidgetsPanel *panel, float x, float y)
UIWidgetsPanel_onMouseMove(UIWidgetsPanel *panel, float x, float y, int button)
panel->OnMouseMove(x, y);
panel->OnMouseMove(x, y, button);
}
UIWIDGETS_API(void)

43
engine/src/shell/platform/unity/android/uiwidgets_panel.h


#include <flutter/fml/memory/ref_counted.h>
#include "shell/platform/unity/gfx_worker_task_runner.h"
#include "lib/ui/window/pointer_data.h"
#include "runtime/mono_api.h"
#include "unity_surface_manager.h"
#include "android_task_runner.h"

GameObjectPanel = 1,
EditorWindowPanel = 2
};
struct MouseState {
bool state_is_down = false;
bool state_is_added = false;
uint64_t buttons = 0;
enum UIWidgetsTouchPhase
{
TouchBegan,
TouchEnded,
TouchMoved,
TouchCancelled
};
class UIWidgetsPanel : public fml::RefCountedThreadSafe<UIWidgetsPanel> {

void OnKeyDown(int keyCode, bool isKeyDown);
void OnMouseMove(float x, float y);
void OnMouseMove(float x, float y, int button);
void OnMouseDown(float x, float y, int button);

private:
UIWidgetsPanel(Mono_Handle handle, UIWidgetsWindowType window_type, EntrypointCallback entrypoint_callback);
MouseState GetMouseState() { return mouse_state_; }
void ResetMouseState() { mouse_state_ = MouseState(); }
void dispatchTouches(float x, float y, int button, UIWidgetsTouchPhase evtType);
void SetMouseStateDown(bool is_down) { mouse_state_.state_is_down = is_down; }
void SetMouseStateAdded(bool is_added) {
mouse_state_.state_is_added = is_added;
}
void SetMouseButtons(uint64_t buttons) { mouse_state_.buttons = buttons; }
void SendMouseMove(float x, float y);
void SendMouseDown(float x, float y);
void SendMouseUp(float x, float y);
void SendMouseLeave();
void SetEventPhaseFromCursorButtonState(UIWidgetsPointerEvent* event_data);
void SendPointerEventWithData(const UIWidgetsPointerEvent& event_data);
static PointerData::Change PointerDataChangeFromUITouchPhase(UIWidgetsTouchPhase phase);
static PointerData::DeviceKind DeviceKindFromTouchType();
Mono_Handle handle_;
UIWidgetsWindowType window_type_;
EntrypointCallback entrypoint_callback_;

std::vector<intptr_t> vsync_batons_;
MouseState mouse_state_;
bool process_events_ = false;
};

40
engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.h


#include <flutter/fml/memory/ref_counted.h>
#include "shell/platform/unity/gfx_worker_task_runner.h"
#include "lib/ui/window/pointer_data.h"
#include "runtime/mono_api.h"
#include "cocoa_task_runner.h"
#include "unity_surface_manager.h"

EditorWindowPanel = 2
};
struct MouseState {
bool state_is_down = false;
bool state_is_added = false;
uint64_t buttons = 0;
enum UIWidgetsTouchPhase
{
TouchBegan,
TouchEnded,
TouchMoved,
TouchCancelled
};
class UIWidgetsPanel : public fml::RefCountedThreadSafe<UIWidgetsPanel> {

void OnKeyDown(int keyCode, bool isKeyDown);
void OnMouseMove(float x, float y);
void OnMouseMove(float x, float y, int button);
void OnScroll(float x, float y, float px, float py);

void CreateInternalUIWidgetsEngine(size_t width, size_t height, float device_pixel_ratio, const char* streaming_assets_path, const char* settings);
MouseState GetMouseState() { return mouse_state_; }
void ResetMouseState() { mouse_state_ = MouseState(); }
void SetMouseStateDown(bool is_down) { mouse_state_.state_is_down = is_down; }
void SetMouseStateAdded(bool is_added) {
mouse_state_.state_is_added = is_added;
}
void SetMouseButtons(uint64_t buttons) { mouse_state_.buttons = buttons; }
void SendMouseMove(float x, float y);
void SendMouseDown(float x, float y);
void SendMouseUp(float x, float y);
void dispatchTouches(float x, float y, int button, UIWidgetsTouchPhase evtType);
void SendMouseLeave();
static PointerData::Change PointerDataChangeFromUITouchPhase(UIWidgetsTouchPhase phase);
void SendScroll(float delta_x, float delta_y, float px, float py);
void SetEventPhaseFromCursorButtonState(UIWidgetsPointerEvent* event_data);
void SendPointerEventWithData(const UIWidgetsPointerEvent& event_data);
static PointerData::DeviceKind DeviceKindFromTouchType();
Mono_Handle handle_;
EntrypointCallback entrypoint_callback_;

std::vector<intptr_t> vsync_batons_;
MouseState mouse_state_;
bool process_events_ = false;
};

178
engine/src/shell/platform/unity/darwin/ios/uiwidgets_panel.mm


vsync_batons_.push_back(baton);
}
void UIWidgetsPanel::SetEventPhaseFromCursorButtonState(
UIWidgetsPointerEvent* event_data) {
MouseState state = GetMouseState();
event_data->phase = state.buttons == 0
? state.state_is_down ? UIWidgetsPointerPhase::kUp
: UIWidgetsPointerPhase::kHover
: state.state_is_down ? UIWidgetsPointerPhase::kMove
: UIWidgetsPointerPhase::kDown;
}
void UIWidgetsPanel::dispatchTouches(float x, float y, int button, UIWidgetsTouchPhase phase)
{
PointerData pointer_data;
pointer_data.Clear();
void UIWidgetsPanel::SendMouseMove(float x, float y) {
UIWidgetsPointerEvent event = {};
event.x = x;
event.y = y;
SetEventPhaseFromCursorButtonState(&event);
SendPointerEventWithData(event);
}
auto packet = std::make_unique<PointerDataPacket>(1);
void UIWidgetsPanel::SendMouseDown(float x, float y) {
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
SetMouseStateDown(true);
}
pointer_data.time_stamp = std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
pointer_data.change = UIWidgetsPanel::PointerDataChangeFromUITouchPhase(phase);
pointer_data.kind = UIWidgetsPanel::DeviceKindFromTouchType();
pointer_data.device = -button;
pointer_data.pointer_identifier = 0;
pointer_data.physical_x = x;
pointer_data.physical_y = y;
pointer_data.physical_delta_x = 0.0;
pointer_data.physical_delta_y = 0.0;
pointer_data.pressure = 1.0;
pointer_data.pressure_max = 1.0;
pointer_data.signal_kind = PointerData::SignalKind::kNone;
packet->SetPointerData(0, pointer_data);
void UIWidgetsPanel::SendMouseUp(float x, float y) {
UIWidgetsPointerEvent event = {};
SetEventPhaseFromCursorButtonState(&event);
event.x = x;
event.y = y;
SendPointerEventWithData(event);
if (event.phase == UIWidgetsPointerPhase::kUp) {
SetMouseStateDown(false);
}
}
void UIWidgetsPanel::SendMouseLeave() {
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kRemove;
SendPointerEventWithData(event);
}
void UIWidgetsPanel::SendScroll(float delta_x, float delta_y, float px, float py) {
UIWidgetsPointerEvent event = {};
// TODO: this is a native method, use unity position instead.
event.x = px;
event.y = py;
SetEventPhaseFromCursorButtonState(&event);
event.signal_kind = UIWidgetsPointerSignalKind::kUIWidgetsPointerSignalKindScroll;
// TODO: See if this can be queried from the OS; this value is chosen
// arbitrarily to get something that feels reasonable.
const int kScrollOffsetMultiplier = 20;
event.scroll_delta_x = delta_x * kScrollOffsetMultiplier;
event.scroll_delta_y = delta_y * kScrollOffsetMultiplier;
SendPointerEventWithData(event);
reinterpret_cast<EmbedderEngine*>(engine_)->DispatchPointerDataPacket(
std::move(packet));
void UIWidgetsPanel::SendPointerEventWithData(
const UIWidgetsPointerEvent& event_data) {
MouseState mouse_state = GetMouseState();
// If sending anything other than an add, and the pointer isn't already added,
// synthesize an add to satisfy Flutter's expectations about events.
if (!mouse_state.state_is_added &&
event_data.phase != UIWidgetsPointerPhase::kAdd) {
UIWidgetsPointerEvent event = {};
event.phase = UIWidgetsPointerPhase::kAdd;
event.x = event_data.x;
event.y = event_data.y;
event.buttons = 0;
SendPointerEventWithData(event);
}
// Don't double-add (e.g., if events are delivered out of order, so an add has
// already been synthesized).
if (mouse_state.state_is_added &&
event_data.phase == UIWidgetsPointerPhase::kAdd) {
return;
PointerData::Change UIWidgetsPanel::PointerDataChangeFromUITouchPhase(UIWidgetsTouchPhase phase)
{
switch(phase) {
case TouchBegan:
return PointerData::Change::kDown;
case TouchMoved:
return PointerData::Change::kMove;
case TouchEnded:
return PointerData::Change::kUp;
case TouchCancelled:
return PointerData::Change::kCancel;
default:
std::cerr << "Unhandled touch phase: " << phase << std::endl;
break;
UIWidgetsPointerEvent event = event_data;
event.device_kind = kUIWidgetsPointerDeviceKindMouse;
event.buttons = mouse_state.buttons;
return PointerData::Change::kCancel;
}
// Set metadata that's always the same regardless of the event.
event.struct_size = sizeof(event);
event.timestamp =
std::chrono::duration_cast<std::chrono::microseconds>(
std::chrono::high_resolution_clock::now().time_since_epoch())
.count();
UIWidgetsEngineSendPointerEvent(engine_, &event, 1);
if (event_data.phase == UIWidgetsPointerPhase::kAdd) {
SetMouseStateAdded(true);
} else if (event_data.phase == UIWidgetsPointerPhase::kRemove) {
SetMouseStateAdded(false);
ResetMouseState();
}
PointerData::DeviceKind UIWidgetsPanel::DeviceKindFromTouchType()
{
return PointerData::DeviceKind::kTouch;
}
void UIWidgetsPanel::OnKeyDown(int keyCode, bool isKeyDown) {

}
}
void UIWidgetsPanel::OnMouseMove(float x, float y) {
void UIWidgetsPanel::OnMouseMove(float x, float y, int button) {
SendMouseMove(x, y);
dispatchTouches(x, y, button, TouchMoved);
if (process_events_) {
SendScroll(x, y, px, py);
}
}
static uint64_t ConvertToUIWidgetsButton(int button) {
switch (button) {
case -1:
return kUIWidgetsPointerButtonMousePrimary;
case -2:
return kUIWidgetsPointerButtonMouseSecondary;
case -3:
return kUIWidgetsPointerButtonMouseMiddle;
}
std::cerr << "Mouse button not recognized: " << button << std::endl;
return 0;
//there should not be scroll events on iPhone!
std::cerr << "Invalid input on iPhone: scroll" << std::endl;
uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0) {
uint64_t mouse_buttons = GetMouseState().buttons | uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseDown(x, y);
}
dispatchTouches(x, y, button, TouchBegan);
uint64_t uiwidgets_button = ConvertToUIWidgetsButton(button);
if (uiwidgets_button != 0) {
uint64_t mouse_buttons = GetMouseState().buttons & ~uiwidgets_button;
SetMouseButtons(mouse_buttons);
SendMouseUp(x, y);
}
dispatchTouches(x, y, button, TouchEnded);
if (process_events_) {
SendMouseLeave();
}
//there should not be mouse leave events on iPhone!
std::cerr << "Invalid input on iPhone: mouse leave" << std::endl;
}
UIWIDGETS_API(UIWidgetsPanel*)

}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y) {
panel->OnMouseMove(x, y);
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y, int button) {
panel->OnMouseMove(x, y, button);
}
UIWIDGETS_API(void)

3
engine/src/shell/platform/unity/darwin/macos/uiwidgets_panel.mm


}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y) {
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y, int button) {
//button id is not useful for desktop since the motions happens on the mouse (including all buttons)
panel->OnMouseMove(x, y);
}

3
engine/src/shell/platform/unity/windows/uiwidgets_panel.cc


}
UIWIDGETS_API(void)
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y) {
UIWidgetsPanel_onMouseMove(UIWidgetsPanel* panel, float x, float y, int button) {
//button id is not useful for desktop since the motions happens on the mouse (including all buttons)
panel->OnMouseMove(x, y);
}

72
Samples/UIWidgetsSamples_2019_4/Assets/WidgetsSample/MobileTouchSample.cs


using System;
using System.Collections.Generic;
using uiwidgets;
using Unity.UIWidgets.engine;
using Unity.UIWidgets.foundation;
using Unity.UIWidgets.material;
using Unity.UIWidgets.rendering;
using Unity.UIWidgets.ui;
using Unity.UIWidgets.widgets;
using UnityEngine;
using UnityEngine.Networking;
using Image = Unity.UIWidgets.widgets.Image;
using ui_ = Unity.UIWidgets.widgets.ui_;
namespace UIWidgetsSample
{
public class MobileTouchSample : UIWidgetsPanel
{
protected override void main()
{
ui_.runApp(new MaterialApp(
title: "Http Request Sample",
home: new Scaffold(
body: new MobileTouchWidget()
)
));
}
}
public class MobileTouchWidget : StatefulWidget
{
public MobileTouchWidget(Key key = null) : base(key)
{
}
public override State createState()
{
return new MobileTouchWidgetState();
}
}
class MobileTouchWidgetState : State<MobileTouchWidget>
{
private float scale = 1;
private int frameNo = 0;
private float rotation = 0;
public override Widget build(BuildContext context)
{
return new Column(
crossAxisAlignment: CrossAxisAlignment.center,
children: new List<Widget>()
{
new Text("Frame: " + frameNo),
new Text("Scale: " + scale),
new Text("Rotation: " + rotation),
new GestureDetector(
child: new Container(height: 300, color: Colors.blue),
onScaleStart: details => { },
onScaleUpdate: details =>
{
scale = details.scale;
rotation = details.rotation;
frameNo += 1;
setState(() => { });
},
onScaleEnd: details => { }
)
});
}
}
}

部分文件因为文件数量过多而无法显示

正在加载...
取消
保存