2

I am just new to android and I've been researching for this for weeks but I still cannot find a way to make it work. :(

I have 4 fragment tabs (created dynamically) in my Main Activity. I enter an amount from a prompt in Main Activity and save it to database. I want to update the value in the first tab after I press the OK button from the prompt to get the amount saved into the database and display it on the first tab fragment.

How can I refresh the values in the first tab?

in my fragment I have this code to display the data. But this only displays the data initially, not after I enter the amount from the Main Activity.

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  final View rootview = inflater.inflate(R.layout.home_tab, container, false);
  super.onCreate(savedInstanceState);
  myDb = new DatabaseHelper(getContext());
  viewBudget(rootview);
  return rootview;
}
public View viewBudget(final View rootview) {
  //get values from database and display in dynamic views
}

I really appreciate your answers. Thank you in advance.

EDIT: Here is my complete code for viewBudget:

public View viewBudget(final View rootview) { //display funds dynamically in config layout | LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState
  //Cursor res = myDb.getAllData();
  //String[] textArray = {"One", "Two", "Three", "Four"};
  Double budget, totalpct, temp_pct;

  Cursor res2 = myDb.getConfigData();
  res2.moveToFirst();

  Cursor res3 = myDb.getLatestIncome();
  res3.moveToFirst();
  //showMessage("Number of rows",Integer.toString(res2.getCount()));

  Cursor res4 = myDb.getTotalExpenes();
  res4.moveToFirst();

  //view contents of config table
  if (res2.getCount() == 0) {
    // show message
    //showMessage("Error","Nothing found");
    //return;
  }

  final ScrollView scrollView = (ScrollView) rootview.findViewById(R.id.scroll_budget);
  LinearLayout linearLayout = (LinearLayout) rootview.findViewById(R.id.ll_budget);

  LinearLayout llh_texts = new LinearLayout(getActivity());
  llh_texts.setOrientation(LinearLayout.HORIZONTAL);
  LinearLayout llh_texts2 = new LinearLayout(getActivity());
  llh_texts2.setOrientation(LinearLayout.HORIZONTAL);
  LinearLayout llh_texts3 = new LinearLayout(getActivity());
  llh_texts3.setOrientation(LinearLayout.HORIZONTAL);
  LinearLayout llh_texts4 = new LinearLayout(getActivity());
  llh_texts4.setOrientation(LinearLayout.HORIZONTAL);

  LinearLayout.LayoutParams lp_llh_texts = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
  llh_texts.setLayoutParams(lp_llh_texts);

  //final View linearLayout = getActivity().findViewById(R.id.ll_config);
  linearLayout.removeAllViews(); //clear layout first - LINE WITH ISSUE
  linearLayout.setGravity(Gravity.CENTER);

  //LinearLayout.LayoutParams lp2 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT);
  //LinearLayout.LayoutParams lp3 = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);

  //compute for value of budget
  budget = 0.0;
  totalpct = 0.0;

  linearLayout.addView(llh_texts); //add button h.linearlayout to parent linearlayout inside scrollview

  TextView textBudget = new TextView(getActivity());
  TextView labelBudget = new TextView(getActivity());
  labelBudget.setText("Budget: ");
  llh_texts.addView(labelBudget);
  llh_texts.addView(textBudget);

  TextView textIncome = new TextView(getActivity());
  TextView labelIncome = new TextView(getActivity());
  labelIncome.setText("Income: ");
  linearLayout.addView(llh_texts2);
  llh_texts2.addView(labelIncome);
  llh_texts2.addView(textIncome);

  TextView textTotalFunds = new TextView(getActivity());
  TextView labelFunds = new TextView(getActivity());
  labelFunds.setText("Total Savings Funds: ");
  linearLayout.addView(llh_texts3);
  llh_texts3.addView(labelFunds);
  llh_texts3.addView(textTotalFunds);

  TextView textTotalExpenses = new TextView(getActivity());
  TextView labelExpenses = new TextView(getActivity());
  labelExpenses.setText("Total Scheduled Expenses: ");
  linearLayout.addView(llh_texts4);
  llh_texts4.addView(labelExpenses);
  llh_texts4.addView(textTotalExpenses);

  //create dynamic objects inside scrollview and dynamic linear layout - horizontal
  for (int i = 0; i < res2.getCount(); i++) {
    LinearLayout llh = new LinearLayout(getActivity());
    llh.setOrientation(LinearLayout.HORIZONTAL);
    LinearLayout.LayoutParams lp_llh = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT);
    llh.setLayoutParams(lp_llh);
    //llh.setLayoutParams(lp4,llh.setOrientation(LinearLayout.HORIZONTAL); //for length and width

    linearLayout.addView(llh);

    //LinearLayout.LayoutParams lp_np = new LinearLayout.LayoutParams(70, LinearLayout.LayoutParams.WRAP_CONTENT);

    temp_pct = (Double.valueOf(res2.getString(2)) / 100) * Double.valueOf(res3.getString(3));

    EditText editText = new EditText(getActivity());
    editText.setText(String.valueOf(temp_pct));
    editText.setEnabled(false);

    EditText editText2 = new EditText(getActivity());
    editText2.setText(String.valueOf(res2.getString(2)) + "%");
    editText2.setEnabled(false);

    //get total of funds
    totalpct = totalpct + temp_pct;

    //showMessage("value",res2.getString(3));
    TextView textView = new TextView(getActivity());
    textView.setText(res2.getString(1));

    llh.addView(textView);

    llh.addView(editText);
    llh.addView(editText2);

    res2.moveToNext();
  }

  //return scrollView;
  //create budget text and content
  budget = Double.valueOf(res3.getString(3)) - (totalpct + res4.getDouble(0));
  textBudget.setText(String.valueOf(budget));
  textIncome.setText(String.valueOf(res3.getString(3)));
  textTotalFunds.setText(String.valueOf(totalpct));
  textTotalExpenses.setText(String.valueOf(res4.getDouble(0)));

  return rootview;
}

