0

I am making an application using fragments and viewpager that will send the users data to the main activity. The data will be collected from multiple fragments and be sent when the submit button (contained within the main activity) is pressed to the activity. Example screenshot:

Text

I am unsure how to get the fragment edittext data. I tried getting it in the onClick listener for the submit button with R.findViewById but it seems I'm missing an important step. I tried looking at videos/tutorials but I could only find applicable ones that got the data when a button was pressed in the fragment, or ones that did the reverse (fragment gets data from activity).

Here is my code: formActivity.java

import androidx.appcompat.app.AppCompatActivity;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;

import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.View;
import android.widget.Button;

import com.google.android.material.tabs.TabLayout;


public class FormActivity extends AppCompatActivity {

    private static final String TAG = "PROCESS";
    TabLayout tabLayout;
    ViewPager viewPager;

    //or set to 0 and change it dynamically in the onPageSelected method
    private int numFrags = 2;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_formactivity);
        viewPager = findViewById(R.id.viewPager);
        tabLayout = findViewById(R.id.tabLayout);
        getTabs();

        //Add onPageChangeListener to get the position of the current tab and change the button text based on the position
        viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {

            @Override
            public void onPageSelected(int position) {
                Button back = findViewById(R.id.backButton);
                Button next = findViewById(R.id.nextButton);
                Button mainReturn = findViewById(R.id.mainReturn);
                Button submit = findViewById(R.id.submitButton);
                PagerAdapter adapter = viewPager.getAdapter();
                //numFrags = viewPager.getAdapter().getCoBunt() -1; - dynamically get new frags

                if (position == 0) {
                    back.setVisibility(View.GONE);
                    next.setVisibility(View.VISIBLE);
                    mainReturn.setVisibility(View.VISIBLE);

                }
                else if (adapter != null && position == numFrags) {
                    back.setVisibility(View.VISIBLE);
                    next.setVisibility(View.GONE);
                    submit.setVisibility(View.VISIBLE);

                    }
                else {
                    back.setVisibility(View.VISIBLE);
                    next.setVisibility(View.VISIBLE);
                    mainReturn.setVisibility(View.GONE);
                    submit.setVisibility(View.GONE);
                }
            }
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
            }
            @Override
            public void onPageScrollStateChanged(int state) {
            }
        });
    }

    //Add fragments to viewPager using the object ViewPagerAdapter
    public void getTabs(){
       final ViewPagerAdapter viewPagerAdapter = new ViewPagerAdapter(getSupportFragmentManager());

        new Handler().post(new Runnable() {
            @Override
            public void run() {
                viewPagerAdapter.addFragment(oneFragment.getInstance(),null); // this line can cause crashes
                viewPagerAdapter.addFragment(twoFragment.getInstance(),null); // this line can cause crashes
                viewPagerAdapter.addFragment(threeFragment.getInstance(),null); // this line can cause crashes

                viewPager.setAdapter(viewPagerAdapter);
                tabLayout.setupWithViewPager(viewPager);
            }
        });
    }

    //METHODS FOR THE BUTTON LISTENERS
    public void goBack(View view) {
        viewPager.setCurrentItem(viewPager.getCurrentItem()-1);

    }
    public void goNext(View view) {
        viewPager.setCurrentItem(viewPager.getCurrentItem() + 1);
    }

    public void submitFormEntries(View view) {
        Log.d(TAG, "returnToMainMenu: ");
    }

    public void returnToMainMenu(View view) {
        Log.d(TAG, "returnToMainMenu: ");
    }
}

ViewPagerAdapter.java

import java.util.ArrayList;
import java.util.List;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentStatePagerAdapter;

public class ViewPagerAdapter extends FragmentStatePagerAdapter {

    private List<Fragment> fragmentList = new ArrayList<>(); // this line can cause crashes
    private List<String> stringList = new ArrayList<>();


    public ViewPagerAdapter(@NonNull FragmentManager fm) {
        super(fm);
    }

    @NonNull
    @Override
    public Fragment getItem(int position) {
        return fragmentList.get(position);
    }

    @Override
    public int getCount() {
        return fragmentList.size();
    }

    @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return stringList.get(position);
    }

    public void addFragment(Fragment fragment, String title){
        fragmentList.add(fragment); // this line can cause crashes
        stringList.add(title);
    }

}

oneFragment.java

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;

public class oneFragment extends Fragment {

    public static oneFragment getInstance(){
        oneFragment oneFragment = new oneFragment();
        return oneFragment;
    }

