コンボボックスに表示されているアイテムのFlexカスタムアイテムレンダラー
-
06-07-2019 - |
質問
コンボボックスでカスタムアイテムレンダラーを使用して、デフォルトのテキストラベルの代わりにカスタム図面を表示しています。
これはドロップダウンリストでは正常に機能しますが、表示されたアイテム(リストが閉じられている場合)はオブジェクトのテキスト表現のままです。
表示されたアイテムをドロップダウンのアイテムと同じようにレンダリングする方法はありますか?
解決
デフォルトでは、これを行うことはできません。ただし、ComboBoxを拡張すると、この機能を簡単に追加できます。以下に簡単な例を示します。これは大まかなバージョンであり、おそらくテスト/調整が必要ですが、これを実現する方法を示しています。
package
{
import mx.controls.ComboBox;
import mx.core.UIComponent;
public class ComboBox2 extends ComboBox
{
public function ComboBox2()
{
super();
}
protected var textInputReplacement:UIComponent;
override protected function createChildren():void {
super.createChildren();
if ( !textInputReplacement ) {
if ( itemRenderer != null ) {
//remove the default textInput
removeChild(textInput);
//create a new itemRenderer to use in place of the text input
textInputReplacement = itemRenderer.newInstance();
addChild(textInputReplacement);
}
}
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
super.updateDisplayList(unscaledWidth, unscaledHeight);
if ( textInputReplacement ) {
textInputReplacement.width = unscaledWidth;
textInputReplacement.height = unscaledHeight;
}
}
}
}
他のヒント
上記の解決策を試しましたが、comboboxを閉じたときにselectedItemが表示されないことがわかりました。 itemRendererデータプロパティをselectedItemにバインドするには、余分なコード行が必要でした:
if ( !textInputReplacement ) {
if ( itemRenderer != null ) {
//remove the default textInput
removeChild(textInput);
//create a new itemRenderer to use in place of the text input
textInputReplacement = itemRenderer.newInstance();
// ADD THIS BINDING:
// Bind the data of the textInputReplacement to the selected item
BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true);
addChild(textInputReplacement);
}
}
Daneのコードをもう少し拡張しました。場合によっては、クリックしてもレンダラーでドロップボックスが開かず、通常のFlex ComboBoxスキンが起動しないことがわかりました。したがって、 replaceTextInput()にイベントリスナーをいくつか追加し、スキンの表示に使用されるComboBoxボタンへの参照を保存しました。これで、通常のComboBoxと同じように動作します。
コードは次のとおりです。
package { import flash.events.Event; import flash.events.KeyboardEvent; import flash.events.MouseEvent; import mx.binding.utils.BindingUtils; import mx.controls.Button; import mx.controls.ComboBox; import mx.core.IFactory; import mx.core.UIComponent; import mx.events.DropdownEvent; /** * Extension of the standard ComboBox that will use the assigned 'itemRenderer' * for both the list items and the selected item. * * Based on code from: * http://stackoverflow.com/questions/269773/flex-custom-item-renderer-for-the-displayed-item-in-the-combobox */ public class ComboBoxFullRenderer extends ComboBox { protected var textInputReplacement:UIComponent; private var _increaseW:Number = 0; private var _increaseH:Number = 0; /** * Keeps track of the current open/close state of the drop down list. */ protected var _isOpen:Boolean = false; /** * Stores a reference to the 'Button' which overlays the ComboBox. Allows * us to pass events to it so skins are properly triggered. */ protected var _buttonRef:Button = null; /** * Constructor. */ public function ComboBoxFullRenderer() { super(); } /** * Sets a value to increase the width of our ComboBox to adjust sizing. * * @param val Number of pixels to increase the width of the ComboBox. */ public function set increaseW(val:Number):void { _increaseW = val; } /** * Sets a value to increase the height of our ComboBox to adjust sizing. * * @param val Number of pixels to increase the height of the ComboBox. */ public function set increaseH(val:Number):void { _increaseH = val; } /** * Override the 'itemRenderer' setter so we can also replace the selected * item renderer. * * @param value The renderer to be used to display the drop down list items * and the selected item. */ override public function set itemRenderer(value:IFactory):void { super.itemRenderer = value; replaceTextInput(); } /** * Override base 'createChildren()' routine to call our 'replaceTextInput()' * method to replace the standard selected item renderer. * * @see #replaceTextInput(); */ override protected function createChildren():void { super.createChildren(); replaceTextInput(); } /** * Routine to replace the ComboBox 'textInput' child with our own child * that will render the selected data element. Will create an instance of * the 'itemRenderer' set for this ComboBox. */ protected function replaceTextInput():void { if ( !textInputReplacement ) { if ( this.itemRenderer != null && textInput != null ) { //remove the default textInput removeChild(textInput); //create a new itemRenderer instance to use in place of the text input textInputReplacement = this.itemRenderer.newInstance(); // Listen for clicks so we can open/close the drop down when // renderer components are clicked. textInputReplacement.addEventListener(MouseEvent.CLICK, _onClick); // Listen to the mouse events on our renderer so we can feed them to // the ComboBox overlay button. This will make sure the button skins // are activated. See ComboBox::commitProperties() code. textInputReplacement.addEventListener(MouseEvent.MOUSE_DOWN, _onMouseEvent); textInputReplacement.addEventListener(MouseEvent.MOUSE_UP, _onMouseEvent); textInputReplacement.addEventListener(MouseEvent.ROLL_OVER, _onMouseEvent); textInputReplacement.addEventListener(MouseEvent.ROLL_OUT, _onMouseEvent); textInputReplacement.addEventListener(KeyboardEvent.KEY_DOWN, _onMouseEvent); // Bind the data of the textInputReplacement to the selected item BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true); // Add our renderer as a child. addChild(textInputReplacement); // Listen for open close so we can maintain state. The // 'isShowingDropdown' property is mx_internal so we don't // have access to it. this.addEventListener(DropdownEvent.OPEN, _onOpen); this.addEventListener(DropdownEvent.CLOSE, _onClose); // Save a reference to the mx_internal button for the combo box. // We will need this so we can call its dispatchEvent() method. for (var i:int = 0; i < this.numChildren; i++) { var temp:Object = this.getChildAt(i); if (temp is Button) { _buttonRef = temp as Button; break; } } } } } /** * Detect open events on the drop down list to keep track of the current * drop down state so we can react properly to a click on our selected * item renderer. * * @param event The DropdownEvent.OPEN event for the combo box. */ protected function _onOpen(event:DropdownEvent) : void { _isOpen = true; } /** * Detect close events on the drop down list to keep track of the current * drop down state so we can react properly to a click on our selected * item renderer. * * @param event The DropdownEvent.CLOSE event for the combo box. */ protected function _onClose(event:DropdownEvent) : void { _isOpen = false; } /** * When we detect a click on our renderer open or close the drop down list * based on whether the drop down is currently open/closed. * * @param event The CLICK event from our selected item renderer. */ protected function _onClick(event:MouseEvent) : void { if (_isOpen) { this.close(event); } else { this.open(); } } /** * React to certain mouse/keyboard events on our selected item renderer and * pass the events to the ComboBox 'button' so that the skins are properly * applied. * * @param event A mouse or keyboard event to send to the ComboBox button. * */ protected function _onMouseEvent(event:Event) : void { if (_buttonRef != null) { _buttonRef.dispatchEvent(event); } } } // end class } // end package
マクレマとマウリッツ・ド・ボーアに感謝します。私のニーズに合うように、このクラスにさらにいくつかを追加しました。
-
itemRendererをオーバーライドして、mxmlではなくASを介してitemRendererを設定した場合に機能するようにします。重複を避けるために、テキスト入力置換コードを独自の関数に移動しました。
-
最初にコンボボックスに対してレンダラーが大きすぎるため、必要に応じてコンボボックスのサイズを変更するために「increaseW」と「increaseH」のセッターを追加しました。
-
textInputReplacementの幅から25を引いて、ドロップダウンボタンと重ならないようにします...さまざまなスキンなどに対応するために、より比例したものを使用することをお勧めします。
コード:
package
{
import mx.binding.utils.BindingUtils;
import mx.controls.ComboBox;
import mx.core.IFactory;
import mx.core.UIComponent;
public class ComboBox2 extends ComboBox
{
public function ComboBox2()
{
super();
}
protected var textInputReplacement:UIComponent;
private var _increaseW:Number = 0;
private var _increaseH:Number = 0;
public function set increaseW(val:Number):void
{
_increaseW = val;
}
public function set increaseH(val:Number):void
{
_increaseH = val;
}
override public function set itemRenderer(value:IFactory):void
{
super.itemRenderer = value;
replaceTextInput();
}
override protected function createChildren():void
{
super.createChildren();
replaceTextInput();
}
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void {
unscaledWidth += _increaseW;
unscaledHeight += _increaseH;
super.updateDisplayList(unscaledWidth, unscaledHeight);
if ( textInputReplacement ) {
textInputReplacement.width = unscaledWidth - 25;
textInputReplacement.height = unscaledHeight;
}
}
protected function replaceTextInput():void
{
if ( !textInputReplacement ) {
if ( this.itemRenderer != null ) {
//remove the default textInput
removeChild(textInput);
//create a new itemRenderer to use in place of the text input
textInputReplacement = this.itemRenderer.newInstance();
addChild(textInputReplacement);
// ADD THIS BINDING:
// Bind the data of the textInputReplacement to the selected item
BindingUtils.bindProperty(textInputReplacement, "data", this, "selectedItem", true);
addChild(textInputReplacement);
}
}
}
}
}
Spark ComboBoxを使用してこれを行う方法を探していました。
このスレッドは私にとって非常に有用でしたが、これまでのところ、mx:ComboBoxを使用してそれを行う方法についての回答しかありませんでした。 Spark ComboBoxを使用してそれを行う方法に関する回答を追加する必要があると考えました。
- ComboBoxの新しいスキンを作成
- textInputを非表示にして無効にする
- 独自のコンポーネントを挿入
これはスキンがどのように見えるかです:
<s:SparkSkin>
<... Lots of other stuff/>
<s:BorderContainer height="25">
<WHATEVER YOU NEED HERE!/>
</s:BorderContainer>
<!-- Disable the textInput and hide it -->
<s:TextInput id="textInput"
left="0" right="18" top="0" bottom="0"
skinClass="spark.skins.spark.ComboBoxTextInputSkin"
visible="false" enabled="false"/>
</s:SparkSkin>
Spark ComboBoxを使用すると、このプロセスは非常に簡単であり、ComboBoxを拡張する必要はありません。
選択した要素のレンダラーを変更する簡単な方法を見つけました。これは、エレメントがFlex 4.0以降の TextInput
クラスから継承する場合にのみ機能します。
Flex v4.5では、 ComboBase.createChildren
の1177行目に、 textInput
に対して定義可能なクラスがスタイルキー textInputClass
:
// Mechanism to use MXFTETextInput.
var textInputClass:Class = getStyle("textInputClass");
if (!textInputClass || FlexVersion.compatibilityVersion < FlexVersion.VERSION_4_0)
{
textInput = new TextInput();
}
else
{
textInput = new textInputClass();
}
コンボのコンストラクターでこのキーの値を変更するだけで、 selectedItem
の独自のレンダラーが作成されます。
public function ComboAvailableProfessor()
{
super();
itemRenderer = new ClassFactory( ProfessorAvailableListItemRenderer );
setStyle( 'textInputClass', ProfessorAvailableSelectedListItemRenderer );
}
最後に、データを表示するには、コンボの data
プロパティを selectedItem
プロパティにバインドする必要があります。
override protected function createChildren():void
{
super.createChildren();
BindingUtils.bindProperty( textInput, 'data', this, 'selectedItem', true );
}