Master-Detail View using ViewPager - java

I'd like to change the master-detail implementation of my Android phone app. Currently, users can select items from a ListView, opening a new activity. To select a different activity, the user must return to the list. Instead of this pogo-sticking, I'd like the user to swipe left and right to page through the documents using a ViewPager. There can be many documents, so I'd like to load at most 3 pages at a time - the current page, the previous, and the next. Paging back and forth should then add and remove pages left and right. I've created an adapter implementing FragmentStatePagerAdapter that handles static content (e.g. TextViews) nicely. Also deleting pages seems to work OK (not included here). But when I add e.g. an EditText content is copied over from one page to the next when paging.
Below is the code for the adapter and for the activity. There are two questions I have:
What is wrong with my adapter that causes the undesired copying of EditText from one fragment to the next?
This is my first shot at this, and it's probably far from an optimal implementation. But I find this to be such a common use case that I almost feel like there would be a ready made framework for it. Could this be achieved much easier?
Pager Adapter:
public class DetailPagerAdapter extends FragmentStatePagerAdapter {
private final List<Fragment> mFragments;
private final static String TAG = "DetailPagerAdapter";
public DetailPagerAdapter(FragmentManager fm, List<Fragment> fragments) {
super(fm);
mFragments = fragments;
}
#Override
public int getCount() {
return mFragments.size();
}
#Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return mFragments.get(position);
}
public void addItem(Fragment fragment) {
mFragments.add(fragment);
notifyDataSetChanged();
}
public void removeItem(int position) {
mFragments.remove(position);
notifyDataSetChanged();
}
public void insertItem(int position, Fragment fragment) {
mFragments.add(position, fragment);
notifyDataSetChanged();
}
}
PagingActivity Base Class:
public abstract class PagingActivity
extends AppCompatActivity
implements ViewPager.OnPageChangeListener {
protected ViewPager mViewPager;
DetailPagerAdapter mViewPagerAdapter;
protected ArrayList<String> mAllItemIds;
private String mPreviousItemId;
private String mCurrentItemId;
private String mNextItemId;
private boolean mMuteOnPageSelected = false;
protected abstract Fragment getNewPageFragment(String id);
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
List<Fragment> initialFragments = new ArrayList<>();
int currentItemIndex = mAllItemIds.indexOf(mCurrentItemId);
int pageSelection = 1;
// Add previous view.
if (currentItemIndex > 0) {
mPreviousItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) - 1);
initialFragments.add(getNewPageFragment(mPreviousItemId));
} else {
pageSelection = 0;
mPreviousItemId = null;
}
// Add current view.
initialFragments.add(getNewPageFragment(mCurrentItemId));
// Add next view.
if (currentItemIndex < mAllItemIds.size() - 1) {
mNextItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) + 1);
initialFragments.add(getNewPageFragment(mNextItemId));
} else {
mNextItemId = null;
}
mViewPagerAdapter = new DetailPagerAdapter(getSupportFragmentManager(), initialFragments);
mViewPager.setAdapter(mViewPagerAdapter);
mViewPager.setCurrentItem(pageSelection);
mViewPager.addOnPageChangeListener(this);
}
#Override
public void onPageSelected(int position) {
if (!mMuteOnPageSelected) {
mCurrentItemId = ((PagingFragment) (mViewPagerAdapter.getItem(mViewPager.getCurrentItem()))).getItemId();
int currentItemIndex = mAllItemIds.indexOf(mCurrentItemId);
// Navigated to the right.
if (position == mViewPagerAdapter.getCount() - 1) {
// Add next if not already pointing at the last available item.
if (currentItemIndex < mAllItemIds.size() - 1) {
mNextItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) + 1);
mViewPagerAdapter.addItem(getNewPageFragment(mNextItemId));
} else {
mNextItemId = null;
}
// If it succeeds remove first item.
int itemCount = mViewPagerAdapter.getCount();
if ((itemCount > 3) || ((itemCount == 3) && (currentItemIndex == mAllItemIds.size() - 1))) {
mMuteOnPageSelected = true;
mViewPagerAdapter.removeItem(0);
mViewPager.setCurrentItem(1);
mMuteOnPageSelected = false;
}
}
// Navigated to the left.
else if (position == 0) {
// Add item on the left if not already pointing at the first available item.
if (currentItemIndex > 0) {
mPreviousItemId = mAllItemIds.get(mAllItemIds.indexOf(mCurrentItemId) - 1);
mViewPagerAdapter.insertItem(0, getNewPageFragment(mPreviousItemId));
} else {
mPreviousItemId = null;
}
// Check if last item needs to be removed and selection updated.
int itemCount = mViewPagerAdapter.getCount();
if (itemCount == 3) {
if (currentItemIndex == 0) {
// Points to the first of two items.
// -> do not change selection
// -> remove rightmost item.
mViewPagerAdapter.removeItem(itemCount - 1);
} else if (currentItemIndex == mAllItemIds.size() - 2) {
// Will point to the middle of 3 items.
// -> nothing to remove
// -> select middle page.
mMuteOnPageSelected = true;
mViewPager.setCurrentItem(1);
mMuteOnPageSelected = false;
}
} else if (itemCount > 3) {
// Pager contains 4 items, first item selected.
// -> remove rightmost item
// -> select middle page.
mMuteOnPageSelected = true;
mViewPagerAdapter.removeItem(itemCount - 1);
mViewPager.setCurrentItem(1);
mMuteOnPageSelected = false;
}
}
mViewPagerAdapter.notifyDataSetChanged();
}
}
}

The second question was the key: Yes, at least the current state can be achieved much easier by letting the adapter handle the full array of items. FragmentStatePagerAdapter only loads as many fragments at a time as needed, so it can handle all the manual work I had done in the activity.
Pager Adapter
public class MyPagerAdapter extends FragmentStatePagerAdapter {
private List<String> mAllItemIds;
public MyPagerAdapter(Context context, FragmentManager fm) {
super(fm);
mAllItemIds = ...
}
#Override
public int getCount() {
return mAllItemIds.size();
}
#Override
public int getItemPosition(Object object) {
return PagerAdapter.POSITION_NONE;
}
#Override
public Fragment getItem(int position) {
return MyFragment.newInstance(mAllItemIds.get(position));
}
public void removeItem(int position) {
// add needed code here to remove item also from source
// ...
mAllItemIds.remove(position);
notifyDataSetChanged();
}
}
Activity
public abstract class PagingActivity extends AppCompatActivity {
protected ViewPager mViewPager;
MyPagerAdapter mViewPagerAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mViewPager = (ViewPager)findViewById(R.id.viewPager);
mViewPagerAdapter = new MyPagerAdapter(this, getSupportFragmentManager());
mViewPager.setAdapter(mViewPagerAdapter);
}
private void deleteItem() {
mViewPagerAdapter.removeItem(mViewPager.getCurrentItem());
}
}

Related

Recyclerview row format changes after search filter

