Android: Pass data(extras) to a fragment - java

I'm new to Android programming and I'm having problems while passing an ArrayList of a Parcelable to a fragment.
This is the Activity that is launched(working well!) where feedlist is an ArrayList of a parcelable Music.
Intent in = new Intent(context, ListMusicsActivity.class);
in.putExtra("arrayMusic", feedList);
activity.startActivity(in);
The fragment Activity onCreate() method:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activitymusiclist);
if(savedInstanceState != null)
{
ListMusicFragment frag = new ListMusicFragment();
frag.setArguments(getIntent().getExtras());
}
}
The Fragment code:
public class ListMusicFragment extends SherlockFragment{
private ArrayList<Music> listMusics = new ArrayList<Music>();
private ListView listMusic;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
listMusics = (ArrayList<Music>) getArguments().getSerializable("arrayMusic");
View view = inflater.inflate(R.layout.musiclistview, container, false);
listMusic = (ListView) view.findViewById(R.id.musicListView);
listMusic.setAdapter(new MusicBaseAdapter(getActivity(), listMusics));
return view;
}
}
I think the problem is in the line
listMusics = (ArrayList<Music>) getArguments().getSerializable("arrayMusic");
Finally this is my Music class:
public class Music implements Parcelable{
private String url;
private String artist;
private String title;
private String duration;
private String info_url;
private String lyrics;
public Music(String url, String artist, String title,
String duration, String lyrics, String info_url)
{
this.url = url;
this.artist = artist;
this.title = title;
this.duration = duration;
this.lyrics = lyrics;
this.info_url = info_url;
}
public Music(Parcel in)
{
url = ParcelUtils.readString(in);
artist = ParcelUtils.readString(in);
title = ParcelUtils.readString(in);
duration = ParcelUtils.readString(in);
info_url = ParcelUtils.readString(in);
lyrics = ParcelUtils.readString(in);
}
public String getUrl()
{
return url;
}
public String getArtist()
{
return artist;
}
public String getTitle()
{
return title;
}
public String getDuration()
{
return duration;
}
public String getLyrics()
{
return lyrics;
}
public String getInfo()
{
return info_url;
}
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags)
{
ParcelUtils.writeString(dest, url);
ParcelUtils.writeString(dest, artist);
ParcelUtils.writeString(dest, title);
ParcelUtils.writeString(dest, duration);
ParcelUtils.writeString(dest, lyrics);
ParcelUtils.writeString(dest, info_url);
}
public static final Parcelable.Creator<Music> CREATOR =
new Parcelable.Creator<Music>() {
public Music createFromParcel(Parcel in)
{
return new Music(in);
}
public Music[] newArray(int size)
{
return new Music[size];
}
};
}
When I run this code the problem I get is a java.lang.NullPointerException in the Fragment onCreateView() method.
I would appreciate a lot if someone pointed me in the right direction to see where I am failing.
EDIT: Problem solved: I just needed to add this line to the fragment Activity onCreate() method(othwewise the getArguments() would return null):
getSupportFragmentManager().beginTransaction()
.add(android.R.id.content, frag).commit();
And add this to the fragment code:
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
Bundle bundle = getArguments();
if(bundle != null)
{
listMusics = bundle.getParcelableArrayList("arrayMusic");
listMusic.setAdapter(new MusicBaseAdapter(getActivity(), listMusics));
}
}
where, listMusics is an ArrayList of Parcelable Music.

Two things. First I don't think you are adding the data that you want to pass to the fragment correctly. What you need to pass to the fragment is a bundle, not an intent. For example if I wanted send an int value to a fragment I would create a bundle, put the int into that bundle, and then set that bundle as an argument to be used when the fragment was created.
Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.setArguments(bundle);
Second to retrieve that information you need to get the arguments sent to the fragment. You then extract the value based on the key you identified it with. For example in your fragment:
Bundle bundle = this.getArguments();
if (bundle != null) {
int i = bundle.getInt(key, defaulValue);
}
What you are getting changes depending on what you put. Also the default value is usually null but does not need to be. It depends on if you set a default value for that argument.
Lastly I do not think you can do this in onCreateView. I think you must retrieve this data within your fragment's onActivityCreated method. My reasoning is as follows. onActivityCreated runs after the underlying activity has finished its own onCreate method. If you are placing the information you wish to retrieve within the bundle durring your activity's onCreate method, it will not exist during your fragment's onCreateView. Try using this in onActivityCreated and just update your ListView contents later.

