Adobe
製品
Acrobat
Creative Cloud
Creative Suite
Digital Marketing Suite
Digital Publishing Suite
Elements
Photoshop
Touch Apps
その他の製品一覧
ソリューション
デジタルマーケティング
デジタルメディア
教育
金融機関
Web Experience Management
その他のソリューション
ラーニング サポート ダウンロード 会社情報
ご購入
アドビストア 安心のサポート& サービス
アカデミックストア 学生、教職員、個人向け
アドビライセンスストア 中小企業向け
ボリュームライセンスについて 企業、教育機関、官公庁向け
販売パートナー
キャンペーン情報
検索
 
情報 サインイン
ようこそ、 さん カート 注文状況 マイアカウント
マイアカウント
注文状況
アカウント情報の変更
コミュニケーションの設定を変更
サインアウト
サインインの目的 お客様のアカウントや体験版ダウンロード、製品の拡張機能、コミュニティエリアへのアクセスなどを管理するため
Adobe
製品 セクション ご購入   検索  
ソリューション 会社情報
サポート ラーニング
サインイン サインアウト 注文状況 マイアカウント
先行予約の提供開始予定日Date. 商品が発送されるまで、クレジットカードには課金されません。提供開始の予定日は変更される場合があります。 先行予約の提供開始予定日Date. ダウンロードの準備が整うまで、クレジットカードには課金されません。提供開始の予定日は変更される場合があります。
個数:
ご購入には学生・教職員個人版の購入資格の確認が必要です。
小計
カートの中身を見る
Adobe Developer Connection / Flashデベロッパーセンター /

Creating ActionScript 3.0 components in Flash – Part 8: Keyboard support

著者 Jeff Kamerer

Jeff Kamerer

Created

9 June 2008

ページ ツール

Facebookでシェア
Twitterでツイート
LinkedInでシェア
ブックマーク
印刷

タグ

必要条件

この記事に必要な予備知識

To get the most from this article series, you should be familiar with Flash Professional, including how to manipulate the Timeline, the Property inspector, the Components panel, the Components inspector, the Library, and the Actions panel.

ユーザーレベル

初級

必要な製品

  • Flash Professional (Download trial)

Sample files for Part 8

  • component_keyboard_support.zip (666 KB)

Welcome to Part 8 of the article series on creating components using ActionScript 3.0. This part continues from the previous segment, where you discovered how to control the focus of the elements within the MenuBar component. In this article I'll cover how to add keyboard support so that users can negotiate through the menu using key commands, as well as their mouse.

For your reference, here are all the parts in this series:

  • Part 1: Introducing components
  • Part 2: MenuBar component prototype
  • Part 3: From prototype to component
  • Part 4: Events
  • Part 5: Styles and skins
  • Part 6: Invalidation model
  • Part 7: Focus management
  • Part 8: Keyboard support
  • Part 9: Shim compiled clip

Whenever you implement focus management for your component, you should always add keyboard support as well. When a user tabs to a component, they will expect to be able to continue using the keyboard to select items within it. For the purposes of our sample project, I added keyboard support to the MenuBar component. I updated the code to allow the arrow keys to navigate the menus, the Escape key to close the menus, and also added functionality so that the Spacebar and Enter keys can be used to select a menu item.

The details of keyboard support for the MenuBar component are sufficiently complicated that I wrote much of this part to provide the full explanation of how this works.

Handling keyDown and keyUp events

If your component needs to handle either the keyDown or keyUp event, you do not need to add your own listener because UIComponent registers keyDownHandler() and keyUpHandler() and you can override these methods. The UIComponent implementations do nothing, so you do not always need to call the super implementation of these methods. If your component extends a class which implements these methods, for example fl.controls.LabelButton, then you might want to call the super implementation; this is a case by case decision you'll need to make by examining the code and stepping through each line of code with the debugger.

I overrode keyDownHandler() to handle all keyboard events. The general form to take for a KeyboardEvent handler is to switch on the event's keyCode for special keys like Escape, Shift, F1–F12, and the arrow keys and match their values against the constants defined in flash.ui.KeyBoard. To handle alphanumeric and punctuation input, you can convert the keyCode to a String with the String.fromCharCode() method. For alphabetic keys this approach always returns the upper case value, and for other keys it always returns the value you would get without holding the Shift key down.

