Notify from child fragment inside ViewPager to his parent an event - android

i have a fragment inside a activity, this fragment contains a viewpager, this is the code:
public class RepliesContainerFragment extends Fragment {
#InjectView(R.id.viewPager)
ViewPager pager;
private PagerAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ReplyActivity replyActivity = (ReplyActivity) getActivity();
List<Replie> replies = getArguments().getParcelableArrayList("replies");
adapter = new PagerAdapter(replyActivity.getSupportFragmentManager(), replies);
setActionBar();
}
private void setActionBar() {
getActivity().getActionBar().setDisplayHomeAsUpEnabled(false);
getActivity().getActionBar().setIcon(getResources().getDrawable(R.drawable.ic_back));
getActivity().getActionBar().setDisplayHomeAsUpEnabled(true);
getActivity().getActionBar().setTitle("Respuestas");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.work_containter_replies, container, false);
ButterKnife.inject(this, view);
pager.setAdapter(adapter);
return view;
}
}
Inside the adapter i had instantiate some fragments like this:
public class ReplyFragment extends Fragment {
#InjectView(R.id.tvQuestion)
TextView tvQuestion;
#InjectView(R.id.frameContainter)
FrameLayout replyContainer;
private ReplyView replyView;
private String question;
private String currentReplie;
private int replieType;
private int questionId;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
replieType = getArguments().getInt("replyType");
question = getArguments().getString("question");
questionId = getArguments().getInt("questionId");
currentReplie = getArguments().getString("currentReplie");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
View view = inflater.inflate(R.layout.work_reply_fragment, container, false);
ButterKnife.inject(this, view);
initQuestionText();
return view;
}
private void persistReply() {
ReplyActivity replyActivity = (ReplyActivity) getActivity();
if (replyActivity != null) {
//DO SOMETHING
//Notify to adapter from here
}
}
}
}
As you can see, i want to notify to my RepliesContainerFragment and his inside adapter that some event happens and do an action..
How can i do it this?
With broadcastreceivers, with callbacks(Interfaces)..?I am not sure.

The very simple solution for this is to pass a listener instance from your parent fragment to a child fragment. You have to create a listener interface, create an instance of it in your parent fragment, and pass that instance to all your children fragments.
By the way there is a topic regarding this problem in the official tutorial.
Hope this helps.

I think it is easier:
private void persistReply() {
ReplyActivity replyActivity = (ReplyActivity) getActivity();
if (replyActivity != null) {
//DO SOMETHING
RepliesContainerFragment frag = ((RepliesContainerFragment)this.getParentFragment());
frag.getAdapter().notifyDataSetChanged();
}
}
}
and make getter setter for adapter

Related

Moving Data from Activity to Another Activity's TabFragment's Fragment

I want to move some data from my Mainactivity to a Fragment...which is inside a tab fragment (which handles a SectionPagerAdapter)....which is inside a fragment in Mainactivity2.
I feel its too complicated and cant get it done.
GameEndedActivity (See wrong and correct variable...these are to be transferred)
public class GameEndedActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game_ended);
int correct = getIntent().getIntExtra("correct", 0);
int wrong = getIntent().getIntExtra("wrong", 0);
}
}
TabFragment
public class TabFragment extends Fragment {
private int tabsCount;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.activity_tab, container, false);
if(this.getContext() instanceof ChallengeOverviewActivity){
tabsCount = 2;
} else {
tabsCount = 3;
}
SectionsPagerAdapter sectionsPagerAdapter = new SectionsPagerAdapter(this.getContext(), getFragmentManager(), tabsCount);
ViewPager viewPager = view.findViewById(R.id.view_pager);
viewPager.setAdapter(sectionsPagerAdapter);
TabLayout tabs = view.findViewById(R.id.tabs);
tabs.setupWithViewPager(viewPager);
return view;
}
}
ResultFragment (Transfer data here - wrong and correct variables)
public class ResultDetailsFragment extends Fragment {
private TextView tvWrong, tvCorrect;
private AnswerListener answerListener;
public interface AnswerListener {
public void correct(int i);
public void wrong(int i);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_result_details, container, false);
tvCorrect = view.findViewById(R.id.tvCorrect);
tvWrong = view.findViewById(R.id.tvWrong);
return view;
}
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
if (context != null) {
answerListener = (AnswerListener) context;
}
}
}
One way to do this is the add arguments to the fragment when you create an instance of it. In your activity when you create the fragment you want to attach you add some arguments to it. These arguments can then be accessed from the fragments onCreate method.
Just do this same thing again when you are in your fragment and create your second one.
Check this answer out which explains more in detail how to do it: https://stackoverflow.com/a/17436739/964887

