4

我有一个ListViewFragment我想在ListView我从另一个返回时更新数据Activity。我已经覆盖了onResume()中的方法Fragment,修改了中的数据Adapter并调用notifyDataSetChanged()了,Adpater但不知何故ListView没有更新。我怀疑我的 有问题Adapter,但我似乎找不到错误。

这是我的代码Adpater

class ManualExceptionsListAdapter extends BaseAdapter {

    private LayoutInflater mInflater;
    private TextView mManualExceptions;
    SwitchCompat mSwitch;
    TextView name;
    final Context context = getActivity();
    final SharedPreferences mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(context);
    int a;
    int ifUse = 0;

    ManualExceptionsListAdapter(LayoutInflater inflater) {
        mInflater = inflater;
    }

    @Override
    public int getCount() {
        return (mPermanentManualException.size()+mContactsExceptionNumber.size());
    }

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

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

    @Override
    public int getItemViewType(int position) {
        if (position < (mContactsExceptionNumber.size())) {
            a = 0;
            if(position == (mContactsExceptionNumber.size()-1)){
                ifUse = 1;
            }
            return a;
        } else {
            a = 1;
            return a;
        }
    }

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

    @Override
    public void notifyDataSetChanged() {
        super.notifyDataSetChanged();
    }

    @Override
    public View getView(int i, View view, ViewGroup viewGroup) {
        final int pos;
        if(mContactsExceptionNumber.size()>0) {
            pos = i - (mContactsExceptionNumber.size());
        }else{
            pos = 0;
        }
        int pos2 = 0;
        int type = getItemViewType(i);
        if(ifUse == 1){
            if(mContactsExceptionNumber.size()>0) {
                pos2 = i - (mContactsExceptionNumber.size());
                Exceptions.index = pos2;
            }
        }
        View v = view;
        if (view == null) {
            switch (type) {
                case 0:
                    v = mInflater.inflate(R.layout.contacts_exception_row, null);
                    name = (TextView) v.findViewById(R.id.contact_name);
                    name.setText(mContactsExceptionNames.get(i));
                    break;
                case 1:
                    v = mInflater.inflate(R.layout.manual_exception_row, null);
                    mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number);
                    mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch);
                    mManualExceptions.setText(mPermanentManualException.get(pos2));
                    mSwitch.setTag(i);
                    try {
                        if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) {
                            mSwitch.setChecked(true);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }else{
            switch (type) {
                case 0:
                    v = mInflater.inflate(R.layout.contacts_exception_row, null);
                    name = (TextView) v.findViewById(R.id.contact_name);
                    name.setText(mContactsExceptionNames.get(i));
                    break;
                case 1:
                    v = mInflater.inflate(R.layout.manual_exception_row, null);
                    mManualExceptions = (TextView) v.findViewById(R.id.manual_exception_number);
                    mSwitch = (SwitchCompat) v.findViewById(R.id.manual_exception_switch);
                    mManualExceptions.setText(mPermanentManualException.get(pos2));
                    mSwitch.setTag(i);
                    try {
                        if (mManualExceptionList.contains(mPermanentManualException.get(pos2))) {
                            mSwitch.setChecked(true);
                        }
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                    break;
            }
        }

        try {
            mSwitch.setOnTouchListener(new View.OnTouchListener() {
                @Override
                public boolean onTouch(View view, MotionEvent motionEvent) {
                    isTouched = true;
                    return false;
                }
            });
        } catch (NullPointerException e) {
            e.printStackTrace();
        }
        try {
            mSwitch.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
                @Override
                public void onCheckedChanged(CompoundButton compoundButton, boolean b) {
                    if (isTouched) {
                        if (b) {
                            if (!mManualExceptionList.contains((mPermanentManualException.get(pos)))) {
                                mManualExceptionList.add((mPermanentManualException.get(pos)));
                            }
                            mSharedPreferences.edit().putString("ManualExceptions", TextUtils.
                                    join(",", mManualExceptionList)).apply();

                        } else {
                            try {
                                mManualExceptionList.remove((mPermanentManualException.get(pos)));
                                mSharedPreferences.edit().putString("ManualExceptions", TextUtils.
                                        join(",", mManualExceptionList)).apply();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                        }
                        Log.d("RejectCall", "Permanent " + TextUtils.join(",", mPermanentManualException));
                        Log.d("RejectCall", TextUtils.join(",", mManualExceptionList));
                    }
                }
            });
        } catch (NullPointerException e) {
            e.printStackTrace();
        }

        return v;
    }

}
4

1 回答 1

9

Adapter您的实施存在多个问题。太多了,我无法就如何解决它给你建议。我只是要解释如何有效地实现一个Adapter,然后你可以将它应用到你的Adapter.

可以说您应该最好地切换到使用新的RecyclerView,它比旧的有许多重大改进ListView。您可以在此处找到RecyclerView文档,并在此处找到有关如何使用它的 Google指南


如果要在 a 中显示数据,ListView应首先为ListView. 对于这个例子,我将使用这个布局:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:padding="12dp">

    <CheckBox
        android:id="@+id/checkbox"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"/>

