Recycler ItemClick doesn't work with Countdown timer - java

I'm trying to develop Letters and Numbers(Countdown) like game. In this concept, user should able to use the arithmetic operations results of given numbers once for the reach goal number in a given time.
I'm using the Recyclerview to show and list the steps of calculation but while Countdown Timer ticking, I can't click the items of recyclerview. It only works when the Countdown Timer finished up.
Please help me to find out the problem.
Here is what I mean as a image: https://hizliresim.com/zWThaa
Here is my adapter
public interface OnItemListener{
void onItemClick(int position);
}
private OnItemListener mOnItemListener;
private ArrayList<String> n1;
private ArrayList<String> n2;
private ArrayList<String> res;
private ArrayList<String> op;
public NotesRecyclerAdapter(ArrayList<String> n1, ArrayList<String> n2, ArrayList<String> res, ArrayList<String> op, OnItemListener onItemListener) {
this.n1 = n1;
this.n2 = n2;
this.res = res;
this.op = op;
this.mOnItemListener = onItemListener;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_item, parent, false);
return new ViewHolder(view, mOnItemListener);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
if(n2.size() <= position ){
holder.second.setText(" ");
}
else{
holder.second.setText(n2.get(position));
}
if(op.size() <= position){
holder.operation.setText(" ");
}
else{
holder.operation.setText(op.get(position));
}
if (res.size() <= position){
holder.result.setText(" ");
}
else {
holder.result.setText(res.get(position));
}
holder.first.setText(n1.get(position));
}
#Override
public int getItemCount() {
return n1.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
TextView first;
TextView operation;
TextView second;
TextView result;
OnItemListener mOnItemListener;
public ViewHolder(View itemView, OnItemListener onItemListener) {
super(itemView);
first = itemView.findViewById(R.id.first);
operation = itemView.findViewById(R.id.op);
second = itemView.findViewById(R.id.secondNum);
result = itemView.findViewById(R.id.res);
this.mOnItemListener = onItemListener;
itemView.setOnClickListener(this);
}
#Override
public void onClick(View v) {
mOnItemListener.onItemClick(getAdapterPosition());
}
}
}
Here is Activity,
public class CalculateActivity extends AppCompatActivity implements Adapter.OnItemListener{
RecyclerView rView;
Adapter adapter;
.//Other definitions etc.
.
protected void onCreate(Bundle savedInstanceState) {
. . . // Other
adapter = new Adapter(firstNumbers,secondNumbers,operationResults,operators,this);
rView.setLayoutManager(new LinearLayoutManager(getApplicationContext()));
rView.setAdapter(adapter);
new CountDownTimer(45000, 1) {
#Override
public void onTick(long millisUntilFinished) {
// Calculation step
}
#Override
public void onFinish() {
}
}.start();
...//Button Listeneres etc...
} // end of onCreate
#Override
public void onItemClick(int position) {
System.out.println(position + " Clicked!");
View view = rView.getChildAt(position);
TextView res = view.findViewById(R.id.res);
moveCount++;
if(moveCount % 2 == 0){
firstNumbers.add((String)res.getText()); //res is textview of the operation results.
}
else{
secondNumbers.add((String)res.getText());
}
view.setClickable(false)
dataChange(); //just notifyDataSetChange
}
PS: I also used this method. I can able to click with this method but it returns wrong value of item's position while Countdown Timer ticking. Just like I explained above, it works perfectly when the timer runs out.

Probably you have a problem with adapter...
I don't know what you do in the onTick method, whether you block something or...
I'm not sure what do you want but consider whether it is possible to do so
You can create Step class like
public class Step {
private String first;
private String operation;
private String second;
private String result;
private boolean disabled; //instead view clickable
//TODO generate getters and setters
}
Try this adapter
public class NotesRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final OnItemListener mOnItemListener;
private final List<Step> items;
public NotesRecyclerAdapter(List<Step> items, OnItemListener onItemListener) {
this.items = items;
this.mOnItemListener = onItemListener;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_item, parent, false);
return new NotesViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
NotesViewHolder mHolder = (NotesViewHolder) holder;
Step item = items.get(position);
mHolder.first.setText(item.getFirst());
if(items.size() <= position){ // Im not sure why this
mHolder.second.setText(" ");
mHolder.operation.setText(" ");
mHolder.result.setText(" ");
}else{
mHolder.second.setText(item.getSecond());
mHolder.operation.setText(item.getOperation());
mHolder.result.setText(item.getResult());
}
if(mOnItemListener != null){
mHolder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
mOnItemListener.onItemClick(mHolder.getAdapterPosition());
}
});
}
}
#Override
public int getItemCount() {
return items.size();
}
public Step getItem(int position) {
if(this.items == null || position < 0 || position >= this.items.size()) return null;
return this.items.get(position);
}
public interface OnItemListener{
void onItemClick(int position);
}
public static class NotesViewHolder extends RecyclerView.ViewHolder {
public TextView first;
public TextView operation;
public TextView second;
public TextView result;
public NotesViewHolder(View itemView) {
super(itemView);
first = itemView.findViewById(R.id.first);
operation = itemView.findViewById(R.id.op);
second = itemView.findViewById(R.id.secondNum);
result = itemView.findViewById(R.id.res);
}
}
}
and onClick method
#Override
public void onItemClick(int position) {
Toast.makeText(this, "Clicked: "+ position, Toast.LENGTH_SHORT).show();
Step step = adapter.getItem(position);
if(step != null){
if(step.isDisabled()){
Toast.makeText(this, "Disabled"+ position, Toast.LENGTH_SHORT).show();
return;
}
moveCount++;
if(moveCount % 2 == 0){
step.setFirst(random number or what); //how do you get number, randomly or...
} else{
step.setSecond(random number or what);
}
step.setDisabled(false); //instead view.setClickable(false)
dataChange(); //just notifyDataSetChange
}
}