I have a recyclerview that displays a list of contacts. To differentiate between contacts that are also users of my app (let's refer to these as app-contacts) and all other contacts (non-app-contacts), i have made the typeface of all app-contacts bold (Typeface.BOLD), and non-app-contacts normal (Typeface.NORMAL). However, when the recyclerview gets filtered while searching for a contact, and app-contacts get displayed in certain rows (let's say rows 1 and 2) with a bold typeface, then those rows remain in a bold typeface. Even when i change the search, and non-app-contacts (which should be in a normal typeface) now occupy those rows (1 and 2), it’s in a bold typeface. Essentially rows 1 and 2 now remain in a bold typeface regardless of the type of contact being displayed in them.
Here is my recyclerview adapter. the onBindViewHolder is where i change the typeface. "is Suma Contact" boolean means the contact is an app contact.
public class SearchRecipientHintsAdapter extends RecyclerView.Adapter<SearchRecipientHintsAdapter.ViewHolder> {
private Context context;
private List<RecipientsContactItem> contactItems;
private final int SELECT_DROPOFF_REQUEST_CODE = 77;
public SearchRecipientHintsAdapter (Context context, List<RecipientsContactItem> contactItems) {
this.context = context;
this.contactItems = contactItems;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.recipients_contact_row,parent,false);
return new ViewHolder(view, context);
}
#Override
public void onBindViewHolder(#NonNull SearchRecipientHintsAdapter.ViewHolder holder, int position) {
RecipientsContactItem contactItem = contactItems.get(position);
holder.name.setText(contactItem.getName());
holder.phoneNumber.setText(contactItem.getPhoneNumber());
if (contactItem.getImage() != null && !contactItem.getImage().isEmpty()) {
try {
Picasso.get().load(contactItem.getImage()).into(holder.image);
} catch (Throwable ignored) { }
} else {
holder.image.setImageDrawable(context.getResources().getDrawable(R.drawable.user_default_img));
}
if (contactItem.isVerified()) {
holder.verificationIcon.setVisibility(View.VISIBLE);
} else {
holder.verificationIcon.setVisibility(View.GONE);
}
if (contactItem.isSumaContact()) {
holder.name.setTypeface(holder.name.getTypeface(), Typeface.BOLD);
switch (contactItem.getPrivacy()) {
case "Public":
holder.publicIcon.setVisibility(View.VISIBLE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.GONE);
holder.inviteButton.setVisibility(View.GONE);
break;
case "Private":
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.VISIBLE);
holder.allowedIcon.setVisibility(View.GONE);
holder.inviteButton.setVisibility(View.GONE);
break;
case "Allowed":
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.VISIBLE);
holder.inviteButton.setVisibility(View.GONE);
break;
}
} else {
holder.name.setTypeface(holder.name.getTypeface(), Typeface.NORMAL);
holder.inviteButton.setVisibility(View.VISIBLE);
holder.publicIcon.setVisibility(View.GONE);
holder.privateIcon.setVisibility(View.GONE);
holder.allowedIcon.setVisibility(View.GONE);
}
}
#Override
public int getItemCount() {
return contactItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
private TextView name;
private TextView phoneNumber;
private ImageView image;
private ImageView verificationIcon;
private Button inviteButton;
private ImageView publicIcon;
private ImageView privateIcon;
private ImageView allowedIcon;
public ViewHolder(#NonNull View itemView, Context ctx) {
super(itemView);
context = ctx;
name = itemView.findViewById(R.id.recipientsCRowNameID);
phoneNumber = itemView.findViewById(R.id.recipientsCRowPhoneID);
image = itemView.findViewById(R.id.recipientsCRowImageID);
verificationIcon = itemView.findViewById(R.id.recipientsCRowVerifiedID);
inviteButton = itemView.findViewById(R.id.recipientsCRowInviteID);
publicIcon = itemView.findViewById(R.id.recipientsCRowPublicID);
privateIcon = itemView.findViewById(R.id.recipientsCRowPrivateID);
allowedIcon = itemView.findViewById(R.id.recipientsCRowAllowedID);
itemView.setOnClickListener(v -> {
//Get position of row
int position = getAdapterPosition();
RecipientsContactItem contactItem = contactItems.get(position);
String uID = contactItem.getUID();
String name = contactItem.getName();
String phoneNumber = contactItem.getPhoneNumber();
String lat = contactItem.getLat();
String lng = contactItem.getLng();
boolean isSumaContact = contactItem.isSumaContact();
if (isSumaContact) {
if (contactItem.getPrivacy().equals("Public") || contactItem.getPrivacy().equals("Allowed")) {
Intent returnRecipientIntent = ((Activity) context).getIntent();
returnRecipientIntent.putExtra("uID", uID);
returnRecipientIntent.putExtra("name", name);
returnRecipientIntent.putExtra("phoneNumber", phoneNumber);
returnRecipientIntent.putExtra("lat", lat);
returnRecipientIntent.putExtra("lng", lng);
returnRecipientIntent.putExtra("isSumaContact", true);
((Activity) context).setResult(Activity.RESULT_OK, returnRecipientIntent);
((Activity) context).finish();
} else {
Toast.makeText(context, R.string.recipients_search_disallowed_toast, Toast.LENGTH_LONG).show();
}
} else {
Intent dropOffSearchIntent = new Intent(context, SelectDropoff.class);
((Activity) context).startActivityForResult(dropOffSearchIntent, SELECT_DROPOFF_REQUEST_CODE);
}
});
inviteButton.setOnClickListener(view -> {
Intent sendInvite = new Intent(android.content.Intent.ACTION_VIEW);
sendInvite.putExtra("address", contactItems.get(getAdapterPosition()).getPhoneNumber());
sendInvite.putExtra("sms_body", context.getResources().getString(R.string.recipients_invite_link));
sendInvite.setType("vnd.android-dir/mms-sms");
try {
context.startActivity(sendInvite);
} catch (Throwable t) {
Toast.makeText(context, "Sorry, invite not working. Please use the invite in your main menu", Toast.LENGTH_LONG).show();
}
});
}
}
#Override
public int getItemViewType(int position) {
return position;
}
public void updateWithSearchFilter (List<RecipientsContactItem> newList) {
contactItems = new LinkedList<>();
contactItems.addAll(newList);
notifyDataSetChanged();
}
}
Here is the onQueryTextChange() in setOnQueryTextListener() where i filter the search and pass the result/new list to the adapter above
public boolean onQueryTextChange(String newText) {
String userInput = newText.toLowerCase();
if (userInput.startsWith("0")) {userInput = userInput.substring(1);}
List<RecipientsContactItem> newList = new LinkedList<>();
for (RecipientsContactItem contactItem : sumaContacts) {
if (contactItem.getName().toLowerCase().contains(userInput) || contactItem.getPhoneNumber().contains(userInput)) {
newList.add(contactItem);
}
}
((SearchRecipientHintsAdapter) searchRHintsAdapter).updateWithSearchFilter(newList);
return true;
}
Shot 1:
the 2 contacts displayed are non-app contacts so their typeface is normal (not bold)
Shot 2. After filtering search to display an app-contact:
the first contact is an contact (bold typeface) and the second is a non-app contact (normal typeface - not bold)
Shot 3. After clearing search filter to display contacts in shot 1:
both contacts are non-app contacts and should be in a normal typeface (not bold). But the first contact is displayed as bold, because an app-contact (which is bold) was briefly displayed there (in shot 2) while filtering search
NB: The problem used to be caused by scrolling too. Till i #Override the getItemViewType() method of the Adapter
Initially, anytime i scroll the recyclerview, the Bold Typeface would be wrongly applied to rows/contacts that shouldn't be bold. Till i found a solution where i had to overrider the getItemViewType() method of the recyclerview adapter like this:
#Override
public int getItemViewType(int position) {
return position;
}
then it was fixed (for scrolling). till i realized that the problem persisted for filtering. So that's what i'm trying to fix now
The problem is
holder.name.setTypeface(holder.name.getTypeface(), Typeface.NORMAL);
When rebinding a viewholder with bold in place, holder.getTypeface() returns the bold typeface that was there earlier. Now, Typeface.NORMAL has the value 0. Here's the setTypeface() implementation from cs.android.com:
public void setTypeface(#Nullable Typeface tf, #Typeface.Style int style) {
if (style > 0) {
if (tf == null) {
tf = Typeface.defaultFromStyle(style);
} else {
tf = Typeface.create(tf, style);
}
setTypeface(tf);
// now compute what (if any) algorithmic styling is needed
int typefaceStyle = tf != null ? tf.getStyle() : 0;
int need = style & ~typefaceStyle;
mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
} else {
mTextPaint.setFakeBoldText(false);
mTextPaint.setTextSkewX(0);
setTypeface(tf);
}
}
Note the if (style > 0) part there. So, passing in Typeface.NORMAL will just set the typeface as-is, without doing any styling on it, so your bold style will stay bold.
To fix that, either pass in a null for typeface if that is appropriate for you, or reset the typeface to a default that fits your needs.
In addition, there's also a perf problem in your
#Override
public int getItemViewType(int position) {
return position;
}
This makes each row have its own specific view type. But you really only have one view type, so you don't need to override this method at all. Or if you do, you can return a constant value.

Calculate int values of each recyclerview item and sum all together when activity finish