EDIT: Added code to 3rd fragment tab based on Bhuvanesh's answer:

@Override
public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setHasOptionsMenu(true); //Make sure you have this line of code.
  setUserVisibleHint(false);
}

//@Override /*<-- this returned  "method does not override method in its superclass" so I commented out*/
public void setUserVisibleHint(final View view, boolean isVisibleToUser) {
  super.setUserVisibleHint(isVisibleToUser);
  if (isVisibleToUser) {
    //get data from database and refresh view.
    viewFunds(view); //this method refreshes the rootview of the 3rd fragment 
  }
}

@Override
public void onViewCreated(final View view, @Nullable Bundle savedInstanceState) {
  super.onViewCreated(view, savedInstanceState);
  ((Main2Activity) getActivity()).setPopupListener(new Main2Activity.PopupListener() {
    @Override
    public void onDialogClick(String value) {
      //After clicking dialog ok button in Activity
      // you will get value here.
      //viewFunds(view);
      setUserVisibleHint(view, true);
    }
  });
}
Glenn P
  • 37
  • 8
  • Do you have viewpager along with tab? – Sandip Fichadiya Sep 11 '17 at 17:10
  • Your problem is an architectural problem. I suggest you pause for a moment and spend one or two days learning about the MVP pattern, for example by reading this awesome intro: https://antonioleiva.com/mvp-android/ **WHY** ? Because in the short-term, you need to understand and apply separation of concerns. Your activity and fragments need to talk and you could just create an Interface and call it a day, but in the end, that separation will not be enough when things get more complicated, it will poorly scale and will be a headache. That has been my experience. – Martin Marconcini Sep 11 '17 at 17:17
  • `getSupportFragmentManager().beginTransaction().detach(yourFragment).attach(yourFragment).commit();` when you want to "reload" the fragment. – HB. Sep 11 '17 at 18:00
  • Hi H.Brooks, I am testing out different approaches. Should I put your code in the Main Activity? What should I replace the `yourFragment` value? I tried to supply it with the class name of my fragment which is **home_tab** but there is an error that says _Expression expected_. Thank you – Glenn P Sep 13 '17 at 15:42

3 Answers3

1

First, define a public method in the Fragment that will re-query the database, and update the UI:

public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
  final View rootview = inflater.inflate(R.layout.home_tab, container, false);
  super.onCreate(savedInstanceState);
  myDb = new DatabaseHelper(getContext());
  viewBudget(rootview);
  return rootview;
}

public View viewBudget(final View rootview) {
  getUpdatedDatabaseData();
}

public void getUpdatedDatabaseData() {
  //get values from database and display in dynamic views
}

Then add an instantiateItem() override to your FragmentPagerAdapter, and keep an array of Fragment references for all Fragments in the ViewPager. Here is a simple example:

class PagerAdapter extends FragmentPagerAdapter {
        String tabTitles[] = new String[] { "One", "Two", "Three", "Four"};
        Context context;

        //This will contain your Fragment references:
        public Fragment[] fragments = new Fragment[tabTitles.length];

        public PagerAdapter(FragmentManager fm, Context context) {
            super(fm);
            this.context = context;
        }
        @Override
        public int getCount() {
            return tabTitles.length;
        }
        @Override
        public Fragment getItem(int position) {
            switch (position) {
            case 0:
                return new FragmentOne();
            case 1:
                return new FragmentTwo();
            case 2:
                return new FragmentThree();   
            case 3:
                return new FragmentFour();
            }
            return null;
        }

        //This populates your Fragment reference array:
        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            Fragment createdFragment = (Fragment) super.instantiateItem(container, position);
            fragments[position]  = createdFragment;
            return createdFragment;
        }

        @Override
        public CharSequence getPageTitle(int position) {
            // Generate title based on item position
            return tabTitles[position];
        }         
 }

Then, when the user has used the dialog to add data to the database, you can use this code in the Activity to update the Fragment in the first Tab (if it is one of the currently active Tabs in the ViewPager)

    Fragment frag = mAdapter.fragments[0];
    if (frag != null && frag instanceof FragmentOne) {
      ((FragmentOne)frag).getUpdatedDatabaseData();
    }