Android pass View as an object to fragment

I'm more googling to find how can i pass simple view as an object to fragment, but i can't.
for example in MainActivity i have simple view as :
TextView text = (TextView) findviewById(R.id.tv_text);
now i want to pass that to fragment. this below code is my attach Fragment on MainActivity
MainActivity :
public void attachFragment() {
fts = getActivity().getFragmentManager().beginTransaction();
mFragment = new FragmentMarketDetail();
fts.replace(R.id.cardsLine, mFragment, "FragmentMarketDetail");
fts.commit();
}
and this is my Fragment:
public class FragmentMarketDetail extends Fragment implements ObservableScrollViewCallbacks {
public static final String SCROLLVIEW_STATE = "scrollviewState";
private ObservableScrollView scrollViewTest;
private Context context;
private int scrollY;
public static FragmentMarketDetail newInstance() {
FragmentMarketDetail fragmentFirst = new FragmentMarketDetail();
return fragmentFirst;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_online_categories, container, false);
scrollViewTest = (ObservableScrollView) view.findViewById(R.id.scrollViewTest);
scrollViewTest.setScrollViewCallbacks(this);
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
context = getActivity().getBaseContext();
}
}
It wouldn't be good practise to pass a view that way. If you want to access the view in your activity from within your fragment class, use getActivity() to access the activity to which your fragment is attached, and from there you find your TextView.
TextView text = (TextView) getActivity().findViewById(R.id.tv_text);
How about adding a set function in your custom fragment
e.g.
public void setTextView(TextView tv){
this.tv = tv
}
and then calling it after
mFragment = new FragmentMarketDetail();
mFragment.setTextView(textView)
Find fragment by tag and invoke a function on it:
mFragment = (FragmentMarketDetail ) getActivity().getFragmentManager().findFragmentByTag(FragmentMarketDetail .class.getSimpleName());
mFragment.passTextView(textView);
Of course fragment must be added to backstack.

onCreateView fragment on tabhost called every time I change the tab

I am using tab host with fragment, the following is the code of the main activity
public class HomeActivity extends Activity{
private FragmentTabHost mTabHost;
private ArrayList<CustomTabIndicator> mCustomTabIndicator;
private ArrayList<BaseFragment> mTabFragments;
private class CustomTabIndicator {
private int mIdResId;
private int mTitleResId;
private int mIconResId;
public CustomTabIndicator(int idResId, int titleResId, int iconResId) {
this.mIdResId = idResId;
this.mTitleResId = titleResId;
this.mIconResId = iconResId;
}
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home_screen);
mTabHost = (FragmentTabHost) findViewById(android.R.id.tabhost);
mTabHost.setup(this, getSupportFragmentManager(), R.id.realtabcontent);
initializeTabIndicatorsAndFragments();
addTabIndicatorsToTabHost();
}
private void initializeTabIndicatorsAndFragments() {
mCustomTabIndicator = new ArrayList<HomeActivity.CustomTabIndicator>();
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_dashboard,
R.string.tab_dashboard, R.drawable.tab_dashboard));
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_feed,
R.string.tab_feed, R.drawable.tab_feed));
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_lists,
R.string.tab_lists, R.drawable.tab_lists));
mCustomTabIndicator.add(new CustomTabIndicator(R.string.tab_me,
R.string.tab_me, R.drawable.tab_me));
mTabFragments = new ArrayList<BaseFragment>();
mTabFragments.add(new DashboardFragment());
mTabFragments.add(new FeedFragment());
mTabFragments.add(new ListsFragment());
mTabFragments.add(new MeFragment());
}
private void addTabIndicatorsToTabHost() {
for (int i = 0; i < mCustomTabIndicator.size(); i++) {
mTabHost.addTab(
mTabHost.newTabSpec(
getString(mCustomTabIndicator.get(i).mIdResId))
.setIndicator(
createTabView(
this,
mCustomTabIndicator.get(i).mTitleResId,
mCustomTabIndicator.get(i).mIconResId)),
mTabFragments.get(i).getClass(), null);
}
}
#SuppressLint("InflateParams")
private View createTabView(final Context context, final int textStringId,
final int imageResId) {
View view = LayoutInflater.from(context).inflate(
R.layout.fragment_tab_header_image_text_layout, null);
ImageView tabIV = (ImageView) view.findViewById(R.id.tab_icon);
tabIV.setImageResource(imageResId);
//TextView titleTV = (TextView) view.findViewById(R.id.tab_title);
//titleTV.setText(textStringId);
return view;
}
}
This is the code of one of the fragements
public class MeFragment extends BaseFragment {
private View mFragementView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.i("AMIRA", "MeFragment - onCreateView");
mFragementView = inflater.inflate(R.layout.fragment_me_screen,container, false);
initializeUIComponents();
initializeUIComponentsData();
initializeUIComponentsTheme();
initializeUIComponentsAction();
return mFragementView;
}
}
The problem now that onCreateView called every time I change the tab, and take long time to render and draw the content of fragment.
So I have tried the following code
public class MeFragment extends BaseFragment {
private View mFragementView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
if (mFragementView != null) {
return mFragementView;
} else {
Log.i("AMIRA", "MeFragment - onCreateView");
mFragementView = inflater.inflate(R.layout.fragment_me_screen,container, false);
initializeUIComponents();
initializeUIComponentsData();
initializeUIComponentsTheme();
initializeUIComponentsAction();
return mFragementView;
}
}
}
and i got the following exception
java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
so can anyone help here ?
First, about the java.lang.IllegalStateException,
You haven't initialized mFragmentView.
Say something like:
mFragmentView = (View)getActivity().findViewById(R.id.mFragmentView);
or:
mFragmentView = (View)getActivity().findFragmentById(R.id.mFragmentView);
Second about the Fragment changing every time you change the tab.
Try this:
add setRetainInstance(true); to the Fragments onAttach() or onCreateView().
Open for correction, as always!
Regards,
Edward Quixote.