I am making a multiple choice app in which the questions are displayed in recyclerview with arraylist. And also the buttons to choose your answer. And get Right answers from sqlite and do if condition with the user input answers to calculate marks in each question. like this....
My problem is i have no idea how to calculate marks with the user input (clicked buttons bta1 true, bta1 false, etc....) when clicked finish button (that is in main activity).
i want to calculate marks in each question, total 5marks for each question. If i clicked finish button and if there is 10 questions and all user input answers are right, the total marks should be 50marks.
This is my recyclerview adapter class
public class Rvadapter extends RecyclerView.Adapter <Rvadapter.MyViewHolder> {
private Context context;
Activity activity;
MyDatabaseHelper myDB;
String questiontitle, question1, question2, question3, question4, question5, answer1, answer2, answer3, answer4, answer5;
String answerdb1, answerdb2, answerdb3, answerdb4, answerdb5;
Cursor cursor;
ArrayList<ItemList> arlist;
int marks, totalmark;
private ArrayList arquestiontitle, arquestion1, arquestion2, arquestion3, arquestion4, arquestion5, aranswer1, aranswer2, aranswer3, aranswer4, aranswer5, armarks;
String idtosee, orderid, orderdate, customername, customeraddress, customerphone, delifee, advance, tax, discount, itemname, itemcount, itemprice, mark;
int a;
Rvadapter(Activity activity, Context context, ArrayList arquestiontitle, ArrayList arquestion1, ArrayList arquestion2, ArrayList arquestion3, ArrayList arquestion4, ArrayList arquestion5, ArrayList aranswer1, ArrayList aranswer2, ArrayList aranswer3, ArrayList aranswer4, ArrayList aranswer5, ArrayList armarks){
this.activity = activity;
this.context = context;
this.arquestiontitle = arquestiontitle;
this.arquestion1 = arquestion1;
this.arquestion2 = arquestion2;
this.arquestion3 = arquestion3;
this.arquestion4 = arquestion4;
this.arquestion5 = arquestion5;
this.aranswer1 = aranswer1;
this.aranswer2 = aranswer2;
this.aranswer3 = aranswer3;
this.aranswer4 = aranswer4;
this.aranswer5 = aranswer5;
this.armarks = armarks;
}
#NonNull
#Override
public MyViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.cardlayoutquestion, parent, false);
return new MyViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
holder.tvquestiontitle.setText(String.valueOf(arquestiontitle.get(position)));
holder.tvquestion1.setText(String.valueOf(arquestion1.get(position)));
holder.tvquestion2.setText(String.valueOf(arquestion2.get(position)));
holder.tvquestion3.setText(String.valueOf(arquestion3.get(position)));
holder.tvquestion4.setText(String.valueOf(arquestion4.get(position)));
holder.tvquestion5.setText(String.valueOf(arquestion5.get(position)));
holder.tvquestionid.setText(String.valueOf(position+1+". "));
marks = Integer.parseInt(String.valueOf(armarks.get(position)));
answerdb1 = String.valueOf(aranswer1.get(position));
answerdb2 = String.valueOf(aranswer2.get(position));
answerdb3 = String.valueOf(aranswer3.get(position));
answerdb4 = String.valueOf(aranswer4.get(position));
answerdb5 = String.valueOf(aranswer5.get(position));
holder.bta1true.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(answer1 == null){
answer1 = "T";
holder.bta1true.setBackgroundResource(R.drawable.selected120);
holder.bta1false.setBackgroundResource(R.drawable.unselected120);
}else if ( answer1.equals("F")){
answer1 = "T";
holder.bta1true.setBackgroundResource(R.drawable.selected120);
holder.bta1false.setBackgroundResource(R.drawable.unselected120);
}else {
answer1 = null;
holder.bta1true.setBackgroundResource(R.drawable.unselected120);
}
}
});
holder.bta1false.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(answer1 == null){
answer1 = "F";
holder.bta1false.setBackgroundResource(R.drawable.selected120);
holder.bta1true.setBackgroundResource(R.drawable.unselected120);
}else if ( answer1.equals("T")){
answer1 = "F";
holder.bta1true.setBackgroundResource(R.drawable.unselected120);
holder.bta1false.setBackgroundResource(R.drawable.selected120);
}else {
answer1 = null;
holder.bta1false.setBackgroundResource(R.drawable.unselected120);
}
}
});
}
private void checkmarks() {
if(answer1!=null){
if(answer1.equals(answerdb1)){
marks++;
}else {
marks--;
}
}
if(answer2!=null){
if(answer2.equals(answerdb2)){
marks++;
}else{
marks--;
}
}
if(answer3!=null){
if(answer3.equals(answerdb3)){
marks++;
}else{
marks--;
}
}
if(answer4!=null){
if(answer4.equals(answerdb4)){
marks++;
}else{
marks--;
}
}
if(answer5!=null){
if(answer5.equals(answerdb5)){
marks++;
}else{
marks--;
}
}
if(marks<0){
marks=0;
}
}
#Override
public int getItemCount() {
return arquestiontitle.size();
}
public static class MyViewHolder extends RecyclerView.ViewHolder {
TextView tvquestiontitle, tvquestion1, tvquestion2, tvquestion3, tvquestion4, tvquestion5, tvquestionid, tvmarks;
Button bta1true, bta1false, bta2true, bta2false, bta3true, bta3false, bta4true, bta4false, bta5true, bta5false;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
tvquestiontitle = itemView.findViewById(R.id.tvquestiontitle);
tvquestion1 = itemView.findViewById(R.id.tvquestion1);
tvquestion2 = itemView.findViewById(R.id.tvquestion2);
tvquestion3 = itemView.findViewById(R.id.tvquestion3);
tvquestion4 = itemView.findViewById(R.id.tvquestion4);
tvquestion5 = itemView.findViewById(R.id.tvquestion5);
tvquestionid = itemView.findViewById(R.id.tvquestionid);
tvmarks = itemView.findViewById(R.id.tvmarks);
bta1true = itemView.findViewById(R.id.bta1true);
bta1false = itemView.findViewById(R.id.bta1false);
bta2true = itemView.findViewById(R.id.bta2true);
bta2false = itemView.findViewById(R.id.bta2false);
bta3true = itemView.findViewById(R.id.bta3true);
bta3false = itemView.findViewById(R.id.bta3false);
bta4true = itemView.findViewById(R.id.bta4true);
bta4false = itemView.findViewById(R.id.bta4false);
bta5true = itemView.findViewById(R.id.bta5true);
bta5false = itemView.findViewById(R.id.bta5false);
}
}
}
Please help me... how to calculate marks of each question (recycler cardview items) and Sum together all of those.. I can manage to get them with only one question at a time with next button, but this time i want to show all questions within the recyclerview..
Use checkboxes instead
First of all. If the user inputs (answers in this case) are Boolean type then you should use checkboxes instead of buttons (much easier to deal with).
The layout will looks something like that:
You can stick with buttons if you want
Create a class to store answers
Then make a class to store answers in it (let call it `Answer`)
public class Answer {
private int questionNumber;//number of the question
private Boolean bta1, bta2, bta3, bta4, bta5;//answers
public Answer() {
}
public Answer(int questionNumber, Boolean bta1, Boolean bta2, Boolean bta3, Boolean bta4, Boolean bta5) {
this.questionNumber = questionNumber;
this.bta1 = bta1;
this.bta2 = bta2;
this.bta3 = bta3;
this.bta4 = bta4;
this.bta5 = bta5;
}
public int getQuestionNumber() {
return questionNumber;
}
public void setQuestionNumber(int questionNumber) {
this.questionNumber = questionNumber;
}
public Boolean getBta1() {
return bta1;
}
public void setBta1(Boolean bta1) {
this.bta1 = bta1;
}
public Boolean getBta2() {
return bta2;
}
public void setBta2(Boolean bta2) {
this.bta2 = bta2;
}
public Boolean getBta3() {
return bta3;
}
public void setBta3(Boolean bta3) {
this.bta3 = bta3;
}
public Boolean getBta4() {
return bta4;
}
public void setBta4(Boolean bta4) {
this.bta4 = bta4;
}
public Boolean getBta5() {
return bta5;
}
public void setBta5(Boolean bta5) {
this.bta5 = bta5;
}
}
Create an object for each question
and create object for each question in the MainActivity
public class MainActivity extends AppCompatActivity {
ArrayList<Answer> answers = new ArrayList<>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeAnswers();
.
.
.
}
// here we create object for each question and make it default value all false
public void initializeAnswers(){
for (int i=1;i<=20;i++){//replace 20 with how many questions you have
answers.add(new Answer(i,false,false,false,false,false));
}
}
// this is the methode that will be called when the user press a checkbox(change its value)
public void setQuestionResult(int questionNumber, int optionNumber, boolean value){
switch (optionNumber){
case 1:
answers.get(questionNumber+1).setBta1(value);
break;
case 2:
answers.get(questionNumber+1).setBta2(value);
break;
case 3:
answers.get(questionNumber+1).setBta3(value);
break;
case 4:
answers.get(questionNumber+1).setBta4(value);
break;
case 5:
answers.get(questionNumber+1).setBta5(value);
break;
}
}
}
Catch user inputs
After that we gonna set click listener to each button(or checkbox) in the recyclerview items, so when the user chose an answer we store it in the objects we made before.
We'll do that in the `ViewHolder`
class MyViewHolder extends RecyclerView.ViewHolder {
int questionNumber;
TextView tvquestiontitle, tvquestion1, tvquestion2, tvquestion3, tvquestion4, tvquestion5, tvquestionid, tvmarks;
CheckBox bta1, bta2, bta3, bta4, bta5;
public MyViewHolder(#NonNull View itemView) {
super(itemView);
tvquestiontitle = itemView.findViewById(R.id.tvquestiontitle);
tvquestion1 = itemView.findViewById(R.id.tvquestion1);
tvquestion2 = itemView.findViewById(R.id.tvquestion2);
tvquestion3 = itemView.findViewById(R.id.tvquestion3);
tvquestion4 = itemView.findViewById(R.id.tvquestion4);
tvquestion5 = itemView.findViewById(R.id.tvquestion5);
tvquestionid = itemView.findViewById(R.id.tvquestionid);
tvmarks = itemView.findViewById(R.id.tvmarks);
bta1 = itemView.findViewById(R.id.bta1);
bta2 = itemView.findViewById(R.id.bta2);
bta3 = itemView.findViewById(R.id.bta3);
bta4 = itemView.findViewById(R.id.bta4);
bta5 = itemView.findViewById(R.id.bta5);
View.OnClickListener onClickListener = new View.OnClickListener() {
#Override
public void onClick(View v) {
// Is the view now checked?
boolean checked = ((CheckBox) v).isChecked();
int optionNumber;// e.g "1 is A" "2 is B" etc..
//get option number form the view id
switch (v.getId()) {
case R.id.bta1:
optionNumber = 1;
break;
case R.id.bta2:
optionNumber = 2;
break;
case R.id.bta3:
optionNumber = 3;
break;
case R.id.bta4:
optionNumber = 4;
break;
case R.id.bta5:
optionNumber = 5;
break;
default:
optionNumber = 0;
}
// here we will pass the changes to setQuestionResult to the Activity to process it
((MainActivity)context).setQuestionResult(questionNumber,optionNumber,checked);
}
};
bta1.setOnClickListener(onClickListener);
bta2.setOnClickListener(onClickListener);
bta3.setOnClickListener(onClickListener);
bta4.setOnClickListener(onClickListener);
bta5.setOnClickListener(onClickListener);
}
}
You may realize that I added an parameter in the top (int questionNumber;), that's gonna identify what question we're dealing with.
You also need to set a value for this parameter in onBindViewHolder.
#Override
public void onBindViewHolder(#NonNull final MyViewHolder holder, final int position) {
holder.questionNumber = position+1;//add one because the position of the first item is 0
.
.
.
}
Calculate marks of each question
Finally. when you want calculate marks of a question, the result is stored in answers to get it just call
answers.get(questioNumber+1)//add one because the index always start from 0
One Last thing. You may face a problem when changing the state of an item in recyclerview, so when you scroll down the first items became invisible and the recyclerview recycle those items, so any changes happened (e. g checking a checkbox) will be lost, and the items will reset to the first state.
If that happened check this solution
Update
As I mentioned before, you can still do it with buttons if you want or any input type you find it better
The only thing you need to change is the way of catching user inputs.
E.g. if you used buttons, you'll have 3 possible inputs:
True. If btn1 is pressed and btn2 is not
False. If btn1 in bot pressed bur btn2 in pressed
Neither. If btn1 and btn2 are not pressed
You can sill use Boolean type to store the input by putting null when the value is Neither true of false(e.g. bta1=null)
The problem with this method is that it's pretty complicated and confusing (but still possible)
So. My suggestion is to use 3 statues checkbox.
This is a third-party library witch you can add to your preject.
It's pretty much a checkbox with three stats
Checked
Unchecked
Indeterminate
You can find it here GitHub
And another one here GitHub