Daniel Nugent
  • 43,104
  • 15
  • 109
  • 137
  • Hello Daniel, thanks for your help. I transferred the code from viewBudget to getUpdatedDatabaseData however, I have an issue with this code after the transfer: `LinearLayout linearLayout =(LinearLayout)rootview.findViewById(R.id.ll_budget);` I use this to add views based on the database values for example: `linearLayout.addView(llh_texts);` Now when I transferred this to getUpdatedDatabaseData, I also need to declare the view as parameter, which might cause issue in: `((FragmentOne)frag).getUpdatedDatabaseData();` What can we do as workaround for this? – Glenn P Sep 12 '17 at 15:19
  • I have included my code for viewBudget for your reference, please see updates above in the question. Thank you. – Glenn P Sep 12 '17 at 15:36
0

You can do it by Interface.

Create interface object in your activity and set the object from your fragment.

In your activity create Lister object like this:

public class MyActivity extends AppCompatActivity {

    private PopupListener popupListener;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        //Your dialog code.
         setPositiveButton("Ok", new DialogInterface.OnClickListener() {
              @Override
              public void onClick(DialogInterface dialog, int which) {
                 popupListener.onDialogClick(your_value);
              }
         })
    }

    public void setPopupListener(PopupListener popupListener) {
        this.popupListener = popupListener;
    }

    public interface PopupListener{
        void onDialogClick(String value);
    }
}

In your fragment you have to set listener like this:

public class MyFragment extends Fragment{

    @Override
    public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        ((MyActivity)getActivity()).setPopupListener(new MyActivity.PopupListener() {
            @Override
            public void onDialogClick(String value) {
                //After clicking dialog ok button in Activity
                // you will get value here.
            }
        });
    }
}

To update the other fragment content of your viewPager override the below method in all of your viewPager fragments.

@Override
public void setUserVisibleHint(boolean isVisibleToUser) {
    super.setUserVisibleHint(isVisibleToUser);
    if (isVisibleToUser) { 
      //get data from database and refresh view.
    }
}

Before that add the following code in you fragments onCreate().

public void onCreate(@Nullable Bundle savedInstanceState) {
    setUserVisibleHint(false);
}

Hope it helps:)

Bhuvanesh BS
  • 13,474
  • 12
  • 40
  • 66
  • Hi Bhuvanesh, I can't seem to make this work to refresh the other fragments. I need to update the current fragment that is displayed. What are the necessary updates needed to the code you provided? Thanks! :) – Glenn P Sep 12 '17 at 16:38
  • Answer updated. Try and ask me if any doubt you get. – Bhuvanesh BS Sep 12 '17 at 18:01
  • Hello Bhuvanesh, please see updates in the original question above and kindly check if I added your code to the 3rd fragment properly. The contents of the 3rd fragment still does not update. Thanks – Glenn P Sep 13 '17 at 15:44
  • Hello Bhuvanesh, I'd like to follow up on this if you have time. Thank you – Glenn P Sep 14 '17 at 08:25
  • Using the updates, I noticed that when I clicked the first tab and get back to 3rd tab again, the view is refreshed. What updates should be done to update the 3rd tab at once after input from activity and not needing to click the 1st tab and go back to the 3rd? Thanks! – Glenn P Sep 14 '17 at 16:22
0

You can use broadcastReceiver in your fragment and send data from Activity through broadcasting intent

While saving into database in MainActivity send this broadcast

Intent intent = new Intent("key_to_identify_the_broadcast");
intent.putExtra("refresh", true);
context.sendBroadcast(intent);

and then for your first fragment create broadcast receiver

private final BroadcastReceiver mHandleMessageReceiver = new 
BroadcastReceiver() {
    @Override
    public void onReceive(Context context, Intent intent) {
        Boolean refresh = 
            intent.getBooleanExtra("refresh",false);
        if(refresh){
           notifyDataSetChanged();
        }
        //you can call any of your methods for using this value for your use case
  }
};

You need to register this broadcast in onCreateView() of fragment

IntentFilter filter = new 
      IntentFilter("key_to_identify_the_broadcast");
       getActivity().getApplicationContext().
              registerReceiver(mHandleMessageReceiver, filter);

You need to unregister in your onDestroy() of your fragment

@Override
public void onDestroy() {
    try {

     getActivity().getApplicationContext().
         unregisterReceiver(mHandleMessageReceiver);

   } catch (Exception e) {
      Log.e("UnRegister Error", "> " + e.getMessage());
   }
   super.onDestroy();
}

You can implement the same broadcast receiver for all your fragments where you want to refresh the list

Shriyansh Gautam
  • 1,084
  • 1
  • 7
  • 13
  • I would not recommend this way for this specific situation. In the case of Fragments in a ViewPager, they will be created and destroyed very fast when the user swipes through all the tabs, so with this solution the receiver would be registered/unregistered very quickly, and many times, as the user swipes between tabs. – Daniel Nugent Sep 11 '17 at 19:15