I prefer Serializable = no boilerplate code. For passing data to other Fragments or Activities the speed difference to a Parcelable does not matter.
I would also always provide a helper method for a Fragment or Activity, this way you always know, what data has to be passed. Here an example for your ListMusicFragment:
private static final String EXTRA_MUSIC_LIST = "music_list";
public static ListMusicFragment createInstance(List<Music> music) {
ListMusicFragment fragment = new ListMusicFragment();
Bundle bundle = new Bundle();
bundle.putSerializable(EXTRA_MUSIC_LIST, music);
fragment.setArguments(bundle);
return fragment;
}
#Override
public View onCreateView(...) {
...
Bundle bundle = intent.getArguments();
List<Music> musicList = (List<Music>)bundle.getSerializable(EXTRA_MUSIC_LIST);
...
}

There is a simple why that I prefered to the bundle due to the no duplicate data in memory. It consists of a init public method for the fragment
private ArrayList<Music> listMusics = new ArrayList<Music>();
private ListView listMusic;
public static ListMusicFragment createInstance(List<Music> music) {
ListMusicFragment fragment = new ListMusicFragment();
fragment.init(music);
return fragment;
}
public void init(List<Music> music){
this.listMusic = music;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.musiclistview, container, false);
listMusic = (ListView) view.findViewById(R.id.musicListView);
listMusic.setAdapter(new MusicBaseAdapter(getActivity(), listMusics));
return view;
}
}
In two words, you create an instance of the fragment an by the init method (u can call it as u want) you pass the reference of your list without create a copy by serialization to the instance of the fragment. This is very usefull because if you change something in the list u will get it in the other parts of the app and ofcourse, you use less memory.

Related

Android: Sending a cardview from one fragment to another

