我有一个奇怪的问题。
我有ListView
的自定义BaseAdapter
。在我的ListView
行布局中,我有TextView
个,Buttons
和SeekBar
。一切都很好,没有任何问题,除了回收。
会发生什么:
隐藏所有SeekBar
,除了可见之外。当行上的SeekBar
正在播放时,MediaPlayer
可见。那部分也很有效。
但是,当用户向上或向下滚动,并且具有可见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
}
});
}
就是这样。
为什么会这样?
有没有办法禁用一行回收?
更新SeekBar
中的所有ListView
是不是一个坏主意,因为只有1 mp会播放?性能有多糟糕?
答案 0 :(得分:7)
在尊重ListView's
回收能力的同时,我将概述解决此问题的最佳方法之一(当然,我认为)。
但首先,必须:
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
<强>解决方案:强>
子类SeekBar
并让它处理所有更新:
定义两种方法,用于标记此 SeekBar
有效&amp; 非活性
标记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);
}
请求的代码:
自定义Seekbar类实现:
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
....
.... />