2011/12/30

客製化 ListView 產生 ScrollRunnable 互搶的問題解法

最近用了一個朋友寫的客製化的 ListView,主要的用途是作類似 iOS 上可以作 Pull down refresh 的功能。

但遇到了一個小問題困擾我很久。

因為 pull down 的 scrolling back to first item 的工作是由一個 ScrollRunnable 搭配 Scroller 實作的,這東西基本上平常運作都沒有問題,但是如果在 Scroll Fling 到最頂端的時候就會有 scroll 亂跳的問題。

深究 ListView/AbsListView 之後,發現其實 AbsListView 內有一個 mFlingRunnable 負責控制 Fling 動作發生時的捲動動作,而當使用另外一個  ScrollRunnable 去控制捲動的時候,會跟原本的 mFlingRunnable 的捲動互搶,造成捲動亂跳的問題。

知道原因了,要解決就很簡單吧?

錯了。

AbsListView 並沒有預留讓開發者存取 mFlingRunnable 的介面,拿不到 Runnable 就無法取消它。難道我要把整個 AbsListView, ListView 搬出來嗎 XDD

還好找到了一個不太好的 workaround。但是鑑於要把整個 AbsListView 移出來的成本太大,就將就著用了。答案是當使用 smoothScrollBy 系列的 method 的時候,AbsListView 將會自己移除 mFlingRunnable 的操作:


    public void smoothScrollBy(int distance, int duration) {
        if (mFlingRunnable == null) {
            mFlingRunnable = new FlingRunnable();
        } else {
            mFlingRunnable.endFling();
        }
        mFlingRunnable.startScroll(distance, duration);
    }

所以當你要操作自己的 ScrollRunnable 的時候,先執行 smoothScrollBy(0, 0) 就行了。因為參數都是 0 所以基本上 Fling 的動作就會取消了。

如果哪位看官有更好的解法請務必跟我說 :D

特別感謝 David Wu 跟我一起看了這個問題!

2011/12/14

Android 自製元件 - PrismFlipper

這幾天公司的 app 要作訂製的 notification bar,花了一點時間做出來,把他整理出來成一個小 view widget,這個 widget 主要是一個客製化的 notification bar,主要是想模擬三角稜柱翻動的樣子。跑起來大概像下面這樣:



使用方法也很簡單,用 flipper.showNext(text, reverse) 就可以使用了,下面附上比較完整的範例


final PrismFlipper flipper = (PrismFlipper)findViewById(R.id.viewFlipper1);
final String[] texts = new String[] { "Refresh", "go to last read position", "last read post" };
flipper.setFrontText(texts[0]);
flipper.setBackground(new ColorDrawable(0xff3465a4));
flipper.setTextColor(Color.WHITE);
Button btn = (Button)findViewById(R.id.button1);
btn.setOnClickListener(new OnClickListener() {
   
 @Override
 public void onClick(View arg0) {
  flipper.showNext(texts[mPosition], false);
  mPosition = (mPosition + 1) % texts.length;
 }
});
        
btn = (Button)findViewById(R.id.button2);
btn.setOnClickListener(new OnClickListener() {
   
 @Override
 public void onClick(View arg0) {
  flipper.showNext(texts[mPosition], true);
  mPosition = (mPosition + 1) % texts.length;
 }
});

完整的範例可以在 github 上找到,授權是 BSD license。