ListView回收开关搜索栏位置

Sli*_* C. 2 android listview seekbar android-recyclerview

我有一个奇怪的问题.我有一个自定义ListViewBaseAdapter.在我的ListView行布局中,我有几个TextViews Buttons,和a 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;
}
Run Code Online (Sandbox Code Playgroud)

我的问题:这有什么明显的解决方案吗?如何仅保留一行不被回收?我不会发布代码,直到它真的有必要,代码太大了.相反,我将发布与问题相关的重要部分:

@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
        }
    });
}
Run Code Online (Sandbox Code Playgroud)

就是这样.为什么会这样?有没有办法禁用一行回收?更新所有SeekBars 是不是一个坏主意,ListView因为只有1 mp将会播放?性能有多糟糕?

Vik*_*ram 7

我将概述解决这一问题的最佳方法之一(当然,我认为),同时尊重ListView's回收利用的能力.

但首先,必须要:

@Override
public int getViewTypeCount() {
    return getCount();
}

@Override
public int getItemViewType(int position) {
    return position;
}
Run Code Online (Sandbox Code Playgroud)

解:

子类SeekBar并让它处理所有更新:

  • 定义两个标记 SeekBar 活动非活动的方法.

  • 标记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);    
}
Run Code Online (Sandbox Code Playgroud)

请求的代码:

自定义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);
    }
}
Run Code Online (Sandbox Code Playgroud)

在适配器中getView(...):

if (Globals.isPlaying && Globals.pos == position) {        
    holder.seekbar.setActive();
} else {
    holder.seekbar.setInactive();    
}
Run Code Online (Sandbox Code Playgroud)

不用说,您需要在xml布局中使用此自定义实现:

<com.your.package.SelfUpdatingSeekbar
    ....
    .... />
Run Code Online (Sandbox Code Playgroud)