ListView
and GridView
to building a list or grid layout. Up to now, with Material Design has been published, by only one widget called RecyclerView
, we can create both of them by changing it's LayoutManager
. Moreover, with it, developer also can do some exciting animations with Action Bar when scrolling screen. So that, it's time we have to change to keep pace with the development of design technology.Through this post, I will present the way to creating horizontal/vertical list and grid layout with this new widget.
Layout Manager for RecyclerView
RecyclerView
is initializing it's LayoutManager
. For example:
//create a horizontal list view
LinearLayoutManager horizontalManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, true);
.
.
//create a vertical list view
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
.
.
//create a grid layout
GridLayoutManager layoutManager = new GridLayoutManager(this, 3); //3 is number of coloumn
And attatch layout manager to RecyclerView
with this method:
recyclerView.setLayoutManager(layoutManager);
Sample horizontal and vertical list view
activity_list_view.xml
And it's programmatically code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="@dimen/activity_horizontal_margin">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="Horizontal ListView"
android:textStyle="bold" />
<android.support.v7.widget.RecyclerView
android:id="@+id/horizontal_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingBottom="10dp"
android:paddingTop="10dp"
android:text="Vertical ListView"
android:textStyle="bold" />
<android.support.v7.widget.RecyclerView
android:id="@+id/recyle_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
ListViewActivity.java
2 simple package info.devexchanges.recyclerview;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public class ListViewActivity extends AppCompatActivity {
private RecyclerView horizontalList;
private RecyclerView verticalList;
private HorizontalListAdapter horizontalAdapter;
private VerticalListAdapter verticalAdapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_view);
horizontalList = (RecyclerView)findViewById(R.id.horizontal_recycler);
verticalList = (RecyclerView)findViewById(R.id.recyle_view);
horizontalList.setHasFixedSize(true);
verticalList.setHasFixedSize(true);
//set horizontal LinearLayout as layout manager to creating horizontal list view
LinearLayoutManager horizontalManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
horizontalList.setLayoutManager(horizontalManager);
horizontalAdapter = new HorizontalListAdapter(this);
horizontalList.setAdapter(horizontalAdapter);
//set vertical LinearLayout as layout manager for vertial listview
LinearLayoutManager layoutManager = new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false);
verticalList.setLayoutManager(layoutManager);
verticalAdapter = new VerticalListAdapter(this);
verticalList.setAdapter(verticalAdapter);
}
}
RecyclerView
adapters in this case (like ListView
adapter):
HorizontalListAdapter.java
package info.devexchanges.recyclerview;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.Toast;
public class HorizontalListAdapter extends RecyclerView.Adapter<HorizontalListAdapter.ViewHolder> {
private Activity activity;
public HorizontalListAdapter(Activity activity) {
this.activity = activity;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.item_horizontal_list, viewGroup, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(HorizontalListAdapter.ViewHolder viewHolder, final int position) {
viewHolder.linearLayout.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity, "Position clicked: " + position, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return 10;
}
/**
* View holder to display each RecylerView item
*/
protected class ViewHolder extends RecyclerView.ViewHolder {
private LinearLayout linearLayout;
public ViewHolder(View view) {
super(view);
linearLayout = (LinearLayout) view.findViewById(R.id.layout);
}
}
}
VerticalListAdapter.java
Running this activity, we'll have this output:
package info.devexchanges.recyclerview;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.RelativeLayout;
import android.widget.Toast;
public class VerticalListAdapter extends RecyclerView.Adapter<VerticalListAdapter.ViewHolder> {
private Activity activity;
public VerticalListAdapter(Activity activity) {
this.activity = activity;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.item_recycler_view, parent, false);
return new ViewHolder(view);
}
@Override
public void onBindViewHolder(ViewHolder viewHolder, final int position) {
if ((position + 1) % 2 == 0) {
viewHolder.imageView.setImageResource(R.drawable.even);
} else {
viewHolder.imageView.setImageResource(R.drawable.odd);
}
viewHolder.container.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity, "Position: " + position, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return 10;
}
/**
* View holder to display each RecylerView item
*/
protected class ViewHolder extends RecyclerView.ViewHolder {
private ImageView imageView;
private RelativeLayout container;
public ViewHolder(View view) {
super(view);
imageView = (ImageView) view.findViewById(R.id.image);
container = (RelativeLayout) view.findViewById(R.id.container);
}
}
}
Sample grid view
RecyclerView
is not much complicated. Firstly, designing a layout:
activity_grid.xml
Simple programmatically code:
<?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="match_parent"
android:paddingBottom="@dimen/activity_vertical_margin"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin">
<android.support.v7.widget.RecyclerView
android:id="@+id/recyle_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
GridViewActivity.java
Layout for each grid's item:
package info.devexchanges.recyclerview;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
public class GridViewActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private GridViewAdapter adapter;
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grid);
recyclerView = (RecyclerView)findViewById(R.id.recyle_view);
recyclerView.setHasFixedSize(true);
//set GridLayoutManager
GridLayoutManager layoutManager = new GridLayoutManager(this, 3);
recyclerView.setLayoutManager(layoutManager);
adapter = new GridViewAdapter(this);
recyclerView.setAdapter(adapter);
}
}
item_grid.xml
And the <?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="120dp"
android:layout_height="120dp">
<ImageView
android:id="@+id/image"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/sample_1" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="#80000000"
android:padding="10dp"
android:textColor="#ffffff" />
</RelativeLayout>
RecyclerView
adapter:
GridViewAdapter.java
Running it, we have this output:
package info.devexchanges.recyclerview;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
public class GridViewAdapter extends RecyclerView.Adapter<GridViewAdapter.ViewHolder> {
private Activity activity;
public GridViewAdapter(Activity activity) {
this.activity = activity;
}
@Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
LayoutInflater inflater = activity.getLayoutInflater();
View view = inflater.inflate(R.layout.item_grid, viewGroup, false);
return new ViewHolder(view);
}
@SuppressLint("SetTextI18n")
@Override
public void onBindViewHolder(GridViewAdapter.ViewHolder viewHolder, final int position) {
if (position % 3 == 0) {
viewHolder.imageView.setImageResource(R.drawable.sample_3);
} else if (position % 3 == 1) {
viewHolder.imageView.setImageResource(R.drawable.sample_1);
} else {
viewHolder.imageView.setImageResource(R.drawable.sample_2);
}
viewHolder.textView.setText("Position: " + (position + 1));
viewHolder.imageView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(activity, "You clicked at position: " + position, Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return 10;
}
/**
* View holder to display each RecylerView item
*/
protected class ViewHolder extends RecyclerView.ViewHolder {
private TextView textView;
private ImageView imageView;
public ViewHolder(View view) {
super(view);
imageView = (ImageView) view.findViewById(R.id.image);
textView = (TextView) view.findViewById(R.id.text);
}
}
}
RecyclerView weaknesses
RecyclerView
has some own weakness, not perfect yet:- Cannot adding header/footer view easily like
ListView
(by calladdHeaderView/addFooterView
method). - It's difficult to handle item clicked event.
- Cannot "auto column" with grid layout. If you would like to set dynamic column for different devices, please initialize the manager with an integer resource value and provide different values for different screens (i.e. values-w600, values-large, values-land).
RecyclerView
will be fixed and improved to change itself better and the Material Design technology become more perfect. Moreover, you should read my previous post about RecyclerView
and CardView
as items to understand the adapter and it's own ViewHolder
. Finally, project source code now availble on @Github.