Interaction betweens Fragments and Activity

Fares :

I'm having some difficulty facing the interactions between Fragments and between Fragments and my Activity, especially ClickListener-wise.

I'm developing a Calculator app composed of 2 fragments:

  • one Fragment contains all the Buttons (numbers and operators)
  • the other contains the 2 TextViews (operation and result)

The goal here is to make these two Fragments communicate between each other. I'm supposed to send characters and strings to a TextView until I click the on the "equals" button (on which I've put a ClickListener) but I can't seem to neither use the other buttons or send the information (it keeps warning me that the TextView is null, despite the interfaces).

This is the Fragment containing all the buttons and operators :

package fr.android.calculator.Fragments;

import android.content.Context;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import fr.android.calculator.R;

/**
 * A simple {@link Fragment} subclass.
 * Activities that contain this fragment must implement the
 * {@link LowerCalculatorFragment.OnFragmentInteractionListener} interface
 * to handle interaction events.
 * Use the {@link LowerCalculatorFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class LowerCalculatorFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";


    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;

    private OnFragmentInteractionListener mListener;

    public LowerCalculatorFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment LowerCalculatorFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static LowerCalculatorFragment newInstance(String param1, String param2) {
        LowerCalculatorFragment fragment = new LowerCalculatorFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_lower_calculator, container, false);
        Button button = new Button(getContext());
        LinearLayout buttonContainer = view.findViewById(R.id.resultButtonFragment);
        button.setText("=");
        button.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT));
        button.setId(R.id.buttonEqualsFragment);
        buttonContainer.addView(button);
        button.setOnClickListener(v -> onButtonPressed(v));

        return view;
    }

    public void onViewCreated(View view, @Nullable Bundle savedInstanceState){
        super.onViewCreated(view,savedInstanceState);
    }

    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(View view) {
        if (mListener != null) {
            mListener.lowerFragmentInteraction(view);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }



    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void lowerFragmentInteraction(View view);
    }
}
<?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"
                                                   tools:context=".Activities.CalculatorSecondActivity"
                                                   android:id="@+id/frameLayout">
    <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent" android:id="@+id/numberOperators" android:baselineAligned="false">
        <LinearLayout
                android:orientation="vertical"
                android:layout_width="match_parent"
                android:layout_height="match_parent" android:id="@+id/numbers" android:layout_weight="1">
            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_weight="1"
                    android:layout_height="match_parent" android:id="@+id/SevenToPlus">
                <Button
                        android:text="7"
                        android:layout_width="0dp"
                        android:layout_height="match_parent" android:id="@+id/button18" android:layout_weight="1"
                        android:onClick="onButtonClick"/>
                <Button
                        android:text="8"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button19" android:layout_weight="1"
                        />
                <Button
                        android:text="9"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button20" android:layout_weight="1"
                        />
                <Button
                        android:text="+"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button21" android:layout_weight="1"
                        />
            </LinearLayout>
            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_weight="1"
                    android:layout_height="match_parent" android:id="@+id/FourToMinus">
                <Button
                        android:text="4"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button22" android:layout_weight="1"
                        />
                <Button
                        android:text="5"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button23" android:layout_weight="1"
                        />
                <Button
                        android:text="6"
                        android:layout_width="0dp"
                        android:layout_height="match_parent" android:id="@+id/button24" android:layout_weight="1"
                        />
                <Button
                        android:text="-"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button25" android:layout_weight="1"
                        />
            </LinearLayout>
            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" android:layout_weight="1" android:id="@+id/OneToAsterix">
                <Button
                        android:text="1"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button26" android:layout_weight="1"
                        />
                <Button
                        android:text="2"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button27" android:layout_weight="1"
                        />
                <Button
                        android:text="3"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button28" android:layout_weight="1"
                        />
                <Button
                        android:text="*"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button29" android:layout_weight="1"
                        />
            </LinearLayout>
            <LinearLayout
                    android:orientation="horizontal"
                    android:layout_width="match_parent"
                    android:layout_height="match_parent" android:layout_weight="1">
                <Button
                        android:text="0"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button32" android:layout_weight="1"
                        />
                <Button
                        android:text="/"
                        android:layout_width="0dp"
                        android:onClick="onButtonClick"
                        android:layout_height="match_parent" android:id="@+id/button33" android:layout_weight="1"
                        />
            </LinearLayout>
        </LinearLayout>
        <LinearLayout
                android:layout_margin="10dp"
                android:layout_width="match_parent"
                android:layout_height="match_parent"
                android:id="@+id/resultButtonFragment" android:layout_weight="4" android:orientation="vertical"
                >
        </LinearLayout>
    </LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>

And this is the Fragment containing both TextViews :

package fr.android.calculator.Fragments;

import android.content.Context;
import android.os.Bundle;
import android.widget.TextView;
import androidx.fragment.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import fr.android.calculator.R;

/**
 * A simple {@link Fragment} subclass.
 * Activities that contain this fragment must implement the
 * {@link UpperCalculatorFragment.OnFragmentInteractionListener} interface
 * to handle interaction events.
 * Use the {@link UpperCalculatorFragment#newInstance} factory method to
 * create an instance of this fragment.
 */
