I have a list made with RecyclerView. I can add the ad to the bottom, but I get an error when I want to add it to the middle.
For example, I can't add the ad to line 2. I get error again when I increase getItemCount value by 1 (return movies.size() + 1). What I haven't tried.
My Adapter
public class SeriesActivityAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Movies> movies;
private static final int ITEM = 0;
private static final int BANNER = 1;
TinyDB tinyDB;
private Activity context;
public SeriesActivityAdapter(Activity c, List<Movies> movies) {
this.movies= movies;
this.context = c;
tinyDB = new TinyDB(c);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType==BANNER && !tinyDB.getBoolean("remove_ads")){
View view = LayoutInflater.from(context).inflate(R.layout.item_ad, parent,false);
return new MyViewBanner(view);
}
else{
View view = LayoutInflater.from(context).inflate(R.layout.item_series_activity, parent,false);
return new MyViewHolder(view);
}
}
#Override
public void onBindViewHolder(#NonNull final RecyclerView.ViewHolder holder, final int position) {
if(holder instanceof MyViewHolder){
final Movies currentMovie = movies.get(position);
final MyViewHolder myholder = (MyViewHolder) holder;
Glide.with(context)
.load(currentMovie.getResim())
.into(myholder.movieImage);
}
else if (holder instanceof MyViewBanner){
final MyViewBanner myBanner = (MyViewBanner) holder;
AdRequest adRequest = new AdRequest.Builder().build();
myBanner.adView.loadAd(adRequest);
}
}
#Override
public int getItemCount() {
return movies.size() + 1;
}
#Override
public int getItemViewType(int position) {
if(position == 2){
return BANNER;
}
return ITEM;
}
}
My Activity
JsonArrayRequest arrayRequest = new JsonArrayRequest(murl,
new Response.Listener<JSONArray>() {
#Override
public void onResponse(JSONArray jsonArray) {
Log.i("log-",jsonArray.toString());
for(int i = 0; i < jsonArray.length(); i++) {
try {
JSONObject jsonObject = jsonArray.getJSONObject(i);
users.add(new Movies(jsonObject.getString("link"),
jsonObject.getString("resim"),
"",jsonObject.getString("tip")));
}
catch(JSONException e) {
Log.i("log-","error"+e.getMessage());
}
}
moviesAdapter.notifyDataSetChanged();
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError volleyError) {...
}
});
Volley.newRequestQueue(this).add(arrayRequest);
My Logcat
Process: com.moktay.izliyoo, PID: 23423
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.get(ArrayList.java:411)
at com.moktay.izliyoo.list.SeriesActivityAdapter.onBindViewHolder(SeriesActivityAdapter.java:64)
Pass null in Arraylist at the position where you want to show ads in recyler view.
Suppose at 2 and 4 position you want ads simplly add null at those position and finally in itemViewType method check for
if(arraylist.get(position)==null)
{
return BANNER
}else{
return VIEWTYPE
}
Related
I have an app that stores some data in room database. At first, my adapter was like that:
public class ViewCourseAdapter extends ListAdapter<Course, ViewCourseAdapter.ViewCourseHolder> {
private int previousPosition = 0;
public ViewCourseAdapter() {
super(DIFF_CALLBACK);
}
private static final DiffUtil.ItemCallback<Course> DIFF_CALLBACK = new DiffUtil.ItemCallback<Course>() {
#Override
public boolean areItemsTheSame(#NonNull Course oldItem, #NonNull Course newItem) {
return oldItem.getId() == newItem.getId();
}
#Override
public boolean areContentsTheSame(#NonNull Course oldItem, #NonNull Course newItem) {
return oldItem.getfName().equals(newItem.getfName());
}
};
#NonNull
#Override
public ViewCourseHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.view_courses_item, parent, false);
return new ViewCourseHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewCourseHolder holder, int position) {
Course currentCourse = getItem(position);
String fullName = currentCourse.getfName() + " " + currentCourse.getlName();
SpannableString SfullName = new SpannableString(fullName);
SfullName.setSpan(new UnderlineSpan(), 0, fullName.length(), 0);
holder.text_view_firstName_1.setText(SfullName);
if (position > previousPosition) {
AnimationUtil.animate(holder, true);
} else {
AnimationUtil.animate(holder, false);
}
previousPosition = position;
}
class ViewCourseHolder extends RecyclerView.ViewHolder {
private TextView text_view_firstName_1;
ViewCourseHolder(#NonNull View itemView) {
super(itemView);
text_view_firstName_1 = itemView.findViewById(R.id.text_view_firstName_1);
}
}
#Override
public int getItemCount() {
return super.getItemCount();
}
And everything was working perfect. Now I want to show ads inside recyclerview, so I changed my adapter:
public class ViewCourseAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int COURSE_VIEW_TYPE = 0;
private static final int AD_VIEW_TYPE = 1;
private List<Course> data;
private List<Object> ad;
private Context context;
private LayoutInflater layoutInflater;
private int previousPosition = 0;
public ViewCourseAdapter(Context context, List<Object> ad) {
this.data = new ArrayList<>();
this.context = context;
this.layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
this.ad = ad;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType ) {
case COURSE_VIEW_TYPE:
View courseView = LayoutInflater.from(parent.getContext()).inflate(R.layout.view_courses_item, parent, false);
return new ViewCourseViewHolder(courseView);
case AD_VIEW_TYPE:
default:
View adView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_ads, parent, false);
return new AdViewHolder(adView);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case COURSE_VIEW_TYPE:
ViewCourseViewHolder holder1 = (ViewCourseViewHolder) holder;
Course currentCourse = (Course) data.get(position);
String fullName = currentCourse.getfName() + " " + currentCourse.getlName();
SpannableString SfullName = new SpannableString(fullName);
SfullName.setSpan(new UnderlineSpan(), 0, fullName.length(), 0);
holder1.text_view_firstName_1.setText(SfullName);
break;
case AD_VIEW_TYPE:
default:
AdViewHolder bannerHolder = (AdViewHolder) holder;
AdView adView = (AdView) ad.get(position);
ViewGroup adCardView = (ViewGroup) bannerHolder.itemView;
if (adCardView.getChildCount() > 0) {
adCardView.removeAllViews();
}
if (adView.getParent() != null) {
((ViewGroup) adView.getParent()).removeView(adView);
}
adCardView.addView(adView);
}
if (position > previousPosition) {
AnimationUtil.animate(holder, true);
} else {
AnimationUtil.animate(holder, false);
}
previousPosition = position;
}
class AdViewHolder extends RecyclerView.ViewHolder {
AdViewHolder(View view) {
super(view);
}
}
#Override
public int getItemViewType(int position) {
return (position % ViewCoursesActivity.ITEMS_PER_AD == 0) ? AD_VIEW_TYPE
: COURSE_VIEW_TYPE;
}
#Override
public int getItemCount() {
return data.size();
}
public void setData(List<Course> newData) {
if (data != null) {
CourseDiffCallback courseDiffCallback = new CourseDiffCallback(data, newData);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(courseDiffCallback);
data.clear();
data.addAll(newData);
diffResult.dispatchUpdatesTo(this);
} else {
data = newData;
}
}
public class ViewCourseViewHolder extends RecyclerView.ViewHolder {
private TextView text_view_firstName_1;
ViewCourseViewHolder(#NonNull View itemView) {
super(itemView);
text_view_firstName_1 = itemView.findViewById(R.id.text_view_firstName_1);
}
}
private class CourseDiffCallback extends DiffUtil.Callback {
private final List<Course> oldItem, newItem;
private CourseDiffCallback(List<Course> oldItem, List<Course> newItem) {
this.oldItem = oldItem;
this.newItem = newItem;
}
#Override
public int getOldListSize() {
return oldItem.size();
}
#Override
public int getNewListSize() {
return newItem.size();
}
#Override
public boolean areItemsTheSame(int oldItemPosition, int newItemPosition) {
return oldItem.get(oldItemPosition).getId() == newItem.get(newItemPosition).getId();
}
#Override
public boolean areContentsTheSame(int oldItemPosition, int newItemPosition) {
return oldItem.get(oldItemPosition).getfName().equals(newItem.get(newItemPosition).getfName());
}
}
And my activity is:
public class ViewCoursesActivity extends AppCompatActivity {
private CourseViewModel courseViewModel;
// A banner ad is placed in every 8th position in the RecyclerView.
public static final int ITEMS_PER_AD = 8;
private static final String AD_UNIT_ID = "ca-app-pub-3940256099942544/6300978111";
// The RecyclerView that holds and displays banner ads and menu items.
private RecyclerView recyclerView;
// List of banner ads and MenuItems that populate the RecyclerView.
private List<Object> data = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_view_courses);
recyclerView = findViewById(R.id.recycler_view_3);
recyclerView.setHasFixedSize(true);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
addBannerAds();
loadBannerAds();
final ViewCourseAdapter adapter = new ViewCourseAdapter(this, data);
recyclerView.setAdapter(adapter);
courseViewModel = ViewModelProviders.of(this).get(CourseViewModel.class);
courseViewModel.setIdForCourse(0);
//courseViewModel.getCourseByStudentId().observe(this, adapter::submitList);
courseViewModel.getCourseByStudentId().observe(this, adapter::setData);
}
#Override
protected void onResume() {
for (Object item : data) {
if (item instanceof AdView) {
AdView adView = (AdView) item;
adView.resume();
}
}
super.onResume();
}
#Override
protected void onPause() {
for (Object item : data) {
if (item instanceof AdView) {
AdView adView = (AdView) item;
adView.pause();
}
}
super.onPause();
}
#Override
protected void onDestroy() {
for (Object item : data) {
if (item instanceof AdView) {
AdView adView = (AdView) item;
adView.destroy();
}
}
super.onDestroy();
}
/**
* Adds banner ads to the items list.
*/
private void addBannerAds() {
// Loop through the items array and place a new banner ad in every ith position in
// the items List.
for (int i = 0; i <= data.size(); i += ITEMS_PER_AD) {
final AdView adView = new AdView(ViewCoursesActivity.this);
adView.setAdSize(AdSize.SMART_BANNER);
adView.setAdUnitId(AD_UNIT_ID);
data.add(i, adView);
}
}
/**
* Sets up and loads the banner ads.
*/
private void loadBannerAds() {
// Load the first banner ad in the items list (subsequent ads will be loaded automatically
// in sequence).
loadBannerAd(0);
}
/**
* Loads the banner ads in the items list.
*/
private void loadBannerAd(final int index) {
if (index >= data.size()) {
return;
}
Object item = data.get(index);
if (!(item instanceof AdView)) {
throw new ClassCastException("Expected item at index " + index + " to be a banner ad"
+ " ad.");
}
final AdView adView = (AdView) item;
// Set an AdListener on the AdView to wait for the previous banner ad
// to finish loading before loading the next ad in the items list.
adView.setAdListener(new AdListener() {
#Override
public void onAdLoaded() {
super.onAdLoaded();
// The previous banner ad loaded successfully, call this method again to
// load the next ad in the items list.
loadBannerAd(index + ITEMS_PER_AD);
}
#Override
public void onAdFailedToLoad(int errorCode) {
// The previous banner ad failed to load. Call this method again to load
// the next ad in the items list.
Log.e("ViewCoursesActivity", "The previous banner ad failed to load. Attempting to"
+ " load the next banner ad in the items list.");
loadBannerAd(index + ITEMS_PER_AD);
}
});
// Load the banner ad.
adView.loadAd(new AdRequest.Builder().build());
}
After that I have two problems:
1. At frist position it display an ad instead of first item from my database, and first item is nowhere. If I check my database first item is there, just not apear in recyclerview.
2. If I want to scroll items, the app crash, and the error is "java.lang.IndexOutOfBoundsException: Index: 8, Size: 1".
What am I doing wrong?
Please help me. Thank you!
Fundamentally, a RecyclerView is used to display a List to the user. Use this to guide your implementation.
Currently, you have two lists:
private List<Course> data;
private List<Object> ad;
You need to find a way to "collate" these down to a single list. Maybe something like this:
private static List<Object> collate(List<Course> data, List<Object> ads) {
List<Object> collated = new ArrayList<>();
while (data.size() > 0 || ads.size() > 0) {
for (int i = 0; i < ITEMS_PER_AD && data.size() > 0; ++i) {
collated.add(data.remove(0));
}
if (ads.size() > 0) {
collated.add(ads.remove(0));
}
}
return collated;
}
With that method defined, you can replace your existing constructor:
private List<Course> data;
private List<Object> ad;
// ...
public ViewCourseAdapter(Context context, List<Object> ad) {
this.data = new ArrayList<>();
this.ad = ad;
// ...
}
with this:
private List<Object> ad;
private List<Object> collated;
// ...
public ViewCourseAdapter(Context context, List<Object> ad) {
this.ad = ad;
this.collated = new ArrayList<>(ad);
// ...
}
And you can replace your existing setData() method:
public void setData(List<Course> newData) {
if (data != null) {
CourseDiffCallback courseDiffCallback = new CourseDiffCallback(data, newData);
DiffUtil.DiffResult diffResult = DiffUtil.calculateDiff(courseDiffCallback);
data.clear();
data.addAll(newData);
diffResult.dispatchUpdatesTo(this);
} else {
data = newData;
}
}
with this:
public void setData(List<Course> newData) {
if (data != null) {
List<Object> newCollated = collate(ad, newData);
// setup DiffUtil callback here...
this.collated = newCollated;
// dispatch DiffUtil result here...
}
}
Now you will have a single list that you can use in your adapter. Then you can change your adapter's methods to use that one list:
#Override
public int getItemCount() {
return collated.size();
}
#Override
public int getItemViewType(int position) {
return (collated.get(position) instanceof Course)
? COURSE_VIEW_TYPE
: AD_VIEW_TYPE;
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
if (holder.getItemViewType() == COURSE_VIEW_TYPE) {
bindCourseHolder((ViewCourseViewHolder) holder, position);
} else if (holder.getItemViewType() == AD_VIEW_TYPE) {
bindAdHolder((AdViewHolder) holder, position);
}
}
private void bindCourseHolder(ViewCourseViewHolder holder, int position) {
// ...
}
private void bindAdHolder(AdViewHolder holder, int position) {
// ...
}
All of these changes are based on the fundamental concept that you just have a single list, and that list holds (potentially) many different types of things.
Thank you #Ben P. for advice...I can't believe I didn't see this...so I modify collate() method like this:
while (data.size() > 0) {
collated.addAll(ad);
for (int i = 0; i < ITEMS_PER_AD && data.size() > 0; ++i) {
collated.add(data.remove(0));
}
}
ad.remove(0);
return collated;
It is there a better way to do this?
I have a recyclerView adapter named as CustomAdapter in the MainActivity, I am getting the data by parsing the JSON and this data is used to set the textview inCustomAdapter class.
When I start my application,I am not showing the recyclerView list and the adapter is not being called. So my problem is I am not able to call the customAdapter's functions like onBindViewHolder,onCreateViewHolder.
public class CustomAdapter extends RecyclerView.Adapter<CustomAdapter.MyViewHolder> {
Context context;
RealmResults<Film> realmResults;
View.OnTouchListener listener;
Realm realm;
public CustomAdapter(Context context, View.OnTouchListener listener) {
this.context = context;
this.listener = listener;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View root = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_liste, parent, false);
MyViewHolder viewHolder = new MyViewHolder(root);
return viewHolder;
}
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, final int position) {
Realm.init(context);
RealmResults<Film> films = realm.where(Film.class).findAll();
Film f = films.get(position);
for(film film:filmler) {
try {
holder.title.setText(film.getTitle());
holder.year.setText(film.getYear());
holder.poster.setImageURI(film.getPoster());
if (film.getPoster().toString().equals("N/A") == true) {
Picasso.get().load(R.drawable.noImage).fit().into(holder.image);
} else {
Picasso.get().load(film.getPoster()).into(holder.image);
}
} catch (NullPointerException e) {
e.printStackTrace();
}
}
#Override
public int getItemCount() {
if(realmResults==null)
{
return 0;
}
return realmResults.size();
}
public class MyViewHolder extends RecyclerView.ViewHolder implements View.OnTouchListener {
public TextView title, year;
public ImageView image;
public LinearLayout linearLayout;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.title_id);
year = itemView.findViewById(R.id.year_id);
image = itemView.findViewById(R.id.image_id);
linearLayout = itemView.findViewById(R.id.linearlayout_id);
linearLayout.setOnTouchListener(listener);
}
#Override
public boolean onTouch(View view, MotionEvent motionEvent) {
if(motionEvent.getAction() == MotionEvent.ACTION_SCROLL){
return true;
}
return false;
}
}
}
My MainActivity Class:
final StringRequest stringRequest1 = new StringRequest(Request.Method.GET, url1, new Response.Listener<String>() {
#Override
public void onResponse(String response) {
progressDialog.dismiss();
JSONObject jsonObject1 = new JSONObject(response);
String response = jsonObject1.getString("Response");
if (response.equals("True") && jsonObject1.has("Search")==true) {
JSONArray array1 = jsonObject1.getJSONArray("Search");
for (int i = 0; i < array1.length(); i++) {
final JSONObject films = array1.getJSONObject(i);
realm1.executeTransactionAsync(new Realm.Transaction() {
#Override
public void execute(Realm realm1) {
Film film = realm1.createObject(Film.class);
try {
film.setTitle(filmler.getString("Title"));
film.setYear(filmler.getString("Year"));
film.setPoster(filmler.getString("Poster"));
realm1.copyToRealm(film);
} catch (JSONException e) {
e.printStackTrace();
}
}
});
}
for (int j = 0; j < array1.length(); j++) {
array1.remove(j);
}
adapter = new CustomAdapter(this, this);
recycle1.setAdapter(adapter);
you are going wrong in onBindViewHolder.
Try to pass list in constructor
public CustomAdapter(Context context, View.OnTouchListener
listener,RealmResults<film> realmResults) {
this.context = context;
this.listener = listener;
this.realmResults = realmResults;
}
and Remove this code from bindViewHolder
Log.d(TAG, "onBindViewHolder : called.");
Realm.init(context);
//film filmler = filmList.get(position);
//realm = Realm.getDefaultInstance();
RealmResults<film> filmler =
realm.where(film.class).findAll();
film f = filmler.get(position);
don't call for loop there in onBindViewHolder for set data just do following
#Override
public void onBindViewHolder(#NonNull MyViewHolder holder, final int
position) {
film item = realmResults.get(position);
if(item!=null && item.isValid()){
holder.baslik.setText(item.getTitle());
holder.yil.setText(item.getYear());
holder.resim.setImageURI(item.getPoster());
if (item.getPoster().toString().equals("N/A")) {
Picasso.get().load(R.drawable.resimyok).fit().into(holder.resim);
} else {
Picasso.get().load(film.getPoster()).into(holder.resim);
}
}
}
Fix this below for getting response and save it to realm
JSONArray array1 = jsonObject1.getJSONArray("Search");
Realm realm = Realm.getDefaultInstance();
realm.beginTransaction();
realm.createOrUpdateAllFromJson(filn.class, array1);
realm.commitTransaction();
//get all saved data from realm
RealmResults<T> results =
realm.where(film.class).query.findAll();
adapter = new
CustomAdapter(getApplicationContext(),MainActivity.this,results);
recycle1.setAdapter(adapter);
arama.getText().clear();
I am building a sample transaction app with the ability to categorize the list item based on its category. e.g. user can add a new list item with category sport and i would like to have a sport as header in the recyclerview and later on if item with category movie added, then the new item will be under movie.
I understand that i have to provide the itemViewType in my adapter class. and then i will have a List<txn> that mixed with different view types.
my current approach in following block is using Map<String, List<Item>> to keep track of each new item for particular category. But I was told one of my colleague that there is more efficient way to handle this.
But what kind of data structures I should use so that I can support
user can add any new category.
when user use the existing category,
then it should be add into the particular section in the recycler
view only?
public class txnAdapter extends
RecyclerView.Adapter<RecyclerView.ViewHolder> {
public static final int TRANSCATION_TYPE = 1;
public static final int HEAD_TYPE = 2;
private List<Txn> txns;
private List<Object> items;
private Map<String, List<Txn>> map;
public txnAdapter() {
map = new HashMap<>();
txns = new ArrayList<>();
items = new ArrayList<>();
}
public void setTxns(List<Txn> txns) {
//make a copy
this.txns.addAll(txns);
addToMap(txns);
resetList(this.txns);
}
public void addTxn(Txn txn) {
//filter by type and insert.
//find the index of category in list and
List<Txn> list = new ArrayList<>();
list.add(txn);
addToMap(list);
resetList(this.txns);
}
void addToMap(List<Txn> txns){
for(Txn t: txns){
if(!map.containsKey(t.getCategory())){
map.put(t.getCategory(), new ArrayList<Txn>());
}
List<Txn> list = map.get(t.getCategory());
list.add(t);
map.put(t.getCategory(), list);
}
}
void resetList(List<Txn> txns) {
items.clear();
for(Map.Entry<String, List<Txn>> e: map.entrySet()){
if(!items.contains(e.getKey())){
items.add(e.getKey());
}
items.addAll(e.getValue());
}
notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//inflat the layout
View itemView = null;
if (viewType == HEAD_TYPE) {
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_header, parent, false);
return new headerViewHolder(itemView);
} else if (viewType == TRANSCATION_TYPE) {
itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item, parent, false);
return new transcationViewHolder(itemView);
}
return new transcationViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object cur = this.items.get(position);
if (holder instanceof headerViewHolder) {
((headerViewHolder) holder).gategoryTV.setText((String) cur);
} else if (holder instanceof transcationViewHolder) {
((transcationViewHolder) holder).titleTV.setText(((Txn) cur).getTitle());
((transcationViewHolder) holder).dateTV.setText(((Txn) cur).getDate());
((transcationViewHolder) holder).amountTV.setText(((Txn) cur).getAmount() + "");
}
}
#Override
public int getItemCount() {
return items == null ? 0 : items.size();
}
#Override
public int getItemViewType(int position) {
if (items.get(position) instanceof String) {
//this is header
return HEAD_TYPE;
} else if (items.get(position) instanceof Txn) {
return TRANSCATION_TYPE;
}
return -1;
}
private class transcationViewHolder extends RecyclerView.ViewHolder {
private TextView titleTV;
private TextView dateTV;
private TextView amountTV;
public transcationViewHolder(#NonNull View itemView) {
super(itemView);
this.titleTV = itemView.findViewById(R.id.title);
this.dateTV = itemView.findViewById(R.id.date);
this.amountTV = itemView.findViewById(R.id.amount);
}
}
private class headerViewHolder extends RecyclerView.ViewHolder {
private TextView gategoryTV;
public headerViewHolder(#NonNull View itemView) {
super(itemView);
this.gategoryTV = itemView.findViewById(R.id.category);
}
}
Try this library I think it should full fill your requirements,
https://github.com/luizgrp/SectionedRecyclerViewAdapter
you have to do some customisation there it surely work.
Unable to add second child in Recyclerview I am passing two different arrays to RecyclerAdapter to display two child layout with different data and views.Is there any solution to add different child layout using same header layout.I added horizontal Recyclerview in vertical Recyclerview and I want to display details like I attached the image
private void setupRecyclerView(RecyclerView recyclerView) {
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
RecyclerAdapter recyclerAdapter = new RecyclerAdapter();
int[] images = new int[]{
R.drawable.finance,
R.drawable.business,
R.drawable.financejob,
R.drawable.ecomchallenges
};
ArrayList<ChildView> childViews = new ArrayList<>();
childViews.add(new ChildView(images[0], "The \"Best\" Startup Pitch Deck - How To Raise Venture Capital", "$100"));
childViews.add(new ChildView(images[1], "An Entire MBA in 1 Course:Award Winning Business School Prof", "$100"));
childViews.add(new ChildView(images[2], "What Finance Job is for You? Explanation of 14 Finance Roles", "$100"));
childViews.add(new ChildView(images[3], "Learn To Build Beautiful HTML5 And CSS3 Websites In 1 Month", "$100"));
int[] courseImage = new int[] {
R.drawable.php,
R.drawable.development,
R.drawable.web,
R.drawable.java
};
ArrayList<CourseByType> courseByTypes = new ArrayList<>();
courseByTypes.add(new CourseByType("Technology", courseImage[0]));
courseByTypes.add(new CourseByType("Business", courseImage[1]));
courseByTypes.add(new CourseByType("Photography", courseImage[2]));
courseByTypes.add(new CourseByType("Development", courseImage[3]));
Log.d("","Above adapter");
recyclerAdapter.addItem(new GroupView("Business", childViews));
Log.d("","Below Child");
recyclerAdapter.addCourseByType(new CourseByHeader("Technology", courseByTypes));
Log.d("","Below Course");
recyclerView.setAdapter(recyclerAdapter);
}
This is the main fragment where I set the values to two different
arraylist ArrayList<ChildView> childViews = new ArrayList<>()
and
ArrayList<CourseByType> courseByTypes = new ArrayList<>()
Values of child views are passing properly but CourseByType values are not passing.This is the adapter class for this fragment class.
RecyclerAdapter.java
public class RecyclerAdapter extends RecyclerView.Adapter<RecyclerAdapter.ViewHolder> {
ArrayList<PassValues> containerArrayList;
ArrayList<GroupView> groupViews;
ArrayList<CourseByHeader>courseByHeaders;
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
View view = LayoutInflater.from(context).inflate(R.layout.group_title, parent, false);
return new ViewHolder(view);
}
public RecyclerAdapter(){
containerArrayList = new ArrayList<>();
groupViews = new ArrayList<>();
courseByHeaders = new ArrayList<>();
}
public void addContainer(PassValues container){
containerArrayList.add(container);
}
public void addItem(GroupView groupView){
Log.d("","Inside Group method");
groupViews.add(groupView);
}
public void addCourseByType(CourseByHeader courseByHeader){
Log.d("","Inside Course method");
courseByHeaders.add(courseByHeader);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Log.d("", "Pass Values out of IF" + position);
ChildViewAdapter childViewAdapter = new ChildViewAdapter();
if(position == 0){
GroupView groupView = groupViews.get(position);
holder.title.setText(groupView.getTitle());
Log.d("", "Passing Values" + groupView.getTitle());
holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.recyclerView.getContext(), LinearLayoutManager.HORIZONTAL, false));
holder.recyclerView.setOnFlingListener(null);
childViewAdapter.addChild(groupView.getChildViewList());
holder.recyclerView.setAdapter(childViewAdapter);
}
if (position == 1) {
CourseByHeader courseByHeader = courseByHeaders.get(position);
holder.title.setText(courseByHeader.getTitle());
Log.d("", "Passing Values" + courseByHeader.getTitle());
holder.recyclerView.setLayoutManager(new LinearLayoutManager(holder.recyclerView.getContext(), LinearLayoutManager.HORIZONTAL, false));
holder.recyclerView.setOnFlingListener(null);
childViewAdapter.addCourse(courseByHeader.getCourseByTypes());
holder.recyclerView.setAdapter(childViewAdapter);
}
}
#Override
public int getItemCount() {
if(getItemViewType(0) == TYPE_HEADER)
return groupViews.size() ;
if (getItemViewType(1) == TYPE_ITEM)
return courseByHeaders.size();
else return -1;
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView title;
RecyclerView recyclerView;
public ViewHolder(View itemView) {
super(itemView);
title = (TextView)itemView.findViewById(R.id.course_title);
recyclerView = (RecyclerView)itemView.findViewById(R.id.group_recycler);
}
}
}
This RecyclerAdapter contains one RecyclerView in that first row has one image and 3 textviews and 2nd row has 1 ImageView and 1 TextView. At position first,one image and 3 textviews are shown but it's not going on 2nd view
This is the view I getting after run on emulator.
This are two child for RecyclerViews
ChildView.java
public class ChildView {
int image;
String course, price;
public ChildView(int image, String course, String price) {
this.image = image;
this.course = course;
this.price = price;
}
public int getImage() {
return image;
}
public String getCourse() {
return course;
}
public String getPrice() {
return price;
}
}
CourseByType.java
public class CourseByType {
String courseName;
int courseImage;
public CourseByType(String courseName, int courseImage) {
this.courseName = courseName;
this.courseImage = courseImage;
}
public String getCourseName() {
return courseName;
}
public int getCourseImage() {
return courseImage;
}
}
CourseByHeader.java
public class CourseByHeader {
String title;
ArrayList<CourseByType> courseByTypes;
public CourseByHeader(String title, ArrayList<CourseByType> courseByTypes) {
this.title = title;
this.courseByTypes = courseByTypes;
}
public String getTitle() {
return title;
}
public ArrayList<CourseByType> getCourseByTypes() {
return courseByTypes;
}
}
GroupView.java
public class GroupView {
String title;
ArrayList<ChildView> childViewList;
String courseBy;
ArrayList<CourseByType> courseByTypes;
public GroupView(String title, ArrayList<ChildView> childViewList) {
this.title = title;
this.childViewList = childViewList;
}
public String getTitle() {
return title;
}
public ArrayList<ChildView> getChildViewList() {
return childViewList;
}
}
Groupview and CouseByType class have title and child list for recycleradapter
ChildViewAdapter.java
public class ChildViewAdapter extends RecyclerView.Adapter {
ArrayList<ChildView> childViewList;
ArrayList<CourseByType> courseByTypes;
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
public class ViewHolder extends RecyclerView.ViewHolder{
public ViewHolder(View itemView) {
super(itemView);
}
}
public class GroupHolder extends ViewHolder {
public ImageView iamView;
public TextView course, price;
public GroupHolder(View itemView) {
super(itemView);
iamView = (ImageView) itemView.findViewById(R.id.course_image);
course = (TextView) itemView.findViewById(R.id.course_by);
price = (TextView) itemView.findViewById(R.id.price);
}
}
public void addCourse(ArrayList<CourseByType> courseByType){
courseByTypes = courseByType;
}
public void addChild(ArrayList<ChildView> childView){
childViewList = childView;
}
public class Course extends ViewHolder {
public ImageView courseTypeImage;
public TextView courseType;
public Course(View itemView) {
super(itemView);
courseTypeImage = (ImageView)itemView.findViewById(R.id.course_image);
courseType = (TextView)itemView.findViewById(R.id.course_name_course);
}
}
public ChildViewAdapter() {
childViewList = new ArrayList<>();
courseByTypes = new ArrayList<>();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
Context context = parent.getContext();
RecyclerView.ViewHolder vh = null;
View v;
if(viewType == TYPE_HEADER){
v = LayoutInflater.from(context).inflate(R.layout.recycler_item, parent, false);
return new GroupHolder(v);
}if(viewType == TYPE_ITEM){
v = LayoutInflater.from(context).inflate(R.layout.type_of_courses, parent, false);
return new Course(v);
}
return vh;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if(holder instanceof GroupHolder){
Log.d("","instance of Group Holder");
ChildView childView = childViewList.get(position);
((GroupHolder)holder).iamView.setImageResource(childView.getImage());
((GroupHolder)holder).course.setText(childView.getCourse());
((GroupHolder)holder).price.setText(childView.getPrice());
return;
}
if(holder instanceof Course){
Log.d("","instance of Course ");
CourseByType courseByType = courseByTypes.get(position);
((Course)holder).courseTypeImage.setImageResource(courseByType.getCourseImage());
((Course)holder).courseType.setText(courseByType.getCourseName());
return;
}
}
#Override
public int getItemCount() {
int size;
if(childViewList.size()>0){
return size = childViewList.size();
}else return size = courseByTypes.size();
}
#Override
public int getItemViewType(int position) {
if(childViewList.size() != 0 && childViewList.size()>0){
return TYPE_HEADER;
}else return TYPE_ITEM;
}
}
This childview adapter has two view types first is one image and 3 text and second view type contain one image and one text.When I pass values from fragment only first view type get displayed and second view type not gets value from fragment.
To show multiple different views in a recyclerview, you have to override getItemViewType() in the recyclerview adapter.
//getItemViewType enables dynamic viewholder creation
#Override
public int getItemViewType(int position) {
//you will need to add a integer with variable name viewTypeCode
//for view1 set viewTypeCode = 100 and for view2 set viewTypeCode = 200
viewTypeCode = itemList.get(position).getViewTypeCode();
return viewTypeCode;
}
This is how the onCreateViewHolder will be different for multiple viewtypes. You will have to modify yours like this
#Override
public FeedViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case 100: return new FeedViewHolder(layoutInflater.inflate(R.layout.v1, parent, false),100);
case 200: return new FeedViewHolder(layoutInflater.inflate(R.layout.v2, parent, false),200);
}
return null;
}
OnBindViewHolder will be similarly modified
#Override
public void onBindViewHolder(final FeedViewHolder holder, int position) {
viewTypeCode = itemList.get(position).getViewTypeCode();
switch ( viewTypeCode) {
case 100:
//your code for v1
case 200:
//your code for v2
}
}
Similarly the ViewHolder class is modified
class FeedViewHolder extends RecyclerView.ViewHolder{
//declare variables here
public FeedViewHolder(View v, int viewType) {
super(v);
switch (viewType) {
//instead of itemView.findViewById you will have to use v.findViewById
case 100:
//your code for v1
case 200:
//your code for v2
}
}
For further reference refer to this SO answer
Don't pass two separate list.Make a custom class like this-
class MyClass {
int viewTypeCode;
CustomClass1 c1;
CustomClass2 c2;
//add the setter getter
}
In your activity while preparing the data.
List<MyClass> itemList = new ArrayList<>();
//put whatever logic you need to make the order of the list
//if CustomClass1 object is put then setViewTypeCode(100), setCustomClass2 = null
//if CustomClass2 object is put then setViewTypeCode(200), setCustomClass1 = null
After data is built, then send this to the adapter.
I am implementing sticky header with RecyclerView. I have three sections (i.e 3 sticky headers) and they are working fine.Now I have taken three arraylists inside each section, I have initialized these list in my adapter and I am trying to get data of these lists on basis of header id inside onBindViewHolder. But it is not giving me the full list,just one string from each list (i.e under first section--data on first position of mylist,,,under second section-- data on second position of mylist1 ---under third section-- data on third position of mylist2)
Please Help !!
Code in Context:
StickyTestAdapter
public class StickyTestAdapter extends RecyclerView.Adapter<StickyTestAdapter.ViewHolder> implements
StickyHeaderAdapter<StickyTestAdapter.HeaderHolder> {
private Context mContext;
private ArrayList<String> mylist;
private ArrayList<String> mylist1;
private ArrayList<String> mylist2;
private static int countposition;
private String HEADER_FIRIST="HEADER_FIRIST";
private String HEADER_SECOND="HEADER_SECOND";
private String HEADER_THIRD="HEADER_THIRD";
public StickyTestAdapter(Context context) {
prepareData();
prepareData1();
prepareData2();
this.mContext=context;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.item_test, viewGroup, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder viewHolder, int position) {
long rowType= getHeaderId(position);
Log.e("getHeaderId----",""+rowType);
if (rowType==0){
if(!mylist.equals(""))
{
Log.e("list_data----", "" + mylist.get(position));
viewHolder.item.setText(mylist.get(position));
}
else
{
Log.e("Error--0--", "" + "error");
}
} else if (rowType==1){
if(!mylist1.equals(""))
{
Log.e("list_data1----", "" + mylist1.get(position));
viewHolder.item.setText(mylist1.get(position));
}
else
{
Log.e("Error---1-", "" + "error");
}
} else if (rowType==2){
if(!mylist2.equals(""))
{
Log.e("list_data2----", "" + mylist2.get(position));
viewHolder.item.setText(mylist2.get(position));
}
else
{
Log.e("Error----2", "" + "error");
}
}
}
#Override
public int getItemCount() {
if (getHeaderId(countposition)==0){
Log.e("mylist",""+mylist.size());
return mylist.size();
}else if (getHeaderId(countposition)==1){
Log.e("mylist1",""+mylist1.size());
return mylist1.size();
}else if (getHeaderId(countposition)==2){
Log.e("mylist2",""+mylist2.size());
return mylist2.size();
}
return 0;
}
#Override
public long getHeaderId(int pos) {
return pos;
}
#Override
public HeaderHolder onCreateHeaderViewHolder(ViewGroup parent) {
final View view = LayoutInflater.from(mContext).inflate(R.layout.header_test, parent, false);
return new HeaderHolder(view);
}
#Override
public void onBindHeaderViewHolder(HeaderHolder viewholder, int count) {
countposition=count;
if (getItemViewType(count)==0){
viewholder.headertext.setText(HEADER_FIRIST);
}else if (getItemViewType(count)==1){
viewholder.headertext.setText(HEADER_SECOND);
}else if (getItemViewType(count)==2){
viewholder.headertext.setText(HEADER_THIRD);
}
}
static class ViewHolder extends RecyclerView.ViewHolder {
public TextView item;
public ViewHolder(View itemView) {
super(itemView);
item = (TextView)itemView.findViewById(R.id.text_item);
}
}
static class HeaderHolder extends RecyclerView.ViewHolder {
public TextView headertext;
public HeaderHolder(View itemView) {
super(itemView);
headertext = (TextView)itemView.findViewById(R.id.header_text);
}
}
#Override
public int getItemViewType(int position) {
return position;
}
public void prepareData()
{
mylist=new ArrayList<>();
mylist.add("rajendra");
mylist.add("rani");
mylist.add("rahul");
}
public void prepareData1()
{
mylist1=new ArrayList<>();
mylist1.add("ravi");
mylist1.add("vikram");
mylist1.add("rakesh");
}
public void prepareData2()
{
mylist2=new ArrayList<>();
mylist2.add("apple");
mylist2.add("ashok");
mylist2.add("vikash");
}
}
Question is quite old. But that code looks complicated to read, personally I try to not reinvent what others did quite well by creating libraries.
GitHub is full of source that you can import. Some examples: FlexibleAdapter, FastAdapter, Epoxy and others.
I have build the first one, but you can try the others as well.
With mine, having multiple views and headers with sections is quite easy, I point here the wiki page where I define the section and how to initialize it.
Along with this feature, you have a lot more functionalities that makes your life easier.
I divide arraylist into 3 section based on alphabet like contactlist.
For that i use SectionedRecyclerViewAdapter
MainActivity.java
public class MainActivity extends AppCompactActivity {
private SectionedRecyclerViewAdapter sectionAdapter;
#Override
public View onCreate(Bundle savedInstanceState) {
setContentView(R.layout.activity_main);
sectionAdapter = new SectionedRecyclerViewAdapter();
for(char alphabet = 'a'; alphabet <= 'z';alphabet++) {
List<String> contacts = getContactsWithLetter(alphabet);
if (contacts.size() > 0) {
sectionAdapter.addSection(new ContactsSection(String.valueOf(alphabet), contacts));
}
}
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
return view;
}
#Override
public void onResume() {
super.onResume();
if (getActivity() instanceof AppCompatActivity) {
AppCompatActivity activity = ((AppCompatActivity) getActivity());
if (activity.getSupportActionBar() != null)
activity.getSupportActionBar().setTitle(R.string.nav_example1);
}
}
private List<String> getContactsWithLetter(char letter) {
List<String> contacts = new ArrayList<>();
for (String contact : getResources().getStringArray(R.array.names_)) {
if (contact.charAt(0) == letter) {
contacts.add(contact);
}
}
return contacts;
}
private class ContactsSection extends StatelessSection {
String title;
List<String> list;
ContactsSection(String title, List<String> list) {
super(new SectionParameters.Builder(R.layout.section_ex1_item)
.headerResourceId(R.layout.section_ex1_header)
.build());
this.title = title;
this.list = list;
}
#Override
public int getContentItemsTotal() {
return list.size();
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
return new ItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
final ItemViewHolder itemHolder = (ItemViewHolder) holder;
String name = list.get(position);
itemHolder.tvItem.setText(name);
itemHolder.imgItem.setImageResource(name.hashCode() % 2 == 0 ? R.drawable.ic_face_black_48dp : R.drawable.ic_tag_faces_black_48dp);
itemHolder.rootView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(), String.format("Clicked on position #%s of Section %s", sectionAdapter.getPositionInSection(itemHolder.getAdapterPosition()), title), Toast.LENGTH_SHORT).show();
}
});
}
#Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new HeaderViewHolder(view);
}
#Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
headerHolder.tvTitle.setText(title);
}
}
private class HeaderViewHolder extends RecyclerView.ViewHolder {
private final TextView tvTitle;
HeaderViewHolder(View view) {
super(view);
tvTitle = (TextView) view.findViewById(R.id.tvTitle);
}
}
private class ItemViewHolder extends RecyclerView.ViewHolder {
private final View rootView;
private final ImageView imgItem;
private final TextView tvItem;
ItemViewHolder(View view) {
super(view);
rootView = view;
imgItem = (ImageView) view.findViewById(R.id.imgItem);
tvItem = (TextView) view.findViewById(R.id.tvItem);
}
}
}
use structure similar to
public class MultiArray<T> {
List<ItemGroup> lists = new ArrayList<>();
public void addList(String headerText, List<T> list) {
lists.add(new ItemGroup(headerText, list));
}
public int itemCount() {
int count = 0;
for (ItemGroup group : lists) {
count += group.count();
}
return count;
}
public T getItem(int position) {
int count = 0;
for (ItemGroup group : lists) {
if (count + group.count() >= position) {
return group.item(position - count);
}
count += group.count();
}
return null;
}
public int getGroupIndex(int position) {
int count = 0;
int groupIndex = 0;
for (ItemGroup group : lists) {
if (count + group.count() >= position) {
return groupIndex;
}
count += group.count();
groupIndex++;
}
return -1;
}
public String getHeaderText(int position){
int count = 0;
for (ItemGroup group : lists) {
if (count + group.count() >= position) {
return group.headerText;
}
count += group.count();
}
return "";
}
class ItemGroup {
public final String headerText;
public final List<T> list;
public ItemGroup(String headerText, List<T> list) {
this.headerText = headerText;
this.list = list;
}
public int count() {
return list.size();
}
public T item(int position) {
return list.get(position);
}
}
}
you can optimize it for faster performance