Reputation: 932
I'm following Big Nerd Ranch Guide
(2015 edition). We started with fragments. In this chapter RecycleView
was introduced.
The goal is to display 100 Crime
s using RecycleView
, like this:
The authors are using Crime
as data object (with fields: id, title, resolved, Date).
We have a superclass Activity
called SingleFragmentActivity
:
public abstract class SingleFragmentActivity extends FragmentActivity {
protected abstract Fragment createFragment();
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_fragment); //container for different fragments...
FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragment_container);
if (fragment == null) {
fragment = createFragment();
fm.beginTransaction()
.add(R.id.fragment_container, fragment)
.commit();
}
}
}
Accompanying layout (as you can see in SingleFragmentActivity) is fragment_container.xml
:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Then we have a subclass, called CrimeListActivity
:
public class CrimeListActivity extends SingleFragmentActivity {
@Override
protected Fragment createFragment() {
return new CrimeListFragment();
}
}
CrimeListFragment
is, as you can imagine, a Fragment with inner classes of ViewHolder
, Adapter
required by RecycleView
to create necessary Views
and recycles them.
public class CrimeListFragment extends Fragment {
private RecyclerView mCrimeRecyclerView;
private CrimeAdapter mAdapter;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_crime_list, container, false);
mCrimeRecyclerView = (RecyclerView) view.findViewById(R.id.crime_recycler_view);
mCrimeRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
updateUI();
return view;
}
private void updateUI() {
CrimeLab crimeLab = CrimeLab.get(getActivity());
List<Crime> crimes = crimeLab.getCrimes();
mAdapter = new CrimeAdapter(crimes);
mCrimeRecyclerView.setAdapter(mAdapter);
}
private class CrimeHolder extends RecyclerView.ViewHolder implements View.OnClickListener{
private Crime mCrime;
private TextView mTitleTextView;
private TextView mDateTextView;
private CheckBox mSolvedCheckBox;
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_check_box);
}
public void bindCrime(Crime crime) {
mCrime = crime;
mTitleTextView.setText(mCrime.getTitle());
mDateTextView.setText(mCrime.getDate().toString());
mSolvedCheckBox.setChecked(mCrime.isSolved());
}
@Override
public void onClick(View v) {
Toast.makeText(getActivity(),mCrime.getTitle() + " clicked!", Toast.LENGTH_SHORT)
.show();
}
}
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) {
/*onCreateViewHolder is called by the RecyclerView when it needs a new View to display an item. In
this method, you create the View and wrap it in a ViewHolder. The RecyclerView does not expect that
you will hook it up to any data yet.
For the View, you inflate a layout from the Android standard library called simple_list_item_1. This
layout contains a single TextView, styled to look nice in a list. Later in the chapter, you will make a
more advanced View for the list items*/
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) {
/*onBindViewHolder: This method will bind a ViewHolder’s View to your model object. It receives
the ViewHolder and a position in your data set. To bind your View, you use that position to find the
right model data. Then you update the View to reflect that model data.
In your implementation, that position is the index of the Crime in your array. Once you pull it out, you
bind that Crime to your View by sending its title to your ViewHolder’s TextView.*/
Crime crime = mCrimes.get(position);
holder.bindCrime(crime);
}
@Override
public int getItemCount() {
return mCrimes.size();
}
}
}
Accompanying layout for this fragment is naturally RecycleView
in xml format, named fragment_crime_list.xml
:
<?xml version="1.0" encoding="utf-8"?>
<androidx.recyclerview.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/crime_recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
And finally the actual ViewItem
through which Adapter
is going to bound the data is given in form of two TextViews
(one for Date of the Crime
, one for title of the Crime
) and one CheckBox
to mark is crime resolved.
It is named list_item_crime.xml
and looks like this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<CheckBox
android:id="@+id/list_item_crime_solved_check_box"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:padding="4dp"/>
<TextView
android:id="@+id/list_item_crime_title_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/list_item_crime_solved_check_box"
android:textStyle="bold"
android:padding="4dp"
tools:text="Crime Title"/>
<TextView
android:id="@+id/list_item_crime_date_text_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_toLeftOf="@id/list_item_crime_solved_check_box"
android:layout_below="@id/list_item_crime_title_text_view"
android:padding="4dp"
tools:text="Crime Date"/>
</RelativeLayout>
Now, this is from the book, I double checked everything, I hope I didn't miss something. My Crime
item, appears only at the top of the screen, I need to scroll down to the bottom of the screen and only the the second Crime item appears. Like this:
Anyone knows what am I missing here? I was looking last three days for this one to fix. Actually asking here is my last hope :)
Upvotes: 1
Views: 197
Reputation: 1616
Didn't get enought time to read all this but i think Set the parent layout height of the viewholder for recyclerview list_item_crime.xml
to wrap_content
i.e something like this
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<CheckBox
android:id="@+id/list_ite.....
Upvotes: 2
Reputation: 7479
The problem is a very hard one to catch. Take a closer look at your list_item_crime.xml
file. Particularly at this line:
android:layout_height="match_parent"
You're setting the height of one single item to the whole available height. If you try to scroll, you should see other items below taking up the same height. Simple fix, change to:
android:layout_height="wrap_content"
When I was first starting out with Android, I hated the RecyclerView
cause so many things can go wrong with it. This has happened to me more than once :-)
Upvotes: 4