ListView回收切换seekbar位置
ListView recycling switches seekbar position
我有一个奇怪的问题。
我有一个自定义 ListView
和 BaseAdapter
。在我的 ListView
行布局中,我有几个 TextView
、Buttons
和一个 SeekBar
。一切都很好,除了回收之外没有任何问题。
发生了什么:
所有 SeekBar
都是隐藏的,只有一个可见。 SeekBar
在行 MediaPlayer
播放时可见。那部分也很好用。
但是,当用户向上或向下滚动时,可见 SeekBar
的行不可见时,它会回收,SeekBar
也会被回收,即使不可见也会不断更新那一行没有播放(mp)。当用户滚动回 return 到正在播放的视图时,SeekBar
是可见的,但没有更新,它的位置是 0。取而代之的是随机 SeekBar
正在更新,但它不可见(我确实在所有 SeekBar
都可见时进行了测试,所以我知道会发生这种情况)
当然我可以做最愚蠢的解决方案并禁用 ListView 回收,但这会使用户体验非常糟糕,并且可能使应用程序 运行 内存不足,并且使用 LargeHeap
是蹩脚的。
所以下面的方法是没有问题的。
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
我的问题:有什么明显的解决办法吗?
如何只保留一排不被回收?
除非真的有必要,否则我不会 post 编写代码,代码太大了。相反,我只会 post 与问题相关的重要部分:
@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
}
});
}
就是这样。
为什么会这样?
有没有办法禁用一行的回收?
更新 ListView
中的所有 SeekBar
是不是一个坏主意,因为只有 1 mp 会播放?这对性能有多糟糕?
我将概述解决此问题的最佳方法之一(当然是我认为),同时尊重 ListView's
回收能力。
但首先,这 必须去 :
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
解法:
Subclass SeekBar
并让它处理所有更新:
定义两个方法来标记this SeekBar
active & inactive .
标记SeekBar
活动时,提供当前位置(进度)。此外,post 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);
}
请求代码:
自定义搜索栏class实施:
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
....
.... />
我有一个奇怪的问题。
我有一个自定义 ListView
和 BaseAdapter
。在我的 ListView
行布局中,我有几个 TextView
、Buttons
和一个 SeekBar
。一切都很好,除了回收之外没有任何问题。
发生了什么:
所有 SeekBar
都是隐藏的,只有一个可见。 SeekBar
在行 MediaPlayer
播放时可见。那部分也很好用。
但是,当用户向上或向下滚动时,可见 SeekBar
的行不可见时,它会回收,SeekBar
也会被回收,即使不可见也会不断更新那一行没有播放(mp)。当用户滚动回 return 到正在播放的视图时,SeekBar
是可见的,但没有更新,它的位置是 0。取而代之的是随机 SeekBar
正在更新,但它不可见(我确实在所有 SeekBar
都可见时进行了测试,所以我知道会发生这种情况)
当然我可以做最愚蠢的解决方案并禁用 ListView 回收,但这会使用户体验非常糟糕,并且可能使应用程序 运行 内存不足,并且使用 LargeHeap
是蹩脚的。
所以下面的方法是没有问题的。
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
我的问题:有什么明显的解决办法吗? 如何只保留一排不被回收? 除非真的有必要,否则我不会 post 编写代码,代码太大了。相反,我只会 post 与问题相关的重要部分:
@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
}
});
}
就是这样。
为什么会这样?
有没有办法禁用一行的回收?
更新 ListView
中的所有 SeekBar
是不是一个坏主意,因为只有 1 mp 会播放?这对性能有多糟糕?
我将概述解决此问题的最佳方法之一(当然是我认为),同时尊重 ListView's
回收能力。
但首先,这 必须去 :
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
解法:
Subclass SeekBar
并让它处理所有更新:
定义两个方法来标记this
SeekBar
active & inactive .标记
SeekBar
活动时,提供当前位置(进度)。此外,postRunnable
将更新此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);
}
请求代码:
自定义搜索栏class实施:
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
....
.... />