I need some help for a summer project
This is my Events fragment
This is my MyList fragment
I'm using a RecyclerView+Cardview to display the Events. The idea is that the user can click the big plus on the right side of each card, and the card would be displayed in the MyList fragment. I would like to ask if it's possible to transfer a card directly from one fragment to another? Also, both fragments are contained within the same activity, which makes it a little trickier(I haven't found any available solutions).
If that is not possible, another way is to transfer the reference type object contained in the CardView to the MyList fragment. However, this is even less straightforward. This is because the button is inflated in the adapter, but there is no reference type object created here. I have seen many tutorials on using the Parcelable interface, however, I don't know how to implement it here when I'm unable to even create the object in the adapter. The reference object is created in another activity and stored in Firebase before it is read and displayed.
I'm going to attach my EventsAdapter.java and EventsItem.java and EventsFragment.java code below, but please let me know if I should include more code to describe the problem.
Thanks for reading my very long post!!
public class EventsAdapter extends RecyclerView.Adapter<EventsAdapter.EventsViewHolder> implements Filterable {
private ArrayList<EventsItem> mEventsList;
private ArrayList<EventsItem> mEventsListFull;
private EventsAdapter.OnItemClickListener mListener;
private Context mContext;
private DateFormat df = DateFormat.getDateInstance(DateFormat.MEDIUM, Locale.UK);
public interface OnItemClickListener {
void onItemClick(int position);
}
//the ViewHolder holds the content of the card
public static class EventsViewHolder extends RecyclerView.ViewHolder {
public ImageView mImageView;
public ImageView mAddButton;
public TextView mTextView1;
public TextView mTextView2;
public TextView mTextView3;
public TextView mTextView4;
public TextView mTextView5;
public EventsViewHolder(Context context, View itemView, final EventsAdapter.OnItemClickListener listener) {
super(itemView);
final Context context1 = context;
mImageView = itemView.findViewById(R.id.imageView);
mAddButton = itemView.findViewById(R.id.image_add);
mTextView1 = itemView.findViewById(R.id.title);
mTextView2 = itemView.findViewById(R.id.event_description);
mTextView3 = itemView.findViewById(R.id.date);
mTextView4 = itemView.findViewById(R.id.location);
mTextView5 = itemView.findViewById(R.id.time);
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onItemClick(position);
}
}
}
});
mAddButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
String str1 = mTextView1.getText().toString();
String str2 = mTextView2.getText().toString();
String str3 = mTextView3.getText().toString();
String str4 = mTextView4.getText().toString();
String str5 = mTextView5.getText().toString();
Bundle bundle = new Bundle();
bundle.putString("title", str1);
bundle.putString("event description", str2);
bundle.putString("date", str3);
bundle.putString("location", str4);
bundle.putString("time", str5);
MylistFragment mlf = new MylistFragment();
mlf.setArguments(bundle);
}
});
}
}
//Constructor for EventsAdapter class. This ArrayList contains the
//complete list of items that we want to add to the View.
public EventsAdapter(Context context, ArrayList<EventsItem> EventsList) {
mEventsList = EventsList;
mContext = context;
mEventsListFull = new ArrayList<>(EventsList); // copy of EventsList for SearchView
}
//inflate the items in a EventsViewHolder
#NonNull
#Override
public EventsAdapter.EventsViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.event_item, parent, false);
EventsAdapter.EventsViewHolder evh = new EventsAdapter.EventsViewHolder(mContext, v, mListener);
return evh;
}
#Override
public void onBindViewHolder(#NonNull EventsAdapter.EventsViewHolder holder, int position) {
EventsItem currentItem = mEventsList.get(position);
holder.mImageView.setImageResource(currentItem.getProfilePicture());
holder.mTextView1.setText(currentItem.getTitle());
holder.mTextView2.setText(currentItem.getDescription());
holder.mTextView3.setText(df.format(currentItem.getDateInfo()));
holder.mTextView4.setText(currentItem.getLocationInfo());
holder.mTextView5.setText(currentItem.getTimeInfo());
}
#Override
public int getItemCount() {
return mEventsList.size();
}
public class EventsItem implements Occasion, Parcelable {
//fields removed for brevity
//constructor removed for brevity
}
public EventsItem() {
}
public EventsItem(Parcel in) {
profilePicture = in.readInt();
timeInfo = in.readString();
hourOfDay = in.readInt();
minute = in.readInt();
locationInfo = in.readString();
title = in.readString();
description = in.readString();
}
public static final Creator<EventsItem> CREATOR = new Creator<EventsItem>() {
#Override
public EventsItem createFromParcel(Parcel in) {
return new EventsItem(in);
}
#Override
public EventsItem[] newArray(int size) {
return new EventsItem[size];
}
};
//getter methods have been removed for brevity
#Override
public int describeContents() {
return 0;
}
#Override
public void writeToParcel(Parcel dest, int flags) {
dest.writeInt(profilePicture);
dest.writeString(timeInfo);
dest.writeString(locationInfo);
dest.writeString(title);
dest.writeString(description);
dest.writeString(df.format(dateInfo));
dest.writeInt(hourOfDay);
dest.writeInt(minute);
}
}
public class EventsFragment extends Fragment {
ArrayList<EventsItem> EventsItemList;
FirebaseDatabase mDatabase;
DatabaseReference mDatabaseReference;
ValueEventListener mValueEventListener;
private RecyclerView mRecyclerView;
private RecyclerView.LayoutManager mLayoutManager;
private EventsAdapter mAdapter;
private View rootView;
public FloatingActionButton floatingActionButton;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_events, container, false);
mDatabase = FirebaseDatabase.getInstance();
mDatabaseReference = mDatabase.getReference().child("Events");
createEventsList();
buildRecyclerView();
floatingActionButton = rootView.findViewById(R.id.fab);
floatingActionButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent = new Intent(getActivity(), EventsAdder.class);
startActivity(intent);
}
});
mValueEventListener = new ValueEventListener() {
#Override
public void onDataChange(#NonNull DataSnapshot dataSnapshot) {
for (DataSnapshot snapshot : dataSnapshot.getChildren()) {
EventsItemList.add(snapshot.getValue(EventsItem.class));
}
EventsAdapter eventsAdapter = new EventsAdapter(getActivity(), EventsItemList);
mRecyclerView.setAdapter(eventsAdapter);
}
#Override
public void onCancelled(#NonNull DatabaseError databaseError) {
}
};
mDatabaseReference.addValueEventListener(mValueEventListener);
setHasOptionsMenu(true);
Toolbar toolbar = rootView.findViewById(R.id.events_toolbar);
AppCompatActivity activity = (AppCompatActivity) getActivity();
activity.setSupportActionBar(toolbar);
return rootView;
}
public void createEventsList() {
EventsItemList = new ArrayList<>();
}
public void buildRecyclerView() {
mRecyclerView = rootView.findViewById(R.id.recyclerview);
mLayoutManager = new LinearLayoutManager(getContext());
mAdapter = new EventsAdapter(getActivity(), EventsItemList);
mRecyclerView.setLayoutManager(mLayoutManager);
mRecyclerView.setAdapter(mAdapter);
mRecyclerView.setItemAnimator(new DefaultItemAnimator());
}
}
If you would like to see the same CardView within the MyListFragment, you could have the MyListFragment contain a RecyclerView, and reuse the same EventsAdapter and EventsViewHolder. The only difference is that rather than populating the adapter with all the children of the "Events" from your database, you would only populate it with the single Event that you want.
Also, since you have made your Event class implement parcelable, you do not need to manually create the bundle when clicking the plus button.
I am assuming you have a single Activity, and you simply want to replace the EventsFragment with the MyListFragment. Checkout the docs for replacing one fragment with another.
Step 1:
Extend your onItemClickListener to look like:
public interface OnItemClickListener {
void onItemClick(int position);
void onPlusButtonClick(int position);
}
and adjust the code in your EventsViewHolder constructor to look like this when the plus button is clicked:
mAddButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (listener != null) {
// no need to manually create the bundle here
// you already have all the information you need
int position = getAdapterPosition();
if (position != RecyclerView.NO_POSITION) {
listener.onPlusButtonClick(position);
}
}
}
});
Step 2:
Implement our new method onPlusButtonClick. As per our discussion in the comments, it seems you do not implement this interface anywhere. You can implement it inside the constructor to your EventsAdapter:
public EventsAdapter(Context context, ArrayList<EventsItem> EventsList) {
mEventsList = EventsList;
mContext = context;
mEventsListFull = new ArrayList<>(EventsList); // copy of EventsList for SearchView
mListener = new OnItemClickListener() {
#Override
public void onItemClick() {
// handle clicking the entire view holder
// NOTE: inside your EventsViewHolder, it looks like you call this method on the entire itemView. This could 'swallow' the click on the plus button. You may need to adjust your code to handle this.
}
#Override
public void onPlusButtonClick(int position) {
MyListFragment myListFragment = new MyListFragment();
Event event = mEventsList.get(position);
Bundle bundle = new Bundle();
bundle.putExtra("event", event); // this will work due to implementing parcelable
myListFragment.setArguments(bundle);
// use mContext since im assuming we areinside adapter
// if in an Activity, no need to use context to get the fragment manager
FragmentTransaction transaction = mContext.getSupportFragmentManager().beginTransaction();
// Replace the EventsFragment with the MyListFragment
// and add the transaction to the back stack so the user can navigate back
transaction.replace(R.id.fragment_container, myListFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
}
}
}
Step 3:
Inside your MyListFragments onCreateView() method:
#Override
public View onCreateView (
LayoutInflater inflater,
ViewGroup container,
Bundle savedInstanceState
) {
Bundle bundle = getArguments();
Event event = bundle.getExtra("event"); // again, will work due to implementing parcelable
// from here you should bind to a recycler view, and you can even reuse your adapter like so:
List<EventsItem> singleEventList = new List<EventsItem>();
singleEventList.add(event);
EventsAdapter adapter = new EventsAdapter(getActivity(), singleEventList);
// be sure to inflate and return your view here...
}
and you should be good to go!
I have left out bits of code here and there for simplicity.. but I hope this is understandable.
As a side note.. in your firebase database listener, it is bad practice to create a new EventsAdapter every single time your data is updated. Instead, you should update the data in the adapter with the new values. Do this by creating a public method inside the adapter such as replaceEvents(List<EventsItem> newEvents), and inside, replace mEventsList with the new events, then call notifyDataSetChanged().

Send data from Activity to Fragment when fragment is onCreateView

Sorry for my english. I have one activity and in this activity in FragmentPagerAdapter exist 5 fragments. All fragments use one object model for inforation(product name, product image ...).
My activity get data from data base. And then it data send to all fragments. My example Activity:
#BindView(R.id.tabs_sep_prod) TabLayout tabs_sep_prod;
#BindView(R.id.viewpager_sep_prod) ViewPager viewpager_sep_prod;
PrepBaseProdFragment prepBaseProdFragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_sep_product);
ButterKnife.bind(this);
prepBaseProdFragment = new PrepBaseProdFragment();
// there i have another fragments
// ...
setupViewPager(viewpager_sep_prod);
tabs_sep_prod.setupWithViewPager(viewpager_sep_prod);
}
private void setupViewPager(ViewPager viewPager) {
SepProductActivity.ViewPagerAdapter adapter = new SepProductActivity.ViewPagerAdapter(
adapter.addFrag(prepBaseProdFragment, getString(R.string.sep_prep_base));
// there i have another addFrag
// ...
viewPager.setOffscreenPageLimit(5);
viewPager.setAdapter(adapter);
}
#Override
public void onResume() {
super.onResume();
// this i call method from presenter, it return data in method setData
if(idCat != null && idProd != null)
sepProductPresenter.getSepProd(idCat, idProd);
}
#Override
public void setData(SepProductModel sepProductModel) {
// there i send data to fragment
prepBaseProdFragment.setDataInf(sepProductModel);
// ...
}
class ViewPagerAdapter extends FragmentPagerAdapter {
private final List<Fragment> mFragmentList = new ArrayList<>();
private final List<String> mFragmentTitleList = new ArrayList<>();
public ViewPagerAdapter(FragmentManager manager) {
super(manager);
}
#Override
public Fragment getItem(int position) {
return mFragmentList.get(position);
}
#Override
public int getCount() {
return mFragmentList.size();
}
public void addFrag(Fragment fragment, String title) {
mFragmentList.add(fragment);
mFragmentTitleList.add(title);
}
#Override
public CharSequence getPageTitle(int position) {
return mFragmentTitleList.get(position);
}
}
my fragment
public class PrepBaseProdFragment extends BaseFragment {
#BindView(R.id.text) TextView text;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_about_prod_prepare_base, parent, false);
ButterKnife.bind(this, view);
Log.e("PrepBaseProdFragment", "PrepBaseProdFragment");
return view;
}
public void setDataInf(SepProductModel sepProductModel) {
text.setText(sepProductModel.getPROPERTY_PR_SUBSTRPREP_UA_VALUE().getTEXT());
}
}
My question: when i send data from activity to fragment, my view do not have time to initialize. In fragment line text.setText(sepProductModel.getPROPERTY_PR_SUBSTRPREP_UA_VALUE().getTEXT()); error
java.lang.NullPointerException: Attempt to invoke virtual method 'void
android.widget.TextView.setText(java.lang.CharSequence)' on a null
object reference
Please, help me solve my problem. I spend many times for this
You can check if the text view is null save the data in a variable inside the fragment and in onCreateView use the data variable if it is filled and set the textview text.
Something like this:
// Inside the Fragment body
private SepProductModel sepProductModel;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_about_prod_prepare_base, parent, false);
ButterKnife.bind(this, view);
Log.e("PrepBaseProdFragment", "PrepBaseProdFragment");
if(this.sepProductModel != null)
text.setText(this.sepProductModel.getPROPERTY_PR_SUBSTRPREP_UA_VALUE().getTEXT());
return view;
}
public void setDataInf(SepProductModel sepProductModel) {
if(text != null){
// use text
}
else this.sepProductModel=sepProductModel;
}
I can advise you to wait for your data and then show fragments to adapter, moreover all of them use the same model. While you dont have data, you can show progress bar.
When you have needed model, you can create instances of your fragments and set bundle arguments for them. You can use method like this:
public static MyFragment newInstance(MyModel model){
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putSerializable(KEY, model);//or args.putParcelable(KEY, model);
fragment.setArguments(args);
return fragment;
}
Notice: your model need to implement Serializable or Parcelable to be putted in bundle. You may read about difference here.
Then in your set data method, when you already have your model you can setup your adapter and set it to view pager, but with this approach:
adapter.addFrag(MyFragment.newInstance(model), getString(R.string.sep_prep_base));
It have to help you, ask me if you have some questions.

