ListView回收开关搜寻杆位置 [英] ListView recycling switches seekbar position

查看:77
本文介绍了ListView回收开关搜寻杆位置的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个奇怪的问题. 我有一个带有BaseAdapter的自定义ListView.在我的ListView行布局中,我只有几个TextViewButtons和一个SeekBar.一切都很好,除了回收再没有其他问题.

I have a strange problem. I have a custom ListView with BaseAdapter. In my ListView row layout, I have few TextViews, 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 SeekBars 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屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