Related

Recycleview cardview Onclick duplicate effects when scrolling

I'm making my first app (calendar),
clicking the cardview item applies the same effect (changing Textview text color and Imageview background) to other items when scrolling the recycleview
in the main activity
RecyclerView daysRecyclerView;
DaysAdapter daysAdapter;
private RecyclerView.LayoutManager horizontalLayout;
ArrayList<DaysModel> daysModels;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_calendar);
daysModels = getDaysList();
initDaysRecycleview();
}
private void initDaysRecycleview(){
daysRecyclerView = findViewById(R.id.daysRecyclerView);
daysRecyclerView.setHasFixedSize(true);
horizontalLayout = new LinearLayoutManager(this,RecyclerView.HORIZONTAL,false);
daysAdapter = new DaysAdapter(daysModels);
daysRecyclerView.setLayoutManager(horizontalLayout);
daysRecyclerView.setAdapter(daysAdapter);
daysAdapter.setOnItemClickListener(new DaysAdapter.OnItemClickListener() {
#Override
public void onItemClick(int position) {
dayClickHandler(position, 1);
}
});
}
public void dayClickHandler(int position, int change){
daysModels.get(position).changeDayHighlight(change);
daysAdapter.notifyItemChanged(position);
}
private ArrayList<DaysModel> getDaysList(){
//Not accurate used for testing
ArrayList<DaysModel> models = new ArrayList<DaysModel>();
String[] dayName ={"SAT","SUN","MON","TUE","WED","THU","FRI","SAT","SUN","MON","TUE","WED","THU","FRI","SAT","SUN","MON","TUE","WED","THU","FRI","SAT","SUN","MON","TUE","WED","THU","FRI","SAT","SUN","MON"};
int i,j;
for (i = 0;i <dayName.length;i++){
j =i+1;
models.add(new DaysModel(dayName[i], j, 0, false));
}
return models;
}
I'm using an interface for the onClickListener.
ViewHolder
public class DaysViewHolder extends RecyclerView.ViewHolder {
TextView hDayName,hDayNumber;
ImageView currentDayHighlight;
CardView dayCard;
public DaysViewHolder(View itemView, DaysAdapter.OnItemClickListener listener) {
super(itemView);
this.hDayName = itemView.findViewById(R.id.dayName);
this.hDayNumber = itemView.findViewById(R.id.dayNumber);
this.currentDayHighlight = itemView.findViewById(R.id.dayHihlight);
this.dayCard = itemView.findViewById(R.id.dayCard);
dayCard.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (listener != null) {
int position = getAdapterPosition();
if(position != RecyclerView.NO_POSITION){
listener.onItemClick(position);
}
}
}
});
}
}
Adapter
public class DaysAdapter extends RecyclerView.Adapter<DaysViewHolder> {
Context context;
ArrayList<DaysModel> daysModels;
OnItemClickListener mListener;
public interface OnItemClickListener{
void onItemClick(int position);
}
public void setOnItemClickListener(OnItemClickListener listener){
mListener = listener;
}
public DaysAdapter(ArrayList<DaysModel> daysModels) {
this.daysModels = daysModels;
}
#NonNull
#Override
public DaysViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.day_card,parent,false);
DaysViewHolder daysViewHolder = new DaysViewHolder(view, mListener);
return daysViewHolder;
}
#Override
public void onBindViewHolder(#NonNull DaysViewHolder holder, int position) {
DaysModel currentItem = daysModels.get(position);
holder.hDayName.setText(currentItem.getDayName());
holder.hDayNumber.setText(String.valueOf(currentItem.getDayNumber()));
if(currentItem.isDayHighlighted() == 1){
holder.currentDayHighlight.setBackgroundResource(R.drawable.tiny_background);
holder.hDayNumber.setTextColor(Color.BLACK);
}
}
#Override
public int getItemCount() {
return daysModels.size();
}
}
sorry for the long code text, I've just started and I am already struggling.
All you need is to set else part for you if condition in bind method of Adapter class.
So change your bind method to something like this:
#Override
public void onBindViewHolder(#NonNull DaysViewHolder holder, int position) {
DaysModel currentItem = daysModels.get(position);
holder.hDayName.setText(currentItem.getDayName());
holder.hDayNumber.setText(String.valueOf(currentItem.getDayNumber()));
if(currentItem.isDayHighlighted() == 1){
holder.currentDayHighlight.setBackgroundResource(R.drawable.tiny_background);
holder.hDayNumber.setTextColor(Color.BLACK);
}else{
holder.currentDayHighlight.setBackgroundResource(/*what ever you want for default situation*/);
holder.hDayNumber.setTextColor(/* for example Color.White*/);
}
}