</RelativeLayout>

为了捆绑我们希望在每个项目中显示的数据,ListView我们编写了一个新类,它只包含私有字段中的数据以及用于获取和设置该数据的 getter 和 setter。这样的类通常称为视图模型。像上面这样的布局的视图模型可能看起来像这样:

public class ExampleViewModel {

    // This is the text which will be set to the CheckBox
    private String text;

    // This boolean will be used to save the checked state of the CheckBox
    private boolean checked;

    public String getText() {
        return text;
    }

    public void setText(String text) {
        this.text = text;
    }

    public boolean isChecked() {
        return checked;
    }

    public void setChecked(boolean checked) {
        this.checked = checked;
    }
}

这种视图模型的每个实例将代表ListView. 当其中一项进入 的可见区域时,ListView它必须绑定到ViewListView(这是我们必须在 中实现的东西getView()Adapter。只要项目可见,模型就会一直绑定到这个View,但是一旦View退出可见区域,ListView它将被回收并绑定到刚刚进入可见区域的不同视图模型。这称为视图回收,这样做是为了最大限度地减少内存占用ListView并提高整体滚动性能和流动性。Views是非常昂贵的物体,尤其是充气ViewsfindViewById()消耗大量性能,回收的主要观点是您只需充气少量Views一次,然后可以重复使用,因此您避免了昂贵的充气和findViewById()以后的充气。

我上面解释的大部分内容都是自动发生的。作为开发人员,您必须做的是在已经有可用的情况下填充正确Views的或重用它们,然后将正确的视图模型绑定到. 我知道,如果您第一次听说它,其中大部分似乎相当复杂和令人困惑,但是一旦我们开始查看代码,它就会变得更加简单和明显。getView()View

所以现在我们有了视图项目的布局ListView和视图模型。我们现在需要做的是编写另一个通常称为视图持有者的类。这些视图持有者本质上是ListView. 每个视图持有者都包含一个View与 中的项目相关联的视图ListView,它们还负责将视图模型的数据绑定到View. 事不宜迟,这里有一个视图持有者与上面的视图模型一起使用:

public class ExampleViewHolder {

    // The reference to the CheckBox is saved so we only have to perform the findViewById() once.
    private final CheckBox checkBox;

    // A reference to the view model which is currently bound to this view holder
    private ExampleViewModel currentModel;

    // The View associated with this view holder is passed into the constructor from the Adapter.
    public ExampleViewHolder(View view) {

        // And here we look for all relevant views
        // In our case we just need the CheckBox
        this.checkBox = (CheckBox) view.findViewById(R.id.checkbox);
    }

    public void bind(ExampleViewModel viewModel) {

        // Unset the listener in case there was one from a previous view model
        this.checkBox.setOnCheckedChangeListener(null);

        // Save a reference to the view model which is currently bound to this view holder
        this.currentModel = viewModel;

        // Bind the data to the CheckBox
        this.checkBox.setText(viewModel.getText());
        this.checkBox.setChecked(viewModel.isChecked());

        // Reset the listener
        this.checkBox.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
            @Override
            public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
                currentModel.setChecked(isChecked);
            }
        });
    }
}

现在我们差不多完成了。现在唯一缺少的是将所有这些放在一起Adapter

public class ExampleAdapter extends BaseAdapter {

    // Each type of view in the `ListView` gets its own id
    // In this example we only have one type of View so we only need one id
    private static final int EXAMPLE_VIEW_ID = 0;

    // The default view id is just a fallback
    private static final int DEFAULT_VIEW_ID = EXAMPLE_VIEW_ID;

    private final LayoutInflater inflater;
    private List<ExampleViewModel> viewModels;

    public ExampleAdapter(Context context, List<ExampleViewModel> viewModels) {

        // The view models are initially passed in through the constructor.
        // You can pass an empty list into the Adapter if there is not data initially.
        this.viewModels = viewModels;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        if(viewModels == null) {
            return 0;
        }

        return viewModels.size();
    }

    @Override
    public Object getItem(int position) {
        return viewModels.get(position);
    }

    @Override
    public long getItemId(int position) {

        final Object model = getItem(position);

        // Here we check if the model is an instance of ExampleViewModel and if yes we return its id
        if(model instanceof ExampleViewModel) {
            return EXAMPLE_VIEW_ID;
        }

        return DEFAULT_VIEW_ID;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(getItemId(position) == EXAMPLE_VIEW_ID) {
            final ExampleViewModel model = (ExampleViewModel) getItem(position);

            final ExampleViewHolder viewHolder;

            // If the convertView is null we need to inflate a new view
            if(convertView == null) {
                final View view = this.inflater.inflate(ExampleViewHolder.LAYOUT, parent, false);   
                viewHolder = new ExampleViewHolder(view);

                // Here we set the viewHolder as tag to the View
                // This is done so we can reuse the same view holder later on
                // Essentially this is the integral part of the whole view recycling process
                view.setTag(viewHolder);
            } else {
                // If the convertView is not null we can just get the view holder with getTag() from the View
                viewHolder = (ExampleViewHolder) convertView.getTag();
            }

            // And we just need to bind the model to the view holder
            viewHolder.bind(model);
        }

        return convertView;
    }
}