A simple keyDownHandler() might look like this:

override protected function keyDownHandler(e:KeyboardEvent):void { switch (e.keyCode) { case Keyboard.UP: trace("up"); break; case Keyboard.DOWN: trace("down"); break; case Keyboard.LEFT: trace("left"); break; case Keyboard.RIGHT: trace("right"); break; } switch (String.fromCharCode(e.keyCode)) { case '1': trace("We're number one!"); break; case 'A': trace("A is for apple"); break; case 'B': trace("B is for banana"); break; case 'C': trace("C is for coconut"); break; } }

Adding keyboard support to MenuBar

(The rest of Part 8 is rather advanced. You can skip to Part 9 if you wish.) Up until this point, our MenuBar component has relied completely on mouse events to open and close menus. The component currently has no way of tracking or displaying a menu item selected with the keyboard.

Enabling selectable

To remedy this, I enabled selection by setting the selectable property to true on the MenuBarTileList instance and all of the MenuList instances, undoing one of the very first changes I had made when creating the prototype in Part 1 of this article series.

Rather than making the selection persistent, I wanted it to last only as long as the keyboard was active in the menu bar or in the specific drop-down menu. Because of this, I left the code in place that set selectable = false and only set the value to true when the controls become active, then immediately setting the value back to false when they are closed. This part was easy to implement, since I already had methods to manage hiding and showing the drop-down menus, as well as opening and closing the menu bar completely. You can see the changes I made to the code below:

