我有VerticalGridView
使用RecyclerView.Adapter
来填充元素。我发现如果潜在元素不在视口中,则不会调用onBindViewHolder()
方法。不幸的是,这导致来自不同方法的NullPointerException
因为我在TextView
方法中捕获onBindViewHolder()
引用并将其传递给外部变量以供稍后操作。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.txtCategoryName.setText(categories.get(position).getStrCategory());
categories.get(position).setTxtViewReference(viewHolder.txtCategoryDefectTotal);
viewHolder.categoryBoxRoot.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
for(CategoryListItem catItem : categories){
if(catItem.getStrCategory().equals(viewHolder.txtCategoryName.getText())){
int index = Defects.getInstance().getCategories().indexOf(catItem) + 1;
MainInterface.grids.get(index).bringToFront();
MainInterface.grids.get(index).setVisibility(View.VISIBLE);
for(VerticalGridView grid : MainInterface.grids){
int gridIndex = MainInterface.grids.indexOf(grid);
if(gridIndex != index){
grid.setVisibility(View.INVISIBLE);
}
}
break;
}
}
}
});
根据我的理解,在TextView
对象被实例化时会创建对Viewholder
的引用。
public class ViewHolder extends RecyclerView.ViewHolder {
public TextView txtCategoryName;
public TextView txtCategoryDefectTotal;
public View categoryBoxRoot;
public ViewHolder(View itemView) {
super(itemView);
txtCategoryName = (TextView) itemView.findViewById(R.id.textViewCategoryName);
txtCategoryDefectTotal = (TextView) itemView.findViewById(R.id.textViewCategoryTotalDefects);
categoryBoxRoot = itemView.findViewById(R.id.root_category_box);
}
}
在onBindViewHolder()
实例化时,是否有办法强制Adapter
在所有元素上调用?
我尝试了here建议但没有取得任何成功。
我了解强制onBindViewHolder()
所有元素都会对whole purpose of the RecycleView.Adapter产生影响。因此,我对如何捕获TextView
引用的任何其他建议持开放态度。
作为此问题的临时修复,我可以在生成NullPointerException
的方法周围使用try catch块。但是,我担心缺乏参考意味着我将来可能会引入错误。
答案 0 :(得分:2)
解决此问题的最简单方法是向下滚动到网格底部,然后返回到顶部。这不能在onCreate()
方法中完成,因为此时网格在技术上不可见。相反,它应该在活动的onResume()
方法中调用。
@Override
public void onResume(){
super.onResume();
VerticalGridView defectGrid = grids.get(0);
RecyclerView.Adapter adapter = defectGrid.getAdapter();
defectGrid.smoothScrollToPosition(adapter.getItemCount()-1);
defectGrid.smoothScrollToPosition(0);
}
这是一个很好的尝试解决方案,但不幸的是它仍然无效。虽然它确实得到了使方法有效的参考,但它不一定有正确的参考。我发现引用可以最终指向不同的TextView,因为RecycleView.Adapter重用视图在不同的区域显示它们。
解决方案:
Mark Keen说他使用notifyDataSetChanged()
时是对的。我通过修复我的onBindViewHolder()
方法使其工作正常。
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final ViewHolder viewHolder = (ViewHolder) holder;
viewHolder.txtCategoryName.setText(categories.get(position).getStrCategory());
viewHolder.txtCategoryDefectTotal.setText(String.valueOf(categories.get(position).getTotalDefectsInCategory()));
}
我还更改了我的数据对象,因此它保存了一个int值,而不是对TextView
的引用,因为上面证明了引用无效。最后,当我按下自定义后退按钮时,我添加了对Adapter
的调用。
grids.get(0).getAdapter().notifyDataSetChanged();
谢谢大家的贡献!
答案 1 :(得分:1)
只需像这样重置Adapter
RecyclerView.Adapter adapter = recList.getAdapter();
recList.setAdapter(null);
recList.setAdapter(adapter);
recList应该是RecyclerView
。
答案 2 :(得分:0)
了解流程
public class ThemeGridAdapter extends RecyclerView.Adapter<ThemeGridAdapter.ViewHolder> {
private OnItemClickedListener listener;
private List<Theme> mDataset;
public static class ViewHolder extends RecyclerView.ViewHolder {
public TextView title;
public TextView subtitle;
// public SimpleDraweeView image;
public ImageView img;
public ViewGroup container;
public ViewHolder(RelativeLayout v) {
super(v);
container = v;
title = (TextView) v.findViewById(R.id.theme_name_text);
subtitle = (TextView) v.findViewById(R.id.theme_name_type);
// image = (SimpleDraweeView) v.findViewById(R.id.cell_img);
img = (ImageView) v.findViewById(R.id.cell_img);
}
}
public ThemeGridAdapter(List<Theme> dataset) {
mDataset = dataset;
}
@Override
public ThemeGridAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
RelativeLayout v = (RelativeLayout) LayoutInflater.from(parent.getContext())
.inflate(R.layout.cell, parent, false);
return new ViewHolder(v);
}
@Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
holder.title.setText(mDataset.get(position).getTitle());
holder.subtitle.setText(mDataset.get(position).getAuthor());
// Uri uri = Uri.parse(mDataset.get(position).getUrl());
holder.img.setImageResource(mDataset.get(position).getImage());
// holder.image.setImageURI(uri);
holder.container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (listener != null)
listener.onItemClicked(holder.getAdapterPosition());
}
});
}
@Override
public int getItemCount() {
return mDataset.size();
}
public void setListener(OnItemClickedListener listener) {
this.listener = listener;
}
}
答案 3 :(得分:0)
检查绑定视图的样式
public class CrimeListFragment extends Fragment {
private static final String SAVED_SUBTITLE_VISIBLE = "subtitle";
private static final int REQUEST_CRIME = 1;
private RecyclerView mRecyclerView;
private CrimeAdapter mCrimeAdapter;
private boolean mSubtitleVisible;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
@Nullable
@Override
public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
mRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view);
mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
if (savedInstanceState != null){
mSubtitleVisible = savedInstanceState.getBoolean(SAVED_SUBTITLE_VISIBLE);
}
updateUI();
return view;
}
private void updateUI() {
CrimeLab crimeLab = CrimeLab.get(getActivity());
List<Crime> crimes = crimeLab.getCrimes();
if (mCrimeAdapter == null) {
mCrimeAdapter = new CrimeAdapter(crimes);
mRecyclerView.setAdapter(mCrimeAdapter);
} else {
mCrimeAdapter.setCrimes(crimes);
mCrimeAdapter.notifyDataSetChanged();
}
updateSubtitle();
}
private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
private TextView mTitleTextView;
private TextView mDateTextView;
private CheckBox mSolvedCheckBox;
private Crime mCrime;
public CrimeHolder(View itemView) {
super(itemView);
itemView.setOnClickListener(this);
mTitleTextView = (TextView) itemView.findViewById(R.id.list_item_crime_title_text_view);
mDateTextView = (TextView) itemView.findViewById(R.id.list_item_crime_date_text_view);
mSolvedCheckBox = (CheckBox) itemView.findViewById(R.id.list_item_crime_solved_checkbox);
}
public void bindCrime(Crime crime) {
mCrime = crime;
mTitleTextView.setText(mCrime.getTitile());
mDateTextView.setText(mCrime.getDate().toString());
mSolvedCheckBox.setChecked(mCrime.isSolved());
}
@Override
public void onClick(View v) {
/*Toast.makeText(getActivity(),mCrime.getTitile() + "clicked!", Toast.LENGTH_SHORT).show();*/
/*Intent for calling activity from fragment*/
/* Intent intent = new Intent(getActivity(), CrimeActivity.class);*/
/*Intent intent = CrimeActivity.newIntent(getActivity(),mCrime.getId());*/
Intent intent = CrimePagerActivity.newIntent(getActivity(), mCrime.getId());
/*startActivityForResult(intent,REQUEST_CRIME);*/
startActivity(intent);
}
}
private class CrimeAdapter extends RecyclerView.Adapter<CrimeHolder> {
private List<Crime> mCrimes;
public CrimeAdapter(List<Crime> crimes) {
mCrimes = crimes;
}
@Override
public CrimeHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater layoutInflater = LayoutInflater.from(getActivity());
View view = layoutInflater.inflate(R.layout.list_item_crime, parent, false);
return new CrimeHolder(view);
}
@Override
public void onBindViewHolder(CrimeHolder holder, int position) {
Crime crime = mCrimes.get(position);
/*holder.mTextView.setText(crime.getTitile());*/
holder.bindCrime(crime);
}
@Override
public int getItemCount() {
return mCrimes.size();
}
public void setCrimes(List<Crime> crimes) {
mCrimes = crimes;
}
}
@Override
public void onResume() {
super.onResume();
updateUI();
}
@Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
if (requestCode == REQUEST_CRIME) {
}
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
inflater.inflate(R.menu.fragment_crime_list, menu);
MenuItem subtitleItem = menu.findItem(R.id.menu_item_show_subtitle);
if (mSubtitleVisible){
subtitleItem.setTitle(R.string.hide_subtitle);
}else {
subtitleItem.setTitle(R.string.show_subtitle);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.menu_item_new_crime:
Crime crime = new Crime();
CrimeLab.get(getActivity()).addCrime(crime);
Intent intent = CrimePagerActivity.newIntent(getActivity(), crime.getId());
startActivity(intent);
return true;
case R.id.menu_item_show_subtitle:
mSubtitleVisible = !mSubtitleVisible;
getActivity().invalidateOptionsMenu();
updateSubtitle();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void updateSubtitle(){
CrimeLab crimeLab = CrimeLab.get(getActivity());
int crimeCount = crimeLab.getCrimes().size();
String subtitle = getString(R.string.subtitle_format,crimeCount);
if (!mSubtitleVisible){
subtitle = null;
}
AppCompatActivity appCompatActivity = (AppCompatActivity)getActivity();
appCompatActivity.getSupportActionBar().setSubtitle(subtitle);
}
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putBoolean(SAVED_SUBTITLE_VISIBLE,mSubtitleVisible);
}
}
答案 4 :(得分:0)
这对我有用,并强制叫git fetch origin otherBranch:master
onBindViewHolder()
答案 5 :(得分:0)
将您的 RecyclerView
包裹在一个 NestedScrollView
中,这将强制提前绑定所有项目。
<androidx.core.widget.NestedScrollView
android:id="@+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvGrid"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:nestedScrollingEnabled="false" />
</androidx.core.widget.NestedScrollView>