public class UpperCalculatorFragment extends Fragment {
    // TODO: Rename parameter arguments, choose names that match
    // the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
    private static final String ARG_PARAM1 = "param1";
    private static final String ARG_PARAM2 = "param2";

    // TODO: Rename and change types of parameters
    private String mParam1;
    private String mParam2;
    private TextView resultDisplay;
    private TextView operations;

    private OnFragmentInteractionListener mListener;

    public UpperCalculatorFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param param1 Parameter 1.
     * @param param2 Parameter 2.
     * @return A new instance of fragment UpperCalculatorFragment.
     */
    // TODO: Rename and change types and number of parameters
    public static UpperCalculatorFragment newInstance(String param1, String param2) {
        UpperCalculatorFragment fragment = new UpperCalculatorFragment();
        Bundle args = new Bundle();
        args.putString(ARG_PARAM1, param1);
        args.putString(ARG_PARAM2, param2);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        if (getArguments() != null) {
            mParam1 = getArguments().getString(ARG_PARAM1);
            mParam2 = getArguments().getString(ARG_PARAM2);
        }
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_upper_calculator, container, false);
        operations = view.findViewById(R.id.operationsFragment);
        resultDisplay = view.findViewById(R.id.resultDisplayFragment);

        return view;
    }

    public void setOperationsText(String operationsText){
        operations.setText(operationsText);
    }

    public void setResultDisplayText(String resultDisplayText){
        resultDisplay.setText(resultDisplayText);
    }

    // TODO: Rename method, update argument and hook method into UI event
    public void onButtonPressed(String operations) {
        if (mListener != null) {
            mListener.operationsFragmentInteraction(operations);
        }
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (context instanceof OnFragmentInteractionListener) {
            mListener = (OnFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    /**
     * This interface must be implemented by activities that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other fragments contained in that
     * activity.
     * <p>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnFragmentInteractionListener {
        // TODO: Update argument type and name
        void operationsFragmentInteraction(String operations);
        void resultDisplayFragmentInteraction(String resultDisplay);
    }
}
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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"
             tools:context=".Fragments.UpperCalculatorFragment"
             >
    <LinearLayout
            android:orientation="horizontal"
            android:layout_width="match_parent"
            android:layout_height="match_parent">
        <TextView
                android:background="@drawable/textview_border"
                android:layout_marginRight="20dp"
                android:layout_width="match_parent"
                android:layout_height="match_parent" android:id="@+id/operationsFragment" android:layout_weight="2"/>
        <TextView
                android:background="@drawable/textview_border"
                android:layout_width="match_parent"
                android:layout_height="match_parent" android:id="@+id/resultDisplayFragment" android:layout_weight="3"/>
    </LinearLayout>
</FrameLayout>

The activity itself that's in the middle is this one :

package fr.android.calculator.Activities;

import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import fr.android.calculator.Fragments.LowerCalculatorFragment;
import fr.android.calculator.Fragments.UpperCalculatorFragment;
import fr.android.calculator.R;
import org.mozilla.javascript.Context;

import java.io.DataInputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Socket;

public class CalculatorSecondActivity extends AppCompatActivity implements LowerCalculatorFragment.OnFragmentInteractionListener, UpperCalculatorFragment.OnFragmentInteractionListener {
    private LowerCalculatorFragment lowerCalculatorFragment;
    private UpperCalculatorFragment upperCalculatorFragment;
    private Context rhino = Context.enter(); // runtime environment 
    private TextView operations;
    private Button value;
    private String result;
    private TextView resultDisplay;
    private Handler handler;
    private DataInputStream dataInputStream;
    private DataOutputStream dataOutputStream;
    private String resp;
    private Socket socket;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_calculator_second);
        lowerCalculatorFragment = LowerCalculatorFragment.newInstance("fragment", "you");
        upperCalculatorFragment = UpperCalculatorFragment.newInstance("new fragment", "you 2");

        getSupportFragmentManager().beginTransaction().add(R.id.lowerCalculator, lowerCalculatorFragment);
        getSupportFragmentManager().beginTransaction().add(R.id.upperCalculator, upperCalculatorFragment);

        getSupportFragmentManager().beginTransaction().commit();

    }

    @Override
    public void operationsFragmentInteraction(String message) {
        upperCalculatorFragment = (UpperCalculatorFragment) getSupportFragmentManager().findFragmentById(R.id.upperCalculator);
        upperCalculatorFragment.setOperationsText(message);

    }

    @Override
    public void resultDisplayFragmentInteraction(String message) {
        upperCalculatorFragment = (UpperCalculatorFragment) getSupportFragmentManager().findFragmentById(R.id.upperCalculator);
        upperCalculatorFragment.setResultDisplayText(message);
    }


    @Override
    public void lowerFragmentInteraction(View view) {

        value = view.findViewById(view.getId());
        System.out.println("Value : " + value.getText());
        if (value != view.findViewById(R.id.buttonEqualsFragment)) {
            System.out.println("Value : " + value.getText());
            operationsFragmentInteraction((String) value.getText());
        } else {
            // Avec Async 
            new AsyncTaskRunner().execute((String) operations.getText());

            /* Avec Handler*/
            //calculate((String) operations.getText());

        }
    }

    public void onButtonClick(View view){
        lowerCalculatorFragment.onButtonPressed(view);
    }

    public void calculate(String operations) {
        Runnable runnable = () -> {
            try {
                socket = new Socket("10.0.2.2", 9876);
                dataInputStream = new DataInputStream(socket.getInputStream());
                dataOutputStream = new DataOutputStream(socket.getOutputStream());
                dataOutputStream.writeUTF(operations);
                result = dataInputStream.readUTF();
                dataOutputStream.close();
                dataInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
            handler.post(() -> resultDisplay.setText(result));
        };

        new Thread(runnable).start();
    }

    private class AsyncTaskRunner extends AsyncTask<String, String, String> {


        @Override
        protected String doInBackground(String... params) {
            try {
                socket = new Socket("10.0.2.2", 9876);
                dataInputStream = new DataInputStream(socket.getInputStream());
                dataOutputStream = new DataOutputStream(socket.getOutputStream());
                dataOutputStream.writeUTF(params[0]);
                result = dataInputStream.readUTF();

                dataOutputStream.close();
                dataInputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }

            publishProgress(params[0]); // Calls onProgressUpdate()
            resp = "Slept for " + 5 + " seconds";
            return resp;
        }

        protected void onProgressUpdate(String... text) {
            resultDisplayFragmentInteraction(result);
        }

    }
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:tools="http://schemas.android.com/tools"
        xmlns:app="http://schemas.android.com/apk/res-auto"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        app:layout_behavior="@string/appbar_scrolling_view_behavior"
        tools:showIn="@layout/activity_calculator_second"
        tools:context=".Activities.CalculatorSecondActivity">


    <LinearLayout android:orientation="vertical"
                  android:layout_width="match_parent"
                  android:layout_height="match_parent" android:weightSum="5">
        <fragment android:layout_width="match_parent" android:layout_height="119dp"
                  android:name="fr.android.calculator.Fragments.UpperCalculatorFragment"
                  android:id="@+id/upperCalculator"
                  tools:layout="@layout/fragment_upper_calculator"
                  android:layout_weight="1"/>
        <fragment android:name="fr.android.calculator.Fragments.LowerCalculatorFragment"
                  android:layout_width="409dp" android:layout_height="413dp"
                  android:id="@+id/lowerCalculator" tools:layout="@layout/fragment_lower_calculator"
                  android:layout_weight="4"/>
    </LinearLayout>


</androidx.constraintlayout.widget.ConstraintLayout>

Do you guys have any ideas why?

Thanks in advance,

Fares.

EDIT : I've added methods to my Fragments after the wise advice from Edgar. If I understood correctly, the interfaces allow us to fetch the informations, that will be used in the methods of the fragments. However even though I've added ClickListeners to my buttons, only the buttonEqualsFragmentis "listened" to. Even if I debug unless I click on the buttonEqualsFragment the other buttons aren't taken in account.

I created a method in the Activity to do that called the Fragment's implemented interface. (onButtonClick)

Edgar :

The problem is you are trying to display the info in the activity and not in the fragment. Try the following suggestions.

In yourUpperCalculatorFragment class place this at the top:

private Textview operations, results;

then in onCreateView() add the following to ensure the textviews are not null:

operations = view.findViewById(R.id.operationsFragment);
results = view.findViewById(R.id.resultDisplayFragment);

Next you have to define a public method still in this fragment (UpperCalculatorFragment) that will handle information from the Activity. I only show result you need another for operation as well

public void displayResult(String result){
    results.setText(result);//this displays the result on textview
}

To display the result/operation you have to call that method from the CalculatorSecondActivity like so;

public void displayResultInFragment(String message) {
    UpperCalculatorFragment upperFrag = 
    (UpperCalculatorFragment) getSupportFragmentManager()
      .findFragmentById(R.id.upperCalculator);
    //show the result here after all calculation
    upperFrag.displayResult(message);

}  

please refer to this android developers docs

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=319976&siteId=1