Recyclerview multiple selection selected order count similar to instagram image selection

Problem
I am trying to develop a gallery selector page for my app. A user can select multiple images/videos from the listed items (from the users phone). The user can select up to 10 items at once and the order of selection should be shown for each item. The selection order should also adjust based on selection and deselection.
eg : If user deselects selection 1 then all other selection after selection 1 should decrement by one.
What I have done
I have already made the recyclerview adapter with diffUtils and handled the multiple selection using recyclerview-selection library. But I can't find a way to show the selection order and adjusting it based on user action.
What an trying to achieve
Code
GalleryAdapter
public class GalleryViewAdapter extends ListAdapter<GalleryThumbnailsModel,GalleryViewAdapter.ViewHolder> {
private Context context;
private ArrayList<GalleryThumbnailsModel> selectedItemModels;
private Interaction interaction;
private SelectionTracker<Long> selectionTracker;
private static int POST_TYPE_IMAGE = 1;
private static int POST_TYPE_VIDEO = 3;
public GalleryViewAdapter(GalleryViewDiffCallback diffCallback,Context context,ArrayList<GalleryThumbnailsModel> selectedItemModels) {
super(diffCallback);
this.context = context;
this.selectedItemModels = selectedItemModels;
setHasStableIds(true);
}
#NonNull
#Override
public GalleryViewAdapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.item_gallery_view,parent,false);
return new GalleryViewAdapter.ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final GalleryViewAdapter.ViewHolder holder, final int position) {
GalleryThumbnailsModel item = getItem(position);
holder.bind(item,selectionTracker.isSelected((long) position));
}
#Override
public long getItemId(int position) {
return (long) position;
}
public void setSelectionTracker(SelectionTracker<Long> selectionTracker) {
this.selectionTracker = selectionTracker;
}
public class ViewHolder extends RecyclerView.ViewHolder {
ImageView postImage;
ImageView postTypeVideo;
ProgressBar progressBar;
TextView postOrderCount;
FrameLayout selectedIcon;
public ViewHolder(View itemView) {
super(itemView);
postImage = itemView.findViewById(R.id.post_gallery_image);
postTypeVideo = itemView.findViewById(R.id.user_post_video);
progressBar = itemView.findViewById(R.id.progress_loader);
selectedIcon =itemView.findViewById(R.id.gallery_selected_item);
postOrderCount = itemView.findViewById(R.id.selected_post_order);
}
public void bind(GalleryThumbnailsModel item, boolean selected) {
new GlideImageLoader(context,postImage,progressBar).load(item.getThumbnail(),null);
if(selected){
selectedIcon.setVisibility(View.VISIBLE);
interaction.onItemSelected(getAdapterPosition(),item);
}else {
selectedIcon.setVisibility(View.INVISIBLE);
interaction.onItemDeselected(getAdapterPosition(),item);
}
if(item.getMediaType() == POST_TYPE_VIDEO){
postTypeVideo.setVisibility(View.VISIBLE);
}else {
postTypeVideo.setVisibility(View.INVISIBLE);
}
}
public ItemDetailsLookup.ItemDetails<Long> getItemDetails(){
return new ItemDetailsLookup.ItemDetails<Long>() {
#Override
public int getPosition() {
return getAdapterPosition();
}
#Nullable
#Override
public Long getSelectionKey() {
return getItemId();
}
#Override
public boolean inSelectionHotspot(#NonNull MotionEvent e) {
return true;
}
};
}
}
public static class GalleryViewDiffCallback extends DiffUtil.ItemCallback<GalleryThumbnailsModel>{
#Override
public boolean areItemsTheSame(#NonNull GalleryThumbnailsModel oldItem, #NonNull GalleryThumbnailsModel newItem) {
return oldItem.getUriPath().equals(newItem.getUriPath());
}
#Override
public boolean areContentsTheSame(#NonNull GalleryThumbnailsModel oldItem, #NonNull GalleryThumbnailsModel newItem) {
return oldItem.equals(newItem);
}
}
public interface Interaction{
void onItemSelected(int position,GalleryThumbnailsModel item);
void onItemDeselected(int position,GalleryThumbnailsModel item);
}
public void setInteraction(Interaction interaction) {
this.interaction = interaction;
}
}
Setting adapter in Fragment
private void setAdapter() {
galleryViewAdapter = new GalleryViewAdapter(new GalleryViewAdapter.GalleryViewDiffCallback(),context,selectedItemsModels);
gridLayoutManager = new GridLayoutManager(context,3,RecyclerView.VERTICAL,false);
galleryViewAdapter.submitList(galleryThumbnailsModels);
galleryViewRecycler.setLayoutManager(gridLayoutManager);
galleryViewRecycler.setHasFixedSize(true);
galleryViewRecycler.setAdapter(galleryViewAdapter);
selectionTracker = new SelectionTracker.Builder<Long>(
"selection",
galleryViewRecycler,
new StableIdKeyProvider(galleryViewRecycler),
new RecyclerSelectionLookup(galleryViewRecycler),
StorageStrategy.createLongStorage()
).withSelectionPredicate(new SelectionTracker.SelectionPredicate<Long>() {
#Override
public boolean canSetStateForKey(#NonNull Long key, boolean nextState) {
// 10 - max selection size
return !nextState || selectionTracker.getSelection().size() < 10;
}
#Override
public boolean canSetStateAtPosition(int position, boolean nextState) {
return false;
}
#Override
public boolean canSelectMultiple() {
return true;
}
})
.build();
selectionTracker.addObserver(new SelectionTracker.SelectionObserver<Long>() {
#Override
public void onSelectionChanged() {
super.onSelectionChanged();
int selectedItemCount = selectionTracker.getSelection().size();
if(selectedItemCount == 0){
addMediaLayout.setVisibility(View.INVISIBLE);
}else if(selectedItemCount > 0 && selectedItemCount < 11){
selectedCountTextView.setText(String.valueOf(selectedItemCount));
addMediaLayout.setVisibility(View.VISIBLE);
}
}
});
galleryViewAdapter.setSelectionTracker(selectionTracker);
galleryViewAdapter.setInteraction(new GalleryViewAdapter.Interaction() {
#Override
public void onItemSelected(int position,GalleryThumbnailsModel item) {
item.setSelectedOrder(selectedItemsModels.size()+1);
selectedItemsModels.add(item);
}
#Override
public void onItemDeselected(int position,GalleryThumbnailsModel item) {
}
});
}
I am only adding the adapter code here. Otherwise the question would become too large.I will share the complete code if this doesn't provide necessary information.
you can pass selectionTracker.selection.size() to bind function in your ViewHolder class, so you will have always the latest count
#Override
public void onBindViewHolder(#NonNull final GalleryViewAdapter.ViewHolder holder, final int position) {
int count = selectionTracker.selection.size()
holder.bind(item,count,selectionTracker.isSelected((long) position));
}