    @Override
    public void onAttach(@NonNull Context context) {
        super.onAttach(context);
    }


    @Nullable
    @Override
    public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.page_one, container,false);

        return view;
    }
}

activity_formactivity.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:orientation="vertical"
    tools:context=".FormActivity">

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/viewPager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>


    <androidx.constraintlayout.widget.ConstraintLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true">

        <Button
            android:id="@+id/backButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/back"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="goBack"
            android:visibility="gone"/>

        <Button
            android:id="@+id/mainReturn"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/mainReturn"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="returnToMainMenu"
            android:visibility="visible"/>

        <Button
            android:id="@+id/nextButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/next"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="goNext"/>

        <Button
            android:id="@+id/submitButton"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:text="@string/submit"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            android:onClick="submitFormEntries"
            android:visibility="gone"/>

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabLayout"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            app:layout_constraintBottom_toBottomOf="parent"
            app:layout_constraintEnd_toEndOf="parent"
            app:layout_constraintStart_toStartOf="parent"
            app:layout_constraintTop_toTopOf="parent"
            app:tabBackground="@drawable/tab_selector"
            app:tabIndicatorHeight="0dp" />

    </androidx.constraintlayout.widget.ConstraintLayout>

</RelativeLayout>

page_one.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:id="@+id/titleOne"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="32dp"
        android:text="@string/title_one"
        android:textColor="@color/colorPrimaryDark"
        android:textSize="32sp"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:id="@+id/questionOne"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:text="@string/questionOne"
        android:textColor="@color/colorPrimaryDark"
        android:textSize="18sp"
        app:layout_constraintBottom_toTopOf="@+id/descriptionOne"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <TextView
        android:id="@+id/descriptionOne"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginBottom="16dp"
        android:text="@string/descriptionOne"
        android:textSize="18sp"
        app:layout_constraintBottom_toTopOf="@+id/answerOne"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent" />

    <EditText
        android:id="@+id/answerOne"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:ems="10"
        android:hint="@string/answerHintOne"
        android:inputType="textMultiLine"
        android:maxLines="4"
        android:minLines="4"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent"
        />
</androidx.constraintlayout.widget.ConstraintLayout>