implementing Admob Native Advanced with RecyclerView but could not get the exact View Types

I already knew how the RecyclerView with different types of view works but this time I'm trying to add the Native Advance Admob ads to my RecyclerView. I followed theseYoutube Tutorials but there was an error printed to my logcat after the app crushed.
Logcat
java.lang.ClassCastException: com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity
at mgb.com.sdalyricsplus.newAdapters.DisplayItemAdapter.onBindViewHolder(DisplayItemAdapter.java:98)
at androidx.recyclerview.widget.RecyclerView$Adapter.onBindViewHolder(RecyclerView.java:7065)
at androidx.recyclerview.widget.RecyclerView$Adapter.bindViewHolder(RecyclerView.java:7107)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryBindViewHolderByDeadline(RecyclerView.java:6012)
at androidx.recyclerview.widget.RecyclerView$Recycler.tryGetViewHolderForPositionByDeadline(RecyclerView.java:6279)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6118)
at androidx.recyclerview.widget.RecyclerView$Recycler.getViewForPosition(RecyclerView.java:6114)
at androidx.recyclerview.widget.LinearLayoutManager$LayoutState.next(LinearLayoutManager.java:2303)
at androidx.recyclerview.widget.LinearLayoutManager.layoutChunk(LinearLayoutManager.java:1627)
at androidx.recyclerview.widget.LinearLayoutManager.fill(LinearLayoutManager.java:1587)
I reviewed the tutorial many times hoping that I've missed something that causes the error but it seems that I followed the tutorial correctly.
Here are my Codes
DisplayItemActivity
public class DisplayItemActivity extends AppCompatActivity{
public static final int NUMBER_OF_AD = 5;
AdLoader adLoader;
FastScrollRecyclerView recyclerView;
Global global;
RoomViewModel model;
List<Object> recyclerViewItems = new ArrayList<>();
List<UnifiedNativeAd> nativeAds = new ArrayList<>();
DisplayItemAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_songs);
MobileAds.initialize(this,"ca-app-pub-2493911630710964~1147957926");
recyclerView = findViewById(R.id.recycler_view);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
recyclerView.setHasFixedSize(true);
global = (Global)getApplication();
model = ViewModelProviders.of(this).get(RoomViewModel.class);
adapter = new DisplayItemAdapter(getLayoutInflater());
recyclerView.setAdapter(adapter);
recyclerViewItems.addAll(model.selectAll());
loadNativeAds();
}
private void loadNativeAds() {
AdLoader.Builder builder = new AdLoader.Builder(this, getResources().getString(R.string.native_advance));
adLoader = builder.forUnifiedNativeAd(new UnifiedNativeAd.OnUnifiedNativeAdLoadedListener() {
#Override
public void onUnifiedNativeAdLoaded(UnifiedNativeAd unifiedNativeAd) {
nativeAds.add(unifiedNativeAd);
if (!adLoader.isLoading()) {
insertAdToList();
}
}
}).withAdListener(new AdListener() {
#Override
public void onAdFailedToLoad(int i) {
super.onAdFailedToLoad(i);
if (!adLoader.isLoading()) {
insertAdToList();
}
}
}).build();
adLoader.loadAds(new AdRequest.Builder().build(), NUMBER_OF_AD);
}
private void insertAdToList() {
int offset = recyclerViewItems.size() / (nativeAds.size() + 1);
int index = 0;
for (UnifiedNativeAd ad : nativeAds) {
recyclerViewItems.add(index,ad);
index = index + offset;
}
adapter.setList(recyclerViewItems);
}
}
And my Adapter
DisplayItemAdapter
public class DisplayItemAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int MENU_ITEM_VIEW_TYPE = 0;
private final int UNIFIED_NATIVE_AD_VIEW_TYPE = 1;
private List<Object> recyclerViewItems = new ArrayList<>();
private Global global;
private String searchTxt = "";
private final String newline = System.getProperty("line.separator");
private ClickSongItemListener clickSongItemListener;
private ChangeFavoriteListener changeFavoriteListener;
private LayoutInflater layoutInflater;
public DisplayItemAdapter(LayoutInflater layoutInflater) {
this.layoutInflater = layoutInflater;
}
public void setList(List<Object> list) {
this.recyclerViewItems = list;
notifyDataSetChanged();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
global = (Global) ((Activity)parent.getContext()).getApplication();
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE:
View adView = LayoutInflater.from(parent.getContext()).inflate(R.layout.native_ad_view,parent,false);
return new UnifiedNativeAdViewHolder(adView);
case MENU_ITEM_VIEW_TYPE :
default:
View songitem = LayoutInflater.from(parent.getContext()).inflate(R.layout.gospel_song_item,parent,false);
return new SongItemViewHolder(songitem);
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
int viewType = getItemViewType(position);
switch (viewType) {
case UNIFIED_NATIVE_AD_VIEW_TYPE :
UnifiedNativeAdViewHolder nativeAdViewHolder = (UnifiedNativeAdViewHolder)holder;
UnifiedNativeAd unifiedNativeAd = (UnifiedNativeAd) recyclerViewItems.get(position);
// poulateNativeAdView(unifiedNativeAd,((UnifiedNativeAdViewHolder)holder).getAdView());
FrameLayout frameLayout =
nativeAdViewHolder.view.findViewById(R.id.ad_frame_placement);
UnifiedNativeAdView adView = (UnifiedNativeAdView) layoutInflater
.inflate(R.layout.ad_unified, null);
populateUnifiedNativeAdView(unifiedNativeAd, adView);
frameLayout.removeAllViews();
frameLayout.addView(adView);
break;
case MENU_ITEM_VIEW_TYPE :
default:
SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position));
}
}
private void populateUnifiedNativeAdView(UnifiedNativeAd nativeAd, UnifiedNativeAdView adView) {
// Set the media view.
adView.setMediaView((MediaView) adView.findViewById(R.id.ad_media));
// Set other ad assets.
adView.setHeadlineView(adView.findViewById(R.id.ad_headline));
adView.setBodyView(adView.findViewById(R.id.ad_body));
adView.setCallToActionView(adView.findViewById(R.id.ad_call_to_action));
adView.setIconView(adView.findViewById(R.id.ad_app_icon));
adView.setPriceView(adView.findViewById(R.id.ad_price));
adView.setStarRatingView(adView.findViewById(R.id.ad_stars));
adView.setStoreView(adView.findViewById(R.id.ad_store));
adView.setAdvertiserView(adView.findViewById(R.id.ad_advertiser));
// The headline and mediaContent are guaranteed to be in every UnifiedNativeAd.
((TextView) adView.getHeadlineView()).setText(nativeAd.getHeadline());
adView.getMediaView().setMediaContent(nativeAd.getMediaContent());
// These assets aren't guaranteed to be in every UnifiedNativeAd, so it's important to
// check before trying to display them.
if (nativeAd.getBody() == null) {
adView.getBodyView().setVisibility(View.INVISIBLE);
} else {
adView.getBodyView().setVisibility(View.VISIBLE);
((TextView) adView.getBodyView()).setText(nativeAd.getBody());
}
if (nativeAd.getCallToAction() == null) {
adView.getCallToActionView().setVisibility(View.INVISIBLE);
} else {
adView.getCallToActionView().setVisibility(View.VISIBLE);
((Button) adView.getCallToActionView()).setText(nativeAd.getCallToAction());
}
if (nativeAd.getIcon() == null) {
adView.getIconView().setVisibility(View.GONE);
} else {
((ImageView) adView.getIconView()).setImageDrawable(
nativeAd.getIcon().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);
}
// This method tells the Google Mobile Ads SDK that you have finished populating your
// native ad view with this native ad.
adView.setNativeAd(nativeAd);
// Get the video controller for the ad. One will always be provided, even if the ad doesn't
// have a video asset.
VideoController vc = nativeAd.getVideoController();
// Updates the UI to say whether or not this ad has a video asset.
if (vc.hasVideoContent()) {
// videoStatus.setText(String.format(Locale.getDefault(),
// "Video status: Ad contains a %.2f:1 video asset.",
// vc.getAspectRatio()));
// Create a new VideoLifecycleCallbacks object and pass it to the VideoController. The
// VideoController will call methods on this object when events occur in the video
// lifecycle.
vc.setVideoLifecycleCallbacks(new VideoController.VideoLifecycleCallbacks() {
#Override
public void onVideoEnd() {
// Publishers should allow native ads to complete video playback before
// refreshing or replacing them with another ad in the same UI location.
super.onVideoEnd();
}
});
} else {
}
}
private void setSongViews(SongItemViewHolder viewHolder, SongsEntity note) {
Context context = viewHolder.itemView.getContext();
if (note.getMedia_extension().equals("audio")) {
Glide.with(context)
.load(R.drawable.music_icon)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}else if (note.getGenre().toLowerCase().contains("karaoke")) {
File file = new File(context.getExternalFilesDir(null)+"/.file"+note.getId());
if (file.exists()) {
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
.load(file)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}else {
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.drawable.karaoke_icon))
.load(note.getMedia_url())
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}
}else {
Glide.with(context)
.load(R.drawable.lyrics_icon)
.thumbnail(00.1f)
.into(viewHolder.iv_thumbnail);
}
viewHolder.title.setText(global.capitalize(note.getTitle()));
viewHolder.artist.setText(global.capitalize(note.getArtist()));
viewHolder.category.setText(note.getGenre());
viewHolder.favorite.setOnCheckedChangeListener(null);
viewHolder.favorite.setChecked(note.getFavorites());
viewHolder.views.setText(note.getFavorite_counter() <2 ? note.getFavorite_counter()+" heart" : note.getFavorite_counter()+" hearts");
String MY_ID = "JUntYdabhUh5XtMhfCIXXwNbsdW2";
if (!note.getUploader_id().equals(MY_ID))
Glide.with(context)
.setDefaultRequestOptions(new RequestOptions().placeholder(R.mipmap.sda_logo).diskCacheStrategy(DiskCacheStrategy.ALL))
.load(note.getUploader_photo_url())
.into(viewHolder.user_logo);
else
Glide.with(context)
.load(R.mipmap.sda_logo)
.into(viewHolder.user_logo);
String lyrics = note.getLyrics().toLowerCase();
String searchFilter = searchTxt.toLowerCase();
if (searchTxt.isEmpty()) {
viewHolder.phrase_end.setText(note.getLyrics());
viewHolder.phrase.setText("");
} else
if (lyrics.contains(searchFilter) && (lyrics.indexOf(searchFilter)) + searchTxt.length() <= lyrics.length()) {
viewHolder.phrase.setText(searchTxt);
String filter = note.getLyrics().substring(lyrics.indexOf(searchFilter) + searchFilter.length());
assert newline != null;
viewHolder.phrase_end.setText(filter.replaceAll(newline, " "));
}else {
viewHolder.phrase_end.setText(note.getLyrics());
viewHolder.phrase.setText("");
}
viewHolder.favorite.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
#Override
public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) {
if (changeFavoriteListener != null) {
changeFavoriteListener.onChange(buttonView.getContext(),note,isChecked);
}
}
});
}
private void poulateNativeAdView(UnifiedNativeAd unifiedNativeAd, UnifiedNativeAdView adView) {
((TextView)adView.getHeadlineView()).setText(unifiedNativeAd.getHeadline());
((TextView)adView.getBodyView()).setText(unifiedNativeAd.getBody());
((TextView)adView.getCallToActionView()).setText(unifiedNativeAd.getCallToAction());
NativeAd.Image icon = unifiedNativeAd.getIcon();
if (icon == null) {
adView.getIconView().setVisibility(View.INVISIBLE);
}else {
((ImageView)adView.getIconView()).setImageDrawable(icon.getDrawable());
adView.getIconView().setVisibility(View.VISIBLE);
}
if (unifiedNativeAd.getPrice() == null) {
adView.getPriceView().setVisibility(View.INVISIBLE);
}else {
adView.getPriceView().setVisibility(View.VISIBLE);
((TextView)adView.getPriceView()).setText(unifiedNativeAd.getPrice());
}
if (unifiedNativeAd.getStore() == null) {
adView.getStoreView().setVisibility(View.INVISIBLE);
}else {
adView.getStoreView().setVisibility(View.VISIBLE);
((TextView)adView.getStoreView()).setText(unifiedNativeAd.getStore());
}
if (unifiedNativeAd.getStarRating() == null) {
adView.getStarRatingView().setVisibility(View.INVISIBLE);
}else {
adView.getStarRatingView().setVisibility(View.VISIBLE);
((RatingBar)adView.getStarRatingView()).setRating(unifiedNativeAd.getStarRating().floatValue());
}
if (unifiedNativeAd.getAdvertiser() == null) {
adView.getAdvertiserView().setVisibility(View.INVISIBLE);
}else {
adView.getAdvertiserView().setVisibility(View.VISIBLE);
((TextView)adView.getAdvertiserView()).setText(unifiedNativeAd.getAdvertiser());
}
adView.setNativeAd(unifiedNativeAd);
}
#Override
public int getItemCount() {
return recyclerViewItems.size();
}
public class SongItemViewHolder extends RecyclerView.ViewHolder {
TextView title, phrase,phrase_end;
TextView artist;
TextView category;
ToggleButton favorite;
LinearLayout layoutWrapper;
LinearLayout phrase_layout;
TextView views;
CircleImageView user_logo;
ImageView iv_thumbnail;
public SongItemViewHolder(#NonNull View itemView) {
super(itemView);
title = itemView.findViewById(R.id.text_view_title);
iv_thumbnail = itemView.findViewById(R.id.iv_thumbnail);
artist = itemView.findViewById(R.id.text_view_artist);
phrase = itemView.findViewById(R.id.phrase);
phrase_end = itemView.findViewById(R.id.phrase_end);
category = itemView.findViewById(R.id.text_view_category);
favorite = itemView.findViewById(R.id.toggleButton_favorite);
layoutWrapper = itemView.findViewById(R.id.layout_wrapper);
phrase_layout = itemView.findViewById(R.id.phrase_layout);
views = itemView.findViewById(R.id.tv_view_status);
user_logo = itemView.findViewById(R.id.user_logo);
}
}
private class UnifiedNativeAdViewHolder extends RecyclerView.ViewHolder {
private View view;
public UnifiedNativeAdViewHolder(View view) {
super(view);
this.view = view;
}
}
}
the logcat says that com.google.android.gms.internal.ads.zzaeh cannot be cast to mgb.com.sdalyricsplus.Database.Entities.SongsEntity and the error was pointed to the viewbindholder
case MENU_ITEM_VIEW_TYPE :
default:
SongItemViewHolder songItemViewHolder = (SongItemViewHolder)holder;
setSongViews(songItemViewHolder, (SongsEntity)recyclerViewItems.get(position)); // this line
my suspect is the viewtype. Maybe the view type was not correctly assigned.
I tried this code also but the error still there.
#Override
public int getItemViewType(int position) {
if (position % DisplayItemActivity.NUMBER_OF_AD == 0) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}else {
return MENU_ITEM_VIEW_TYPE;
}
}
can someone help me to find the cause of the error?
You need to merge your SongItem and UnifiedNativeAd to become 1 single data source.
Make a new object class name SongAdsData to merge your Song object and Ads from Admob into 1 single object like below:
public class SongAdsData {
public int getType() {
return type;
}
public UnifiedNativeAd getAds() {
return ads;
}
public Post getSong() {
return Song;
}
public int type; // 1 is ads and 2 is songs
public UnifiedNativeAd ads;
public Song song; // here is ur Song object as usual
}
In your adapter, modify it as below:
public class SongAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int UNIFIED_ADS_VIEW = 1;
private static final int SONG_ITEM_VIEW = 2;
private List<SongAdsData> songAdsDataList;
//here your onBindViewHolder,here your refer to item of `songAdsDataList`
// here your onCreateViewHolder
public void setSongAdsDataList(List<SongAdsData> songAdsDataForRecyclerView) {
this.songAdsDataList = songAdsDataForRecyclerView;
notifyDataSetChanged();
}
// Here is the function that insert the ads to RecyclerView
public void insertAdToRecyclerView(List<UnifiedNativeAds> nativeAds) {
int offset = songAdsDataList.size() / (nativeAds.size() + 1);
int index = 0;
for (UnifiedNativeAd ad : nativeAds) {
SongAdsData adsData = new SongAdsData();
adsData.song = null;
adsData.ads = ad;
adsData.type = 1; //1 for ads,2 for song
songAdsDataList.add(index,adsData);
index = index + offset;
}
notifyDataSetChanged();
}
#Override
public int getItemViewType (int position) {
if(songAdsDataList.get(position).getType() == 1){ // So here you compare the type of the object it the position
return UNIFIED_ADS;
}else{
return SONG_ITEM_VIEW;
}
}
#Override
public int getItemCount() {
if(songAdsDataList != null){
return songAdsDataList.size();
}else{
return 0;
}
}
}
So finally in your DisplayItemActivity
I not sure how you get your SongItem(your data from server or somewhere else),but the idea is transform your SongItem into SongAdsData that we created in the step one,
Example like this:
private void displaySongFromYourServer(List<Songs> songs) {
List<SongAdsData> songAdsDataList = new ArrayList<>();
for(Songs song : songs){
SongAdsData data = new SongAdsData();
data.ads = null;
data.song = song;
data.type = 2;
songAdsDataList.add(data);
}
songAdapter.setSongAdsDataList(songAdsDataList);
}
So by now,your Song item will become SongAdsData. At the same time, your UnifiedNativeAd object also need to transform become SongAdsData, therefore we need to
move all thing inside insertAdToList into SongAdapter(See the insertAdToRecyclerView in Song adapter above),so the it can refer to the same List of the recyclerView.
Therefore in your insertAdToList of DisplayItemActivity should become like this:
private void insertAdToList() {
songAdapter.insertAdsToRecyclerView(nativeAds); //called to the function inside songAdapter.
}
Hope you get the idea.
You need to override getItemViewType(int position) to check the instance of your Object in order to return the right view type:
#Override
public int getItemViewType(int position) {
if (this.recyclerViewItems.get(position) instanceof UnifiedNativeAd) {
return UNIFIED_NATIVE_AD_VIEW_TYPE;
}else {
return MENU_ITEM_VIEW_TYPE;
}
}