Multiple type of views in Recycler View not setting correctly

I have two array list and want to set the adapter so that it looks like below screen one. But when I am setting adapter I am getting view like second screen. This is my code of Adapter:
public class ItemArrayAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
final int VIEW_TYPE_MESSAGE = 0;
final int VIEW_TYPE_IMAGE = 1;
private static final String TAG = "ItemArrayAdapter";
Context context;
ArrayList<BeforLoginDao.Article> mArticleList;
ArrayList<BeforLoginDao.Quiz> mQuizList;
int i=0;
public ItemArrayAdapter(Context context,ArrayList<BeforLoginDao.Quiz> mQuizList, ArrayList<BeforLoginDao.Article> mArticleList ){
this.context = context;
this.mArticleList = mArticleList;
this.mQuizList = mQuizList;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType){
View itemView;
if(viewType == VIEW_TYPE_MESSAGE){
itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.article_layout,parent,false);
return new ArticleViewHolder(itemView);
}
else if(viewType == VIEW_TYPE_IMAGE){
itemView= LayoutInflater.from(parent.getContext()).inflate(R.layout.quiz,parent,false);
return new QuizViewHolder(itemView);
}
return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder viewHolder, int position){
Log.e(TAG, "onBindViewHolder: " +position);
if(viewHolder instanceof ArticleViewHolder){
((ArticleViewHolder) viewHolder).populate(mArticleList.get(position));
}
else if(viewHolder instanceof QuizViewHolder){
((QuizViewHolder) viewHolder).populate(mQuizList.get(position - mArticleList.size()));
}
}
#Override
public int getItemCount(){
return mArticleList.size() + mQuizList.size();
}
#Override
public int getItemViewType(int position){
Log.e(TAG, "getItemViewType: " +position );
if(position < mArticleList.size()){
return VIEW_TYPE_MESSAGE;
}
if(position - mArticleList.size() < mQuizList.size()){
return VIEW_TYPE_IMAGE;
}
return -1;
}
public class ArticleViewHolder extends RecyclerView.ViewHolder {
public TextView mTitle, mTag1, mTag2 ;
ImageView mImageView;
public ArticleViewHolder(View itemView) {
super(itemView);
mTitle = itemView.findViewById(R.id.tv_article_title);
mTag1 = itemView.findViewById(R.id.tv_article_tag1);
mTag2 = itemView.findViewById(R.id.tv_article_tag2);
mImageView=itemView.findViewById(R.id.img_article);
}
public void populate(BeforLoginDao.Article chatWrapper){
mTitle.setText(chatWrapper.articleTitle.toString());
mTag1.setText(chatWrapper.subtag1);
mTag2.setText(chatWrapper.subtag2);
}
}
public class QuizViewHolder extends RecyclerView.ViewHolder {
public TextView tvQuizTitle, tvQuizTag1, tvQuizTag2 ;
public QuizViewHolder(View itemView) {
super(itemView);
tvQuizTitle = itemView.findViewById(R.id.tv_quiz_title);
tvQuizTag1 =itemView.findViewById(R.id.tv_quiz_tag1);
tvQuizTag2=itemView.findViewWithTag(R.id.tv_quiz_tag2);
}
public void populate(BeforLoginDao.Quiz imageDataWrapper){
Log.e(TAG, "ViewHolderTwo: " +imageDataWrapper.toString() );
tvQuizTitle.setText(imageDataWrapper.quizTitle);
tvQuizTag1.setText(imageDataWrapper.quiz_subtag1);
}
}
}
To solve this error I also checked many solution on SO.
Ref: enter link description here
Below is screen one and screen two. Screen one that I need, Screen two that I am getting.

