2015-11-19 78 views
2

我有一個奇怪的問題。 我有一個自定義ListViewBaseAdapter。在我的ListView行佈局中,我有幾個TextView s,Buttons和一個SeekBar。一切都很好,除了回收之外沒有任何問題。ListView回收開關seekbar位置

發生了什麼: 所有的SeekBar都是隱藏的,除了一個是可見的。正在播放MediaPlayer行時,SeekBar可見。這部分工作也很好。

但是,當用戶向上或向下滾動,並可見SeekBar行是拿出來看,它回收,並SeekBar被回收也和它不斷更新甚至壽是不可見的,而此行不是打(MP) 。當用戶滾動回到正在播放的視圖時,SeekBar是可見的,但不是更新,它的位置是0.相反,隨機SeekBar正在更新,但它不可見(當所有SeekBar都可見時,我測試了我知道會發生)

當然,我可以做最愚蠢的解決方案,並禁用ListView的回收,但這使得用戶體驗非常糟糕,並可能使應用程序內存不足,並使用LargeHeap是跛腳。 所以下面的方法是沒有問題的。

@Override 
public int getViewTypeCount() { 
    return getCount(); 
} 

@Override 
public int getItemViewType(int position) { 
    return position; 
} 

我的問題:有沒有什麼明顯的解決辦法呢? 如何保持只有一行被回收? 我不會發布代碼,直到它真的有必要,代碼太大。相反,我將只是張貼相關的問題最重要的部分:

@Override 
public View getView(final int position, View convertView, ViewGroup parent) { 
    View row = convertView; 
    holder = null; 
    if (row == null) { 
     LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 
     row = inflater.inflate(R.layout.item, parent, false); 
     holder = new Ids(row); 
     row.setTag(holder); 
    } else { 
     holder = (Ids) row.getTag(); 
    } 

    rowItemClass = (ListViewRow) getItem(position); 
    if (Globals.isPlaying && Globals.pos == position) {   
     holder.seekbar.setVisibility(View.VISIBLE); 
    } else { 
     holder.seekbar.setVisibility(View.GONE);  
    } 

    holder.seekbar.setTag(position); 
    final Ids finalHolder = holder; 

    ... 
    OnClickListener{.... 
    Globals.mp.start(); 
    finalHolder.seekbar.setProgress(0); 
    finalHolder.seekbar.setMax(Globals.mp.getDuration()); 
    .. 

    updateTimeProgressBar = new Runnable() { 
     @Override 
     public void run() { 
      if (Globals.isPlaying) { 
       finalHolder.seekbar.setProgress(Globals.mp.getCurrentPosition()); 
       int currentPosition = Globals.mp.getCurrentPosition(); 
       handler.postDelayed(this, 100); 
      } 
     } 
    }; 

    handler.postDelayed(updateTimeProgressBar, 100); 
    finalHolder.seekbar.setOnSeekBarChangeListener(new OnSeekBarChangeListener() { 
     @Override 
     public void onStopTrackingTouch(SeekBar seekBar) { 
      handler.removeCallbacks(updateTimeProgressBar); 
      Globals.mp.seekTo(finalHolder.seekbar.getProgress()); 
      int currentPosition = Globals.mp.getCurrentPosition(); 
      handler.postDelayed(updateTimeProgressBar, 500); 
     } 

     @Override 
     public void onStartTrackingTouch(SeekBar seekBar) { 
      handler.removeCallbacks(updateTimeProgressBar); 
     } 

     @Override 
     public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 
     // Intentionally left empty 
     } 
    }); 
} 

,就是這樣。 這是怎麼回事? 有沒有辦法禁用一行回收? 因爲只有1個mp會被播放,所以更新ListView中的所有SeekBar s會不好?性能有多糟糕?

+1

關於保持一個參考它的狀態,並檢查該行是否再次可見什麼。當它可見時,您可以重新分配在視圖本身被回收時不斷更新的狀態。 – abbath

+0

你可以通過notifItemChanged(position)來完成。 –

+0