这就是你所需要的。这几乎是Adapter. 如果您正在处理两种或多种不同类型的视图,则需要为每种类型编写视图模型和视图持有者类。您可以编写一个名为的接口ViewModel,如下所示:

public interface ViewModel {

}

你的每个视图模型都应该实现这个接口。然后您可以List<ViewModel>Adapter其中使用可以包含所有不同类型的视图模型。

public class TypeOneViewModel implements ViewModel {

}

public class TypeTwoViewModel implements ViewModel {

}

一旦你的所有视图模型都实现了这个接口,你就可以这样做:

final List<ViewModel> models = new ArrayList<ViewModel>();
models.add(new TypeOneViewModel());
models.add(new TypeTwoViewModel());
...

而这List现在包含多种不同类型的视图模型,然后可以传递给Adapter. 然后Adapter看起来像这样:

public class ExampleAdapter extends BaseAdapter {

    private static final int TYPE_ONE_VIEW_ID = 0;
    private static final int TYPE_TWO_VIEW_ID = 1;
    private static final int DEFAULT_VIEW_ID = TYPE_ONE_VIEW_ID;

    private final LayoutInflater inflater;
    private List<ViewModel> viewModels;

    public ExampleAdapter(Context context, List<ViewModel> viewModels) {
        this.viewModels = viewModels;
        this.inflater = LayoutInflater.from(context);
    }

    @Override
    public int getCount() {
        if(viewModels == null) {
            return 0;
        }

        return viewModels.size();
    }

    @Override
    public ViewModel getItem(int position) {
        return viewModels.get(position);
    }

    @Override
    public long getItemId(int position) {

        final ViewModel model = getItem(position);

        if(model instanceof TypeOneViewModel) {
            return TYPE_ONE_VIEW_ID;
        }

        if(model instanceof TypeTwoViewModel) {
            return TYPE_TWO_VIEW_ID;
        }

        return DEFAULT_VIEW_ID;
    }

    @Override
    public View getView(int position, View convertView, ViewGroup parent) {

        if(getItemId(position) == TYPE_ONE_VIEW_ID) {
            final TypeOneViewModel model = (TypeOneViewModel) getItem(position);

            final TypeOneViewHolder viewHolder;
            if(convertView == null) {
                final View view = this.inflater.inflate(TypeOneViewHolder.LAYOUT, parent, false);
                viewHolder = new TypeOneViewHolder(view);
                view.setTag(viewHolder);
            } else {
                viewHolder = (TypeOneViewHolder) convertView.getTag();
            }

            viewHolder.bind(model);
        }

        if(getItemId(position) == TYPE_TWO_VIEW_ID) {
            final TypeTwoViewModel model = (TypeTwoViewModel) getItem(position);

            final TypeTwoViewHolder viewHolder;
            if(convertView == null) {
                final View view = this.inflater.inflate(TypeTwoViewHolder.LAYOUT, parent, false);
                viewHolder = new TypeTwoViewHolder(view);
                view.setTag(viewHolder);
            } else {
                viewHolder = (TypeTwoViewHolder) convertView.getTag();
            }

            viewHolder.bind(model);
        }

        return convertView;
    }
}

您还可以通过编写抽象类来统一您的视图持有者。这样的抽象类看起来像这样:

public abstract class ViewHolder<T extends ViewModel> {
    protected final View itemView;

    public ViewHolder(View view) {
        this.itemView = view;
    }

    public abstract void bind(T model);
}

如果你使用这个抽象类作为你的视图持有者的基类,你可以这样写:

public class TypeOneViewHolder extends ViewHolder<TypeOneViewModel> {
    public TypeOneViewHolder(View view) {
        super(view);

        ...
    }

    public void bind(TypeOneViewModel model) {
        ...
    }
}

虽然这部分并不是真正需要的。在处理多个不同类型的项目时,最重要的部分ListView是所有模型都实现了一个通用接口,因此您可以安全地将它们放在同一个List.

无论如何,这整件事看起来比你的要简单和干净得多Adapter,不是吗?这样,您就可以在显示数据的数据和显示数据的数据之间完美分离,ListView并且Views更易于维护。您可以轻松地在视图持有者中实现动画,而不必担心视图回收,并且许多需求变得更容易实现。当然,这RecyclerView将所有这些提升到一个新的水平。它的工作方式大致相同,但比ListView.


我完全忘记了一件事:您可以List使用 getter 公开视图模型的内部,以便您可以从外部修改视图模型。将这样的方法添加到Adapter

public List<ExampleViewModel> viewmodels() {
    return viewModels;
}

public void setViewModels(List<ExampleViewModel> models) {
    viewModels = models;
}

然后你可以像这样修改视图模型Adapter

adapter.setViewModels(newData);
...
adapter.viewmodels().add(viewModel);

当您完成修改数据后,您可以通过ListView调用notifyDataSetChanged().Adapter

于 2014-12-01T00:30:10.003 回答