ViewPager setCurrentItem(position) not working after orientation change

i'm facing an issue i can't seem to resolve and i'd like some help.
My app is composed by an activity containing a fragment. After the user taps on a suggestion, activity's method onSuggestionClicked(String cardId) is called and activity's content is replaced with a new PagerFragment_new.
#Override
public void onSuggestionClicked(String cardId) {
Log.d("activity","ID:\t" + cardId);
PagerFragment_new pagerFragment = PagerFragment_new.getInstance(
Injection.provideMtgDatabase(getApplicationContext()),
activityPresenter.getAllCardExpansionIds(cardId),
this);
ActivityUtils.replaceFragment(getSupportFragmentManager(), pagerFragment, R.id.MTGRecallActivity_container);
}
PagerFragment_new contains a ViewPager to display CardInfoFragment_new
public class PagerFragment_new extends MtgRecallFragment_new implements PagerFragmentContract.View {
private View fragmentView;
private ViewPager viewPager;
private PagerFragmentContract.Presenter presenter;
private ScreenSlidePageAdapter_new adapter;
private MtgDatabase database;
private String[] printingsIds;
private ScreenSlidePageAdapter_new.ActivePresenterListener listener;
public PagerFragment_new() {
//Required empty constructor
}
public static PagerFragment_new getInstance(MtgDatabase database, String[] printingsIds, ScreenSlidePageAdapter_new.ActivePresenterListener listener) {
PagerFragment_new instance = new PagerFragment_new();
instance.setDatabase(database);
instance.setPrintingsIds(printingsIds);
instance.listener = listener;
return instance;
}
//other stuff
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
setRetainInstance(true);
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
setHasOptionsMenu(true);
fragmentView = inflater.inflate(R.layout.fragment_pager, container, false);
viewPager = (ViewPager) fragmentView.findViewById(R.id.PAGERFRAG_VIEWPAGER);
adapter = new ScreenSlidePageAdapter_new(getChildFragmentManager(), printingsIds, database, viewPager);
adapter.setListener(listener);
viewPager.setAdapter(adapter);
viewPager.setOffscreenPageLimit(1);
return fragmentView;
}
#Override
public void setPresenter(PagerFragmentContract.Presenter presenter) {
this.presenter = presenter;
}
}
The adapter to manage ViewPager's element's is ScreenSlidePageAdapter_new, it is responsible of instantiating CardInfoFragments.
public class ScreenSlidePageAdapter_new extends FragmentStatePagerAdapter {
private String[] printingsIds;
private String firstItemId;
private MtgDatabase database;
public ActivePresenterListener activity;
private ViewPager viewPager;
public ScreenSlidePageAdapter_new(FragmentManager fm, String[] printingsIds, MtgDatabase database, ViewPager viewPager) {
super(fm);
this.printingsIds =printingsIds;
this.database = database;
this.firstItemId = printingsIds[0];
this.viewPager = viewPager;
}
#Override
public Fragment getItem(int position) {
//todo create a bundle with cardId
Bundle bundle = new Bundle();
bundle.putString("CardID", printingsIds[position]);
bundle.putString("FirstItemId", firstItemId);
CardInfoFragment_new cardInfoFragment_new = CardInfoFragment_new.getInstance(viewPager);
cardInfoFragment_new.setArguments(bundle);
CardInfoPresenter cardInfoPresenter = new CardInfoPresenter(cardInfoFragment_new, database);
activity.setActivePresenter(cardInfoPresenter);
return cardInfoFragment_new;
}
#Override
public void destroyItem(ViewGroup container, int position, Object object) {
super.destroyItem(container, position, object);
}
public void setListener(ActivePresenterListener listener) {
this.activity = listener;
}
#Override
public int getCount() {
return printingsIds.length;
}
public interface ActivePresenterListener {
void setActivePresenter(BasePresenter presenter);
}}
Finally, here's the code of CardInfoFragment:
public class CardInfoFragment_new extends MtgRecallFragment_new
implements CardInfoContract.View,
CardPrintingsAdapter.OnItemClickListener {
private ViewPager viewPager;
public CardInfoContract.Presenter presenter;
private View fragmentView;
private MTGCard mtgCard;
RecyclerView printingsRecyclerView;
CardPrintingsAdapter cardPrintingsAdapter;
private String firstItemId; //the first id
public CardInfoFragment_new() {
//required empty constructor
}
private void setViewPager(ViewPager viewPager) {
this.viewPager = viewPager;
}
public static CardInfoFragment_new getInstance(ViewPager viewPager) {
CardInfoFragment_new cardInfoFragment_new = new CardInfoFragment_new();
cardInfoFragment_new.setViewPager(viewPager);
return cardInfoFragment_new;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
setRetainInstance(true);
presenter.start();
String cardId = getArguments().getString("CardID");
firstItemId = getArguments().getString("FirstItemId");
mtgCard = presenter.getCardData(cardId);
super.onCreate(savedInstanceState);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
fragmentView = inflater.inflate(R.layout.fragment_card_info_new, container, false);
setUpCardInfoView(fragmentView);
return fragmentView;
}
public void setUpCardInfoView(final View view){
//others view set-up action omitted
cardprintingsContainer = (LinearLayout) view.findViewById(R.id.CARDINFO_printingsContainer);
printingsRecyclerView = (RecyclerView)fragmentView.findViewById(R.id.CARDINFO_printings_recyclerView);
final List<String> printings = presenter.getCardPrintings(firstItemId, mtgCard.getName());
if (printings.size() < 40) {
setupPrintings(printings,printingsRecyclerView);
}
else {
LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
LinearLayout printingsRow = new LinearLayout(getContext());
printingsRow = (LinearLayout)inflater.inflate(R.layout.card_info_printings_row,printingsRow,true);
printingsRow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
setupPrintings(printings, printingsRecyclerView);
}
});
ImageView printingssetImage = (ImageView)printingsRow.findViewById(R.id.CARDINFO_printingsRow_setImage);
printingssetImage.setVisibility(View.INVISIBLE);
TextView printingssetName = (TextView)printingsRow.findViewById(R.id.CARDINFO_printingsRow_setName);
printingssetName.setText("Click to see all the "+printings.size()+ " versions");
cardprintingsContainer.addView(printingsRow);
}
}
public void setupPrintings(List<String> printings, RecyclerView recyclerView) {
cardPrintingsAdapter =
new CardPrintingsAdapter(getContext(), printings);
cardPrintingsAdapter.setOnItemClickListener(this);
UnscrollableLayoutManager layoutManager = new UnscrollableLayoutManager(getContext(), LinearLayoutManager.VERTICAL, false);
recyclerView.setAdapter(cardPrintingsAdapter);
recyclerView.setLayoutManager(layoutManager);
}
#Override
public void onItemClick(View itemView, int position) {
int i = viewPager.getAdapter().getCount();
viewPager.setCurrentItem(position);
}
#Override
public void onDestroy() {
presenter = null;
mtgCard = null;
super.onDestroy();
}}
When user click on a RecyclerView item, CardInfoFragment's method onItemClick(View itemView, int position) is called and the viewpager is set to current position. Everything as expected.
THE PROBLEM
When the orientation changes, fragments are able to retain their states, but viewPager.setCurrentItem(position) does nothing, and i'm not able to change displayed fragment.
I already checked:
ViewPager instance and adapters instances corresponds to those passed to Fragments
onItemClick is fired
this questions question1 question2
If i use
android:configChanges="orientation|screenSize"
in manifest it works even after config changes, but i don't want to use this option since is discouraged.
Thanks in advance!
By default, when certain key configuration changes happen on Android (a common example is an orientation change), Android fully restarts the running Activity to help it adjust to such changes.
When you define android:configChanges="keyboardHidden|orientation" in your AndroidManifest, you are telling Android: "Please don't do the default reset when the keyboard is pulled out, or the phone is rotated; I want to handle this myself. Yes, I know what I'm doing".
So use configChanges.
The use of this is discouraged, IF you don't know how to use it. But for a case like this, I would suggest you use it. IF not, still the activity will be recreated. which just takes time, resources, and I don't see why you would want that to happen.