如何保持有關行狀態的參考? 我曾嘗試檢查'if(holder.seekbar.getVisiblity == View.VISIBLE)',但由於某種原因,這根本不起作用。不知道它是否讀錯了'Seekbar'。 'notifyItemChanged'方法呢?你可以說得更詳細點嗎? –

回答

7

我將概述解決這個問題的最佳方法之一(當然,在我看來),同時尊重ListView's回收的能力。

但首先,這有得去

@Override 
public int getViewTypeCount() { 
    return getCount(); 
} 

@Override 
public int getItemViewType(int position) { 
    return position; 
} 

解決方案:

子類SeekBar,並讓它處理所有更新:

  • 定義了兩個方法,將標記這個SeekBar活動 & 不活動

  • 當標記SeekBar活動時,提供當前位置(進度)。除了,Runnable,將更新此SeekBar

  • 當標誌着SeekBar不活躍,只需刪除回調的Runnable和收工

裏面你Adapter,唯一的國家您需要維護的是跟蹤當前正在播放的歌曲。您可以通過跟蹤您的的位置,如ListView所示,或者通過按住當前正在播放的曲目的ID來執行此操作(當然,這隻適用於您的模型使用ID)。

事情是這樣的:

// Define `setActiveAtPosition(int position)` and `setInactive()` 
// in your custom SeekBar 

if (Globals.isPlaying && Globals.pos == position) { 
    // Define `setActiveAtPosition(int position)` in your custom SeekBar  
    holder.seekbar.setActiveAtPosition(Globals.mp.getCurrentPosition()); 
    holder.seekbar.setVisibility(View.VISIBLE); 
} else { 
    holder.seekbar.setInactiveForNow(); 
    holder.seekbar.setVisibility(View.GONE);  
} 

請求代碼:

自定義搜索欄類實現:

public class SelfUpdatingSeekbar extends SeekBar implements SeekBar.OnSeekBarChangeListener { 

    private boolean mActive = false; 

    private Runnable mUpdateTimeProgressBar = new Runnable() { 
     @Override 
     public void run() { 
      if (Globals.isPlaying) { 
       setProgress(Globals.mp.getCurrentPosition()); 
       postDelayed(this, 100L); 
      } 
     } 
    }; 

    public SelfUpdatingSeekbar(Context context, AttributeSet attrs) { 
     super(context, attrs); 
     setOnSeekBarChangeListener(this); 
    } 

    public void setActive() { 
     if (!mActive) { 
      mActive = true; 
      removeCallbacks(mUpdateTimeProgressBar); 
      setProgress(Globals.mp.getCurrentPosition()); 
      setVisibility(View.VISIBLE); 
      postDelayed(mUpdateTimeProgressBar, 100L); 
     } 
    } 

    public void setInactive() { 
     if (mActive) { 
      mActive = false; 
      removeCallbacks(mUpdateTimeProgressBar); 
      setVisibility(View.INVISIBLE); 
     } 
    } 

    @Override 
    public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { 

    } 

    @Override 
    public void onStartTrackingTouch(SeekBar seekBar) { 
     removeCallbacks(mUpdateTimeProgressBar); 
    } 

    @Override 
    public void onStopTrackingTouch(SeekBar seekBar) { 
     removeCallbacks(mUpdateTimeProgressBar); 
     Globals.mp.seekTo(getProgress()); 
     postDelayed(mUpdateTimeProgressBar, 500); 
    } 
} 

在您的適配器getView(...)

if (Globals.isPlaying && Globals.pos == position) {   
    holder.seekbar.setActive(); 
} else { 
    holder.seekbar.setInactive();  
} 

不用說,你需要在你的xml配置使用自定義實現:

<com.your.package.SelfUpdatingSeekbar 
    .... 
    .... /> 
+0

值得嘗試,嘗試一下,看看會發生什麼,但它可能是一個很好的解決方案。 –

+0

我不能讓它工作,我以爲我知道如何做到這一點,但事實證明,我沒有。如果問題不多,你可以發佈代碼嗎? –

+0

@SlimC。嗨,我已經添加了實現。它應該可以直接使用。如果我錯過了什麼,請留下評論。 – Vikram