I am showing banner ads within my android app. I followed official doc for implementing admob banner contentView ads and I successfully loaded ads but now my recyclerview starts lagging like hell. If i remove ads from my code scroll becomes smooth. Please help me with this problem.
Here's my fragment class
public class WallpaperFragment extends Fragment {
private RecyclerView recyclerView;
private DatabaseReference wallRef;
private String category;
private FirebaseAuth mAuth;
private final String CATEGORY = "Category";
private Context context;
private ArrayList<Object> modelList = new ArrayList<>();
private GridLayoutManager manager;
// The number of native ads to load and display.
public int NUMBER_OF_ADS = 5;
// List of native ads that have been successfully loaded.
private List<NativeAd> mNativeAds = new ArrayList<>();
private WallpaperAdapter adapter;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fg_wallpaper, container, false);
category = "Recent";
init(view);
Util.showDialog(context, "Loading wallpapers...");
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
return (position % (SPACE_BETWEEN_ADS + 1) == SPACE_BETWEEN_ADS) ? 2 : 1;
}
});
setScrollListener();
initializeFirebaseDatabase();
return view;
}
private void init(View view) {
context = getContext();
MobileAds.initialize(context, getString(R.string.admob_app_id));
wallRef = FirebaseDatabase.getInstance().getReference().child(WALL).child(CATEGORY).child(category).getRef();
mAuth = FirebaseAuth.getInstance();
recyclerView = view.findViewById(R.id.recyclerView);
manager = new GridLayoutManager(context, 2, GridLayoutManager.VERTICAL, false);
recyclerView.setLayoutManager(manager);
recyclerView.addItemDecoration(new GridItemDecor(0));
recyclerView.setHasFixedSize(true);
recyclerView.setItemAnimator(null);
}
private void setScrollListener() {
recyclerView.addOnScrollListener(new HidingScrollListener() {
#Override
public void onHide() {
if (context instanceof ToolbarShowHideListener) {
((ToolbarShowHideListener) context).hideBottomNavigation();
}
}
#Override
public void onShow() {
if (context instanceof ToolbarShowHideListener) {
((ToolbarShowHideListener) context).showBottomNavigation();
}
}
});
}
private void initializeFirebaseDatabase() {
mNativeAds.clear();
wallRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
modelList.clear();
for (DataSnapshot childDataSnapshot : dataSnapshot.getChildren()) {
WallpaperModel model = childDataSnapshot.getValue(WallpaperModel.class);
modelList.add(model);
}
NUMBER_OF_ADS = modelList.size() / 2;
loadNativeAd();
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
});
}
private void loadNativeAd() {
loadNativeAd(0);
}
private void insertAdsInMenuItems() {
if (mNativeAds.size() <= 0) {
return;
}
int offset = (modelList.size() / mNativeAds.size()) + 1;
int index = 2;
for (NativeAd ad : mNativeAds) {
modelList.add(index, ad);
index = index + offset;
}
if (adapter == null) {
adapter = new WallpaperAdapter(context, modelList, Objects.requireNonNull(mAuth.getCurrentUser()).getUid(), true, WALLPAPER_FRAGMENT);
recyclerView.setAdapter(adapter);
} else {
adapter.updateList(modelList);
}
}
private void loadNativeAd(final int adLoadCount) {
if (adLoadCount >= NUMBER_OF_ADS) {
insertAdsInMenuItems();
return;
}
AdLoader.Builder builder = new AdLoader.Builder(context, /*getString(R.string.ad_unit_id)*/"ca-app-pub-3940256099942544/2247696110");
AdLoader adLoader = builder.forContentAd(new NativeContentAd.OnContentAdLoadedListener() {
#Override
public void onContentAdLoaded(NativeContentAd ad) {
// A content ad loaded successfully, call this method again to
// load the next ad in the items list.
mNativeAds.add(ad);
loadNativeAd(adLoadCount + 1);
}
}).withAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(int errorCode) {
// A native ad failed to load. Call this method again to load
// the next ad in the items list.
Log.e("MainActivity", "The previous native ad failed to load. Attempting to" +
" load another.");
loadNativeAd(adLoadCount + 1);
}
}).build();
// Load the Native Express ad.
adLoader.loadAd(new AdRequest.Builder().addTestDevice("9F50A23B86C21B90330202FAECE3C331").build());
}
}
and here's my adapter class,
public class WallpaperAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private int height, fragment_id;
private String uId;
private Animation fadeout, fadein;
private DatabaseReference favCheckRef;
private final String CATEGORY = "Category";
private ArrayList<Object> modelList;
private boolean showLikeBtn;
public WallpaperAdapter(Context context, ArrayList<Object> modelList, String uId, boolean showLikeBtn, int fragment_id) {
this.context = context;
this.modelList = modelList;
this.uId = uId;
this.showLikeBtn = showLikeBtn;
height = context.getResources().getDisplayMetrics().heightPixels;
fadeout = AnimationUtils.loadAnimation(context, R.anim.anim_fade_out);
fadein = AnimationUtils.loadAnimation(context, R.anim.anim_fade_in);
favCheckRef = FirebaseDatabase.getInstance().getReference().child(WALL).child(USER)
.child(uId).child(FAVOURITES).getRef();
this.fragment_id = fragment_id;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
switch (viewType) {
case NATIVE_CONTENT_AD_VIEW_TYPE:
View nativeContentLayoutView = LayoutInflater.from(
parent.getContext()).inflate(R.layout.ad_content,
parent, false);
return new NativeContentAdViewHolder(nativeContentLayoutView);
case MENU_ITEM_VIEW_TYPE:
// Fall through.
default:
View dataView = LayoutInflater.from(context).inflate(R.layout.single_wallpaper_unit, parent, false);
return new ViewHolder(dataView);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
Util.dismissDialog();
switch (viewType) {
case NATIVE_CONTENT_AD_VIEW_TYPE:
NativeContentAd contentAd = (NativeContentAd) modelList.get(position);
populateAdView(contentAd, (NativeContentAdView) holder.itemView);
break;
case MENU_ITEM_VIEW_TYPE:
// fall through
default:
final ViewHolder wallHolder = (ViewHolder) holder;
WallpaperModel model = (WallpaperModel) modelList.get(position);
wallHolder.wallpaper.getLayoutParams().height = (int) (height / 2.5);
wallHolder.name.setText(model.getName());
favCheck(wallHolder, model);
Glide.with(wallHolder.wallpaper.getContext())
.load(model.getThumbnail())
.into(wallHolder.wallpaper);
break;
}
}
#Override
public int getItemViewType(int position) {
Object recyclerViewItem = modelList.get(position);
if (recyclerViewItem instanceof NativeContentAd) {
return NATIVE_CONTENT_AD_VIEW_TYPE;
}
return MENU_ITEM_VIEW_TYPE;
}
#Override
public int getItemCount() {
return modelList.size();
}
private void populateAdView(NativeContentAd ad,NativeContentAdView adView){
((TextView) adView.getHeadlineView()).setText(ad.getHeadline());
List<NativeAd.Image> images = ad.getImages();
if (images.size() > 0) {
((ImageView) adView.getImageView()).setImageDrawable(images.get(0).getDrawable());
}
adView.setNativeAd(ad);
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView name;
ImageView wallpaper, favouriteIcon;
private ViewHolder(View itemView) {
super(itemView);
wallpaper = itemView.findViewById(R.id.wallpaper);
name = itemView.findViewById(R.id.name);
favouriteIcon = itemView.findViewById(R.id.favouriteIcon);
}
}
public class NativeContentAdViewHolder extends RecyclerView.ViewHolder {
NativeContentAdViewHolder(View view) {
super(view);
NativeContentAdView adView = (NativeContentAdView) view;
adView.setHeadlineView(adView.findViewById(R.id.contentad_headline));
adView.setImageView(adView.findViewById(R.id.contentad_image));
}
}
}
You are loading native express, not banner.
The problem is the ads have images, just like a normal viewholder, you have to handel the caching of the images, I see you already use glide...
Also, it's not a good idea showing your ad ID.
Last thing, NONE of this matters because native express ads are deprecated and will anyway not work.
Another option is to use Native Ads Advance, but unless you have a few million downloads you will not be able to use it - look here:
Note: Native Ads Advanced is currently released to a limited set of publishers. If you're interested in participating, reach out to your account manager to discuss the possibility.
What I would suggest is using some ad provider like Appodeal which supports native ads.
Related
I am working on a Bus app and visualized the seats of a bus in an activity using Recyclerview as follows
I am taking the no of available and booked seats from Firebase. What I want to do is
update the color of the seat according to the count of available
and booked seats.
For example, if booked seats: 2 then there should be 2 green-colored seats in the recycler view. Here is my code
SeatsFragment.java:
public class SeatsFragment extends Fragment {
public static int mCountSeat, mTotal;
public static StringBuffer mSb;
private int mSeatNo = 0;
private RecyclerView mRvViewSeats;
private ImageView mIvAvailable, mIvBooked;
private LinearLayout mLinear, mLlDack, mLlDynamic;
private List<AbstractItem> mAbstractItemsList;
private List<SeatModel> mSeatModelsItemsList;
private TextView tvAvailabe, tvBooked;
SeatAdapter adapter;
// Firebase
FirebaseDatabase database;
DatabaseReference seatsRef;
public SeatsFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_seats, container, false);
tvAvailabe = view.findViewById(R.id.tvAvailableSeats);
tvBooked = view.findViewById(R.id.tvBookedSeats);
database = FirebaseDatabase.getInstance();
seatsRef = database.getReference(Common.SEATS_REFERENCE).child("2").child(Common.AVAILABLE_SEATS);
// Toast.makeText(getActivity(), seatsRef.toString(), Toast.LENGTH_SHORT).show();
seatsRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
tvBooked.setText(snapshot.getValue().toString());
tvAvailabe.setText(String.valueOf(40 - (Long.parseLong(snapshot.getValue().toString()))));
mSeatModelsItemsList.add(new SeatModel(SeatType.AVAILABLE.BOOKED));
initializeSeats();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
return view;
}
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
initLayouts(view);
initializeSeats();
}
/* initialize seats */
private void initializeSeats() {
mIvAvailable.setColorFilter(ContextCompat.getColor(getActivity(), R.color.view_color));
mIvBooked.setColorFilter(ContextCompat.getColor(getActivity(), R.color.colorPrimary));
for (int i = 0; i < 40; i++) {
mSeatModelsItemsList.add(new SeatModel(SeatType.BOOKED));
mSeatModelsItemsList.add(new SeatModel(SeatType.AVAILABLE));
int mCOLUMNS = 5;
if (i % mCOLUMNS == 0 || i % mCOLUMNS == 4) {
mSeatNo++;
mAbstractItemsList.add(new EdgeItem(String.valueOf(mSeatNo)));
} else if (i % mCOLUMNS == 1 || i % mCOLUMNS == 3) {
mSeatNo++;
mAbstractItemsList.add(new CenterItem(String.valueOf(mSeatNo)));
} else {
mAbstractItemsList.add(new EmptyItem(mSeatModelsItemsList));
}
GridLayoutManager mManager = new GridLayoutManager(getActivity(), mCOLUMNS);
mRvViewSeats.setLayoutManager(mManager);
adapter = new SeatAdapter(mSeatModelsItemsList, mAbstractItemsList, getActivity());
mRvViewSeats.setAdapter(adapter);
}
mSeatNo = 0;
}
private void initLayouts(View view) {
mLinear = view.findViewById(R.id.llOffer);
mIvAvailable = view.findViewById(R.id.ivAvailable);
mIvBooked = view.findViewById(R.id.ivSeatFragmentBooked);
mAbstractItemsList = new ArrayList<>();
mSeatModelsItemsList = new ArrayList<>();
mSb = new StringBuffer();
mRvViewSeats = view.findViewById(R.id.rvSeat);
mLlDack = view.findViewById(R.id.lvDack);
mLlDynamic = view.findViewById(R.id.llDynamicContent);
}
}
Look at the initializeSeats() method above. I've added two items(available and booked) to the list that is passed to the adapter. These items should be added according to the no of available and booked seats
e.g
if booked seats are 38, then 38 booked items should be added to the list
When I try to call initializeSeats() in the onDataChange() it adds the correct no of seats to list but
since seats are added through loop every time seats get doubled, 1st time 40,80 and so on.
SeatsAdapter.java:
public class SeatAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<SeatModel> mSeatItem;
private List<AbstractItem> mItems;
private LayoutInflater mLayoutInflater;
private Context mCtx;
public SeatAdapter(List<SeatModel> mSeatItem, List<AbstractItem> mItems, Context mCtx) {
this.mSeatItem = mSeatItem;
this.mItems = mItems;
this.mCtx = mCtx;
mLayoutInflater = LayoutInflater.from(mCtx);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if (viewType == AbstractItem.TYPE_CENTER) {
View itemView = mLayoutInflater.inflate(R.layout.list_item_seat, parent, false);
return new CenterViewHolder(itemView);
} else if (viewType == AbstractItem.TYPE_EDGE) {
View itemView = mLayoutInflater.inflate(R.layout.list_item_seat, parent, false);
return new EdgeViewHolder(itemView);
} else {
View itemView = new View(mCtx);
return new EmptyViewHolder(itemView);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int type = mItems.get(position).getType();
final SeatModel seatModel = mSeatItem.get(position);
if (type == AbstractItem.TYPE_CENTER) {
final CenterViewHolder centerViewHolder = (CenterViewHolder)holder;
if (seatModel.getSeatType().equals(SeatType.AVAILABLE)){
centerViewHolder.mIvSeat.setVisibility(View.VISIBLE);
} else {
centerViewHolder.mIvSeatBooked.setVisibility(View.VISIBLE);
}
} else if (type == AbstractItem.TYPE_EDGE) {
final EdgeViewHolder edgeViewHolder = (EdgeViewHolder) holder;
if (seatModel.getSeatType().equals(SeatType.AVAILABLE)){
edgeViewHolder.mIvSeat.setVisibility(View.VISIBLE);
} else {
edgeViewHolder.mIvSeatBooked.setVisibility(View.VISIBLE);
}
}
}
#Override
public int getItemCount() {
return mItems.size();
}
#Override
public int getItemViewType(int position) {
return mItems.get(position).getType();
}
/*view holder*/
private static class CenterViewHolder extends RecyclerView.ViewHolder {
private ImageView mIvSeat, mIvSeatBooked;
CenterViewHolder(View itemView) {
super(itemView);
mIvSeat = itemView.findViewById(R.id.ivSeat);
mIvSeatBooked = itemView.findViewById(R.id.ivSeatBooked);
}
}
/*view holder*/
private static class EdgeViewHolder extends RecyclerView.ViewHolder {
private ImageView mIvSeat, mIvSeatBooked;
EdgeViewHolder(View itemView) {
super(itemView);
mIvSeat = itemView.findViewById(R.id.ivSeat);
mIvSeatBooked = itemView.findViewById(R.id.ivSeatBooked);
}
}
/*view holder*/
private static class EmptyViewHolder extends RecyclerView.ViewHolder {
EmptyViewHolder(View itemView) {
super(itemView);
}
}
}
Any help or guidance would be appreciable.
Thank you.
You just keep on adding new data in your onDataChange callback, you should clear the data when you have a new data change.
There is no need to create new adapter each time, you create your adapter once and then you just notify about changes, you should add a method in your adapter that will take a new list so you can update it in other way not only though the constructor only.
I would strongly recommend taking a look at Diff Utils It is the best way to update data inside RecyclerView and the most performant.
There are many resources on it so you can find a lot about it.
Example: https://iammert.medium.com/using-diffutil-in-android-recyclerview-bdca8e4fbb00
Inside your value eventListener you need to clear the list every time, and because you are not doing this so every time the new data gets added to the older ones that's why you are facing this issue. Add this line mSeatModelsItemsList.clear();
Solution is here
seatsRef.addValueEventListener(new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot snapshot) {
mSeatModelsItemsList.clear();// add this line
tvBooked.setText(snapshot.getValue().toString());
tvAvailabe.setText(String.valueOf(40 - (Long.parseLong(snapshot.getValue().toString()))));
mSeatModelsItemsList.add(new SeatModel(SeatType.AVAILABLE.BOOKED));
initializeSeats();
}
#Override
public void onCancelled(#NonNull DatabaseError error) {
}
});
I am trying to build my 1st app, and want to insert an admob native ad in a recyclerview.
I have created the viewpager from android studio's default tabbed activity, and have inserted a recyclerview, which is working fine.
Now, from AdMob Native Advanced Ads in an Android Feed I am trying to insert a native ad.
But, the codelab example uses Fragment Transaction. Do I must need to implement that?
It will be helpful for me, if I can implement the complete ad related part in the fragment only (except the initialisation).
Here is my current status of the fragment:
public class SunFragment extends Fragment {
public static final int NUMBER_OF_ADS = 1;
private AdLoader adLoader;
private List<Object> mRecyclerViewItems = new ArrayList<>();
private List<UnifiedNativeAd> mNativeAds = new ArrayList<>();
ArrayList<Object> sunsList;
Typeface sunfont;
Double Dlat;
Double Dlang;
//to be called by the MainActivity
public SunFragment() {
// Required empty public constructor
}
private static final String KEY_LOCATION_NAME = "location_name";
public String TAG = "SunFragment";
public String location;//="No location name found";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState != null) {
location = savedInstanceState.getCharSequence(KEY_LOCATION_NAME).toString();
}
setRetainInstance(true);
}
public List<Object> getRecyclerViewItems() {
return mRecyclerViewItems;
}
private void loadNativeAds() {
AdLoader.Builder builder = new AdLoader.Builder(getContext(), getString(R.string.ad_unit_id));
adLoader = builder.forUnifiedNativeAd(
new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
#Override
public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
// A native ad loaded successfully, check if the ad loader has finished loading
// and if so, insert the ads into the list.
mNativeAds.add(unifiedNativeAd);
if (!adLoader.isLoading()) {
//insertAdsInMenuItems();
mRecyclerViewItems.add(3, mNativeAds);
}
}
}).withAdListener(
new AdListener() {
#Override
public void onAdFailedToLoad(int errorCode) {
// A native ad failed to load, check if the ad loader has finished loading
// and if so, insert the ads into the list.
Log.e("MainActivity", "The previous native ad failed to load. Attempting to"
+ " load another.");
if (!adLoader.isLoading()) {
//insertAdsInMenuItems();
}
}
}).build();
// Load the Native ads.
adLoader.loadAds(new AdRequest.Builder().build(), NUMBER_OF_ADS);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
//onSaveInstanceState(new Bundle());
if (getArguments() != null) {
Dlat = getArguments().getDouble("loclat");
Dlang = getArguments().getDouble("loclang");
} else {
Dlat = 0.0;
Dlang = 0.0;
}
View rootView = inflater.inflate(R.layout.fragment_sun, container, false);
RecyclerView rv = rootView.findViewById(R.id.rv_recycler_view);
rv.setNestedScrollingEnabled(false);
rv.setHasFixedSize(true);
RecyclerView.LayoutManager llm = new LinearLayoutManager(getActivity());
rv.setLayoutManager(llm);
// System.out.println("location " + location);
setRetainInstance(true);
RecyclerView.Adapter adapter = new SunAdapter(getContext(), sunsList);
rv.setAdapter(adapter);
return rootView;
}
#Override
public void onSaveInstanceState(Bundle locState) {
super.onSaveInstanceState(locState);
if (location != null) {
locState.putCharSequence(KEY_LOCATION_NAME, location);
}
}
}
and the adapter is:
public class SunAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context mContext;
private final ArrayList<Object> mSunsList;
private static final int MENU_ITEM_VIEW_TYPE = 0;
private static final int UNIFIED_NATIVE_AD_VIEW_TYPE = 1;
public SunAdapter(Context context, ArrayList<Object> sunsList) {
this.mContext = context;
this.mSunsList = sunsList;
}
public class MyViewHolder extends RecyclerView.ViewHolder {
private TextView textViewId;
private TextView textViewSicon;
private TextView textViewStime;
private TextView textViewEicon;
private TextView textViewEtime;
private TextView ntextViewSicon;
private TextView ntextViewStime;
private TextView ntextViewEicon;
private TextView ntextViewEtime;
MyViewHolder(View itemView) {
super(itemView);
textViewId = itemView.findViewById(R.id.tv_city);
textViewSicon = itemView.findViewById(R.id.tv_MSicon);
textViewStime = itemView.findViewById(R.id.tv_MStime);
textViewEicon = itemView.findViewById(R.id.tv_MEicon);
textViewEtime = itemView.findViewById(R.id.tv_MEtime);
ntextViewSicon = itemView.findViewById(R.id.tv_NSicon);
ntextViewStime = itemView.findViewById(R.id.tv_NStime);
ntextViewEicon = itemView.findViewById(R.id.tv_NEicon);
ntextViewEtime = itemView.findViewById(R.id.tv_NEtime);
// mSportsImage = itemView.findViewById(R.id.sportsImage);
}
}
#Override
public int getItemCount() {
return mSunsList.size();
}
#Override
public int getItemViewType(int position) {
Object sunList = mSunsList.get(position);
if (sunList instanceof UnifiedNativeAd) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}
return MENU_ITEM_VIEW_TYPE;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup viewGroup, int viewType) {
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE:
View unifiedNativeLayoutView = LayoutInflater.from(
viewGroup.getContext()).inflate(R.layout.ad_unified,
viewGroup, false);
return new UnifiedNativeAdViewHolder(unifiedNativeLayoutView);
case MENU_ITEM_VIEW_TYPE:
// Fall through.
default:
View menuItemLayoutView = LayoutInflater.from(viewGroup.getContext()).inflate(
R.layout.card_item, viewGroup, false);
return new MyViewHolder(menuItemLayoutView);
}
}
// #RequiresApi(api = Build.VERSION_CODES.N)
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE:
UnifiedNativeAd nativeAd = (UnifiedNativeAd) mSunsList.get(position);
populateNativeAdView(nativeAd, ((UnifiedNativeAdViewHolder) holder).getAdView());
break;
case MENU_ITEM_VIEW_TYPE:
// fall through
default:
MyViewHolder myViewHolder = (MyViewHolder) holder;
SunSession sunSession = (SunSession) mSunsList.get(position);
myViewHolder.textViewId.setText(sunSession.getId());
myViewHolder.textViewSicon.setText(HtmlCompat.fromHtml(sunSession.getSicon(), HtmlCompat.FROM_HTML_MODE_LEGACY));
myViewHolder.textViewStime.setText(sunSession.getStime());
myViewHolder.textViewEicon.setText(HtmlCompat.fromHtml(sunSession.getEicon(), HtmlCompat.FROM_HTML_MODE_LEGACY));
myViewHolder.textViewEtime.setText(sunSession.getEtime());
myViewHolder.ntextViewSicon.setText(HtmlCompat.fromHtml(sunSession.getNSicon(), HtmlCompat.FROM_HTML_MODE_LEGACY));
myViewHolder.ntextViewStime.setText(sunSession.getNStime());
myViewHolder.ntextViewEicon.setText(HtmlCompat.fromHtml(sunSession.getNEicon(), HtmlCompat.FROM_HTML_MODE_LEGACY));
myViewHolder.ntextViewEtime.setText(sunSession.getNEtime());
}
}
private void populateNativeAdView(UnifiedNativeAd nativeAd,
UnifiedNativeAdView adView) {
// Some assets are guaranteed to be in every UnifiedNativeAd.
((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
((TextView) adView.getBodyView()).setText(nativeAd.getBody());
((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
NativeAd.Image icon = nativeAd.getIcon();
if (icon == null) {
adView.getIconView().setVisibility(View.INVISIBLE);
} else {
((ImageView) adView.getIconView()).setImageDrawable(icon.getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (nativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
} else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView) adView.getPriceView()).setText(nativeAd.getPrice());
}
if (nativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
} else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView) adView.getStoreView()).setText(nativeAd.getStore());
}
if (nativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
} else {
((RatingBar) adView.getStarRatingView())
.setRating(nativeAd.getStarRating().floatValue());
adView.getStarRatingView().setVisibility(View.VISIBLE);
}
if (nativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
} else {
((TextView) adView.getAdvertiserView()).setText(nativeAd.getAdvertiser());
adView.getAdvertiserView().setVisibility(View.VISIBLE);
}
// Assign native ad object to the native view.
adView.setNativeAd(nativeAd);
}
}
with this situation, the normal recyclerview is working, but no ad is inserted.
It will be really helpful if somebody shows me how to insert the ad, following the codelab, which probably means, how to implemeant this section in my fragment.
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 put banner ads from admob in my recyclerview and it lags my recyclerview when the user scrolls, here's my adapter code of the recyclerview I'm talking about. What could be wrong that it makes the recyclerview lag?
public class NewReleasesAdapter extends RecyclerView.Adapter< RecyclerView.ViewHolder> implements BillingProcessor.IBillingHandler {
public static final String TAG = NewReleasesAdapter.class.getSimpleName();
private LayoutInflater mInflater;
private Context mContext;
private List<Release> mNewReleases;
private DatabaseHelper mDatabaseHelper;
// A game/release item view type.
private static final int GAME_ITEM_VIEW_TYPE = 0;
// The banner ad view type.
private static final int BANNER_AD_VIEW_TYPE = 1;
// Billing
private BillingProcessor mBillingProcessor;
private boolean mIsProVersion;
// Ads
private AdRequest mAdRequest;
public NewReleasesAdapter(Context context) {
mContext = context;
mInflater = LayoutInflater.from(mContext);
mDatabaseHelper = DatabaseHelper.getDatabaseHelper(mContext);
mBillingProcessor = new BillingProcessor(
mContext,
"...",
this);
mBillingProcessor.initialize();
mBillingProcessor.loadOwnedPurchasesFromGoogle();
// Is in europe
if (ConsentInformation.getInstance(mContext).isRequestLocationInEeaOrUnknown()) {
switch (ConsentInformation.getInstance(mContext).getConsentStatus()) {
case PERSONALIZED:
// Personalized ads
mAdRequest = new AdRequest.Builder().build();
break;
case NON_PERSONALIZED:
// fall though to none Personalized ads
default:
// None Personalized ads
Bundle extras = new Bundle();
// npa is short for "none personalized ads"
extras.putString("npa", "1");
mAdRequest = new AdRequest.Builder()
.addNetworkExtrasBundle(AdMobAdapter.class, extras)
.build();
break;
}
} else {
// Personalized ads
mAdRequest = new AdRequest.Builder().build();
}
}
public void setNewReleases(List<Release> newReleases) {
this.mNewReleases = newReleases;
notifyDataSetChanged();
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch(viewType) {
case GAME_ITEM_VIEW_TYPE:
return new NewGameHolder(mInflater.inflate(R.layout.newly_added_game, parent, false));
case BANNER_AD_VIEW_TYPE:
return new ViewHolderAdMob(mInflater.inflate(R.layout.list_item_ad, parent, false), mAdRequest);
default:
return new NewGameHolder(mInflater.inflate(R.layout.newly_added_game, parent, false));
}
}
#Override
public int getItemViewType(int position) {
// isn't pro version
if (mIsProVersion) {
if ((position + 1) % 4 == 0) {
return BANNER_AD_VIEW_TYPE;
}
}
return GAME_ITEM_VIEW_TYPE;
}
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case BANNER_AD_VIEW_TYPE:
// nothing to do
break;
case GAME_ITEM_VIEW_TYPE:
// A lot of set texts
break;
}
}
#Override
public int getItemCount() {
if (mNewReleases == null) {
return 0;
}
return mNewReleases.size();
}
#Override
public void onProductPurchased(#NonNull String productId, #Nullable TransactionDetails details) {
}
#Override
public void onPurchaseHistoryRestored() {
}
#Override
public void onBillingError(int errorCode, #Nullable Throwable error) {
// Log.d(TAG, "Error: " + errorCode + " : " + error.getMessage());
}
#Override
public void onBillingInitialized() {
mBillingProcessor.loadOwnedPurchasesFromGoogle();
if (mBillingProcessor.isPurchased("gaming_reminder_pro")) {
mIsProVersion = true;
// Toast.makeText(mContext, "You're a pro user! Thanks!", Toast.LENGTH_SHORT).show();
} else {
// Show ads
mIsProVersion = false;
}
}
public BillingProcessor getBillingProcessor() {
return mBillingProcessor;
}
public static class NewGameHolder extends RecyclerView.ViewHolder {
public TextView name;
public TextView summary;
public TextView releaseDate;
public TextView platforms;
public TextView genres;
public ImageView cover;
public Button favorite;
public ProgressBar coverProgressBar;
public RelativeLayout newReleaseLayout;
public NewGameHolder(View itemView) {
super(itemView);
name = itemView.findViewById(R.id.game_name);
summary = itemView.findViewById(R.id.summary);
releaseDate = itemView.findViewById(R.id.release_date);
platforms = itemView.findViewById(R.id.platforms);
genres = itemView.findViewById(R.id.genres);
cover = itemView.findViewById(R.id.cover_image);
favorite = itemView.findViewById(R.id.favorite);
coverProgressBar = itemView.findViewById(R.id.cover_progress_bar);
newReleaseLayout = itemView.findViewById(R.id.new_release);
}
}
public static class ViewHolderAdMob extends RecyclerView.ViewHolder {
public AdView mAdView;
public ViewHolderAdMob(View view, final AdRequest adRequest) {
super(view);
mAdView = view.findViewById(R.id.adView);
if (adRequest != null) {
mAdView.loadAd(adRequest);
mAdView.setAdListener(new AdListener(){
#Override
public void onAdLoaded() {
mAdView.setVisibility(View.VISIBLE);
}
});
}
}
}
}
Some more helpful info: Generally, 4 banner ads are shown in this recyclerview and they are only shown for none-pro users. I have a pro iap (in app purchase) that removes the ads
It's best to show native ads in a recycler view as it's more customizable in every way. If you must show banner ads, I'd suggest to preload the adviews in an activity or a fragment, then add or show them in the recycler view as needed. ViewHolder isn't a good place to load ads.
You can follow this google provided example
https://github.com/googleads/googleads-mobile-android-examples/tree/master/java/advanced/BannerRecyclerViewExample
Hey community I want to make a RecyclerView like in the chat applications.
I tried setStackFromEnd and it's working good.
final LinearLayoutManager llm = new LinearLayoutManager(getApplicationContext());
llm.setOrientation(LinearLayoutManager.VERTICAL);
llm.setStackFromEnd(true);
My on load more code based on this tutorial, At the end of tutorial you should see a video : Recyclerview Bottom Progress
mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
totalItemCount = linearLayoutManager.getItemCount();
lastVisibleItem = linearLayoutManager.findLastVisibleItemPosition();
if (!isLoading && totalItemCount <= (lastVisibleItem + visibleThreshold)) {
if (mOnLoadMoreListener != null) {
mOnLoadMoreListener.onLoadMore();
}
isLoading = true;
}
}
});
So I need to make a RecyclerView like in chat applications. Stack from end is working perfect but I need to trigger load more method when I go top instead of going bottom. Also add new items on top instead of bottom. Thanks in advance.
You don't need to make use of onScrollListener, if you simply want to show a Button "Load Earlier Messages" at the top. You can simply create an xml for your load more Button, make that as the first row of your RecyclerView and create an Interface to handle click events. I am posting code for the Adpater from one of my projects. Hope that will help you in getting some idea to proceed further.
/**
* Created by Mukesh on 21/12/2015.
*/
public class ChatListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context mContext;
private List<UserMessage> userMessagesList;
private LayoutInflater mLayoutInflater;
private static final int ROW_TYPE_LOAD_EARLIER_MESSAGES = 0;
private static final int ROW_TYPE_SENDER = 1;
private static final int ROW_TYPE_RECEIVER = 2;
private int userId;
private boolean isLoadEarlierMsgs;
private LoadEarlierMessages mLoadEarlierMessages;
public ChatListAdapter(Context context, int userId, List<UserMessage> userMessagesList) {
mContext = context;
this.userMessagesList = userMessagesList;
mLayoutInflater = LayoutInflater.from(mContext);
this.userId = userId;
mLoadEarlierMessages = (LoadEarlierMessages) mContext;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case ROW_TYPE_LOAD_EARLIER_MESSAGES:
return new LoadEarlierMsgsViewHolder(mLayoutInflater.inflate(R.layout
.row_load_earlier_messages, parent, false));
case ROW_TYPE_SENDER:
return new SenderMsgViewHolder(mLayoutInflater.inflate(R.layout.row_sender_msg,
parent, false));
case ROW_TYPE_RECEIVER:
return new ReceiverMsgViewHolder(mLayoutInflater.inflate(R.layout
.row_receiver_msg, parent, false));
default:
return null;
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case ROW_TYPE_LOAD_EARLIER_MESSAGES:
LoadEarlierMsgsViewHolder loadEarlierMsgsViewHolder =
(LoadEarlierMsgsViewHolder) holder;
if (isLoadEarlierMsgs) {
loadEarlierMsgsViewHolder.btLoadEarlierMessages
.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (mLoadEarlierMessages != null) {
mLoadEarlierMessages.onLoadEarlierMessages();
}
}
});
} else {
loadEarlierMsgsViewHolder.btLoadEarlierMessages.setVisibility(View.GONE);
}
break;
case ROW_TYPE_SENDER:
SenderMsgViewHolder senderMsgViewHolder = (SenderMsgViewHolder) holder;
// set data for your sender chat bubble
break;
case ROW_TYPE_RECEIVER:
ReceiverMsgViewHolder receiverMsgViewHolder = (ReceiverMsgViewHolder) holder;
// set data for your receiver chat bubble
break;
}
}
#Override
public int getItemCount() {
return userMessagesList.size() + 1;
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return ROW_TYPE_LOAD_EARLIER_MESSAGES; // row load earlier messages
} else if (userMessagesList.get(position - 1).getUser_id() == userId) {
return ROW_TYPE_SENDER; // sender row;
} else {
return ROW_TYPE_RECEIVER; // receiver row;
}
}
public interface LoadEarlierMessages {
void onLoadEarlierMessages();
}
public void setLoadEarlierMsgs(boolean isLoadEarlierMsgs) {
this.isLoadEarlierMsgs = isLoadEarlierMsgs;
}
static class LoadEarlierMsgsViewHolder extends RecyclerView.ViewHolder {
#Bind(R.id.btLoadEarlierMsgs) Button btLoadEarlierMessages;
public LoadEarlierMsgsViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class SenderMsgViewHolder extends RecyclerView.ViewHolder {
public SenderMsgViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
static class ReceiverMsgViewHolder extends RecyclerView.ViewHolder {
public ReceiverMsgViewHolder(View itemView) {
super(itemView);
ButterKnife.bind(this, itemView);
}
}
}
And finally implement the LoadEarlierMessages listener in your ChatActivity and Override the onLoadEarlierMessages() method
/**
* Created by Mukesh on 21/12/2015.
*/
public class ChatActivity extends AppCompatActivity
implements ChatListAdapter.LoadEarlierMessages {
// getting users recent messages and init RecyclerView
private void showUserMessages() {
// initialising LayoutManager for RecyclerView and setting that to RecyclerView
mLinearLayoutManager = new LinearLayoutManager(this);
mLinearLayoutManager.setStackFromEnd(true); // to start the list from bottom
rvChatsList.setLayoutManager(mLinearLayoutManager);
// initialising RecyclerView Adapter and setting that to the RecyclerView
mChatListAdapter = new ChatListAdapter(this, userId, userMessagesList);
rvChatsList.setAdapter(mChatListAdapter);
// getting count of friend/contact messages and toggling visibility of load more button accordingly
int count = mDataBaseHandler.getCountOfMessages(contactId);
if (count != 0) {
if (count > Constants.MESSAGES_LIMIT_FOR_LOCAL_DATABASE) {
mChatListAdapter.setLoadEarlierMsgs(true);
} else {
mChatListAdapter.setLoadEarlierMsgs(false);
}
userMessagesList.addAll(mDataBaseHandler.getAllMessagesOfContact(contactId));
mChatListAdapter.notifyDataSetChanged();
}
}
#Override
public void onLoadEarlierMessages() {
ArrayList<UserMessage> tempList = mDataBaseHandler
.getPreviousMessagesOfContact(contactId, userMessagesList.size());
if (tempList.size() > 0) {
if (tempList.size() < Constants.MESSAGES_LIMIT_FOR_LOCAL_DATABASE) {
mChatListAdapter.setLoadEarlierMsgs(false);
}
View v = rvChatsList.getChildAt(0);
int top = (v == null) ? 0 : v.getTop();
for (int i = 0; i < tempList.size(); i++) {
userMessagesList.add(0, tempList.get(i));
}
mChatListAdapter.notifyDataSetChanged();
mLinearLayoutManager.scrollToPositionWithOffset(tempList.size(), top);
} else {
mChatListAdapter.setLoadEarlierMsgs(false);
}
}
}
Hope this will help..!!