value disapears when trying to get it back on second Fragment

I opening Fragment B from Fragment A with startActivityForResult when Fragment B closes I get String value from EditText to Fragment A and everything works fine but now I opening same Fragment B from Fragment A and I want it to have that earlier returned value but it disappears
here I'm storing purposeText value from editText but not saving to json yet I want it to be saved on exiting Fragment A
public class Cloud {
private static final String JSON_ID = "id";
private static final String JSON_PURPOSE = "purpose";
private UUID mId;
private String purposeText = "";
public Cloud() {
mId = UUID.randomUUID();
}
public Cloud(JSONObject json) throws JSONException {
mId = UUID.fromString(json.getString(JSON_ID));
if (json.has(JSON_PURPOSE)){
purposeText = json.getString(JSON_PURPOSE);
}
}
public JSONObject toJSON() throws JSONException{
JSONObject json = new JSONObject();
json.put(JSON_ID, mId.toString());
json.put(JSON_PURPOSE, purposeText);
return json;
}
#Override
//Using get and set methods to get it in private String
public String getPurposeText(){return purposeText;}
public void setPurposeText(String purpose){purposeText = purpose;}
public UUID getId() {return mId;};
}
In Fragment A I'm getting text and setting it to textview and it works, then setting it back to setter in Cloud class
public class AddFragment extends Fragment {
private static final String TAG = "Cload";
public static final String EXTRA_DREAM_ID = "com.example.tadas.dreamcload1.dream_id";
private static final int REQUEST_DATE = 0;
private static final int REQUEST_TEXT =1;
private Cloud dCloud;
private TextView purposeView;
private String comeBackPurpose;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
dCloud = new Cloud();
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getActivity().setTitle("Add new dream");
}
#Override
public View onCreateView(LayoutInflater inflater,
ViewGroup parent, Bundle savedInstanceState){
View v = inflater.inflate(R.layout.fragment_add,parent,false);
purposeView = (TextView)v.findViewById(R.id.dream_purpose);
purposeView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent i = new Intent(getActivity(),DreamPurposeActivity.class);
startActivityForResult(i,REQUEST_TEXT);
}
});
return v;
}
#Override
public void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);
if (requestCode == REQUEST_TEXT){
comeBackPurpose = data.getStringExtra("comeBackPurpose");
// Log.e(TAG,"SHITFACE " + comeBackPurpose);
purposeView.setText(comeBackPurpose);
dCloud.setPurposeText(comeBackPurpose);
Log.e(TAG,"SHITFACE " + dCloud.getPurposeText());
}
}
public static AddFragment newInstance(UUID dreamId) {
Bundle args = new Bundle();
args.putSerializable(EXTRA_DREAM_ID, dreamId);
AddFragment fragment = new AddFragment();
fragment.setArguments(args);
return fragment;
}
}
Now In Fragment B I'm trying to set that text back to EditText in onCreateView method but it gets default value, what should I do to fix that?
public class PurposeFragment extends Fragment {
public static final String EXTRA_D = "com.dropstudio.drea.dr_ids";
private static final String TAG = "Cload";
private EditText purposeEText;
private String purposeEditText;
private Cloud mCloud;
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCloud = new Cloud();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dream_purpose_edit,container,false);
purposeEText = (EditText)v.findViewById(R.id.purpose_edit);
purposeEText.setText(mCloud.getPurposeText());
return v;
}
public void savePurpose(){
purposeEditText = purposeEText.getText().toString();
mCloud.setPurposeText(purposeEditText);
Intent returnIntent = new Intent();
returnIntent.putExtra("comeBackPurpose",purposeEditText);
getActivity().setResult(Activity.RESULT_OK,returnIntent);
getActivity().finish();
}
public static PurposeFragment newInstance(UUID dreaIds) {
Bundle args = new Bundle();
args.putSerializable(EXTRA_D, dreaIds);
PurposeFragment fragment = new PurposeFragment();
fragment.setArguments(args);
return fragment;
}
}
In Fragment B , you are creating a new Cloud object in onCreate() and trying to get the value from that object in onCreateView() which will always be null.
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
mCloud = new Cloud(); //New instance will be created
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.dream_purpose_edit,container,false);
purposeEText = (EditText)v.findViewById(R.id.purpose_edit);
purposeEText.setText(mCloud.getPurposeText()); // 'mCloud' is a new object everytime and hence will not have your saved value
return v;
}
For your task, either use SharedPreferences or pass the saved value while instantiating Fragment B.