Maintain fragment's view state

I have seen Link1 for this issue but could understand it right. I have a fragment that loads a list. When i click the list item it opens another activity. But i press back button it loads the list again. I want it to be at the same scroll position where it was before. In above mentioned link it specifies to use flag but i haven't got the point.
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dashboard);
android.app.Fragment fragment = new MeFragment();
getFragmentManager().beginTransaction().replace(R.id.layout_FragmentsContainer, fragment).addToBackStack(null).commit();
}
}
public class MeFragment extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.inflate(R.layout.fragment_me, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
meLV = (ListView) getView().findViewById(R.id.lv_Inbox);
loadingListProgress = (ProgressBar) getView().findViewById(R.id.progress_LoadingList);
meList = new ArrayList<Message>();
meAdapter = new MessagesListAdapter(getActivity(), meList);
//addFooter();
meLV.setAdapter(meAdapter);
meLV.setOnItemClickListener(this);
pageCount = 0;
loadmoreProgressDialog = new ProgressDialog(getActivity());
loadmoreProgressDialog.setTitle("Please wait ...");
loadmoreProgressDialog.setMessage("Loading more ...");
loadmoreProgressDialog.setCancelable(true);
loadUserMessages();
meLV.setOnScrollListener(new EndlessScrollListener() {
#Override
public void onLoadMore(int page, int totalItemsCount) {
// TODO Auto-generated method stub
//addFooter();
loadmoreProgressDialog.show();
loadUserMessages();
}
});
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position,
long id) {
// TODO Auto-generated method stub
Utils.showToast_msg(getActivity(), "MessageItemClicked");
ReferralDetailFragment fragment = new ReferralDetailFragment();
getFragmentManager().beginTransaction().replace(R.id.layout_FragmentsContainer, fragment).addToBackStack(null).commit();
}
}
public class ReferralDetailFragment extends Fragment implements OnClickListener {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_referraldetail,container, false);
linkToAcknowledge = (TextView) view.findViewById(R.id.lbl_Link_to_Acknowledge);
return view;
}
}
I implemented a simple solution for this in my app, basically when you press back to go to the fragment again, onCreateView() is called. Here in onCreateView() you have done all initialization, so we change
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.fragment_me, container, false);
/*
*Whatever you want to do
*
*/
return view;
}
to:
View view;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
if(view==null){
view = inflater.inflate(R.layout.fragment_me, container, false);
/*
*Whatever you want to do
*
*/
}
else{
((ViewGroup)view.getParent()).removeView(view);
}
return view;
}
Here, we move View view outside and make it a class variable. So if it is the first time the fragment is called, it is null and the initialization occurs, otherwise it goes to else black. Else block is required because onCreateView() adds whatever it returns as a child of the view's parent, so since view is already there, we remove it and onCreateView automatically adds it again.
According to our exchange in the comments, I completely deletde my answer and re-write a new one.
I copy/paste the code from one of my apps and removing the useless things and changing the names. Hope there is not too many typing mistakes, at that it is the minimum required to have it working.
When I pop back to FirstFragment from SecondFragment, the scroll position of FirstFragment is the same as when I clicked an item to load the SecondFragment.
Note that I don't extend FragmentActivity. I have an activity which loads the fragments.
Extend/modify to match your needs.
MainActivity :
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_layout);
}
}
FirstFragment Class :
public class FirstFragment extends Fragment implements OnItemClickListener {
private ListView mListView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.first_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mListView = (ListView) getView().findViewById(R.id.listview_first_fragment);
mListView.setAdapter(mAdapter); // depends on your adapter
mListView.setOnItemClickListener(this);
}
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mListView.setItemChecked(position, true);
//in case you need, set the bundle here, for example pass the position
Bundle arguments = new Bundle();
arguments.putInt("position", position);
SecondFragment secondFragment = new SecondFragment();
secondFragment.setArguments(arguments);
getFragmentManager().beginTransaction().replace(R.id.fragment_container, secondFragment).addToBackStack(null).commit();
}
}
SecondFragment Class :
public class SecondFragment extends Fragment {
private Integer mPosition;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.second_fragment_layout, container, false);
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
Bundle arguments = getArguments();
if (arguments == null) {
mPosition= 0;
} else {
mPosition= arguments.getInt("Position");
}
}
}
What you are trying to achieve may be done with help of savedInstanceState. i also had this kind of problem which i resolved by using add() method instead of replace() in transition.
If you can change your method or already not using add() than give it a shot.
and if add() method didn't do the trick then check the implementation of savedInstanceState.
correctly save instance state.
How to save states of fragment views.

