Sla*_*lay 3 android android-listview android-viewholder recycler-adapter android-recyclerview
我有一个ListView,Fragment我希望更新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;
}
}
Run Code Online (Sandbox Code Playgroud)
您的Adapter实施存在多个问题.对我来说太多了,无法就如何修复它给你建议.我将解释如何有效地实现一个Adapter,然后你可以将它应用到你的Adapter.
我只想说你应该优化切换到使用新旧的RecyclerView,这对旧的有很多重大改进ListView.你可以在这里找到RecyclerView文档和Googles指南如何在这里使用它.
如果要显示数据,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>
Run Code Online (Sandbox Code Playgroud)
为了捆绑我们想要在每个项目中显示的数据,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;
}
}
Run Code Online (Sandbox Code Playgroud)
这种视图模型的每个实例都代表一个项目ListView.当一个项目进入的可视区域ListView它必须绑定到View的ListView(这是我们在实现东西getView()的Adapter).只要该项目可见,模型将保持与此项目的绑定View,但是一旦View退出可见区域,ListView它将被回收并绑定到刚刚进入可见区域的不同视图模型.这称为视图回收,它可以最大限度地减少内存占用ListView并提高整体滚动性能和流动性.Views是非常昂贵的物体,特别是膨胀Views和findViewById()成本很高的性能和主要的观点回收是你必须只膨胀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);
}
});
}
}
Run Code Online (Sandbox Code Playgroud)
现在我们差不多完成了.现在唯一缺少的是将所有这些插入到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;
}
}
Run Code Online (Sandbox Code Playgroud)
这就是你所需要的一切.这几乎是一个最佳实践Adapter.如果要处理两种或更多不同类型的视图,则需要为每种类型编写视图模型和视图持有者类.你可以编写一个ViewModel看起来像这样的接口:
public interface ViewModel {
}
Run Code Online (Sandbox Code Playgroud)
您的每个视图模型都应该实现此接口.然后List<ViewModel>,您可以在Adapter其中使用包含所有不同类型的视图模型.
public class TypeOneViewModel implements ViewModel {
}
public class TypeTwoViewModel implements ViewModel {
}
Run Code Online (Sandbox Code Playgroud)
只要所有视图模型都实现此接口,您就可以执行以下操作:
final List<ViewModel> models = new ArrayList<ViewModel>();
models.add(new TypeOneViewModel());
models.add(new TypeTwoViewModel());
...
Run Code Online (Sandbox Code Playgroud)
而这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;
}
}
Run Code Online (Sandbox Code Playgroud)
您还可以通过编写抽象类来统一视图持有者.这样的抽象类看起来像这样:
public abstract class ViewHolder<T extends ViewModel> {
protected final View itemView;
public ViewHolder(View view) {
this.itemView = view;
}
public abstract void bind(T model);
}
Run Code Online (Sandbox Code Playgroud)
如果您将此抽象类用作视图持有者的基类,则可以像下面这样编写它们:
public class TypeOneViewHolder extends ViewHolder<TypeOneViewModel> {
public TypeOneViewHolder(View view) {
super(view);
...
}
public void bind(TypeOneViewModel model) {
...
}
}
Run Code Online (Sandbox Code Playgroud)
虽然这部分并不是真的需要.处理多种不同类型的项目时最重要的部分ListView是所有模型都实现了一个通用接口,因此您可以安全地将它们放在同一个接口中List.
无论如何,整个事情看起来比你更简单,更干净Adapter,不是吗?这样,你必须在数据之间的完美分离ListView和Views其显示的数据,这是一个很多更容易维护.您可以轻松地在视图持有者中实现动画,而无需关注视图回收,并且许多要求变得更加简单.在RecyclerView采取这一切当然是一个新的水平.它的工作方式大致相同,但有几个重大改进ListView,我真的建议你看看它.
我完全忘记了一件事:您可以List使用getter 公开视图模型的内部,以便您可以从外部修改视图模型.将这样的方法添加到Adapter:
public List<ExampleViewModel> viewmodels() {
return viewModels;
}
public void setViewModels(List<ExampleViewModel> models) {
viewModels = models;
}
Run Code Online (Sandbox Code Playgroud)
然后你可以Adapter像这样修改视图模型:
adapter.setViewModels(newData);
...
adapter.viewmodels().add(viewModel);
Run Code Online (Sandbox Code Playgroud)
而当你完成修改数据时,你可以更新ListView通过调用notifyDataSetChanged()的Adapter.
| 归档时间: |
|
| 查看次数: |
2035 次 |
| 最近记录: |