how can i change the output of a fragment base in another fragment in android studio

I made this code for an app it suppose to give a list of body part like arm and legs and one you click on one in the first fragment a list of exercise should appear in the second fragment this list is suppose to change if the user press another option and is there were I have the problem it displays the first option automatic but it not change after the user press another option please how can I change the content in the second fragment.
Main class
public class MainActivity extends Activity
implements WorkoutListFragment.WorkoutListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
WorkoutDetailFragment frag = (WorkoutDetailFragment)
getFragmentManager().findFragmentById(R.id.detail_frag);
frag.setWorkoutId(1);
}
public void itemClicked(long id){
int itemSelected = (int)id;
String message = String.format("ID selected %d", itemSelected);
Log.v("DEBUG", message);
}
}
Second class
public class Workout {
private String name;
private String description;
public Workout(String name, String description) {
this.name = name;
this.description = description;
}
public static final Workout[] workouts= {
new Workout("Chest Workout", "3 Bench Press\n10Flys\nWide Dips"),
new Workout("Legs", "5 Squats\n3x12 Leg Curl\n3x10 Leg Press"),
new Workout("Back", "10 Pullups\n 3x8 Dumbell Rows"),
new Workout("Arms", "10 Biceps Curls\n10 Dips\n3x10 Preacher Curls")
};
public String getName() {
return name;
}
public String getDescription() {
return description;
}
#Override
public String toString() {
return this.name;
}
}
Third class
public class WorkoutDetailFragment extends Fragment {
private final String TAG = "DEBUG";
private long workoutId;
public WorkoutDetailFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_workout_detail, container, false);
}
#Override
public void onStart(){
super.onStart();
View view = getView();
if(view !=null){
TextView title = (TextView)view.findViewById(R.id.TextTitle);
Workout workout = Workout.workouts[(int)workoutId];
title.setText(workout.getName());
Log.v(TAG, workout.getName());
TextView description = (TextView)view.findViewById(R.id.TextDescription);
description.setText(workout.getDescription());
Log.v(TAG, workout.getDescription());
}
}
public void setWorkoutId(long id){
this.workoutId = id;
}
}
Fourth class
public class WorkoutListFragment extends ListFragment {
static interface WorkoutListener {
void itemClicked(long id);
}
private WorkoutListener listener;
public WorkoutListFragment() {
// Required empty public constructor
}
#Override
public void onAttach(Activity activity){
super.onAttach(activity);
this.listener = (WorkoutListener)activity;
}
#Override
public void onListItemClick(ListView l, View v, int position, long id){
if (listener != null){
listener.itemClicked(id);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// create a string array with workout names
String[] names = new String [Workout.workouts.length];
for (int i=0; i < names.length; i++){
names[i] = Workout.workouts[i].getName();
}
// Create an array adapter
ArrayAdapter<String> adapter = new ArrayAdapter<String>(
inflater.getContext(),
android.R.layout.simple_list_item_1,
names);
// bind the adapter to the control
setListAdapter(adapter);
// this call gives you the default layout view for the
// ListFragment
return super.onCreateView(inflater, container, savedInstanceState);
}
}
Inside an onClickListener or whatever listens for clicks you should write this in your activity:
FragmentManager fragementManager = getFragmentManager();
fragementManager.beginTransaction().replace(R.id.layout, new NextFragment()).commit();
In this example R.id.layout should be the id of your layout in where your fragment is placed. NextFragment should be the class of the fragment you want to display.
Hope this helps! :)

Categories

Resources