i am making app with Android Studio using Rest Api from my WordPress website , i have done all and its working , now i have implemented adMob ads between post and it also works but the problem is when ad shows then the very last post is missing from list, like i have 6 posts in my website and when ad shows once in between posts than the 6th post is missing. this is my code in adapter..
#Override
public void onBindViewHolder(#NotNull RecyclerView.ViewHolder holder, int position) {
AuthorModel authormodel = dataset.get(getRealPosition(position));
if (holder instanceof MyDataHolder){
((MyDataHolder)holder).showModel(authormodel);
}
}
private int getRealPosition(int position) {
if (LIST_AD_DELTA == 0) {
return position;
} else {
return position - position / LIST_AD_DELTA;
}
}
#Override
public int getItemCount() {
Log.d("SIZE", String.valueOf(dataset.size()));
return dataset.size();
}
#Override
public int getItemViewType(int position){
if (position > 0 && position % LIST_AD_DELTA == 0) {
return AD;
}
return viewTypeData;
}
you can see i log the dataset.size and it returns 6 that should be 7 i think when include ads ,
when is use code like this than all posts are showing but no ads,,
private int getRealPosition(int position) {
return position;
}
#Override
public int getItemCount() {
Log.d("SIZE", String.valueOf(dataset.size()));
return dataset.size();
}
#Override
public int getItemViewType(int position){
return viewTypeData;
}
but i also want to insert ads without losing post, please help
Use Live Data to get API information. Also, create a function for editing and delete old data the inside of the function and write the new data. Thus, every time you get an ad from the API, the data size changes and the item increases or decreases
Related
I have a recyclerview with multiple view types and pagination enabled. Scrolling up after two pages the items are not holding the view and are duplicating. I have implemented getItemId return unique id and getViewtype return unique value. Also have the the adapter's setHasStableIds to true. Looking forward for any help.
Thank you.
getItemId
if (postList.get(position).getId()== null){
return position;
} else{
return postList.get(position).getId();
}
getItemViewType
if (postList.get(position).getPostType().equals("contest")){
return TYPE_CONTEST;
} else if(postList.get(position).getPostType().equals("suggestion")){
return TYPE_SUGGESTION;
} else{
return TYPE_POST_POLL;
}
} else{
return TYPE_HEADER;
}
Try something like this:
#Override
public int getItemViewType(int position) {
if (isValidPosition(position)) {
Dataset d = mDataset[position];
if ("contest".equals(d.getViewType())) {
return VIEW_TYPE_CONTEST;
} else if ("suggestion".equals(d.getViewType())) {
return VIEW_TYPE_SUGGESTION;
}
// all other cases here
}
// default type
return VIEW_TYPE_CONTEST;
}
and then :
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
Dataset d = mDataset[position];
if (holder instanceof ContestVH) {
ContestDataset contest = (ContestDataset) d;
ContestVH vh = (ContestVH) holder;
// bind your contest views here
} else if (holder instanceof SuggestionVH) {
SuggestionDataset suggestion = (SuggestionDataset) d;
SuggestionVH vh = (SuggestionVH) holder;
// bind your suggestion views here
}
// all other cases here
}
this way your String view type is your only source of truth.
For pagination in recyclerview you should use onScrollListener this
recyclerList.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
boolean isLast = isLastItemDisplaying(recyclerView); //get last displayed item
if (loading && isLast) { //this check if your data is loading and if its last list item list will not load again
loading = false;
//get your data according to page count it will refresh your recycler list
}
}
});
because in pagination you can get data from Api on page scroll in recycler view. So data will come in chunks according to page count. For this behaviour you can use on scroll listener in recycler list and refresh data on scroll of list until last item of list arrived.
i hope it will help you :)
Hi everyone I'm stuck in this and need help :
Each item has a CheckBox and I set setOnLongClickListener for root element of my items in RecyclerView like this :
holder.faviorateVideoItemRelative.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View arg0) {
if (chk_visible)
{
return true ;
}
holder.chk_faviorateVideo.setChecked(!holder.chk_faviorateVideo.isChecked());
chk_visible = true ;
checkedItemsCNT = 1 ;
deleteListVideoCourses.add(data.get(holder.getAdapterPosition())) ;
notifyDataSetChanged() ;
return true ;
}
});
If I scroll down , when I make a long click on one of items , the CheckBox of wrong item get checked !
It because as you use RecycleView it reuse your view every time when you scroll. RecycleView reuse your resource like this
So when you scroll it's showing the wrong state of your view
Solution
If you write any logic for check-in onBindViewHolder then you have to use both part for true and false
if(yourCondition){
//code if condition is true
}else {
//code if condition is false
}
Other Solution
Simply you can solve it just using one statement to stop your RecycleView to reuse your view state like this
#Override
public void onBindViewHolder(ReqNotificationAdapter.MyViewHolder holder, int position) {
holder.setIsRecyclable(false);
//.................your other code
}
I use it to solve my problem.. hope it will solve yours if you don't have a problem stop Recycling with your purpose.
Could be an issue in your bind rather than setting of the value.
A common mistakes is not un-checking the view in the bind.
Make sure where you are setting checked, you have an else statement and set it to unchecked.
RecyclerView and Listview reuse views as they scroll, which includes any previously checked boxes. So it is important to un-check them if appropriate.
public void bindView(View view, ..... //varies on implementation. rough idea.
{
CheckBox mycheckbox = (CheckBox)view.findViewById(R.id.myidofcheckbox);
int pos = view.getPosition();
if(pos == 1) //example
mycheckbox.setChecked(true);
else
mycheckbox.setChecked(false);
Solution 1:
You can override two method getItemId, getItemViewType.
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
Like this:
public class EasyAdapter extends RecyclerView.Adapter<EasyAdapter.ViewHolder>{
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
}
Solution 2:
Set setIsRecyclable false.
EasyAdapter easyAdapter=new EasyAdapter();
easyAdapter.setIsRecyclable(false);
Yes it happens with ListView and RecyclerView
Try this code,
holder.faviorateVideoItemRelative.setTag(position);
holder.faviorateVideoItemRelative.setOnLongClickListener(new View.OnLongClickListener() {
public boolean onLongClick(View arg0) {
int tempPos = (int)holder.faviorateVideoItemRelative.getTag();
if (chk_visible)
{
return true ;
}
if(!holder.chk_faviorateVideo.isChecked())
{
holder.chk_faviorateVideo.setChecked(true);
}
else
{
holder.chk_faviorateVideo.setChecked(false);
}
holder.chk_faviorateVideo.setChecked(!holder.chk_faviorateVideo.isChecked());
chk_visible = true ;
checkedItemsCNT = 1 ;
deleteListVideoCourses.add(data.get(tempPos)) ;
notifyDataSetChanged() ;
return true ;
}
});
Hope it will help you.
When you initialize your OnLongClickListener, it is reused in views when they are no longer visible because they are cached.
To solve the worry, create a new class to explicitly link an object to a listener and not just the view.
For example :
private class BufferVideoLongClickListener implements View.OnLongClickListener {
private VideoObject video; // And other stuff like checked, etc
BufferVideoLongClickListener(VideoObject video, ...) {
this.video = video;
...
}
#Override
public void onLongClick(View view) {
// Do your check and other things
if(checked)
...
}
}
And in your onBindViewHolder :
holder.faviorateVideoItemRelative.setOnLongClickListener(new BufferVideoLongClickListener(video, ...));
After using the above mentioned solutions,
The following solutions worked like a charm for me.
#Override
public long getItemId(int position) {
return position;
}
#Override
public int getItemViewType(int position) {
return position;
}
using holder.setIsRecyclable(false) removes the desired selection as well when you come back to it after the whole scrolling process.
I am implementing recyclerview with multiple layouts.Usually we have multiple viewholders for different layouts and override other methods as per the required layout.I have successfully implemented this.But now i have a different scenario like: A recyclerview that shows some videos (say 3) then another layout(say layout x), again 3 videos and then again layout x and so on.Suppose i have 10 videos then in this case the itemcount would be 10 + 3 as 3 layout x would be displayed.But the videos are loaded while scrolling.So how can i determine the number of views to return in getItemCount();
I mean
#Override
public int getItemCount() {
return ListofVideos.size() + "WHAT??"
}
layout is like this
If all the videos are loaded at at once then it is easy to calculate the number of views like if i have 21 videos i would have total 27 views(i.e 21 videos and 6 layout X views). But when the list is loaded on scroll how can i determine the number of views?
Your Adapter is responsible to populate view so it has all views of your RecyclerView while your ListofVideos (may) have only video links.
Whenever you scroll your RecyclerView, Adapter is responsible to inflate views.
What you should do?
Create an interface
public interface BaseItem {
int ITEM_TYPE_HEADER = 0;
int ITEM_TYPE_SUB_HEADER = 1;
int ITEM_TYPE_ROW_NORMAL = 2;
int getItemType();
}
And implement this interface with your adapter's video item like
public class YourAdapterVideoItem implements BaseItem {
// rest of your code
#Override
public int getItemType() {
return ITEM_TYPE_ROW_NORMAL;
}
}
Create your adapter's header item
public class YourAdapterHeaderItem implements BaseItem {
// rest of your code
#Override
public int getItemType() {
return ITEM_TYPE_HEADER;
}
}
Update your adapter with
public class YourAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<BaseItem> items = new ArrayList<BaseItem>();
#Override
public BaseRecyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
BaseRecyclerViewHolder holder;
switch (viewType) {
case BaseItem.ITEM_TYPE_ROW_NORMAL:
default:
// inflate your default items
break;
case BaseItem.ITEM_TYPE_HEADER:
// inflate your default items
break;
}
return holder;
}
#Override
public void onBindViewHolder(BaseRecyclerViewHolder viewHolder, int position) {
BaseItem base = getItemAt(position);
switch (base.getItemType()) {
case BaseItem.ITEM_TYPE_HEADER:
// populate your header view
break;
case BaseItem.ITEM_TYPE_ROW_NORMAL:
// populate your actual view
break;
}
}
#Override
public int getItemCount() {
return items == null ? 0 : items.size();
}
#Override
public int getItemViewType(int position) {
return getItemAt(position).getItemType();
}
public BaseItem getItemAt(int position) {
return items == null ? null : items.get(position);
}
}
When you want to add header use YourAdapterHeaderItem for your videos use YourAdapterVideoItem.
Hope this helps
Edit
For adding headers in GridLayoutManager have a look at RecyclerView GridLayoutManager with full width header
i want to add header to recycle view i am trying to achieve it using
#Override
public int getItemViewType(int position) {
// depends on your problem
if (position == 0 || position == 4) {
return 2;
} else {
return 1;
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
if (viewType == 1) {
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.cardview_row, null);
return new ViewHolder(itemLayoutView);
} else if (viewType == 2) {
View itemLayoutView = LayoutInflater.from(parent.getContext()).inflate(
R.layout.cardview_row1, null);
return new ViewHolders(itemLayoutView);
}
return null;
}
but how i can do it in run time like when i don't know position when it should show section like i have json
{
"DATA": [
"hi",
"heloo",
"bye"
],
"MAHA": [
"ans",
"rs",
"re",
"jab",
"bad"
]
}
where data and maha is section i want to display other elements
currently i am making arraylist of all elements and adding hardcore
value for section but how i can do this viva using above json
What you need is an expandable recyclerview with parents(headers) and children(entries). Here you go:
https://github.com/bignerdranch/expandable-recycler-view
If you want to show the entries always and dont profit from the expandable feature, just do: expAdapter.expandAllParents().
I know you dont want to use 3rd library parties but in my opinion this is the best way to deal with it and saves you a lot of time. Moreover if someone else has the same question he maybe finds this solution useful.
Create a class like this for your JSON data, and create one Section class per node (in your example, one for DATA and another for MAHA):
class Section {
String header;
List<String> itemList;
}
Then create your custom adapter:
public class SectionedRecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private List<Section> sectionList;
public SectionedRecyclerViewAdapter(List<Section> sectionList) {
this.sectionList = sectionList;
}
#Override
public int getItemViewType(int position) {
int currentPos = 0;
for (Section section : sectionList) {
// check if position is in this section
// +1 for the header, -1 because idx starts at 0
if (position >= currentPos && position <= (currentPost + section.itemList.size() + 1 - 1)) {
if (position == currentPos) {
return 2; // header
} else {
return 1; // item
}
}
// +1 for the header
currentPos += section.itemList.size() + 1;
}
throw new IndexOutOfBoundsException("Invalid position");
}
// use the onCreateViewHolder method from your question...
}
I know you don't want to use a 3rd party lib, but you can check how the getItemViewType method of SectionedRecyclerViewAdapter is done here.
I have a RecyclerView that can show items as list, small grids or large grid and this can be change at runtime. Depending on what style user chooses i inflate different layout in onCreateViewHolder.
I also use layoutManger.setSpanSizeLookUp() to switch between styles. My code looks like this
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
if(showType == ProductAdapter.SHOW_TYPE_SMALL_GRID)
return 1;
else
return columnCount; //show one item per row
}
});
#Override
public void onClick(View v) {
if(showType == ProductAdapter.SHOW_TYPE_SMALL_GRID)
showType = ProductAdapter.SHOW_TYPE_LARGE_GRID;
else
showType = ProductAdapter.SHOW_TYPE_SMALL_GRID;
int firstVisibleItem = layoutManager.findFirstVisibleItemPosition();
adapter = new ProductAdapter(getActivity(), productList, showType);
recyclerView.setAdapter(adapter);
layoutManager.scrollToPosition(firstVisibleItem);
}
The problem is to force onCreateViewHolder to be called I'm creating a new object every time user changes the style. Is there any other way?! to force onBindViewHolder() to be recalled. I simply use adapter.notifyDataSetChanged() How can i get something similar for onCreateViewHolder?
Any solution that doesn't uses multiple adapters is good enough!
What you need to do is:
Modify your Adapter:
Specify two types of Views that your Adapter can inflate:
private static final int LARGE_GRID_ITEM = -1;
private static final int SMALL_GRID_ITEM = -2;
Create a field that can store current type mCurrentType
Use your Adapter's getItemViewType. For example like this:
#Override
public int getItemViewType (int position) {
return mCurrentType;
}
In your createViewHolder use the viewType to decide what type of ViewHolder you need to create.
public final RecyclerView.ViewHolder createViewHolder (ViewGroup parent, int viewType){
if (viewType == LARGE_GRID_ITEM) {
//return large grid view holder
} else {
//return small grid view holder
}
}
Additionally you can create methods:
public void toggleItemViewType () {
if (mCurrentType == LARGE_GRID_ITEM){
mCurrentType = SMALL_GRID_ITEM;
} else {
mCurrentType = LARGE_GRID_ITEM;
}
}
public boolean displaysLargeGrid(){
return mCurrentType == LARGE_GRID_ITEM;
}
Modify the code you posted:
layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
if (adapter.displaysLargeGrid()) {
return 1;
} else {
return columnCount; //show one item per row
}
}
});
#Override
public void onClick(View v) {
adapter.toggleItemViewType();
adapter.notifyDataSetChanged();
}
Its not the optimal choice but it's better to create a new Adapter, which will call onCreateViewHolder(). This way you can avoid your troubles, by the cost of very tiny performance issues.