TableViewでアコーディオンメニューのようなものを実装したがあまり実用的でなかった
本題に入る前に近況など
最近、ブログ更新頻度落ちてるのですが、主に以下3つが要因かなと思ってます
- クラフトビールのお店情報を検索閲覧できるiPhone/Androidアプリを優先的(*)に作業してる
- 知り合いに頼まれて本業に関連するキャリア関連のお話を30分弱したり、昨日の勉強会でこんなLTしたり、明後日の勉強会でLTする予定
- Titanium Mobile 使ったスマフォアプリのワークショップを不定期に開催しており、そこでも使えるようにKDP向けにTitaniumの書籍原稿執筆中
ブログ書きたい内容は結構あるんだけど、当面はiPhone/Androidアプリ開発に注力してそれが一段落したら、もう少し更新頻度あげていこうかなと思ってます
(*) 今回はデザインも自分でやる方針にして、アイコンをSketch 2 & Pixelmator使って頑張って作ってみました
ここから本題
こんな↓感じのものを作ったのですが、このエントリのタイトルそのままで動作がカクカクして正直実用的でないかなと思ってます
実装にあたって
あらかじめ、
var prefectures; prefectures = [ {"name":"北海道","area":"北海道・東北"}, {"name":"青森県","area":"北海道・東北"}, {"name":"岩手県","area":"北海道・東北"}, {"name":"宮城県","area":"北海道・東北"}, {"name":"秋田県","area":"北海道・東北"}, {"name":"山形県","area":"北海道・東北"}, {"name":"福島県","area":"北海道・東北"}, {"name":"茨城県","area":"関東"}, {"name":"栃木県","area":"関東"} // 以下省略
みたいな都道府県のエリアと、都道府県名を紐付けたデータを準備しました。
そして、このデータを元にして、
"北海道・東北":[ {"name":"北海道","area":"北海道・東北"} {"name":"青森県","area":"北海道・東北"} ], "関東":[ {"name":"茨城県","area":"関東"}, {"name":"栃木県","area":"関東"} ]
みたいな構造のものを生成するためにUnderscore.jsのgroupBy()を以下のように使うことにします
_makePrefectureCategory: (data) -> _ = require("lib/underscore-1.4.3.min") result = _.groupBy(data,(row) -> return row.area ) return result
あとは、都道府県のエリア名、つまり、「北海道・東北」「関東」・・・という項目のものだけをTableViewで表示しておき、項目がタッチされた時に、タッチされた行の下にそのエリアに紐づく都道府県名を1つづつ追加しています
_showSubMenu:(prefectureNameList,curretRowIndex) -> index = curretRowIndex for item in prefectureNameList subMenu = Ti.UI.createTableViewRow width:'auto' height:40 borderWidth:0 className:'subMenu' prefectureName:item.name backgroundGradient: type: 'linear' startPoint: x: '0%', y: '0%' , endPoint: x: '0%' y: '100%' , colors: @colorSet subMenuLabel = Ti.UI.createLabel width:240 height:40 top:5 left:30 color:'#222' font: fontSize:14 fontWeight:'bold' text:item.name subMenu.add subMenuLabel @table.insertRowAfter(index,subMenu,{animated:false}) @_sleep(100) index++ Ti.API.info "index is #{index}" # Ti.API.info item.name return
という感じで、それに紐づく都道府県名を、insertRowAfter()使って、1つづつ動的に追加しています。
insertRowAfter()を使うアプローチだと限界がある気がする
TableView insertRow animation - change durationという記事を昨日たまたま見つけて、
it seems to be set to 200 ms.
という感じで、rowの挿入するアニメーションで200msの時間が設定されているっぽいし、実際にっ自分が書いてても、感覚的にはこれ位の時間がかかってるように感じます。
カクカクする理由の1つに、insertRowした後に、独自に作ったsleep()で処理を止めているのですが、実はこの処理を入れないと、再現性がないのですが、
[ERROR] no row found for index.
という時がありinsertRowの処理が追いつかないのかと思い、苦肉の策でsleep()を入れた所、今度は動作がカクカクするという状況なので、insertRowAfter()を使うアプローチだと限界あるのかなと思ってます。
@ryugoo_さんに昨日あった時に、insertRowAfter/Beforeは避けたほうがいいとアドバイスもらって、やっぱりTableView.setData()の方がいいのかと思ってます
折角作ったのと、将来気が向いて手直しするかもしれなので、コード以下に貼っておくことにします。
## shopDataTableView.coffee class shopDataTableView constructor: () -> prefectures = [ {"name":"北海道","area":"北海道・東北"}, {"name":"青森県","area":"北海道・東北"}, {"name":"岩手県","area":"北海道・東北"}, {"name":"宮城県","area":"北海道・東北"}, {"name":"秋田県","area":"北海道・東北"}, {"name":"山形県","area":"北海道・東北"}, {"name":"福島県","area":"北海道・東北"}, {"name":"茨城県","area":"関東"}, {"name":"栃木県","area":"関東"}, {"name":"群馬県","area":"関東"}, {"name":"埼玉県","area":"関東"}, {"name":"千葉県","area":"関東"}, {"name":"東京都","area":"関東"}, {"name":"神奈川県","area":"関東"}, {"name":"新潟県","area":"中部"}, {"name":"富山県","area":"中部"}, {"name":"石川県","area":"中部"}, {"name":"福井県","area":"中部"}, {"name":"山梨県","area":"中部"}, {"name":"長野県","area":"中部"}, {"name":"岐阜県","area":"中部"}, {"name":"静岡県","area":"中部"}, {"name":"愛知県","area":"中部"}, {"name":"三重県","area":"近畿"}, {"name":"滋賀県","area":"近畿"}, {"name":"京都府","area":"近畿"}, {"name":"大阪府","area":"近畿"}, {"name":"兵庫県","area":"近畿"}, {"name":"奈良県","area":"近畿"}, {"name":"和歌山県","area":"近畿"}, {"name":"鳥取県","area":"中国・四国"}, {"name":"島根県","area":"中国・四国"}, {"name":"岡山県","area":"中国・四国"}, {"name":"広島県","area":"中国・四国"}, {"name":"山口県","area":"中国・四国"}, {"name":"徳島県","area":"中国・四国"}, {"name":"香川県","area":"中国・四国"}, {"name":"愛媛県","area":"中国・四国"}, {"name":"高知県","area":"中国・四国"}, {"name":"福岡県","area":"九州・沖縄"}, {"name":"佐賀県","area":"九州・沖縄"}, {"name":"長崎県","area":"九州・沖縄"}, {"name":"熊本県","area":"九州・沖縄"}, {"name":"大分県","area":"九州・沖縄"}, {"name":"宮崎県","area":"九州・沖縄"}, {"name":"鹿児島県","area":"九州・沖縄"}, {"name":"沖縄県","area":"九州・沖縄"} ] @table = Ti.UI.createTableView backgroundColor:'#fff' separatorColor: '#ccc' width:'auto' height:'auto' left:0 top:0 @colorSet = [ color: "#fff" position: 0.0 , color: "#eee" position: 0.3 , color: "#ededed" position: 1.0 ] @table.addEventListener('click',(e) => that = @ opendFlg = e.row.opendFlg prefectureNameList = e.row.prefectureNameList curretRowIndex = e.index if opendFlg is false @_showSubMenu(prefectureNameList,curretRowIndex) e.row.opendFlg = true else if opendFlg is true @_hideSubMenu(curretRowIndex,prefectureNameList.length) e.row.opendFlg = false else Ti.API.info e.row.prefectureName (e)) return ) rows = [] PrefectureCategory = @_makePrefectureCategory prefectures for categoryName of PrefectureCategory numberOfPrefecture = PrefectureCategory[categoryName].length prefectureNameList = PrefectureCategory[categoryName] textLabel = Ti.UI.createLabel width:240 height:40 top:5 left:5 color:'#222' font: fontSize:18 fontWeight:'bold' text:"#{categoryName}" if Titanium.Platform.osname is "iphone" row = Ti.UI.createTableViewRow width:'auto' height:40 borderWidth:0 className:'shopData' numberOfPrefecture:numberOfPrefecture prefectureNameList:prefectureNameList opendFlg:false backgroundGradient: type: 'linear' startPoint: x: '0%', y: '0%' , endPoint: x: '0%' y: '100%' , colors: @colorSet row.add textLabel else if Titanium.Platform.osname is "android" row = Ti.UI.createTableViewRow width:'auto' height:80 className:'shopData' numberOfPrefecture:numberOfPrefecture prefectureNameList:prefectureNameList opendFlg:false view = Ti.UI.createView width:'auto' height:80 backgroundGradient: type: 'linear' startPoint: x: '0%', y: '0%' , endPoint: x: '0%' y: '100%' , colors: @colorSet view.add textLabel row.add view else Ti.API.info 'no data' rows.push row @table.setData rows return @table # 都道府県のリスト情報から日本の地域x都道府県名の以下の様なリストを作成する # "北海道・東北":[ {},{} ], # "関東":[{},] # : _makePrefectureCategory: (data) -> _ = require("lib/underscore-1.4.3.min") result = _.groupBy(data,(row) -> return row.area ) return result _showSubMenu:(prefectureNameList,curretRowIndex) -> index = curretRowIndex Ti.API.info "curretRowIndex is #{curretRowIndex} and #{prefectureNameList.length}" for item in prefectureNameList subMenu = Ti.UI.createTableViewRow width:'auto' height:40 borderWidth:0 className:'subMenu' prefectureName:item.name backgroundGradient: type: 'linear' startPoint: x: '0%', y: '0%' , endPoint: x: '0%' y: '100%' , colors: @colorSet subMenuLabel = Ti.UI.createLabel width:240 height:40 top:5 left:30 color:'#222' font: fontSize:14 fontWeight:'bold' text:item.name subMenu.add subMenuLabel @table.insertRowAfter(index,subMenu,{animated:false}) @_sleep(100) index++ Ti.API.info "index is #{index}" # Ti.API.info item.name return _hideSubMenu:(curretRowIndex,numberOfPrefecture) => if curretRowIndex is 0 startPosition = numberOfPrefecture else startPosition = numberOfPrefecture + curretRowIndex endPosition = curretRowIndex+1 Ti.API.info "start is #{startPosition} and end is #{endPosition}" for counter in [startPosition..endPosition] @table.deleteRow counter @_sleep(100) return # 以下URLを参照してビジーループというアプローチでsleepを実装 # http://yanor.net/wiki/?JavaScript%2F%E3%82%BF%E3%82%A4%E3%83%9E%E3%83%BC%E5%87%A6%E7%90%86%2Fsleep%E3%81%84%E3%82%8D%E3%81%84%E3%82%8D _sleep:(time) -> d1 = new Date().getTime() d2 = new Date().getTime() d2 = new Date().getTime() while d2 < d1 + time return _createShopDataRow:(placeData) -> titleLabel = Ti.UI.createLabel width:240 height:20 top:5 left:5 color:'#222' font: fontSize:16 fontWeight:'bold' text:"#{placeData.name}" addressLabel = Ti.UI.createLabel width:240 height:20 top:25 left:20 color:'#444' font: fontSize:12 text:"#{placeData.address}" if Titanium.Platform.osname is "iphone" row = Ti.UI.createTableViewRow width:'auto' height:45 borderWidth:0 hasChild:true placeData:placeData className:'shopData' backgroundGradient: type: 'linear' startPoint: x: '0%', y: '0%' , endPoint: x: '0%' y: '100%' , colors: @colorSet row.add titleLabel row.add addressLabel else if Titanium.Platform.osname is "android" row = Ti.UI.createTableViewRow width:'auto' height:80 className:'shopData' hasDetail:true view = Ti.UI.createView width:'auto' height:80 backgroundGradient: type: 'linear' startPoint: x: '0%', y: '0%' , endPoint: x: '0%' y: '100%' , colors: @colorSet view.add textLabel row.add view else Ti.API.info 'no platform' return row module.exports = shopDataTableView