List<ParseUser> .contains ParseUser always FALSE

I have an if statement written below:
//Set Friend Action OnClickListener & Image
if (ParseUser.getCurrentUser().getList("friendsArray").contains(searchResultsList.get(position))) {
Glide.with(holder.imgFriendAction.getContext()).load(R.drawable.ic_phone_black_24dp).into(holder.imgFriendAction);
ImageViewCompat.setImageTintList(holder.imgFriendAction, ColorStateList.valueOf(searchContext.getColor(R.color.green)));
}
else if (ParseUser.getCurrentUser().getList("pendingFriendsArray").contains(searchResultsList.get(position))) {
Glide.with(holder.imgFriendAction.getContext()).load(R.drawable.ic_check_black_24dp).into(holder.imgFriendAction);
ImageViewCompat.setImageTintList(holder.imgFriendAction, ColorStateList.valueOf(searchContext.getColor(R.color.gray_dark)));
}
else {
Glide.with(holder.imgFriendAction.getContext()).load(R.drawable.ic_person_add_black_24dp).into(holder.imgFriendAction);
ImageViewCompat.setImageTintList(holder.imgFriendAction, ColorStateList.valueOf(searchContext.getColor(R.color.colorPrimary)));
}
The problem is that every single time I run that statement it always returns FALSE for both if statements even though I know for a fact that 'friendsArray' & 'pendingFriendsArray' return TRUE in many circumstances.
Both arrays contain pointers to the _User table.
searchResultsList is declared as follows:
private List<ParseUser> searchResultsList;
I've logged all three items (friendsArray, pendingFriendsArray & searchResultsList.get(position)) to the console and they show the following:
D/friendsArray: [com.parse.ParseUser#ae66779, com.parse.ParseUser#8371cbe, com.parse.ParseUser#32d511f, com.parse.ParseUser#5fd2c6c, com.parse.ParseUser#7dd0235, com.parse.ParseUser#9c446ca, com.parse.ParseUser#5fe03b]
D/pendingFriendsArray: [com.parse.ParseUser#7c6a358, com.parse.ParseUser#3688cb1, com.parse.ParseUser#480596]
D/searchResultsList.get(position) =: com.parse.ParseUser#5fe03b
The entire class is below:
public class SearchUserAdapter extends RecyclerView.Adapter<SearchUserAdapter.ViewHolder> {
private Context searchContext;
private List<ParseUser> searchResultsList;
OnItemClickListener onItemClickListener;
public SearchUserAdapter(Context context, List<ParseUser> dataSet) {
searchContext = context;
searchResultsList = dataSet;
}
public interface OnItemClickListener {
public void onItemClick(View view, ParseUser searchUserObject, int position);
}
public void setOnItemClickListener(final OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(searchContext).inflate(R.layout.ly_search_user, parent,false);
return new ViewHolder(v);
}
#Override
public void onBindViewHolder(final ViewHolder holder, final int position) {
//Set User Name
holder.txtUserName.setText(searchResultsList.get(position).getString("fullName"));
//Set User Location
holder.txtUserLocation.setText(GlobalFunctions.getParseUserLocationAsString(holder.txtUserName.getContext(), searchResultsList.get(position)));
//Set User Profile Image
if (searchResultsList.get(position).getParseFile("profilePicture") != null) {
Glide.with(holder.imgUserProfilePicture.getContext()).applyDefaultRequestOptions(RequestOptions.circleCropTransform()).load(searchResultsList.get(position).getParseFile("profilePicture").getUrl()).into(holder.imgUserProfilePicture);
}
else {
Glide.with(holder.imgUserProfilePicture.getContext()).applyDefaultRequestOptions(RequestOptions.circleCropTransform()).load(R.drawable.ic_profile_place_holder).into(holder.imgUserProfilePicture);
}
//Set Row OnClickListener
holder.rlUserItem.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (searchResultsList.get(position).getObjectId().equalsIgnoreCase(ParseUser.getCurrentUser().getObjectId())) {
Intent openProfile;
openProfile = new Intent(holder.rlUserItem.getContext(), TimelineActivity.class);
holder.rlUserItem.getContext().startActivity(openProfile);
}
else {
Intent openOtherProfile = new Intent(holder.rlUserItem.getContext(), OtherUserTimelineActivity.class);
openOtherProfile.putExtra("otherUserProfileId", searchResultsList.get(position).getObjectId());
holder.rlUserItem.getContext().startActivity(openOtherProfile);
}
}
});
//Set Friend Action OnClickListener & Image
if (ParseUser.getCurrentUser().getList("friendsArray").contains(searchResultsList.get(position))) {
Glide.with(holder.imgFriendAction.getContext()).load(R.drawable.ic_phone_black_24dp).into(holder.imgFriendAction);
ImageViewCompat.setImageTintList(holder.imgFriendAction, ColorStateList.valueOf(searchContext.getColor(R.color.green)));
}
else if (ParseUser.getCurrentUser().getList("pendingFriendsArray").contains(searchResultsList.get(position))) {
Glide.with(holder.imgFriendAction.getContext()).load(R.drawable.ic_check_black_24dp).into(holder.imgFriendAction);
ImageViewCompat.setImageTintList(holder.imgFriendAction, ColorStateList.valueOf(searchContext.getColor(R.color.gray_dark)));
}
else {
Glide.with(holder.imgFriendAction.getContext()).load(R.drawable.ic_person_add_black_24dp).into(holder.imgFriendAction);
ImageViewCompat.setImageTintList(holder.imgFriendAction, ColorStateList.valueOf(searchContext.getColor(R.color.colorPrimary)));
}
holder.imgFriendAction.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
friendActionListenerAction(holder, searchResultsList.get(position));
}
});
}
private void friendActionListenerAction(ViewHolder holder, ParseUser parseUser) {
if (ParseUser.getCurrentUser().getList("friendsArray").contains(parseUser)) {
FLKCallUtils.showCallDialog(holder.imgFriendAction.getContext());
}
else if (ParseUser.getCurrentUser().getList("pendingFriendsArray").contains(parseUser)) {
//Do nothing
}
else {
//Add Friend
FLKFriendUtils.sendFriendRequestFromUserToUser(ParseUser.getCurrentUser(), parseUser);
//Update Image
Glide.with(holder.imgFriendAction.getContext()).load(R.drawable.ic_check_black_24dp).into(holder.imgFriendAction);
ImageViewCompat.setImageTintList(holder.imgFriendAction, ColorStateList.valueOf(searchContext.getColor(R.color.gray_dark)));
}
}
#Override
public int getItemCount() {
return searchResultsList.size();
}
class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public MediumRobotoTextView txtUserName;
public RegularRobotoTextView txtUserLocation;
public RelativeLayout rlUserItem;
ImageView imgUserProfilePicture;
ImageView imgFriendAction;
public ViewHolder(View itemView) {
super(itemView);
rlUserItem = (RelativeLayout) itemView.findViewById(R.id.rl_user_container);
rlUserItem.setOnClickListener(this);
txtUserName = (MediumRobotoTextView) itemView.findViewById(R.id.txt_user_name);
txtUserLocation = (RegularRobotoTextView) itemView.findViewById(R.id.txt_user_location);
imgUserProfilePicture = (ImageView) itemView.findViewById(R.id.img_profile_picture);
imgUserProfilePicture.setOnClickListener(this);
imgFriendAction = (ImageView) itemView.findViewById(R.id.img_friend_action);
imgFriendAction.setOnClickListener(this);
}
#Override
public void onClick(View v) {
//TODO - do something here if you wish
}
}
Upon further investigation I found that the parse-android SDK does not fetch pointers the same every single time. For example when I fetch 'friendsArray', let's say right now, it will return
[com.parse.ParseUser#ae66779, com.parse.ParseUser#8371cbe, com.parse.ParseUser#32d511f, com.parse.ParseUser#5fd2c6c, com.parse.ParseUser#7dd0235, com.parse.ParseUser#9c446ca, com.parse.ParseUser#5fe03b]
However if I then fetch it, let's say in 5 minutes, it will return
[com.parse.ParseUser#ec99877, com.parse.ParseUser#674bcg, com.parse.ParseUser#749hhc, com.parse.ParseUser#6fh3d6dg, com.parse.ParseUser#jdj8dk, com.parse.ParseUser#4c966ca, com.parse.ParseUser#3f0eeb]
Additionally, I noted that even the pointer to searchResultsList.get(position) changes it's reference every time I loaded it.
The way I got around this was to create a function (seen below) that returns an array of the actual objectId's of the pointers inside the 'friendsArray'. This way I can guarantee that it will always be returning the same items and can therefore create an accurate 'contains' comparison.
public static List<String> friendsArrayObjectIdsArray() {
//Create Array of Friends
List<ParseUser> friendsArray = ParseUser.getCurrentUser().getList("friendsArray");
//Create Temp Array of Object Id's
List<String> tempObjectIdsArray = new ArrayList<>();
//Iterate List
for (ParseUser friendUser : friendsArray) {
tempObjectIdsArray.add(friendUser.getObjectId());
}
return tempObjectIdsArray;
}
I then run the following comparison to get the result I am looking for
if (FLKUserUtils.friendsArrayObjectIdsArray().contains(searchResultsList.get(position).getObjectId())) {
//Do something
}

How to remove the fragment from its calling fragment?

How to remove the fragment from his calling fragment ?
i have search this thing but i didn't get solution according to my requirement. In my application, there is one FragmentActivity which has the viewPager.
This viewPager contains 3 Fragments. For fragment I am using FragmentStatePagerAdapter.
Suppose there is 3 fragment: A, B, C; and D fragment C also contain a child fragment E.
I have call fragment E in the onCreateView() method and in the onDestroy() method of fragment C, I have remove the child fragment E.
So what is happening when I slid viewPager from C to B, and B to A - when I come back from A to B, now current displaying fragment is B. Now if I slid fragment C should be display, but in place of fragment C the child fragment of C, fragment E is displaying after then fragment E then fragment C is displaying now in this condition i have not seen Fragment E over the fragment C, the child fragment E is not interacting with viewpager but why it is added in viewPager,
i have try to destroy the child fragment E in onPause() and in onDestroy() method of C, but nothing is happening. Please any one help me.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
MYDataManager.sharedHandler().changeLanguageConfiguration();
overridePendingTransition(R.anim.activity_open_translate, R.anim.activity_close_scale);
setContentView(R.layout.activity_myscanner);
appFlow = new ArrayList(Arrays.asList(MYConstant.kProfileMenuSettings, MYConstant.kScanner, MYConstant.kRestaurantListing));
viewpager = (ViewPager) findViewById(R.id.view_pager);
viewpager.addOnPageChangeListener(this);
viewpager.setOffscreenPageLimit(0);
reloadViewPager(1);
}
public void reloadViewPager(int currentItem) {
adapter = new FragmentStatePagerAdapter(getSupportFragmentManager()) {
#Override
public Fragment getItem(int position) {
return getFragmentWithPosition(position);
}
#Override
public int getCount() {
return appFlow.size();
}
#Override
public int getItemPosition(Object object) {
return POSITION_NONE;
}
};
viewpager.setAdapter(adapter);
viewpager.setCurrentItem(currentItem);
}
private MYBaseFragment getFragmentWithPosition(int position) {
String screen = appFlow.get(position);
MYBaseFragment fragment = null;
if(screen.equals(MYConstant.kProfileMenuSettings)) {
myProfileSettingFragment = new MYProfileSettingFragment();
fragment = myProfileSettingFragment;
} else if(screen.equals(MYConstant.kScanner)) {
fragment = new MYScannerParentFragment();
} else if(screen.equals(MYConstant.kRestaurantListing)) {
myRestaurantListFragment = new MYRestaurantListFragment();
fragment = myRestaurantListFragment;
} else if(screen.equals(MYConstant.kRestaurantDetails)) {
myResraurantDetailsFragment = new MYResraurantDetailsFragment();
fragment = myResraurantDetailsFragment;
} else if(screen.equals(MYConstant.kCurrentRestaurantDetails)) {
MYQRCode qrData = MYDataManager.sharedHandler().getQRData();
if(MYUitilities.checkQRValidation(qrData)) {
fragment = new MYCurResDetails();
}
} else if(screen.equals(MYConstant.kSettings)) {
fragment = new MYSettingFragment();
} else if(screen.equals(MYConstant.kViewMenu)) {
fragment = new MYResCatListFragment();
} else if(screen.equals(MYConstant.kMenuCategoryListing)) {
fragment = new MYResCatListFragment();
} else if(screen.equals(MYConstant.kViewMenuViewPage)) {
// myResMenuViewPagerFragment = new MYResMenuViewPagerFragment();
// fragment = myResMenuViewPagerFragment;
fragment = new MYResMenuViewPagerFragment();
} else if(screen.equals(MYConstant.kReviewOrder)) {
fragment = new MYReviewOrderFragment();
} else if(screen.equals(MYConstant.kYourOrder)) {
fragment = new MYYourOrderFragment();
} else if(screen.equals(MYConstant.kSettings)) {
fragment = new MYSettingFragment();
} else if(screen.equals(MYConstant.kOrderHistory)) {
fragment = new MYOrderHistoryFragment();
} else if(screen.equals(MYConstant.kCheckout)) {
fragment = new MYCheckoutFragment();
} else if(screen.equals(MYConstant.kCallWaiter)) {
fragment = new MYCallWaiterFragment();
} else if(screen.equals(MYConstant.kYourProfile)) {
myYourProfileFragment = new MYYourProfileFragment();
fragment = myYourProfileFragment;
}
fragment.setFragmentInteractionListener(MYScannerActivity.this);
return fragment;
}
#Override
public void onPageSelected(final int position) {
if(currentPosition >= 0) {
final String previousScreen = appFlow.get(currentPosition);
final String currentScreen = appFlow.get(position);
System.out.println("previousScreen-" + previousScreen);
System.out.println("currentScreen-" + currentScreen);
if(previousScreen.equals(MYConstant.kYourProfile) && currentScreen.equals(MYConstant.kProfileMenuSettings)) {
appFlow.remove(MYConstant.kYourProfile);
adapter.notifyDataSetChanged();
viewpager.setCurrentItem(0);
}
if(previousScreen.equals(MYConstant.kRestaurantDetails) && currentScreen.equals(MYConstant.kRestaurantListing)) {
appFlow.remove(MYConstant.kRestaurantDetails);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kViewMenu) && currentScreen.equals(MYConstant.kRestaurantDetails)) {
appFlow.remove(MYConstant.kViewMenu);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kViewMenuViewPage) && currentScreen.equals(MYConstant.kViewMenu)) {
appFlow.remove(MYConstant.kViewMenuViewPage);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kViewMenuViewPage) && currentScreen.equals(MYConstant.kMenuCategoryListing)) {
appFlow.remove(MYConstant.kViewMenuViewPage);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kYourOrder) && currentScreen.equals(MYConstant.kViewMenuViewPage)) {
appFlow.remove(MYConstant.kYourOrder);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kReviewOrder) && currentScreen.equals(MYConstant.kViewMenuViewPage)) {
appFlow.remove(MYConstant.kReviewOrder);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kYourOrder) && currentScreen.equals(MYConstant.kReviewOrder)) {
appFlow.remove(MYConstant.kYourOrder);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kYourOrder) && currentScreen.equals(MYConstant.kViewMenuViewPage)) {
appFlow.remove(MYConstant.kReviewOrder);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kSettings) && currentScreen.equals(MYConstant.kProfileMenuSettings)) {
appFlow.remove(MYConstant.kSettings);
adapter.notifyDataSetChanged();
viewpager.setCurrentItem(0);
} else if(previousScreen.equals(MYConstant.kOrderHistory) && currentScreen.equals(MYConstant.kProfileMenuSettings)) {
appFlow.remove(MYConstant.kOrderHistory);
adapter.notifyDataSetChanged();
viewpager.setCurrentItem(0);
} else if(previousScreen.equals(MYConstant.kCheckout) && currentScreen.equals(MYConstant.kViewMenuViewPage)) {
appFlow.remove(MYConstant.kCheckout);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kCheckout) && currentScreen.equals(MYConstant.kReviewOrder)) {
appFlow.remove(MYConstant.kCheckout);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kCheckout) && currentScreen.equals(MYConstant.kYourOrder)) {
appFlow.remove(MYConstant.kCheckout);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kCallWaiter) && currentScreen.equals(MYConstant.kCheckout)) {
appFlow.remove(MYConstant.kCallWaiter);
adapter.notifyDataSetChanged();
} else if(previousScreen.equals(MYConstant.kRestaurantListing) && currentScreen.equals(MYConstant.kScanner)) {
if(MYDataManager.sharedHandler().isQRCodeScanned()) {
appFlow.remove(MYConstant.kRestaurantListing);
if(!appFlow.contains(MYConstant.kCurrentRestaurantDetails)) {
appFlow.add(MYConstant.kCurrentRestaurantDetails);
}
adapter.notifyDataSetChanged();
}
} else if(previousScreen.equals(MYConstant.kCurrentRestaurantDetails) && currentScreen.equals(MYConstant.kScanner)) {
} else if(previousScreen.equals(MYConstant.kMenuCategoryListing) && currentScreen.equals(MYConstant.kCurrentRestaurantDetails)) {
appFlow.remove(MYConstant.kMenuCategoryListing);
adapter.notifyDataSetChanged();
}
}
Fragment fragment = ((FragmentStatePagerAdapter) viewpager.getAdapter()).getItem(position);
if(fragment instanceof MYProfileSettingFragment) {
}
currentPosition = position;
}
You Please try these.
FragmentManager fragmentManager = getActivity().getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
Fragment currentFragment = fragmentManager.findFragmentById(R.id.frame_container);
fragmentTransaction.remove(currentFragment);//remove current fragment
So what is happening when I slid viewPager from C to B, and B to A - when I come back from A to B, now current displaying fragment is B.
I had faced something before like you have now. I was registering and unregistereing a listener on fragments life cycle events. But in viewpager, even i switched to another fragment, the previously fragment never be destroyed, you can check
Before dive into your case, i wanna mention about 'off page limit'. So you cant set to zero. Check this link. So when you set 0, it defaults to 1
viewpager.setOffscreenPageLimit(0);
If im not missing something, in this case, when you switch from C to B
If you had visited D fragment before C, D will be destroyed, B will be created, C still will be living.
Assuming you coming from first case (C to B, and then B to A)
C will be destroyed, B lives, A will be created
Bottom line, with ViewPager you have min two fragments. One is you are currently interacting and second (screen off)

Categories