Используйте Ace Plugin, чтобы сделать iOS/Android TabBar & TableView с использованием специфичного для платформы Javascript?
Я работаю над приложением JavaScript для iOS, Android и Магазина Windows, используя VS2015 TACO для iOS и Android. Я только что установил плагин Ace, и я пробираюсь через примеры кода Javascript/Ace. Для iOS и Android я хочу использовать собственный пользовательский интерфейс для: 1) нижней панели вкладок и 2) прокручиваемого списка выбора со значками и текстом (в основном, WinJS.UI ListView). Видео Ace Intro на YouTube показывает пример вкладки в разметке XAML, но я не знаком с XAML. Может кто-нибудь указать мне образцы Javascript Ace Plugin для пользовательского интерфейса панели вкладок и табличного представления для iOS и Android? Я был бы благодарен.
ОБНОВЛЕНИЕ #1 Спасибо Адаму Натану. Используя его код, я получаю закрепленную панель вкладок в симуляторе Mac iPad. Он прекрасно смотрится как в книжной, так и в альбомной ориентации. Я использую этот код для включения и выключения:
TEST_ACE.dockTabs = function () {
if (TEST_ACE.pageTabBar == null) {
// Surround the WebView with native UI
TEST_ACE.pageTabBar = TEST_ACE.buildDockedTabsWithoutXaml();
// Replace the WebView with the loaded native page
// (For the title setting in XAML to work on Android, ShowTitle must be
// set to true in the Android section of config.xml.)
ace.getHostPage().setContent(TEST_ACE.pageTabBar);
// Reparent the WebView inside the native page
TEST_ACE.pageTabBar.setContent(ace.getHostWebView());
// Save the native page so we can set its title as tabs are clicked
_nativePage = TEST_ACE.pageTabBar;
}
else {
ace.getHostPage().setContent(ace.getHostWebView());
TEST_ACE.pageTabBar = null;
}
}
Когда я отключаю панель вкладок, я все еще вижу нижний блок, в котором раньше находились значки, но теперь он пуст. То есть, когда я пытаюсь отключить как верхнюю строку заголовка, так и нижнюю панель вкладок, я отклоняю только верхнюю строку заголовка, но нижний блок, теперь пустой, все еще там. Если я изменю ориентацию (книжная / альбомная), то нижний блок исчезнет. Есть ли лучший способ очистить HostPage с помощью панели вкладок перед его сбросом в HostWebView?
Для Android я обновил файл config.xml, чтобы установить для ShowTitle значение true:
<name>AcePluginTester</name>
<description>A blank project that uses Apache Cordova to help you build an app that targets multiple mobile platforms: Android, iOS, Windows, and Windows Phone.</description>
<author href="http://cordova.io" email="dev@cordova.apache.org">Apache Cordova Team </author>
<content src="index.html" />
<access origin="*" />
<vs:features />
<preference name="SplashScreen" value="screen" />
<preference name="windows-target-version" value="8.1" />
<!-- Support for Cordova 5.0.0 plugin system -->
<plugin name="cordova-plugin-whitelist" version="1" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
<preference name="ShowTitle" value="true"/>
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
<preference name="orientation" value="all" />
</platform>
<!--blah blah blah-->
Но когда я запускаю приложение в эмуляторе Android Android, я получаю эту ошибку:
Uncaught Error: Native error: java.lang.RuntimeException: Error setting FrameLayout's UserControl.Content to Windows.UI.Xaml.Controls.Page{6b1647a V.E...... ......I. 0,0-0,0}
at run.ace.IncomingMessages.set(IncomingMessages.java:206)
at run.ace.NativeHost$2.run(NativeHost.java:239)
at android.os.Handler.handleCallback(Handler.java:739)
at android.os.Handler.dispatchMessage(Handler.java:95)
at android.os.Looper.loop(Looper.java:148)
at android.app.ActivityThread.main(ActivityThread.java:5417)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:726)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:616)
Caused by: java.lang.RuntimeException: Cannot set title on the main page in Android unless you set <preference name="ShowTitle" value="true"/> in config.xml.
at run.ace.TabBar.setTitle(TabBar.java:39)
at Windows.UI.Xaml.Controls.Page.updateTitle(Page.java:63)
at Windows.UI.Xaml.Controls.Page.processBars(Page.java:71)
at Windows.UI.Xaml.Controls.ViewGroupHelper.setProperty(ViewGroupHelper.java:51)
at run.ace.IncomingMessages.set(IncomingMessages.java:191)
... 8 more
See http://ace.run/docs/errors for help.
cordova.js (314,13)
Я что-то пропустил для Android?
Еще раз спасибо. Это будет здорово!
ОБНОВЛЕНИЕ № 2
Я переместил предпочтение ShowTitle в корень виджета, и все еще получаю ошибку. Я получаю ошибку как со значением ="True" (в верхнем регистре True), так и со значением ="true". Вот мой файл config.xml в целом:
<?xml version="1.0" encoding="utf-8"?>
<widget xmlns:cdv="http://cordova.apache.org/ns/1.0" xmlns:vs="http://schemas.microsoft.com/appx/2014/htmlapps" id="io.cordova.myapp740bed" version="1.0.1" xmlns="http://www.w3.org/ns/widgets" defaultlocale="en-US">
<name>AcePluginTester</name>
<description>A blank project that uses Apache Cordova to help you build an app that targets multiple mobile platforms: Android, iOS, Windows, and Windows Phone.</description>
<author href="http://cordova.io" email="dev@cordova.apache.org">Apache Cordova Team </author>
<content src="index.html" />
<access origin="*" />
<vs:features />
<preference name="SplashScreen" value="screen" />
<preference name="windows-target-version" value="8.1" />
<!-- Support for Cordova 5.0.0 plugin system -->
<plugin name="cordova-plugin-whitelist" version="1" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<allow-intent href="tel:*" />
<allow-intent href="sms:*" />
<allow-intent href="mailto:*" />
<allow-intent href="geo:*" />
<platform name="android">
<allow-intent href="market:*" />
</platform>
<platform name="ios">
<allow-intent href="itms:*" />
<allow-intent href="itms-apps:*" />
<preference name="orientation" value="all" />
</platform>
<platform name="android">
<icon src="res/icons/android/icon-36-ldpi.png" density="ldpi" />
<icon src="res/icons/android/icon-48-mdpi.png" density="mdpi" />
<icon src="res/icons/android/icon-72-hdpi.png" density="hdpi" />
<icon src="res/icons/android/icon-96-xhdpi.png" density="xhdpi" />
</platform>
<platform name="ios">
<!-- iOS 8.0+ -->
<!-- iPhone 6 Plus -->
<icon src="res/icons/ios/icon-60-3x.png" width="180" height="180" />
<!-- iOS 7.0+ -->
<!-- iPhone / iPod Touch -->
<icon src="res/icons/ios/icon-60.png" width="60" height="60" />
<icon src="res/icons/ios/icon-60-2x.png" width="120" height="120" />
<!-- iPad -->
<icon src="res/icons/ios/icon-76.png" width="76" height="76" />
<icon src="res/icons/ios/icon-76-2x.png" width="152" height="152" />
<!-- iOS 6.1 -->
<!-- Spotlight Icon -->
<icon src="res/icons/ios/icon-40.png" width="40" height="40" />
<icon src="res/icons/ios/icon-40-2x.png" width="80" height="80" />
<!-- iPhone / iPod Touch -->
<icon src="res/icons/ios/icon-57.png" width="57" height="57" />
<icon src="res/icons/ios/icon-57-2x.png" width="114" height="114" />
<!-- iPad -->
<icon src="res/icons/ios/icon-72.png" width="72" height="72" />
<icon src="res/icons/ios/icon-72-2x.png" width="144" height="144" />
<!-- iPhone Spotlight and Settings Icon -->
<icon src="res/icons/ios/icon-small.png" width="29" height="29" />
<icon src="res/icons/ios/icon-small-2x.png" width="58" height="58" />
<!-- iPad Spotlight and Settings Icon -->
<icon src="res/icons/ios/icon-50.png" width="50" height="50" />
<icon src="res/icons/ios/icon-50-2x.png" width="100" height="100" />
</platform>
<platform name="windows">
<icon src="res/icons/windows/Square150x150Logo.scale-100.png" width="150" height="150" />
<icon src="res/icons/windows/Square150x150Logo.scale-240.png" width="360" height="360" />
<icon src="res/icons/windows/Square30x30Logo.scale-100.png" width="30" height="30" />
<icon src="res/icons/windows/Square310x310Logo.scale-100.png" width="310" height="310" />
<icon src="res/icons/windows/Square44x44Logo.scale-100.png" width="44" height="44" />
<icon src="res/icons/windows/Square44x44Logo.scale-240.png" width="106" height="106" />
<icon src="res/icons/windows/Square70x70Logo.scale-100.png" width="70" height="70" />
<icon src="res/icons/windows/Square71x71Logo.scale-100.png" width="71" height="71" />
<icon src="res/icons/windows/Square71x71Logo.scale-240.png" width="170" height="170" />
<icon src="res/icons/windows/StoreLogo.scale-100.png" width="50" height="50" />
<icon src="res/icons/windows/StoreLogo.scale-240.png" width="120" height="120" />
<icon src="res/icons/windows/Wide310x150Logo.scale-100.png" width="310" height="150" />
<icon src="res/icons/windows/Wide310x150Logo.scale-240.png" width="744" height="360" />
</platform>
<platform name="wp8">
<icon src="res/icons/wp8/ApplicationIcon.png" width="62" height="62" />
<icon src="res/icons/wp8/Background.png" width="173" height="173" />
</platform>
<platform name="android">
<splash src="res/screens/android/screen-hdpi-landscape.png" density="land-hdpi" />
<splash src="res/screens/android/screen-ldpi-landscape.png" density="land-ldpi" />
<splash src="res/screens/android/screen-mdpi-landscape.png" density="land-mdpi" />
<splash src="res/screens/android/screen-xhdpi-landscape.png" density="land-xhdpi" />
<splash src="res/screens/android/screen-hdpi-portrait.png" density="port-hdpi" />
<splash src="res/screens/android/screen-ldpi-portrait.png" density="port-ldpi" />
<splash src="res/screens/android/screen-mdpi-portrait.png" density="port-mdpi" />
<splash src="res/screens/android/screen-xhdpi-portrait.png" density="port-xhdpi" />
</platform>
<platform name="ios">
<splash src="res/screens/ios/screen-iphone-portrait.png" width="320" height="480" />
<splash src="res/screens/ios/screen-iphone-portrait-2x.png" width="640" height="960" />
<splash src="res/screens/ios/screen-ipad-portrait.png" width="768" height="1024" />
<splash src="res/screens/ios/screen-ipad-portrait-2x.png" width="1536" height="2048" />
<splash src="res/screens/ios/screen-ipad-landscape.png" width="1024" height="768" />
<splash src="res/screens/ios/screen-ipad-landscape-2x.png" width="2048" height="1536" />
<splash src="res/screens/ios/screen-iphone-568h-2x.png" width="640" height="1136" />
<splash src="res/screens/ios/screen-iphone-portrait-667h.png" width="750" height="1334" />
<splash src="res/screens/ios/screen-iphone-portrait-736h.png" width="1242" height="2208" />
<splash src="res/screens/ios/screen-iphone-landscape-736h.png" width="2208" height="1242" />
</platform>
<platform name="windows">
<splash src="res/screens/windows/SplashScreen.scale-100.png" width="620" height="300" />
<splash src="res/screens/windows/SplashScreen.scale-240.png" width="1152" height="1920" />
<splash src="res/screens/windows/SplashScreenPhone.scale-240.png" width="1152" height="1920" />
</platform>
<platform name="wp8">
<splash src="res/screens/wp8/SplashScreenImage.jpg" width="480" height="800" />
</platform>
<plugin name="cordova-plugin-crosswalk-webview" version="1.5.0" src="https://github.com/crosswalk-project/cordova-plugin-crosswalk-webview" />
<plugin name="cordova-plugin-ace" version="0.0.12" src="https://github.com/Microsoft/ace" />
<engine name="ios" spec="~3.9.2" />
<engine name="android" spec="~4.1.1" />
<plugin name="cordova-plugin-ace" spec="https://github.com/microsoft/ace.git" />
<preference name="ShowTitle" value="true" />
</widget>
Я получаю ошибку даже после запуска "Чистого решения" и "Очистить кэш Cordova".
Я выложу штрих-код вкладки в отдельном обновлении.
ОБНОВЛЕНИЕ № 3 Содержимое моего приложения представляет собой холст WebGL. Когда мой Mac iPad2 Simulator (портрет) впервые рисует, размер окна составляет 768 (ширина) x 1004 (высота), как показано ниже в выводе журнала консоли JavaScript:
Window size: 768 x 1004
debug.js (29,24)
Window size: 768 x 960
debug.js (29,24)
Window size: 768 x 960
debug.js (29,24)
Когда я открываю панель вкладок, она изменяется до высоты 960. Когда я закрываю панель вкладок (оставаясь в портретном положении), высота окна не возвращается к 1004. Таким образом, похоже, что окно не изменяет размеры, когда вкладка Панель закрыта. Как только я поворачиваю устройство в альбомную ориентацию, полное окно перерисовывается, и нижняя полоса исчезает, аналогично, когда я поворачиваюсь назад к книжной ориентации, полный размер окна возвращается к 768 x 1004, и пустой нижней панели также там нет. Вот мой код тестирования (в процессе):
Внутри index.js, когда срабатывает onDeviceReady, мой код вызывает GAME.initialize():
function onDeviceReady() {
// Handle the Cordova pause and resume events
document.addEventListener( 'pause', onPause.bind( this ), false );
document.addEventListener( 'resume', onResume.bind( this ), false );
// TODO: Cordova has been loaded. Perform any initialization that requires Cordova here.
GAME.initialize();
};
Вот код в game.js:
//game.js
var GAME = { NAME: "hiio", VERMAJOR: 0, VERMINOR: 9, DEBUG: true, BUILD: 1, CLASSNAME: "GAME" };
GAME.initialize = function () {
"use strict";
//log if debug
loginit();
log(GAME.NAME + " " + GAME.VERMAJOR + "." + GAME.VERMINOR + (GAME.DEBUG ? " (Debug)" : " (Release)"));
//renderer
var testcanvas = document.getElementById("idcanvas");
if (!GAME.renderer) GAME.renderer = new THREE.WebGLRenderer( { canvas: testcanvas } );
//scene
if (!GAME.scene) GAME.scene = new THREE.Scene();
//camera
GAME.setcamera();
//orbitcontrols
GAME.orbctls = new THREE.OrbitControls(GAME.camera);
// ambient
var al = new THREE.AmbientLight(0x222222, 0.5);
GAME.scene.add(al);
// light
var dl = new THREE.DirectionalLight(0xffffff, 1.0);
dl.position.set(2000, 2000, 500);
GAME.scene.add(dl);
// axes
GAME.scene.add(new THREE.AxisHelper(4000));
// Sphere
var geometry = new THREE.SphereGeometry(500, 24, 8);
var material = new THREE.MeshPhongMaterial({
color: 0x00ffff,
transparent: true,
opacity: 0.7,
});
var mesh = new THREE.Mesh(geometry, material);
GAME.scene.add(mesh);
//FPS
GAME.stats = initStats();
//events
window.addEventListener("resize", GAME.setcamera, false);
//Ace
TEST_ACE.initialize();
animate(); //run
}
//camera
GAME.setcamera = function () {
"use strict";
var c = document.getElementById("idcanvas");
var h = window.innerHeight;
var w = window.innerWidth;
if (!GAME.camera) { //new
GAME.camera = new THREE.PerspectiveCamera(45, w / h, 1, 16000);
GAME.camera.position.set(50, 100, 3000);
GAME.scene.add(GAME.camera);
}
else { //change
GAME.camera.aspect = (w / h);
}
if (GAME.renderer) { //onsize
GAME.renderer.setSize(w, h); //this sets the height of the canvas.
log("Window size: " + window.innerWidth + " x " + window.innerHeight);
}
if (GAME.orbctls) {
GAME.target = new THREE.Vector3(0, 0, 0);
GAME.orbctls.target0 = GAME.target.clone();
GAME.orbctls.reset();
}
GAME.camera.updateProjectionMatrix();
}
function initStats() {
"use strict";
var stats = new Stats();
stats.setMode(0);
stats.domElement.style.position = 'absolute';
stats.domElement.style.left = '0px';
stats.domElement.style.top = '0px';
document.getElementById("Stats-output").appendChild(stats.domElement);
return stats;
}
//animate loop
var banimate = false;
function animate() {
"use strict";
requestAnimationFrame(animate);
if (banimate) { loge("animate re-entry."); return; }
banimate = true;
GAME.renderer.render(GAME.scene, GAME.camera);
GAME.stats.update();
banimate = false;
};
//end
Вот код test_act.js (немного грязно, извините):
//test_ace.js
var TEST_ACE = { CLASSNAME: "TEST_ACE" };
TEST_ACE.popupSegCtrl = null;
TEST_ACE.pageTabBar = null;
TEST_ACE.initialize = function () {
"use strict";
var abutton1 = document.getElementById("idbutton1"); //Tab Bar
abutton1.addEventListener("click", TEST_ACE.dockTabs, false);
return;
}
TEST_ACE.buildDockedTabsWithoutXaml = function () {
var page = new ace.Page();
page.setTitle("Docked");
var tabBar = new ace.TabBar();
var b1 = new ace.AppBarButton();
var b2 = new ace.AppBarButton();
var b3 = new ace.AppBarButton();
var b4 = new ace.AppBarButton();
var b5 = new ace.AppBarButton();
b1.setLabel("Docked");
b1.setIcon("www/images/info-{platform}.png");
b1.addEventListener("click", function () { onTabClick(b1, 0); });
b2.setLabel("Overlay");
b2.setIcon("www/images/info-{platform}.png");
b2.addEventListener("click", function () { onTabClick(b2, 1); });
b3.setLabel("Fullscreen");
b3.setIcon("www/images/info-{platform}.png");
b3.addEventListener("click", function () { onTabClick(b3, 2); });
b4.setLabel("Code");
b4.setIcon("www/images/info-{platform}.png");
b4.addEventListener("click", function () { onTabClick(b4, 3); });
b5.setLabel("Plat-Specific UI");
b5.setIcon("www/images/info-{platform}.png");
b5.addEventListener("click", function () { onTabClick(b5, 4); });
// TabBar's "content property" maps to a PrimaryCommands property.
// Therefore, to add children, we get the PrimaryCommands collection:
tabBar.getPrimaryCommands().add(b1);
tabBar.getPrimaryCommands().add(b2);
tabBar.getPrimaryCommands().add(b3);
tabBar.getPrimaryCommands().add(b4);
tabBar.getPrimaryCommands().add(b5);
page.setBottomAppBar(tabBar);
return page;
}
function onTabClick(a, b) {
return;
}
TEST_ACE.dockTabs = function () {
if (TEST_ACE.pageTabBar == null) {
// Surround the WebView with native UI
TEST_ACE.pageTabBar = TEST_ACE.buildDockedTabsWithoutXaml();
// Replace the WebView with the loaded native page
// (For the title setting in XAML to work on Android, ShowTitle must be
// set to true in the Android section of config.xml.)
ace.getHostPage().setContent(TEST_ACE.pageTabBar);
// Reparent the WebView inside the native page
TEST_ACE.pageTabBar.setContent(ace.getHostWebView());
// Save the native page so we can set its title as tabs are clicked
_nativePage = TEST_ACE.pageTabBar;
}
else {
ace.getHostPage().setContent(ace.getHostWebView());
TEST_ACE.pageTabBar = null;
GAME.setcamera();
}
}
//end
2 ответа
Образец, на который вы ссылаетесь, содержит пример нижней панели вкладок (обратите внимание, что он отображается сверху в Android). Он появляется в примере, как только вы нажмете первую ссылку.
ОБНОВЛЕНИЕ: Вот как вы делаете пример панели вкладок на JavaScript без разметки:
function buildDockedTabsWithoutXaml() {
var page = new ace.Page();
page.setTitle("Docked");
var tabBar = new ace.TabBar();
var b1 = new ace.AppBarButton();
var b2 = new ace.AppBarButton();
var b3 = new ace.AppBarButton();
var b4 = new ace.AppBarButton();
var b5 = new ace.AppBarButton();
b1.setLabel("Docked");
b1.setIcon("www/images/info-{platform}.png");
b1.addEventListener("click", function() { onTabClick(b1, 0); });
b2.setLabel("Overlay");
b2.setIcon("www/images/info-{platform}.png");
b2.addEventListener("click", function() { onTabClick(b2, 1); });
b3.setLabel("Fullscreen");
b3.setIcon("www/images/info-{platform}.png");
b3.addEventListener("click", function() { onTabClick(b3, 2); });
b4.setLabel("Code");
b4.setIcon("www/images/info-{platform}.png");
b4.addEventListener("click", function() { onTabClick(b4, 3); });
b5.setLabel("Plat-Specific UI");
b5.setIcon("www/images/info-{platform}.png");
b5.addEventListener("click", function () { onTabClick(b5, 4); });
// TabBar's "content property" maps to a PrimaryCommands property.
// Therefore, to add children, we get the PrimaryCommands collection:
tabBar.getPrimaryCommands().add(b1);
tabBar.getPrimaryCommands().add(b2);
tabBar.getPrimaryCommands().add(b3);
tabBar.getPrimaryCommands().add(b4);
tabBar.getPrimaryCommands().add(b5);
page.setBottomAppBar(tabBar);
return page;
}
function dockTabs() {
// Surround the WebView with native UI
var page = buildDockedTabsWithoutXaml();
// Replace the WebView with the loaded native page
// (For the title setting in XAML to work on Android, ShowTitle must be
// set to true in the Android section of config.xml.)
ace.getHostPage().setContent(page);
// Reparent the WebView inside the native page
page.setContent(ace.getHostWebView());
// Save the native page so we can set its title as tabs are clicked
_nativePage = page;
}
Тем не менее, в Ace, возможно, потребуется больше работы, чтобы дать вам кроссплатформенный опыт работы с Table View. Существует элемент управления ListBox, но на данный момент он имеет минимальную функциональность. В какой-то момент я экспериментировал с кроссплатформенным элементом управления TableView. Я постараюсь добавить это в Ace, чтобы помочь удовлетворить ваши потребности. Дайте мне знать, если у вас есть другие вопросы / пожелания!
Я полагаю, что для проблемы Android ваш предпочтительный элемент ShowTitle должен быть прямым потомком корневого элемента виджета. Вы можете увидеть пример в нижней части https://github.com/Microsoft/ace/blob/master/examples/AceExamples/config.xml. Если это так, то комментарий внутри AceExamples действительно неправильный, и мы должны это исправить! Извини за это.
Что касается проблемы с iOS, я просто попытался воспроизвести ее с похожим кодом AceExamples на всех 5 симуляторах iPad, но не смог. Вы могли бы поделиться своим кодом?