I'm very new, so any alternative suggestions along with the answer would also be appreciated.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
MKer
  • 83
  • 2
  • 8
  • if you are using fragment and you want your data in parent activity than use interface for this its a best approach – Amit pandey Jan 07 '21 at 05:16
  • Thanks for the comment, do you have any links to an example of how to do this with multiple fragments to the main activity? – MKer Jan 07 '21 at 05:38
  • This will help you, [Callback from fragment to Activity](https://stackoverflow.com/a/65554394/9701793) – rahat Jan 11 '21 at 15:50

4 Answers4

2

Use a Bundle. Here's an example:

Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.setArguments(bundle);

Bundle has put methods for lots of data types. See this

Then in your Fragment, retrieve the data (e.g. in onCreate() method) with:

Bundle bundle = this.getArguments();
if (bundle != null) {
        int myInt = bundle.getInt(key, defaultValue);
}

set values of first fragment in bundle.setArguments and onCreate() of second fragment get values using bundle.getInt() in second fragment put same as bundle.setArguemnts with value of first fragment and second fragment so in 3rd fragmrnt you will get values of both fist and second fragment.


fragment 1:

 Bundle bundle = new Bundle();
        bundle.putString("FRAGEMNT1", "VALUE FRAGEMENT 1");
        fragment.setArguments(bundle);

in fragment 2's onCrete()

Bundle bundle = this.getArguments();
if (bundle != null) {
    String frag1Value = bundle.getString("FRAGEMNT1");
}

now put both value in fragment 2

Bundle bundle = new Bundle();
        bundle.putString("FRAGEMNT1", "VALUE FRAGEMENT 1");
        bundle.putString("FRAGEMNT2, "VALUE FRAGEMENT 2);
        fragment.setArguments(bundle);

in fragment 3 onCrete()

Bundle bundle = this.getArguments();
if (bundle != null) {
    String frag1Value = bundle.getString("FRAGEMNT1");
     String frag2Value = bundle.getString("FRAGEMNT2");
}

now put both value in fragment 3

Bundle bundle = new Bundle();
        bundle.putString("FRAGEMNT1", "VALUE FRAGEMENT 1");
        bundle.putString("FRAGEMNT2, "VALUE FRAGEMENT 2);
        bundle.putString("FRAGEMNT3, "VALUE FRAGEMENT 3");
        fragment.setArguments(bundle);

now in activit's onCrete

Bundle bundle = this.getArguments();
if (bundle != null) {
    String frag1Value=bundle.getString("FRAGEMNT1");
    String frag2Value=bundle.getString("FRAGEMNT2");
    String frag3Value=bundle.getString("FRAGEMNT3");
}
M123
  • 1,203
  • 4
  • 14
  • 31
  • I'm a bit lost, you are saying to get values of the 2 fragments in the third fragment? I want to access the edittext fields from all 3 fragments in the mainactivity/formactivity – MKer Jan 07 '21 at 05:36
  • We are passing data from first fragment to second and second to third and last combine all three fragment data in activity – M123 Jan 12 '21 at 04:51
1

You can use SharedPreferences to store the data and retrieve the data when you need it. Example :

Setting values in Preference:

SharedPreferences.Editor editor = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE).edit();
 editor.putString("key", "your answer");
 editor.apply();

Retrieve data from preference:

SharedPreferences prefs = getSharedPreferences(MY_PREFS_NAME, MODE_PRIVATE); 
String answer= prefs.getString("key", "");
AkterUzzaman
  • 52
  • 2
  • 9
  • Thanks for the answer, but all the examples I could find do this with an onclick listener in the fragment. In my case I need to access the edittext data in the fragment by clicking a button in the mainactivity/formactivity – MKer Jan 07 '21 at 12:27
  • you can easily access the data after saving it into SharedPreferences. just retrive it into Activity – AkterUzzaman Jan 07 '21 at 13:03
  • I see, it's just I'm more asking on how to do it in the fragment while a button is clicked *outside* of the fragment. I can't find any examples of this without an onclick listener in the fragment. Do you know where I can find one? – MKer Jan 07 '21 at 21:14
1

How about using ViewModel that is tied to FormActivity and save edit text value there https://developer.android.com/topic/libraries/architecture/viewmodel?gclid=Cj0KCQiA3NX_BRDQARIsALA3fIJFaBMomx-KDhghEHZghVGwGaafvDwMpGzFxQFGm3M3_e0lv9Bzt1UaAoq7EALw_wcB&gclsrc=aw.ds

class FormViewModel : ViewModel() {
   val answers: Map<String, String> = mutableMapOf()
}

fromFragment

editText.addTextChangedListener(object : TextWatcher {
      override fun afterTextChanged(s: Editable?) {
         val viewmodel = ViewModelProviders.of(requireActivity()).get(FormViewModel::class.java) 
         viewModel.answer["SOME_KEY_HERE"] = s.toString()
      }
      
    
      override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) {
      }
    
      override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) {
      }
    })

from activity you can access the value such

ViewModelProvider.of(this).get(FormViewModel::class.java).answer

Sorry, i write it in kotlin but you should be able to grab the idea

jfawkes
  • 396
  • 1
  • 7
  • Sorry, I'm very new and this is a new concept, so I can only understand in Java. I like the idea of using a viewmodel but am finding very few examples on how to do this with multiple fragments to the activity (not fragment>fragment, or activity>fragment) – MKer Jan 07 '21 at 12:53
1

The correct way to implement a regular FragmentPagerAdapter is:

public class ViewPagerAdapter extends FragmentPagerAdapter {
    public ViewPagerAdapter(FragmentManager fragmentManager) {
        super(fragmentManager);
    }

    @Override
    public Fragment getItem(int position) {
        if(position == 0) return new RulesFragment();
        if(position == 1) return new TreeFragment();
        if(position == 2) return new PredictionFragment();

        throw new IllegalStateException("Unexpected position " + position);
    }

    @Override
    public int getCount() {
        return 3;
    }

    @Override
    public CharSequence getPageTitle(int position) {
        if(position == 0) return "TREE RULES";
        if(position == 1) return "REGRESSION TREE";
        if(position == 2) return "PREDICTION";
      
        throw new IllegalStateException("Unexpected position " + position);
    }
}

To get a reference to a Fragment created by a ViewPager, use the following findFragmentByTag scheme:

Fragment fragment = supportFragmentManager.findFragmentByTag("android:switcher:" + viewPager.getId() + ":" + fragmentPosition)

The correct way to communicate between multiple fragments is to use a ViewModel scoped to a shared scope (in this case, probably the Activity).

For that, refer to https://developer.android.com/codelabs/kotlin-android-training-view-model#0

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428