Nareszcie znalazłem trochę czasu żeby napisać coś tutaj. Ostatnio zacząłem się zastanawiać czy pisać po english czy po polsku. Co myślicie o tym? / What do you think about it, to blog in english or polish? A może w obu wersjach? Nevermind.

Siędząc ostatnio we flexie pisałem program, który korzysta z 3 list. Akcje opierały się na double-click na każdym elemencie każdej listy.

Napotkałem takie oto problemy:

1. Edycja elementu listy na double-click.

Lista miała być edytowalna (myList.editable = true), ale tryb edycji miał włączać się dopiero na double-click. Pojedynczy click miał służyć tylko do zaznaczania. Nie było prostej metody więc trzeba było szukać hacka. Było kilka. Wziąłem najprostszy :) Oryginał znajdziecie tutaj. Ja musiałem tą wersję trochę zmienić, żeby działała dobrze, kiedy mamy włączoną opcje allowMultipleSelection.

import flash.utils.Timer;
private var timer:Timer = new Timer(400,1)
private function startEdit(evt:ListEvent):void {
if(!timer.running || lastIndex != transcriptionList.selectedIndex) {
timer.start()
evt.preventDefault()
lastIndex = transcriptionList.selectedIndex
}
}
<mx:List dataProvider="{DataContainer.transcriptionData}" allowMultipleSelection="true"
itemEditBeginning="startEdit(event)" editable="true"></mx:List>

W tym przypadku zbędne jest używanie eventu double-click, bo jest on symulowany. Wydaje sie to być najprostszą opcją. Jeśli macie jakieś lepsze pomysły piszcie.

2. Scrollowanie na wielowierszowym elemencie listy typu UITextField.

Kolejny problem. Klasa TextField. Jak większość pewnie doświadczyła – czasem potrafi wydłużyć czas pracy i spowolnić efekty. Że zacytuję dzisiejszego tweet @bartekd:

„The TextField class should be called MineField”

W moim przypadku będzie to bardziej UITextField który jest rozszerzoną klasą TextField zawierającą to wszystko, co można dodać we Flexie. Używana najcześciej wewnątrz komponentów. Więcej możliwości – więcej problemów. Ale do rzeczy.

Moja lista zawiera teksty, krótsze i dłuższe. Stwierdziłem że lepiej będzie wyglądało jak użyję opcji wyświetlania całego tekstu, czyli:

<mx:List dataProvider="{DataContainer.transcriptionData}"
variableRowHeight="true" wordWrap="true"></mx:List>

Było fajnie, ale przy użyciu scrolla, każdy UITextField, który był akurat pod kursorem, przewijał się o jedną dodatkową linię. No i musiałem sobie radzić perfidnym sposobem ominięcia tego (wiadomo – czas leci).

<mx:List dataProvider="{DataContainer.transcriptionData}"
variableRowHeight="true" wordWrap="true" mouseWheel="onMouseWheel(event)></mx:List>
private function onMouseWheel(e:MouseEvent):void {

try {
if (e.target.className == 'UITextField') {
UITextField(e.target).mouseWheelEnabled = false
}
} catch(error:Error) {}
}

Brzydko, ale działa :)

3. Dynamiczne przypisywanie ikon do elementu listy

Ostatnia rzecz. Jedna z list zawierała listę zadań do zrobienia. Pomyślałem, że fajnie by wyglądało jakby przy każdym elemencie listy była ikonka ze statusem danego zadania. No to pojechałem.

Na początek standard, załączenie grafik:

[Embed(source="icons/listEdited.png")]
private var EditedIcon:Class;
[Embed(source="icons/listFinished.png")]
private var FinishedIcon:Class;

Następnie, zdefiniowanie i dodanie iconFunction – funkcji, która będzie na podstawie analizy danych konkretnego elementu zwracać wcześniej zadeklarowaną ikonkę. Należy pamiętać, że funkcja zwraca klasę z grafiką (wcześniej załączoną – Embed) lub string z nazwą tej klasy.

//item jest wymaganym parametrem - określa on element z dataProvidera.
private function audioListIconFunction(item:Object):Class{

switch (item.status) {
case 0:
// zwracamy null jeśli nie chcemy żadnej ikonki
return null
break;
case 1:
return EditedIcon
break;
case 2:
return FinishedIcon
break;
default:
return null
break;
}
}

<mx:List dataProvider="{DataContainer.allData}
iconFunction="audioListIconFunction"></mx:List>

Ale niestety tutaj pojawił się problem. Funkcja ta jest wykonywana na samym początku tworzenia się listy. A dokładniej mówiąc przy dodawaniu każdego kolejnego elementu. Próbowałem z invalidateDisplayList() i innymi tego typu ale nic to nie dało. Stwierdziłem, że skoro działa przy dodawaniu itemu to wywalę i wstawie w to samo miejsce objekt, który chcę odświeżyć.

//currentAllDataItem.transcription - dataProvider dla mojej listy.
currentAllDataItem.transcription.addEventListener(CollectionEvent.COLLECTION_CHANGE,updateCurrentItem)
//currentAllDataItem zawiera aktualnie odświeżany zbiór danych

private function updateCurrentItem(e:Event=null):void {
var itemIndex:int = DataContainer.allData.getItemIndex(currentAllDataItem)
var item:Object = DataContainer.allData.removeItemAt(itemIndex)
DataContainer.allData.addItemAt(item,itemIndex)
audioList.selectedIndex = itemIndex

}

Użyłem formy e:Event=null – żeby można było łatwo wywoływać tą funkcję zarówno z eventListenera jak i normalnie. Jeśli chcemy oprzeć się tylko na eventListenerach to polecam zabawę w szukanie i wiązanie danych z użyciem CollectionEvent. W mojej wersji należy pamiętać żeby zawsze mieć dostępny aktualnie odświeżany element danych. Jeśli rozwiązaliście lepiej ten problem, piszcie.

Uff. Trochę tego było. Zaczęły się wakacje to mam nadzieję, że znajdę więcej czasu na bloga, bo ostatnio się trochę zapuścił. Sporo rzeczy do opisania. Warsaw Flash Camp, spotkanie AUG (jeśli się w końcu odbędzie), kolejne przykłady i może jakaś cała przykładowa aplikacja. Zobaczymy.

Póki co,

Narazie!