Recyclerview holder mixes on scroll

When scroll recyclerview some items mixes. After I add ads after every 15 items, holder get wrong data. Some items are vip items. I will change background color of these items. But when I scroll it dublicates mixes. How can I solve?
This is my adapter
private Context mCtx;
private List<Car> carList;
private RecyclerViewAnimator mAnimator;
private int AD_TYPE=1;
private int CONTENT_TYPE=2;
private int LIST_AD_DELTA=15;
public ProductAllCarAdapter(RecyclerView recyclerView,Context mCtx, List<Car> carList) {
this.mCtx = mCtx;
this.carList = carList;
mAnimator = new RecyclerViewAnimator(recyclerView);
}
#Override
public ProductAllCarAdapter.ProductViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == AD_TYPE){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_add_item, null);
ProductAllCarAdapter.ProductViewHolder vh = new ProductAllCarAdapter.ProductViewHolder(itemView);
mAnimator.onCreateViewHolder(itemView);
return vh;
} else {
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.product_car_item, null);
ProductAllCarAdapter.ProductViewHolder vh = new ProductAllCarAdapter.ProductViewHolder(itemView);
mAnimator.onCreateViewHolder(itemView);
return vh;
}
}
#Override
public int getItemViewType(int position) {
if (position>0 && position % LIST_AD_DELTA == 0)
return AD_TYPE;
return CONTENT_TYPE;
}
#Override
public void onBindViewHolder(ProductAllCarAdapter.ProductViewHolder holder, int position) {
if (getItemViewType(position) == CONTENT_TYPE) {
final Car car = carList.get(holder.getAdapterPosition());
GlideApp.with(mCtx).load(car.getImg()).into(holder.imageView);
if (car.getVip() == 1) {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorVip));
holder.imageViewVIP.setVisibility(View.VISIBLE);
}
final String carid = String.valueOf(car.getCarid());
mAnimator.onBindViewHolder(holder.itemView, position);
} else {
holder.mView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Context mcontext = view.getContext();
Bundle bundle = ActivityOptionsCompat.makeCustomAnimation(mcontext, android.R.anim.fade_in, android.R.anim.fade_out).toBundle();
Intent intent = new Intent(mcontext, AdsItem.class);
mcontext.startActivity(intent, bundle);
}
});
mAnimator.onBindViewHolder(holder.itemView, position);
}
}
private int getRealPosition(int position) {
if (LIST_AD_DELTA == 0) {
return position;
} else {
return position - position / LIST_AD_DELTA;
}
}
#Override
public long getItemId(int position) { return position; }
#Override
public int getItemCount() {
int additionalContent = 0;
if (carList.size() > 0 && carList.size() > LIST_AD_DELTA) {
additionalContent = ( carList.size() / LIST_AD_DELTA);
}
return carList.size() + additionalContent;
}
public static class ProductViewHolder extends RecyclerView.ViewHolder {
private View mView;
ImageView imageView, imageViewVIP;
RelativeLayout relativeLayout;
public ProductViewHolder(View itemView) {
super(itemView);
mView = itemView;
imageView = itemView.findViewById(R.id.imageView);
imageViewVIP = itemView.findViewById(R.id.imageViewVIP);
relativeLayout = itemView.findViewById(R.id.relativeLayoutpc);
}
public void setOnClickListener(View.OnClickListener listener) {
mView.setOnClickListener(listener);
}
}
I think problem onBindViewHolder function use wrong holder. ArrayList also return true value but on scroll it mixes.
You just have to add the corresponding else of the following if statement block.
if (car.getVip() == 1) {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorVip));
holder.imageViewVIP.setVisibility(View.VISIBLE);
} else {
holder.relativeLayout.setBackgroundColor(ContextCompat.getColor(mCtx, R.color.colorNormal));
holder.imageViewVIP.setVisibility(View.GONE);
}
This is inside your onBindViewHolder function where the view type is CONTENT_TYPE.
Hope that solves your problem.

