ListView回收开关搜寻杆位置 [英] ListView recycling switches seekbar position
问题描述
我有一个奇怪的问题.
我有一个带有BaseAdapter
的自定义ListView
.在我的ListView
行布局中,我只有几个TextView
,Buttons
和一个SeekBar
.一切都很好,除了回收再没有其他问题.
I have a strange problem.
I have a custom ListView
with BaseAdapter
. In my ListView
row layout, I have few TextView
s, Buttons
, and a SeekBar
. Everything works great, no problems with anything except with recycling.
会发生什么:
除了可见的SeekBar
,所有SeekBar
都被隐藏.在播放MediaPlayer
时,可以看到SeekBar
.这部分也很好.
What happens:
All SeekBar
's are hidden, except one which is visible. The SeekBar
is visible when MediaPlayer
on row is playing. That part is working great too.
但是,当用户向上或向下滚动时,可见SeekBar
的行不在视野中,它将回收,并且SeekBar
也被回收,即使在不可见且该行未播放的情况下,它也会不断更新(mp).当用户向后滚动以返回到正在播放的视图时,SeekBar
可见,但不更新,其位置为0.相反,随机SeekBar
正在更新,但不可见(我测试了所有
But, when user scrolls up or down, and the row with visible SeekBar
is out of view, it recycles, and SeekBar
is recycled too and it keeps updating even tho it's not visible and that row is not playing(mp). And when users scrolls back to return to the view which is playing, SeekBar
is visible, but not updating and it's position is 0. Instead random SeekBar
is being updated, but it's not visible(I did test when all SeekBar
's are visible so I know that happens)
我当然可以完成大多数愚蠢的解决方案并禁用ListView回收,但是这会使用户体验真的很糟糕,并且可能会使应用程序用尽内存,并且使用LargeHeap
是la脚的.
因此,以下方法毫无疑问.
Of course I could've done most idiotic solution and disable ListView recycling but this makes user experience really bad and could possibly make app run out of memory, and using LargeHeap
is lame.
So the following methods are out of question.
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
我的问题:对此有任何明显的解决方案吗? 如何防止仅一行被回收? 在真正必要之前,我不会发布代码,因为代码太大.相反,我将只发布与该问题有关的重要部分:
My question: Is there any obvious solution to this? How do I keep only one row from being recycled? I won't post code until it's really necessary, the code is too large. Instead I will just post important parts related to the issue:
@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
是个坏主意吗?这对性能有多糟糕?
and That's it.
Why is this happening?
Is there a way to disable recycling for one row?
Would be it be a bad idea to update all SeekBar
s in ListView
since only 1 mp will be playing? How bad is that for performance?
推荐答案
我将在尊重ListView's
回收能力的同时概述解决这一问题的最佳方法之一(当然,在我看来).
I'll outline one of the best ways to tackle this (in my opinion, of course) while respecting a ListView's
ability to recycle.
但是首先,这要走了:
@Override
public int getViewTypeCount() {
return getCount();
}
@Override
public int getItemViewType(int position) {
return position;
}
解决方案:
子类SeekBar
并让其处理所有更新:
Subclass SeekBar
and let it handle all updates:
-
定义两个方法,这些方法将标记 this
SeekBar
active & 无效.
在标记SeekBar
活动时,提供当前位置(进度).同时, post 将更新此SeekBar
When marking a SeekBar
active, provide the current position (progress). Alongside, post the Runnable
that will update this SeekBar
在标记SeekBar
无效时,只需删除Runnable
的回调并将其命名为一天
When marking a SeekBar
inactive, simply remove callbacks for the Runnable
and call it a day
在Adapter
中,您需要维护的唯一状态是跟踪当前播放的歌曲.您可以通过跟踪ListView
看到的 position 或保持当前正在播放的曲目的ID
来执行此操作(当然,仅当您的模型使用ID).
Inside your Adapter
, the only state that you need to maintain is keeping track of the currently playing song. You can do this either by tracking the position as seen by your ListView
, or by holding on to the ID
of the currently playing track (this, of course, will only work if your model uses IDs).
类似这样的东西:
// 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类实现:
Custom Seekbar class implementation:
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布局中使用此自定义实现:
Needless to say that you will need to use this custom implementation in your xml layout:
<com.your.package.SelfUpdatingSeekbar
....
.... />
这篇关于ListView回收开关搜寻杆位置的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!