I've written a game where the user inputs the number of player and every player gets an own tab with an empty table.
Therefore I used a PagerAdapterClass (extends FragmentStatePagerAdapter) and a viewpager.
So every player has the same fragmentView.
Now the user can put variables into the table, bu everytime I switch between the tabs, the input gets lost.
Well, i 'fixed' that problem by adding this to my pageradapter:
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
But it's more stopping the viewpager from destroying than actually saving the data.
My main goal is to really save that stuff in that table.
I already tried https://stackoverflow.com/a/17135346/11956040 but i cannot get mContent because i cannot get the reference of the fragment, because all fragments are not created on their own but all at the same time (or something like that).
I also don't know how to set a Tag.
This way: https://stackoverflow.com/a/18993042/11956040
doesn't work for me.
MainActivity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
Toolbar toolbar = findViewById(R.id.toolbar2);
setSupportActionBar(toolbar);
...
//numPlayer = num of tabs
SectionsPagerAdapter adapter = new SectionsPagerAdapter(numPlayer, getSupportFragmentManager());
ViewPager viewPager = findViewById(R.id.view_pager);
viewPager.setAdapter(adapter);
TabLayout tabs = findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
if(numPlayer >= 5) {
tabs.setTabMode(TabLayout.MODE_SCROLLABLE);
}
}
PagerAdapter:
public class SectionsPagerAdapter extends FragmentStatePagerAdapter {
private int tabNum;
public SectionsPagerAdapter(int tabNum, FragmentManager fm) {
super(fm);
this.tabNum = tabNum;
}
#Override
public PlaceholderFragment getItem(int position) {
return PlaceholderFragment.newInstance(position);
}
#Nullable
#Override
public CharSequence getPageTitle(int position) {
int playerNum = position + 1;
return "Spieler " + playerNum;
}
#Override
public int getCount() {
// Show 2 total pages.
return tabNum;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
}
}
Fragment:
public static PlaceholderFragment newInstance(int index) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle bundle = new Bundle();
bundle.putInt("player", index);
fragment.setArguments(bundle);
return fragment;
}
There must be a solution but I cannot find it or cannot implement it.
Pls help.
Solved my problem this way:
define 2 dimensional ArrayList for rows and columns and counter for columns:
private ArrayList<ArrayList<Integer>> columnArray;
private int column;
onCreateView (for fragments) set column = 0 and add one entry with an empty list to columnArray
and set the first rowList on column index of columnArray:
pointArray.add(column, new ArrayList<Integer>());
final ArrayList<Integer> rowList = pointArray.get(column);
fill the empty rowListwith 0 (maybe it also works in an other way, but I made it this way to have on empty EditTexts a 0 and can easily replace them)
define View.OnFocusChangeListener for all EditTexts like this:
/*I dont know if I could set column final in general,
but you need to set a final int because you call this value in an inner class*/
final int pos = column
for (int i = 0; i <= getEditTexts(pos).size() - 1; i++) {
EditText editTexts = getEditTexts(pos).get(i);
final String editTextsTag = editTexts.getTag().toString();
View.OnFocusChangeListener listener = new View.OnFocusChangeListener() {
#Override
public void onFocusChange(View view, final boolean b) {
if (view.getTag().toString().equals(editTextsTag) && !b) {
//fills rowList
addEntries(pos, rowList);
//adds rowList to columnArray
columnArray.set(pos, rowList);
//save the columnsArray or use it
saveData(columnArray);
}
}
};
editTexts.setOnFocusChangeListener(listener);
define method which collects data from each cell, depending on column position (pos), add it to rowList
for example:
private void addEntries(int pos, ArrayList<Integer> rowList) {
for(int i = 0; i <= 16; i++) {
//this requires EditText_label, i made them dynamically
String edit_label = "edit_" + pos + i;
EditText editText = table.findViewWithTag(edit_label);
String mEditTextString = editText.getText().toString();
try {
int thisValue = Integer.parseInt(mEditString);
rowList.set(j, thisValue);
} catch (NumberFormatException e) {
//maybe you do not need this, but I need it for something else
int thisValue = 0;
rowList.set(j, thisValue);
}
}
}
define a method for saving the columnArray. I used an interface to give it to parent Activity: Here you can find how I made it
Otherwise you can convert the columnArray to a String and save it in a database.
NOTE
I made it with column value set beacuse I increase the value for every column I add during runtime using a method. If you just have one column, you dont need to set it. Just use 0 instead of pos, column
Related
Main Goal:-
I have a list of sports news. Each item contains a sport name and some info. Clicking on it will show the latest news regarding that particular sport. The user has the option to swipe to dismiss a news, if they don't want it in the list or they can also drag and drop it, for example, if they want to see some news on top of others.
Each item in the list is represented programmatically as a Sport.java object.
I want to retain the state of the list upon device orientation changes.
What I've tried:-
For the list, I have an arraylist of sport objects (ArrayList). I learned that to save a list of custom objects, they objects themselves need to be Parcelable. For this, I implemented the Parcelable.java interface like this:
package com.example.android.materialme;
import android.os.Parcel;
import android.os.Parcelable;
import androidx.annotation.NonNull;
/**
* Data model for each row of the RecyclerView.
*/
class Sport implements Parcelable {
//Member variables representing the title and information about the sport
private String title;
private String info;
private String detail;
private final int imageResource;
/**
* Constructor for the Sport data model
* #param title The name if the sport.
* #param info Information about the sport.
*/
Sport(String title, String info, String detail, int imageResource) {
this.title = title;
this.info = info;
this.detail = detail;
this.imageResource = imageResource;
}
protected Sport(#NonNull Parcel in) {
title = in.readString();
info = in.readString();
detail = in.readString();
imageResource = in.readInt();
}
public static final Creator<Sport> CREATOR = new Creator<Sport>() {
#Override
public Sport createFromParcel(Parcel in) {
return new Sport(in);
}
#Override
public Sport[] newArray(int size) {
return new Sport[size];
}
};
String getTitle() {
return title;
}
String getInfo() {
return info;
}
int getImageResource(){
return imageResource;
}
String getDetail(){
return detail;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel parcel, int i) {
parcel.writeString(title);
parcel.writeString(info);
parcel.writeString(detail);
parcel.writeInt(imageResource);
}
}
and then I used
outState.putParcelableArrayList(KEY, sportsList);
but, this doesn't work. The screen is just blank upon rotating device.
I tried debugging the app and found that the arraylist was being passed correctly with the data intact, it's just that the app is not being able to display it for some reason.
Also, the implementation of the fab button is so that it resets the whole list to its initial condition upon click. The fab works normally but if the orientation is changed once, it stops working (app doesn't crash). Changing the orientation back also doesn't fix the fab. So, to get the list again for any other test, I have to rerun the entire app.
Complete Code:-
MainActivity.java
package com.example.android.materialme;
import android.content.res.TypedArray;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import java.util.ArrayList;
import java.util.Collections;
public class MainActivity extends AppCompatActivity {
//Member variables
private RecyclerView mRecyclerView;
private ArrayList<Sport> mSportsData;
private SportsAdapter mAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FloatingActionButton fab = findViewById(R.id.fab);
fab.setOnClickListener(view -> resetSports());
//Initialize the RecyclerView
mRecyclerView = (RecyclerView)findViewById(R.id.recyclerView);
//Set the Layout Manager
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
//Initialize the ArrayLIst that will contain the data
mSportsData = new ArrayList<>();
//Initialize the adapter and set it ot the RecyclerView
mAdapter = new SportsAdapter(this, mSportsData);
mRecyclerView.setAdapter(mAdapter);
initializeData(savedInstanceState);
ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN,
ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT) {
#Override
public boolean onMove(#NonNull RecyclerView recyclerView, #NonNull RecyclerView.ViewHolder viewHolder, #NonNull RecyclerView.ViewHolder target) {
int from = viewHolder.getAdapterPosition();
int to = target.getAdapterPosition();
Collections.swap(mSportsData, from, to);
mAdapter.notifyItemMoved(from, to);
return true;
}
#Override
public void onSwiped(#NonNull RecyclerView.ViewHolder viewHolder, int direction) {
mSportsData.remove(viewHolder.getAdapterPosition());
mAdapter.notifyItemRemoved(viewHolder.getAdapterPosition());
}
});
helper.attachToRecyclerView(mRecyclerView);
}
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
outState.putParcelableArrayList("state", mSportsData);
}
/**
* Method for initializing the sports data from resources.
*/
private void initializeData(Bundle savedInstanceState) {
if(savedInstanceState!=null){
mSportsData.clear();
mSportsData = savedInstanceState.getParcelableArrayList("state");
} else {
//Get the resources from the XML file
String[] sportsList = getResources().getStringArray(R.array.sports_titles);
String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
String[] sportsDetail = getResources().getStringArray(R.array.sports_detail);
TypedArray sportsImageResource = getResources().obtainTypedArray(R.array.sports_images);
//Clear the existing data (to avoid duplication)
mSportsData.clear();
//Create the ArrayList of Sports objects with the titles and information about each sport
for (int i = 0; i < sportsList.length; i++) {
mSportsData.add(new Sport(sportsList[i], sportsInfo[i], sportsDetail[i], sportsImageResource.getResourceId(i, 0)));
}
sportsImageResource.recycle();
}
//Notify the adapter of the change
mAdapter.notifyDataSetChanged();
}
public void resetSports(){
initializeData(null);
}
}
App Images:-
#1 Initial List
#2 Changed List (Card #2 for sport basketball is swiped)
Orientation change to landscape:-
Even though the question is 4 months old and you probably don't need the answer anymore:
The problem is that you initialize the adapter with mSportsData, but reassign another value to the variable later in initializeData(). The ArrayList bound to the adapter is still the empty one it got initialized with.
A way to solve it would be to initialize mSportsData with either a new ArrayList if savedInstanceState is null or else the saved value, and to call initializeData only if savedInstanceState is null. You can remove the argument and therefore the if from initalizeData() completely.
// Initialize the ArrayList that will contain the data.
if (savedInstanceState == null) {
mSportsData = new ArrayList<>();
} else {
mSportsData = savedInstanceState.getParcelableArrayList("state");
}
//Initialize the adapter and set it ot the RecyclerView (nothing changed here)
mAdapter = new SportsAdapter(this, mSportsData);
mRecyclerView.setAdapter(mAdapter);
if (savedInstanceState == null) {
initializeData();
}
Remove this function call from OnCreate initializeData(savedInstanceState); and call it in OnResume
I couldn't get it working with the Parcelable way so what I tried doing (and it worked) was save all the info contained inside all the sport objects in separate lists and put those in the bundle instead.
In the initialiseData(), I just used these lists then to create a new list of sport objects with the exact data.
This shouldn't be the way of doing it but my app just doesn't loads the saved list if it's passed as a parcelable and I don't know why.
Here's the working code for now:-
#Override
protected void onSaveInstanceState(#NonNull Bundle outState) {
super.onSaveInstanceState(outState);
ArrayList<String> titles = new ArrayList<>();
ArrayList<String> infos = new ArrayList<>();
ArrayList<String> details = new ArrayList<>();
ArrayList<Integer> imgs = new ArrayList<>();
for(Sport sport: mSportsData){
titles.add(sport.getTitle());
infos.add(sport.getInfo());
details.add(sport.getDetail());
imgs.add(sport.getImageResource());
}
outState.putStringArrayList("title", titles);
outState.putStringArrayList("info", infos);
outState.putStringArrayList("detail", details);
outState.putIntegerArrayList("img", imgs);
}
private void initializeData(Bundle savedInstanceState) {
boolean hasData = false;
if(savedInstanceState!=null){
if(savedInstanceState.containsKey("title")){
//Get the resources from the bundle
ArrayList<String> sportsList = savedInstanceState.getStringArrayList("title");
ArrayList<String> sportsInfo = savedInstanceState.getStringArrayList("info");
ArrayList<String> sportsDetail = savedInstanceState.getStringArrayList("detail");
ArrayList<Integer> sportsImageResource = savedInstanceState.getIntegerArrayList("img");
//Clear the existing data (to avoid duplication)
mSportsData.clear();
//Create the ArrayList of Sports objects with the titles and information about each sport
for (int i = 0; i < sportsList.size(); i++) {
mSportsData.add(new Sport(sportsList.get(i), sportsInfo.get(i), sportsDetail.get(i), sportsImageResource.get(i)));
}
hasData = true;
}
}
if(!hasData) {
//Get the resources from the XML file
String[] sportsList = getResources().getStringArray(R.array.sports_titles);
String[] sportsInfo = getResources().getStringArray(R.array.sports_info);
String[] sportsDetail = getResources().getStringArray(R.array.sports_detail);
TypedArray sportsImageResource = getResources().obtainTypedArray(R.array.sports_images);
//Clear the existing data (to avoid duplication)
mSportsData.clear();
//Create the ArrayList of Sports objects with the titles and information about each sport
for (int i = 0; i < sportsList.length; i++) {
mSportsData.add(new Sport(sportsList[i], sportsInfo[i], sportsDetail[i], sportsImageResource.getResourceId(i, 0)));
}
sportsImageResource.recycle();
}
//Notify the adapter of the change
mAdapter.notifyDataSetChanged();
}
I have the following code for the recyclerview adapter for an android app that I'm working on right now:
#Override
public void onBindViewHolder(final FeedViewHolder contactViewHolder, final int i) {
final FeedInfo ci = feedInfoList.get(i);
//Set the text of the feed with your data
contactViewHolder.feedText.setText(ci.getFeed());
contactViewHolder.surNameText.setText(ci.getSurName());
contactViewHolder.nameText.setText(ci.getFirstName());
contactViewHolder.feedDate.setText(ci.getDate());
contactViewHolder.numberOfGoingText.setText(ci.getNumber_of_going());
contactViewHolder.numberOfInterestedText.setText(ci.getNumber_of_interested());
//seteaza fotografia de profil in postare
new ProfilePictureDownloadImage(contactViewHolder.profilePicture).execute(ci.getProfileImageURL());
ImageButton interestedButton = contactViewHolder.interestedButton;
interestedButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
int position = i;
FeedInfo fi = feedInfoList.get(position);
int displayedNumberOfInterested = Integer.parseInt(ci.getNumber_of_interested()) + 1;
contactViewHolder.numberOfInterestedText.setText(Integer.toString(displayedNumberOfInterested));
System.out.println("emilutzy interested from within" + fi.getPostID());
contactViewHolder.surNameText.setText("kk");
}
});
}
The problem is the click listener. In theory the button I press should increment the number right next to it. However, since I have to declare onBindViewHolder's arguments as final, only the first click works, the rest of the clicks do not change the value of the number. I am new to Android, so could you please help me find a better solution?
There's a nice method called getAdapterPosition() that you can use in your RecyclerView's ViewHolder.
Instead of setting the click listener in onBindViewHolder, set it in the constructor of your ViewHolder like so:
public class FeedViewHolder extends RecyclerView.ViewHolder {
private TextView feedText;
private TextView surNameText;
private Button interestedButton;
// ... the rest of your viewholder elements
public FeedViewHolder(View itemView) {
super(itemView);
feedtext = itemView.findViewById(R.id.feedtext);
// ... find your other views
interestedButton.setOnClickListener(new View.OnClickListener() {
final FeedInfo fi = feedInfoList.get(getAdapterPosition());
int numInterested = Integer.parseInt(ci.getNumber_of_interested()) + 1;
// setting the views here might work,
// but you will find that they reset themselves
// after you scroll up and down (views get recycled).
// find a way to update feedInfoList,
// I like to use EventBus to send an event to the
// host activity/fragment like so:
EventBus.getDefault().post(
new UpdateFeedInfoListEvent(getAdapterPosition(), numInterested));
// in your host activity/fragment,
// update the list and call
// notifyDatasetChanged/notifyDataUpdated()
//on this RecyclerView adapter accordingly
});
}
}
Don't set your position in onBindViewHolder to final (Android Studio will warn you why).
I'm not sure how the object FeedInfo looks like but you could also at a method called for example increaseNumberOfInterested() which would increase the value of Number_of_interested by one and would persist in the object when the recyclerview recycle the cell. it would like kind of like below
#Override
public void onBindViewHolder(final FeedViewHolder contactViewHolder, final int i) {
final FeedInfo ci = feedInfoList.get(i);
//Set the text of the feed with your data
contactViewHolder.numberOfInterestedText.setText(ci.getNumber_of_interested());
contactViewHolder.interestedButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//Increase the number of interested in the object, so it can be persisted when cell is reclycled
ci.setNumberOfInterested(ci.getNumber_of_interested()) + 1);
//Get new value and display
contactViewHolder.numberOfInterestedText.setText(Integer.toString(ci.getNumber_of_interested()));
}
I'm trying to pass the int pub_or_priv from my Activity1 to Myadapter.
I've looked at posts here and I think I've followed them correctly but it's still not working. The value in my adapter is always 0 whereas it should be 0,1 or 2, as is the case with the value in my Activity1.
Here's what I've done.
In my Activity1 I get "publicorprivate" from my server and convert it to an int with:
//convert public_or_private to an integer
pub_or_priv = Integer.parseInt(obj.getString("publicorprivate"));
For different cells in my recyclerView it will be 0,1 or 2.
Now I want to pass this to my adapter so in my adapter contructor I have:
public MyAdapter(List<Review> reviewUsers, Activity activity, int pub_or_priv) {
this.activity = activity;
the_reviews = reviewUsers;
this.mPub_or_priv = pub_or_priv;
}
And in my Activity1:
pAdapter = new MyAdapter(reviewList, this, pub_or_priv);
Then in MyAdapter:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
Review r = the_reviews.get(position);
//shared_status will be Just U, Private or Public
String shared_status ="";
if(mPub_or_priv==0){
//change colour depending on value
((ReviewHolder) viewHolder).phone_user_name.setTextColor(Color.parseColor("#DA850B"));
shared_status = "Just U";
}
if(mPub_or_priv==1){
((ReviewHolder) viewHolder).phone_user_name.setTextColor(Color.parseColor("#0A7FDA"));
shared_status = "Private";
}
if(mPub_or_priv==2){
((ReviewHolder) viewHolder).phone_user_name.setTextColor(Color.parseColor("#2AB40E"));
shared_status = "Public";
}
((ReviewHolder) viewHolder).phone_user_name.setText(shared_status);
etc..etc..
However in the recyclerView in all cells the phone_user_name textbox is always "Just U", in the #DA850B colour, whereas in fact it is supposed to be Private and Public in some cells, as per the pub_or_priv value in Activity1.
How can I get pub_or_priv correctly into my adapter?, thanks.
It looks like you're using one value in the activity/adapter, so of course all rows show the same value.
In the adapter, use the value in the Review instance that corresponds to the current cell:
#Override
public void onBindViewHolder(final RecyclerView.ViewHolder viewHolder, final int position) {
Review review = the_reviews.get(position);
int pubOrPriv = review.getPublicOrPrivate();
//shared_status will be Just U, Private or Public
String shared_status = "";
if (pubOrPriv == 0) {
//change colour depending on value
((ReviewHolder) viewHolder).phone_user_name.setTextColor(Color.parseColor("#DA850B"));
shared_status = "Just U";
}
if (pubOrPriv == 1) {
((ReviewHolder) viewHolder).phone_user_name.setTextColor(Color.parseColor("#0A7FDA"));
shared_status = "Private";
}
if (pubOrPriv == 2) {
((ReviewHolder) viewHolder).phone_user_name.setTextColor(Color.parseColor("#2AB40E"));
shared_status = "Public";
}
//.............
}
I want to display a list of match objects (match = two users having liked each other) in a recycler view with the help of an adapter.
This is my activity which is meant to display those matches:
public class MatchesActivity extends AppCompatActivity {
// variables:
private RecyclerView mMatchesRecyclerView;
private RecyclerView.Adapter mMatchItemAdapter;
private RecyclerView.LayoutManager mMatchesLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_matches);
mMatchesRecyclerView = findViewById(R.id.matches_recyclerView);
mMatchesRecyclerView.setNestedScrollingEnabled(false);
mMatchesRecyclerView.setHasFixedSize(true);
// set layout manager & pass it to the recycler view:
mMatchesLayoutManager = new LinearLayoutManager(MatchesActivity.this);
mMatchesRecyclerView.setLayoutManager(mMatchesLayoutManager);
// set match adapter & pass it to the recycler view:
mMatchItemAdapter = new MatchItemAdapter(getMatchesList(), MatchesActivity.this);
mMatchesRecyclerView.setAdapter(mMatchItemAdapter);
// add test items to the recycler view:
Match testMatch = new Match("abcdefgh");
matchesList.add(testMatch);
mMatchItemAdapter.notifyDataSetChanged();
Log.d("MatchesActivity", "TEST LIST: " + matchesList.toString());
}
private ArrayList<Match> matchesList = new ArrayList<Match>();
private List<Match> getMatchesList() {
Log.d("MatchesActivity", "getMatchesList function: " + matchesList.toString());
return matchesList;
}
}
And this is my adapter which is supposed to inflate the relevant layout & populate it with relevant object data:
public class MatchItemAdapter extends RecyclerView.Adapter<MatchViewholder> {
private List<Match> mMatchesList;
private Context mViewContext;
public MatchItemAdapter(List<Match> matchesList, Context context) {
this.mMatchesList = matchesList;
this.mViewContext = context;
Log.d("MatchItemAdapter", "Constructor: " + mMatchesList.toString());
}
// inflate the layout:
#Override
public MatchViewholder onCreateViewHolder(ViewGroup parent, int viewType) {
View layoutView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_matches, null, false);
RecyclerView.LayoutParams lp = new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT);
layoutView.setLayoutParams(lp);
MatchViewholder matchViewholder = new MatchViewholder(layoutView);
Log.d("MatchItemAdapter", "onCreateViewHolder: " + mMatchesList.toString());
return matchViewholder;
}
// populate each row within the layout:
#Override
public void onBindViewHolder(MatchViewholder holder, int position) {
Log.d("MatchItemAdapter", "onBindViewHolder: " + mMatchesList.toString());
holder.mMatchID.setText(mMatchesList.get(position).getMatchID());
}
#Override
public int getItemCount() {
return 0;
}
}
The Match class currently only takes matchID parameter which is string. An object is created with a default image and this matchID string.
At the moment, I have no real match objects from database ready, so I wanted to check that the recycler view along with adapter are working as expected before i move on to that later.
However, when I go to Matches Activity, it is empty, showing nothing at all. As you can see from the MatchesActivity onCreate method, I created a test Match object with matchID = "abcdefgh" and then added that to the matchesList. So I am expecting the "abcdefgh" text to be passed to the adapter and to be shown in the MatchesActivity.
My log statements indicate that the Match object has been created and added to the list successfully, however, getMatchesList() function returns an empty list which is then used in the Adapter constructor too, (I think this is) causing Activity not show anything.
I am relatively new to Android and Java development, especially recycler view and adapters, but from what I gathered it seems to be as if the
mMatchItemAdapter.notifyDataSetChanged();
is not working properly as everything seems to be fine up until that point. Any help would be appreciated!
You're returning 0. What you should do instead is return the length of the mMatchesList list.
#Override
public int getItemCount() {
return mMatchesList.size();
}
So basically I have 2 Fragments - FragmentConverter and FragmentFavourites, and I have one MainActivity. I'm trying to pass 4 arrays from the first fragment to the second one using an Interface called Communicator. The specific snippets are show below:
public interface Communicator {
public void respond(String[] names, String[] codes, String[] symbols, int[] images);
}
This is a method inside FragmentFavourites:
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// TODO Auto-generated method stub
String[] checkedNames = new String[counter];
String[] checkedCodes = new String[counter];
String[] checkedSymbols = new String[counter];
int[] checkedImages = new int[counter];
comm = (Communicator) getActivity();
int index = 0;
if (item.getItemId() == R.id.action_save){
for (int i=0;i<checked.size();i++){
if (checked.get(i) == true){
checkedNames[index] = names[i];
checkedCodes[index] = codes[i];
checkedSymbols[index] = symbols[i];
checkedImages[index] = images[i];
index++;
}
}
comm.respond(checkedNames, checkedCodes, checkedSymbols, checkedImages);
}
return super.onOptionsItemSelected(item);
}
This is the implemented interface method inside MainActivity:
#Override
public void respond(String[] names, String[] codes, String[] symbols,
int[] images) {
// TODO Auto-generated method stub
FragmentConverter frag = (FragmentConverter) fragmentPagerAdapter.getItem(1);
frag.changeData(names, codes, symbols, images);
}
And this is a method that collects the data in FragmentConverter:
public void changeData(String[] names, String[] codes, String[] symbols, int[] images){
this.names = names;
this.codes = codes;
this.symbols = symbols;
this.images = images;
Log.d("TEST", symbols.length + names.length + codes.length + images.length + "");
tvOneRate.setText(names[1]);
}
Now the problem is that whenever I try to change a ui component inside FragmentConverter, I get a NullPointerException, though the Log.d statement returns the correct results.
EDIT1: getItem() method of FragmentPagerAdapter:
#Override
public Fragment getItem(int i) {
// TODO Auto-generated method stub
Fragment frag = null;
if (i == 0){
frag = new FragmentFavourites();
}
if (i == 1){
frag = new FragmentConverter();
}
return frag;
}
EDITED:
When you call fragmentPagerAdapter.getItem(1) you are getting a new instance of the fragment so you are referring to a different object. this is why the view is null and you get the NullPointerException. If you need an adapter for only 2 fragments, you can try with something like that:
public class YourPagerAdapter extends android.support.v4.app.FragmentPagerAdapter {
private FragmentFavourites mFragFavourites;
private FragmentConverter mFragConverter;
public YourPagerAdapter() {
// ... your code above
this.mFragFavourites = new FragmentFavourites();
this.mFragConverter = new FragmentConverter();
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return mFragFavourites;
case 1:
return mFragConverter;
default:
return null;
}
}
}
As above carlo.marinangeli has suggested when you call fragmentPagerAdapter.getItem(1) you are getting a new instance of the fragment so you are referring to a different object
So to get same object from you adapter you need to store your object. you can try following method in your adapter -
public Fragment getFragmentAtPosition(int position) {
return registeredFragments.get(position);
}
where registeredFragments is -
private SparseArray<Fragment> registeredFragments = new SparseArray<Fragment>();
and fill this sparseArray in getItem method like below -
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
fragment = FragmentPost.newInstance(position);
registeredFragments.put(position, fragment);
return fragment;
}
return null;
}
By using fragmentPagerAdapter.getItem(pos) method I was referring to a new object every time the respond() method was called. I fixed it by using findFragmentByTag() method instead:
#Override
public void respond(String[] names, String[] codes, String[] symbols,
int[] images) {
FragmentManager manager = getSupportFragmentManager();
FragmentConverter frag = (FragmentConverter) manager.findFragmentByTag("android:switcher:" + pager.getId() + ":" + 1);
frag.changeData(names, codes, symbols, images);
}
you can get that error because you are assuming that you have got the FragmentConverter and the views associated to it.
Without a logcat it becomes a little bit difficult to help you but basically what I would like to do in a situation like this is to pass everything through the activity without letting know the existence of the other fragment to the fragments.
F1 modifies a state object into the activity
F2 has to register as a
listener to the activity (be aware that the fragment can be attached
and detached in the view pager)
The Activity as soon it receives an updated, looks for all the registered listeners and if there is someone it delivers the updated