Android: weird reaction when adding views in RecyclerView

I'm using a custom recyclerView that can have a footer and a header, that shoudnt influence the animations tho, here is a video of what's happening: http://www.videosprout.com/video?id=00fae6ac-39ff-47b6-b981-803d2773b67b
Why is every view moving one position back and then back to where it was instead of not doing that?
Here's my adapter:
public class AddEventsAdapter extends HFRecyclerViewAdapter<String, AddEventsAdapter.ViewHolder> {
public AddEventsAdapter(Context context) {
super(context);
}
#Override
public void footerOnVisibleItem() {
}
#Override
public void addData(int position, String item) {
super.addData(position, item);
}
#Override
public ViewHolder onCreateDataItemViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.event_item, parent, false);
return new ViewHolder(v);
}
#Override
public void onBindDataItemViewHolder(ViewHolder holder, int position) {
holder.itemTv.setText(getData().get(position));
}
class ViewHolder extends RecyclerView.ViewHolder{
TextView itemTv;
public ViewHolder(View itemView) {
super(itemView);
itemTv = (TextView)itemView.findViewById(R.id.eventName);
}
}
}
The implementation:
final AddEventsAdapter MyAdapter = new AddEventsAdapter(this);
AddEventsRecycler.setAdapter(MyAdapter);
AddEventsRecycler.setLayoutManager(new LinearLayoutManager(this));
//add footer
final View footerView = LayoutInflater.from(this).inflate(R.layout.events_footer, AddEventsRecycler, false);
MyAdapter.setFooterView(footerView);
footerView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
MyAdapter.addData(0, "Event number" + ++g);
}
});
ArrayList<String> data = new ArrayList<>();
data.add("Vacation");
MyAdapter.setData(data);
The custom RecyclerAdapter:
public abstract class HFRecyclerViewAdapter<T, VH extends RecyclerView.ViewHolder> extends BaseRecyclerViewAdapter<T> {
public HFRecyclerViewAdapter(Context context) {
super(context);
}
private static final int TYPE_HEADER = Integer.MAX_VALUE;
private static final int TYPE_FOOTER = Integer.MAX_VALUE - 1;
private static final int ITEM_MAX_TYPE = Integer.MAX_VALUE - 2;
private RecyclerView.ViewHolder headerViewHolder;
private RecyclerView.ViewHolder footerViewHolder;
class HFViewHolder extends RecyclerView.ViewHolder {
HFViewHolder(View v) {
super(v);
}
}
public void setHeaderView(View header){
if (headerViewHolder == null || header != headerViewHolder.itemView) {
headerViewHolder = new HFViewHolder(header);
notifyDataSetChanged();
}
}
public void setFooterView(View foot){
if (footerViewHolder == null || foot != footerViewHolder.itemView) {
footerViewHolder = new HFViewHolder(foot);
notifyDataSetChanged();
}
}
public void removeHeader(){
if (headerViewHolder != null){
headerViewHolder = null;
notifyDataSetChanged();
}
}
public void removeFooter(){
if (footerViewHolder != null){
footerViewHolder = null;
notifyDataSetChanged();
}
}
public boolean isHeader(int position){
return hasHeader() && position == 0;
}
public boolean isFooter(int position){
return hasFooter() && position == getDataItemCount() + (hasHeader() ? 1 : 0);
}
private int itemPositionInData(int rvPosition){
return rvPosition - (hasHeader() ? 1 : 0);
}
private int itemPositionInRV(int dataPosition){
return dataPosition + (hasHeader() ? 1 : 0);
}
#Override
public void notifyMyItemInserted(int itemPosition) {
notifyItemInserted(itemPositionInRV(itemPosition));
}
#Override
public void notifyMyItemRemoved(int itemPosition) {
notifyItemRemoved(itemPositionInRV(itemPosition));
}
#Override
public void notifyMyItemChanged(int itemPosition) {
notifyItemChanged(itemPositionInRV(itemPosition));
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if (viewType == TYPE_HEADER) {
return headerViewHolder;
} else if (viewType == TYPE_FOOTER) {
return footerViewHolder;
}
return onCreateDataItemViewHolder(parent, viewType);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
if (!isHeader(position) && !isFooter(position))
onBindDataItemViewHolder((VH)holder, itemPositionInData(position));
if (isFooter(position)){
footerOnVisibleItem();
}
}
public abstract void footerOnVisibleItem();
#Override
public int getItemCount() {
int itemCount = getDataItemCount();
if (hasHeader()) {
itemCount += 1;
}
if (hasFooter()) {
itemCount += 1;
}
return itemCount;
}
#Override
public int getItemViewType(int position) {
if (isHeader(position)) {
return TYPE_HEADER;
}
if (isFooter(position)) {
return TYPE_FOOTER;
}
int dataItemType = getDataItemType(itemPositionInData(position));
if (dataItemType > ITEM_MAX_TYPE) {
throw new IllegalStateException("getDataItemType() must be less than " + ITEM_MAX_TYPE + ".");
}
return dataItemType;
}
public int getDataItemCount() {
return super.getItemCount();
}
/**
* make sure your dataItemType < Integer.MAX_VALUE-1
*
* #param position item view position in rv
* #return item viewType
*/
public int getDataItemType(int position){
return 0;
}
public boolean hasHeader(){
return headerViewHolder != null;
}
public boolean hasFooter(){
return footerViewHolder != null;
}
public abstract VH onCreateDataItemViewHolder(ViewGroup parent, int viewType);
public abstract void onBindDataItemViewHolder(VH holder, int position);
}
EDIT: Same is happening when removing a view, with removeData(getAdapterPosition()
why
this is happening because the element is being added to at the beginning of the array (index 0). when this happens, the RecyclerView will react as shown in the video, because the it pretends the backing data store is a list, and all elements are moved over one index, and the element is inserted in the beginning...which happens to be at the top.
you can see that it won't do this ugly behavior if elements are added at the end of the adapter: MyAdapter.addData(MyAdapter.getItemCount() - 1, "Event ");. but of course, this is not what you want either, because it is the wrong index...and now it would seem like existing elements are actually jumping up one index on the GUI and things...but this is an interesting experiment to verify the theory.
a solution
still add elements at the end of the array using MyAdapter.addData(MyAdapter.getItemCount() - 1, "Event ");, but make the LinearLayoutManager display elements in reverse! this can be done by:
setting the attribute directly like so: linearLayoutManager.setReverseLayout(true)
or you can use this handy constructor: LinearLayoutManager(context,orientation,isReversed)
you may have to do something about the headers and footers are positioned in the adapter to make sure they stay as headers and footers in the RecyclerView.Adapter though...once the RecyclerView.LayoutManager is displaying things in reverse.

Categories