Access to view elements in fragments

I have an activity which receive data from other activity. This data has to be showed in a TextView in a fragment inflated in the activity. So, the code is:
public class DetailActivity extends Activity {
private int idEstablecimiento;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit(); //Here the fragment is added to the activity
}
Bundle extras = getIntent().getExtras();
if (extras != null) {
idEstablecimiento = extras.getInt("idEstablecimiento");
}
TextView textView = (TextView) this.findViewById(R.id.nombreView); //Declared in fragmentDetail, which has been already inflated at this point, but still null
textView.setText(this.idEstablecimiento);
}
//...
//Non related stuff...
//...
public static class PlaceholderFragment extends Fragment {
private int idEstablecimiento;
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_detail,
container, false);
return rootView;
}
}
The problem is that the textView in the onCreate() method is null, why is this happening if the fragment has been already inflated? Shouldn't I be able to access to the IU elements once the fragment has been inflated in the onCreate() method? Or if not, what is the rigth way to do access to it?
You have to find the TextView in the Fragment.
public static class PlaceholderFragment extends Fragment {
private int idEstablecimiento;
public PlaceholderFragment() {
}
public PlaceholderFragment(int idEstablecimiento) {
this.idEstablecimiento = idEstablecimiento;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_detail,
container, false);
TextView text = rootView.findViewById(R.id.nombreView);
text.setText(idEstablecimiento);
return rootView;
}
And then you can get the text and commit it.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_detail);
Bundle extras = getIntent().getExtras();
if (extras != null) {
idEstablecimiento = extras.getInt("idEstablecimiento");
}
if (savedInstanceState == null) {
getFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment(idEstablecimiento)).commit();
}
}
Regards
Nils
If you want to get the View from a fragment layout you should use the view inside the oncreateView of the fragment and then get the textView inside the oncreate view..
example:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_detail,
container, false);
TextView textView = (TextView) rootView.this.findViewById(R.id.nombreView); //Declared in fragmentDetail, which has been already inflated at this point, but still null
textView.setText("I am here");
return rootView;
}

Categories

Resources