以下內容為原創,轉載請注明:
來自天天博客:http://www.cnblogs.com/tiantianbyconan/p/3819304.html
公司項目中有這么一個需求,所以需要自己實現下。效果類似android4.0以上原生的DatePicker這種。
這個WheelView控件我已經放在github上了,大家有興趣可以看看,地址:https://github.com/wangjiegulu/WheelView,歡迎Star或者Fork哦!(建庫的時候忘了選ignore了--,所以有些gen什么的直接無視掉,我也懶得改了--)
先貼上效果圖:
講下思路吧,因為這是一個滾動選擇器,所以首先想到的是可以基於ListView或者ScrollView自定義。
剛開始我用的是繼承ListView來實現,把滑動選擇的每個item作為listview中的一個item,然后根據FirstVisiblePosition、LastVisiblePosition來判斷某個item是否在選中的區域,但是后來發現偶爾有時候調用getFirstVisiblePosition和getLastVisiblePosition的結果跟我看到的界面顯示的item並不一致(我是在Adapter中調用的這兩個方法),后來分析可能是因為我調用了scrollTo后沒有刷新adapter的原因吧。
再后來,就打算使用ScrollView來實現了,其中的每個item都是一個View(我這里使用了TextView)。因為這個選中框是在中間的位置,所以剛啟動時第一個item應該需要在中間選中框顯示,所以前面應該使用空白補全。最后一個也是如此,后面也需要空白補全。
在滑動過程中需要實現這種場景:滑動結束后,必須且只有一個item完整顯示在選擇框中。所以我們必須要監聽滾動停止的事件,然后在滾動停止后判斷是不是滿足前面的場景,如果沒有,則需要代碼中實現滾動到正確位置。但是,蛋疼的是,sdk中竟然沒有提供原生的監聽滾動停止的api,然后在網上找了很久,得到的結果是只能另辟蹊徑,使用了stackoverflow一個網友的做法通過主動檢測是否停止滾動的方法去實現(總覺得方法有點坑,額 好吧 但是暫時先用了這個方法)。
思路就講到這里了。
使用方式:
/** * Author: wangjie * Email: tiantian.china.2@gmail.com * Date: 7/1/14. */ @AILayout(R.layout.main) public class MainActivity extends AIActivity { public static final String TAG = MainActivity.class.getSimpleName(); private static final String[] PLANETS = new String[]{"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Uranus", "Neptune", "Pluto"}; @AIView(R.id.main_wv) private WheelView wva; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); wva.setOffset(1); wva.setItems(Arrays.asList(PLANETS)); wva.setOnWheelViewListener(new WheelView.OnWheelViewListener() { @Override public void onSelected(int selectedIndex, String item) { Logger.d(TAG, "selectedIndex: " + selectedIndex + ", item: " + item); } }); } @AIClick({R.id.main_show_dialog_btn}) public void onClickCallbackSample(View view) { switch (view.getId()) { case R.id.main_show_dialog_btn: View outerView = LayoutInflater.from(context).inflate(R.layout.wheel_view, null); WheelView wv = (WheelView) outerView.findViewById(R.id.wheel_view_wv); wv.setOffset(2); wv.setItems(Arrays.asList(PLANETS)); wv.setSeletion(3); wv.setOnWheelViewListener(new WheelView.OnWheelViewListener() { @Override public void onSelected(int selectedIndex, String item) { Logger.d(TAG, "[Dialog]selectedIndex: " + selectedIndex + ", item: " + item); } }); new AlertDialog.Builder(context) .setTitle("WheelView in Dialog") .setView(outerView) .setPositiveButton("OK", null) .show(); break; } } }
注意:這個Demo只是一個臨時解決方案,比較適合選項較少的時候使用,比如選擇性別、星期等。因為里面的買個item都是一個單獨的View,並沒有去實現View的緩存和重用,所以大家這點需要注意下。如果有時間我會完善優化這個代碼,實現View的復用。關於View的復用,大家可以fork后自己嘗試改寫,我講下主要的思路(我的思路,當然不能代表是最優的方案)是:假設,每頁顯示5個item以供選擇,那么剛開始加載時,先生成6個item,其中6個是正常的6個選項所new出來的item,注意!因為每頁顯示5個item,所以第6個item不可見,但是卻已經new出來了,其實可以正常顯示。然后往下滾動時,監聽滾動事件,第6個完全顯示時,這個時候第一個item已經不可見了,所以可以重用該item的view。所以如果往下滾要顯示第7個item時,重用第一個item的view,作為第7個item的view顯示數據。再往下也是一樣。總之,重用當前不可見的item的view給即將顯示的item。
注意:項目使用到了一部分注解、日志方面的東西,跟實現WheelView沒關系,但是可以簡化代碼,提高編碼效率
AndroidInject:https://github.com/wangjiegulu/androidInject
AndroidBucket:https://github.com/wangjiegulu/AndroidBucket