private function openMenuBar(menuToOpen:List):void { // enable selectable for keyboard support myMenuBar.selectable = true; // open the List drop down menu hideAllMenusExcept(menuToOpen); ... } private function closeMenuBar():void { // close all menus hideAllMenusExcept(null); // reset the state of keepMenuOpen, just to make sure it isn't left funky keepMenuOpen = false; // disable selection when menu bar closed myMenuBar.selectedIndex = -1; myMenuBar.selectable = false; ... } private function hideAllMenusExcept(except:List):void { for (var i:int = 0; i < myMenus.length; i++) { var theMenu:List = myMenus[i] as List; if (theMenu == except) { theMenu.visible = true; theMenu.selectable = true; } else { theMenu.visible = false; theMenu.selectedIndex = -1; theMenu.selectable = false; } } }

Since MenuBar does not have any selectable skins, I also changed the custom cell renderers for the menu bar and the drop-down menus to use the over skins instead. I copied the code from the implementation of BaseButton, which was being used by the cell renderer classes currently, and changed it slightly. To illustrate the changes, I've included the code for both the MenuCellRenderer and the BaseButton below:

/* * MenuCellRenderer version */ override protected function drawBackground():void { var styleName:String = (enabled) ? mouseState : "disabled"; if (selected) { styleName = "over"; } styleName += "Skin"; var bg:DisplayObject = background; background = getDisplayObjectInstance(getStyleValue(styleName)); addChildAt(background, 0); if (bg != null && bg != background) { removeChild(bg); } } /* * BaseButton version */ protected function drawBackground():void { var styleName:String = (enabled) ? mouseState : "disabled"; if (selected) { styleName = "selected"+styleName.substr(0,1).toUpperCase()+styleName.substr(1); } styleName += "Skin"; var bg:DisplayObject = background; background = getDisplayObjectInstance(getStyleValue(styleName)); addChildAt(background, 0); if (bg != null && bg != background) { removeChild(bg); } }

I changed MenuBarCellRenderer to extend MenuCellRenderer because it needed the exact same code to fix drawBackground(). I could have copied and pasted the code into both classes, but that would have created a maintenance problem, and to follow best practices, this is really an ideal situation to leverage inheritance:

public class MenuBarCellRenderer extends MenuCellRenderer {

You may be getting tired of hearing me say this, but I couldn't have achieved this part of the development process without reading and debugging the ActionScript source for the User Interface components. Being able to copy that code and make minor changes to it was a huge time saver as well.

keyDownHandler()

To understand the keyDownHandler() method and its auxiliary method, dispatchItemSelectedEvent(), you can read the commented code below:

/* * keyboard handling */ override protected function keyDownHandler(e:KeyboardEvent):void { // keyboard support does nothing if there are not any drop-down menus if (myMenus.length < 1) return; var theMenu:List; // if the menu bar is not open and up or down key was hit, then open it if (myMenuBar.selectedIndex < 0) { // enable selectable myMenuBar.selectable = true; // first switch on key to determine which drop-down menu should be opened switch (e.keyCode) { case Keyboard.UP: case Keyboard.DOWN: case Keyboard.RIGHT: myMenuBar.selectedIndex = 0; break; case Keyboard.LEFT: myMenuBar.selectedIndex = (myMenuBar.length - 1); break; } // open the drop-down menu openMenuBar(myMenus[myMenuBar.selectedIndex]); theMenu = myMenus[myMenuBar.selectedIndex] as List; // now switch to see whether the first or last item in the // drop-down menu should be selected switch (e.keyCode) { case Keyboard.RIGHT: case Keyboard.LEFT: case Keyboard.DOWN: theMenu.selectedIndex = 0; break; case Keyboard.UP: theMenu.selectedIndex = (theMenu.length - 1); break; } // done! return; } // this code path is hit if the menu bar was already open switch (e.keyCode) { case Keyboard.UP: // the up key moves the drop-down menu selection up, // or down to the bottom if the selection is at the top theMenu = myMenus[myMenuBar.selectedIndex] as List; if (theMenu.selectedIndex <= 0) { theMenu.selectedIndex = (theMenu.length - 1); } else { theMenu.selectedIndex--; } break; case Keyboard.DOWN: // the down key moves the drop-down menu selection down, // or up to the top if the selection is at the bottom theMenu = myMenus[myMenuBar.selectedIndex] as List; if (theMenu.selectedIndex < 0 || (theMenu.selectedIndex + 1) >= theMenu.length) { theMenu.selectedIndex = 0; } else { theMenu.selectedIndex++; } break; case Keyboard.LEFT: // the left key closes the currently opened drop-down menu // and opens the one immediately to its left, or if the // leftmost menu was open then it opens the rightmost menu if (myMenuBar.selectedIndex <= 0) { myMenuBar.selectedIndex = (myMenuBar.length - 1); } else { myMenuBar.selectedIndex--; } theMenu = myMenus[myMenuBar.selectedIndex] as List; hideAllMenusExcept(theMenu); theMenu.selectedIndex = 0; break; case Keyboard.RIGHT: // the right key closes the currently opened drop-down menu // and opens the one immediately to its right, or if the // rightmost menu was open then it opens the leftmost menu if (myMenuBar.selectedIndex < 0 || (myMenuBar.selectedIndex + 1) >= myMenuBar.length) { myMenuBar.selectedIndex = 0; } else { myMenuBar.selectedIndex++; } theMenu = myMenus[myMenuBar.selectedIndex] as List; hideAllMenusExcept(theMenu); theMenu.selectedIndex = 0; break; case Keyboard.SPACE: case Keyboard.ENTER: // space or enter will dispatch a menu event if a // drop-down menu item is selected dispatchItemSelectedEvent(); closeMenuBar(); case Keyboard.ESCAPE: // escape will close the menu without selecting any items closeMenuBar(); break; } } private function dispatchItemSelectedEvent():void { // get the menu bar index and label var menuIndex:int = myMenuBar.selectedIndex; // we will not dispatch an event if no menu bar item is selected if (menuIndex < 0) return; var menuLabel:String = myMenuBar.dataProvider.getItemAt(menuIndex).label; // get the drop-down menu item index and label var theMenu:List = myMenus[myMenuBar.selectedIndex] as List; var itemIndex:int = theMenu.selectedIndex; // we will not dispatch an event if no drop-down item is selected if (itemIndex < 0) return; var itemLabel:String = theMenu.dataProvider.getItemAt(itemIndex).label; // dispatch the event dispatchEvent(new MenuEvent(MenuEvent.ITEM_SELECTED, false, false, menuIndex, menuLabel, itemIndex, itemLabel)); }

Coordinating mouse input and keyboard input

Once I had written the keyDownHandler() method and made other code changes to get selection working in the MenuBar component's subcomponents, keyboard support was working great, but it was not interacting very well with mouse support. After selecting Control > Test Movie and doing some tests, I noticed that if I started changing the menu selection with the keyboard and then switched to using the mouse, I saw multiple menu items in the over state. If I started navigating through the menu with the mouse and then tried to take over the control with the keyboard, the next item in the menu was not selected properly. This issue occurred because the keyboard support was driven by selection, but the mouse support did not currently interact with selection at all.

To resolve this issue, I added some code to menuBarMouseHandler() to make sure that mouse interactions were setting the menu bar selection. The updated version of the code is shown below, with some of the comments removed:

private function menuBarMouseHandler(e:MouseEvent):void { var cellRenderer:ICellRenderer = e.target as ICellRenderer; if (cellRenderer == null) return; switch (e.type) { case MouseEvent.MOUSE_DOWN: var theMenu:List = getChildByName(cellRenderer.data.label) as List; openMenuBar(theMenu); // see MOUSE_UP handling below for discussion of keepMenuOpen keepMenuOpen = true; // set selectedIndex to improve interaction between // mouse support and keyboard support myMenuBar.selectedIndex = cellRenderer.listData.index; break; case MouseEvent.MOUSE_OVER: theMenu = getChildByName(cellRenderer.data.label) as List; hideAllMenusExcept(theMenu); // set selectedIndex to improve interaction between // mouse support and keyboard support myMenuBar.selectedIndex = cellRenderer.listData.index; break; case MouseEvent.MOUSE_UP: // this event will only be hit on the first mouseUp after the // first mouseDown which opened the menus. We need to handle // this to prevent the stage mouseUp listener from closing the // menus. We could stop the stage listener from getting the // event at all by calling e.stopPropagation(), but since this // will eventually become component code that could be used in // an arbitrary application, it seems dangerous to stop event // propagation since we do not know what sort of event handling // the user might be coding on top of ours. So instead we use // the keepMenuOpen:Boolean. myMenuBar.removeEventListener(MouseEvent.MOUSE_UP, menuBarMouseHandler); keepMenuOpen = true; break; } }

I had to start listening for mouseOver events on the drop-down menu MenuList instances, adding the listeners in openMenuBar() and removing them in closeMenuBar() like I was already doing for other event listeners on the MenuList instances. I added code to menuMouseHandler() to handle this event. I was also able to simplify the event dispatching code, by using the same private method to dispatch the itemSelected event for both keyboard and mouse interaction:

private function menuMouseHandler(e:MouseEvent):void { var cellRenderer:ICellRenderer = e.target as ICellRenderer; if (cellRenderer == null) return; switch (e.type) { case MouseEvent.MOUSE_UP: // dispatch event and close menu bar dispatchItemSelectedEvent(); closeMenuBar(); break; case MouseEvent.MOUSE_DOWN: // keep stage listener from closing the menus // more on use of keepMenuOpen in comment // for mouseUp event in menuBarMouseHandler() keepMenuOpen = true; break; case MouseEvent.MOUSE_OVER: // set selectedIndex to improve interaction between // mouse support and keyboard support var theMenu:List = cellRenderer.listData.owner as List; theMenu.selectedIndex = cellRenderer.listData.index; break; } }

After these changes to the code were implemented, everything about the behavior of the MenuBar component was almost working, but I found that I was still having cosmetic problems when I started control with the mouse and then took over control with the keyboard. After making some tests, I identified the problem. The over skin would remain on a menu item as long as the mouse was hovering over it, even if I moved the selection to another menu item. I fixed this issue with another change to MenuCellRenderer, and it was only necessary to apply the fix in one place because I altered MenuBarCellRenderer to subclass this class:

override protected function drawBackground():void { var styleName:String = (enabled) ? mouseState : "disabled"; if (selected) { styleName = "over"; } else if (styleName == "over") { styleName = "up"; } styleName += "Skin"; var bg:DisplayObject = background; background = getDisplayObjectInstance(getStyleValue(styleName)); addChildAt(background, 0); if (bg != null && bg != background) { removeChild(bg); } }

Where to go from here

In the last part of this article series, you'll examine how to work with compiled clips. We'll put the finishing touches on our MenuBar component by ensuring that when developers use your components, their FLA files will publish quickly. I'll provide some tips and resources to help you optimize your components before distributing them and also touch on some compatibility issues that you should consider when developing components.

To learn more about how to handle events and control keyboard input with ActionScript 3.0, see Introduction to event handling in ActionScript 3.0.

More Like This

  • Getting started with the Adobe SiteCatalyst extension for Flash Professional CS5
  • ActionScript 2からActionScript 3への移行:重要な概念と変更点
  • Web video template: Media presentation with details
  • Using the Flash OSMF Media Player template
  • Understanding ActionScript 3 debugging in Flash
  • Exploring a unified component workflow between Flex and Flash
  • Formatting text for localized Flash projects
  • Exploring the Flash video templates and tutorials
  • Introducing the ActionScript 3.0 debugger
  • Multitouch and gesture support on the Flash Platform

製品

  • Acrobat
  • Creative Cloud
  • Creative Suite
  • Digital Marketing Suite
  • Digital Publishing Suite
  • Elements
  • モバイルアプリ
  • Photoshop
  • Touch Apps

ソリューション

  • デジタルマーケティング
  • コンテンツオーサリング
  • Web Experience Management

業種別ソリューション

  • 教育
  • 金融機関

サポート

  • ヘルプ&サポート
  • 注文と返品
  • ダウンロードに関するヘルプ
  • ユーザー登録に関するヘルプ

ラーニング

  • ADC: Adobe Developer Center
  • Adobe TV
  • Design Magazine
  • Photoshop Magazine
  • Focus In

ご購入方法

  • アドビストア
  • アカデミックストア
  • アドビライセンスストア
  • ボリュームライセンスについて
  • 販売パートナー
  • キャンペーン情報

ダウンロード

  • Adobe Reader
  • Adobe Flash Player
  • Adobe AIR
  • Adobe Shockwave Player

会社情報

  • プレスルーム
  • パートナープログラム
  • 企業の社会的責任(英語)
  • 採用情報
  • 投資家の皆様へ(英語)
  • イベント&セミナー
  • Legal(英語)
  • セキュリティ
  • お問い合わせ
国・地域および言語の選択 日本(変更)
国・地域および言語の選択 閉じる

North America

Europe, Middle East and Africa

Asia Pacific

  • Canada - English
  • Canada - Français
  • Latinoamérica
  • México
  • United States

South America

  • Brasil
  • Africa - English
  • Österreich - Deutsch
  • Belgium - English
  • Belgique - Français
  • België - Nederlands
  • България
  • Hrvatska
  • Česká republika
  • Danmark
  • Eastern Europe - English
  • Eesti
  • Suomi
  • France
  • Deutschland
  • Magyarország
  • Ireland
  • Israel - English
  • ישראל - עברית
  • Italia
  • Latvija
  • Lietuva
  • Luxembourg - Deutsch
  • Luxembourg - English
  • Luxembourg - Français
  • الشرق الأوسط وشمال أفريقيا - اللغة العربية
  • Middle East and North Africa - English
  • Moyen-Orient et Afrique du Nord - Français
  • Nederland
  • Norge
  • Polska
  • Portugal
  • România
  • Россия
  • Srbija
  • Slovensko
  • Slovenija
  • España
  • Sverige
  • Schweiz - Deutsch
  • Suisse - Français
  • Svizzera - Italiano
  • Türkiye
  • Україна
  • United Kingdom
  • Australia
  • 中国
  • 中國香港特別行政區
  • Hong Kong S.A.R. of China
  • India - English
  • 日本
  • 한국
  • New Zealand
  • 台灣

Southeast Asia

  • Includes Indonesia, Malaysia, Philippines, Singapore, Thailand, and Vietnam - English

Copyright © 2012 Adobe Systems Incorporated. All rights reserved.

利用条件 | プライバシーポリシーとCookie (更新)

Reviewed by TRUSTe: site privacy statement