BaseAdapter.notifyDataSetChanged() 隐藏用户的点击

BaseAdapter.notifyDataSetChanged() hides user's clicks

我的应用程序 activity 显示通过自定义实现填充的列表视图 一个基础适配器。 Items 由一个 progressBar 和一个 CheckBox 组成。

progressBars 值由后台线程通过使用调用 BaseAdapter.notifyDataSetChanged() 的处理程序进行更新。 在此应用中,用户应该能够选中复选框。

问题是 notifyDataSetChanged() 似乎隐藏或取消了用户对 CheckBox 的点击。 发生的情况是 onClickListener.onClick() 被称为用户点击的 1/5 次。

一开始,我认为 BaseAdapter.getView() 设置的值会覆盖用户在 CheckBox 上单击所设置的任何值。所以我在 getView() 中尝试了这样的事情:

if (checkBox.isChecked != data.isChecked(){
    checkBox.setOnCheckedChangeListener(null);
    checkBox.setChecked(data.isChecked());
    checkBox.setOnCheckedChangeListener(cbClickListener);
}

没有变化,在 onClickListener.onClick() 中添加日志清楚地表明每次都不会调用 onClick。 我该怎么办?

下面是非常经典的适配器代码:

class InputsConfAdapter extends BaseAdapter {
    private static LayoutInflater inflater = null;
    private final Model model;
    private final inputsConfAdapterInterface handler;
    private int[] positions;

    //---------------------------------------------------------------------------------------------------

    class CbClickListener implements CheckBox.OnCheckedChangeListener{

        int inputId;

        @Override
        public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
            handler.setFailSafe((byte) inputId, isChecked);
       }
    }
    //---------------------------------------------------------------------------------------------------

    public InputsConfAdapter(LayoutInflater inflater, Model model, inputsConfAdapterInterface handler) {
        super();
        InputsConfAdapter.inflater = inflater;
        this.handler = handler;
        this.model = model;
    }

    @Override
    public int getCount() {
        return model.inputs.length;
    }

    @Override
    public Object getItem(int i) {
        return i;
    }

    @Override
    public long getItemId(int i) {
        return i;
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        if (view == null) {
            view = inflater.inflate(R.layout.theIem, null);
            ViewHolder viewHolder = new ViewHolder();
            viewHolder.pbPosition = (ProgressBar) view.findViewById(R.id.pbPosition);
            viewHolder.checkBox = (CheckBox) view.findViewById(R.id.cb);

            final CbClickListener cbClickListener = new CbClickListener();
            viewHolder.checkBox.setOnCheckedChangeListener(cbClickListener);
            viewHolder.cbClickListener = new CbClickListener();

            view.setTag(viewHolder);
        }

        final ViewHolder viewHolder = (ViewHolder) view.getTag();
        final CheckBox checkBox = viewHolder.checkBox;
        final ProgressBar pbPosition = viewHolder.pbPosition;
        final CbClickListener cbClickListener = viewHolder.cbClickListener;

        final Input data = model.inputs[i];

//--- progressBar
        if (positions != null)
            pbPosition.setProgress(positions[i]);

//--- checkBox
        cbClickListener.inputId = i;
        checkBox.setOnCheckedChangeListener(null);
        checkBox.setChecked(data.isChecked());
        checkBox.setOnCheckedChangeListener(cbClickListener);
        return view;
    }

    @Override
    public boolean hasStableIds() {
        return true;
    }

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

    public void serPositions(int[] positions) {
        this.positions = positions;
    }

    private class ViewHolder {
        public CheckBox checkBox;
        public ProgressBar pbPosition;
        CbClickListener cbClickListener;

        public ViewHolder() {

        }
    }

    public interface inputsConfAdapterInterface {
        void setFailSafe(byte inputId, boolean value);
    }
}

@Khaled 的更新:在接收 listView 的片段中。 有一个 "synchronizer":

  private final Synchronizer synchronizer = new Synchronizer(new Handler(), new DefaultListener() {
        @Override
        public void ValuesReceived(ValueStruct values) {
            if (getActivity() != null) {
                adapter.serPositions(values.values);
                adapter.notifyDataSetChanged();
            }
        }
    });

更新程序线程调用 synchronizer.valuesReceived() 发布一个可运行的 在 gui 线程中实例化的处理程序中:

public interface ISink {
    void ValueReceived(
        ValueStruct inputValue
    );
}


public class defaultListener implements ISink {
}


public class Synchronizer implements ISink {
    private ISink sink;
    private Handler handler;

    public Synchronizer(Handler handler, ISink sink) {
        this.handler = handler;
        this.sink = sink;
    }

    private class ValueRunnable implements Runnable {
        ValueStruct s;

        ValueRunnable(ValueStruct s) {
            this.s = new ValueStruct(s);
        }

        @Override
        public void run() {
            sink.ValuesReceived(s);
        }
    }

    @Override
    public void ValuesReceived(ValueStruct s) {
        handler.post(new ValueRunnable(s));
    }
}

您可能点击的是列表项而不是复选框。要解决此问题 在 xml 中使复选框不可点击并处理对整个项目的检查点击。

我认为你应该考虑使用 OnCheckedChangeListener 而不是 OnClickListener

    checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
        @Override
        public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) {
            handler.setFailSafe((byte) inputId, isChecked);
        }
    });

以下是我为解决此问题所做的工作。这不是很令人满意,但它确实有效。 这个想法是对每个进度条进行手动更新,而不是使用 notifyDataSetChanged()。

在适配器中,我添加了一个 ArrayList 来存储进度条。 最后如果:

 public View getView(int i, View view, ViewGroup viewGroup) {
        if (view == null) {

我加了

bars.add(viewHolder.pbPosition);

为了让每个progressBar都知道自己关联的item,在

的最后
 @Override
    public View getView(int i, View view, ViewGroup viewGroup) {

我加了

pbPosition.setTag(i);

我删除了对 adapter.notifyDataSetChanged();

的调用

我将 setPositions(...) 更改为

public void serPositions(int[] positions) {
    for (ProgressBar bar :
            bars) {
        int index = (int) bar.getTag();
        bar.setProgress(positions[index]);
    }
}