ListView回收开关搜索栏位置

时间:2015-11-19 15:00:49

标签: android listview android-recyclerview seekbar

我有一个奇怪的问题。 我有ListView的自定义BaseAdapter。在我的ListView行布局中,我有TextView个,ButtonsSeekBar。一切都很好,没有任何问题,除了回收。

会发生什么: 隐藏所有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会播放?性能有多糟糕?

1 个答案:

答案 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
    ....
    .... />