0

My app's MainActivity has a ViewPager which has three children(swipe views). When swiped to last child, the ActionBar gets two menu items. On clicking one item, a Dialog pops up to add a name of an item into a Database. On clicking second menu item, another Dialog pops up that prompts user to enter the name of the item that is to be removed from the Database. These Dialogs are getting constructed by separate Dialog Fragments. Basically what I want is to get the callback event from the Dialog that the positive or negative button has been clicked and wanna perform some action in the fragment which is as I mentioned is the last child of my ViewPager. I've seen the Android documentation as well as a Youtube video to learn how to implement the interface, yet my app's crashing.

Here's the code for my DialogFragment which shows a Dialog that prompts the user to enter input and save it to the Database.

package com.example.android.mybusiness;

import android.app.AlertDialog;
import android.app.Dialog;
import android.content.Context;
import android.content.DialogInterface;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.Toast;

public class AddCigaretteNameDialog extends DialogFragment {

    EditText userInput;
    String cigaretteName;
    DbHelper dbHelper;

    public AddCigaretteNameDialogListener listener;

    public interface AddCigaretteNameDialogListener {
        void onDialogPositiveClick(String name);
    }

    @NonNull
    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {

        dbHelper = new DbHelper(getContext());

        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        // SET THE TITLE FOR THE DIALOG.
        builder.setTitle(R.string.add_title);
        // GET THE LAYOUT INFLATOR
        LayoutInflater inflater = getActivity().getLayoutInflater();
        // INFLATE THE LAYOUT AND PUT IT INTO A VARIABLE.
        // PASS NULL AS PARENT VIEW, BECAUSE IT'S GOING IN THE DIALOG
        View view = inflater.inflate(R.layout.edit_text_for_dialogs, null);
        // GET THE EDIT_TEXT FROM THE 'VIEW' VARIABLE.
        userInput = view.findViewById(R.id.cigarette_name_user_input);
        // SET THE LAYOUT FOR THE DIALOG
        builder.setView(view);
        // SET ACTION BUTTONS
            builder.setPositiveButton(R.string.save, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    // GET THE USER INPUT FROM THE EDIT_TEXT ABOVE AND PUT IT INTO A VARIABLE.
                    cigaretteName = userInput.getText().toString();
                    // PUT THE USER INPUT IN THE DATABASE.
                    Boolean result = dbHelper.insertCigaretteName(cigaretteName);
                    if (result) {

                        // SHOW SUCCESS MESSAGE THAT CIGARETTE NAME HAS BEEN INSERTED.
                        Toast.makeText(getContext(), cigaretteName + " has been added successfully!", Toast.LENGTH_SHORT).show();

                        listener.onDialogPositiveClick(cigaretteName);



                    } else {Toast.makeText(getContext(), "Something is wrong", Toast.LENGTH_SHORT).show();}
                }
            })
            .setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                @Override
                public void onClick(DialogInterface dialogInterface, int i) {
                    dismiss();
                }
            });



        // RETURN THE DIALOG.
        return builder.create();
    }


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

        // VERIFY THAT THE HOST ACTIVITY IMPLEMENTS THE CALLBACK INTERFACE
        try {
            listener = (AddCigaretteNameDialogListener) getTargetFragment();
        } catch (ClassCastException e){
            // THE ACTIVITY DOESN'T IMPLEMENT INTERFACE, THROW AN EXCEPTION.
            throw new ClassCastException(getActivity().toString() + " must implement listener");
        }

    }
}

And here's the fragment which is the last child of the view pager.

package com.example.android.mybusiness;

import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.Spinner;
import android.widget.Toast;


/**
 * A simple {@link Fragment} subclass.
 */
public class Purchase extends Fragment implements AddCigaretteNameDialog.AddCigaretteNameDialogListener {
    Spinner spinner;
    DbHelper dbHelper;
    ArrayAdapter<String> arrayAdapter;



    // FRAGMENT'S CONSTRUCTOR.
    public Purchase() { }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // KIND OF REQUIREMENT FOR OPTION MENUS.
        setHasOptionsMenu(true);
        // INSTANTIATING THE OBJECT TO RUN A FUNCTION, WHICH GETS THE CIGARETTES NAME.
        dbHelper = new DbHelper(getContext());

        // Inflate the layout for this fragment
        // AND PUT IT INTO A VARIABLE SO WIDGETS CAN BE ACCESSED.
        View view = inflater.inflate(R.layout.fragment_purchase, container, false);
        // FIND THE spinner.
        spinner = view.findViewById(R.id.spinner);

        // CREATING THIS ADAPTER TO POPULATE THE SPINNER WIDGET.
        arrayAdapter = new ArrayAdapter<String>(getContext(), android.R.layout.simple_list_item_1, dbHelper.getAllCigaretteNames());

        // SETTING THE ADAPTER TO THE SPINNER, WHICH WILL PROVIDE THE CONTENT TO BE SELECTED BY ME.
        spinner.setAdapter(arrayAdapter);


        return view;
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        inflater.inflate(R.menu.menu, menu);
        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.add_cigarette:
                AddCigaretteNameDialog addCigaretteNameDialog = new AddCigaretteNameDialog();
                addCigaretteNameDialog.show(getFragmentManager(), "add_cigarette_name");
                return true;
            case R.id.remove_cigarette:
                RemoveCigaretteNameDailog RemoveCigaretteNameDailog = new RemoveCigaretteNameDailog();
                RemoveCigaretteNameDailog.show(getFragmentManager(), "add_cigarette_name");
            ;    return true;
            default:
                return super.onOptionsItemSelected(item);
        }

    }

    @Override
    public void onDialogPositiveClick(String name) {
        arrayAdapter.add(name);
    }
}

Here's the crash log:

12-07 13:13:59.278 25327-25327/com.example.android.mybusiness E/AndroidRuntime: FATAL EXCEPTION: main Process: com.example.android.mybusiness, PID: 25327 java.lang.NullPointerException: Attempt to invoke interface method 'void com.example.android.mybusiness.AddCigaretteNameDialog$AddCigaretteNameDialogListener.onDialogPositiveClick(java.lang.String)' on a null object reference at com.example.android.mybusiness.AddCigaretteNameDialog$2.onClick(AddCigaretteNameDialog.java:58) at com.android.internal.app.AlertController$ButtonHandler.handleMessage(AlertController.java:162) at android.os.Handler.dispatchMessage(Handler.java:102) at android.os.Looper.loop(Looper.java:135) at android.app.ActivityThread.main(ActivityThread.java:5296) at java.lang.reflect.Method.invoke(Native Method) at java.lang.reflect.Method.invoke(Method.java:372) at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:912) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:707)

Line 58 is

listener.onDialogPositiveClick(cigaretteName);

Thanks in advance!

Savin Sharma
  • 769
  • 5
  • 22
Ari
  • 97
  • 1
  • 1
  • 11
  • your crash log is getTargetFragment() is null? – MathanG Dec 07 '18 at 07:29
  • 1
    post your crash log – MathanG Dec 07 '18 at 07:30
  • I'll just post it. Gimme few minutes – Ari Dec 07 '18 at 07:35
  • done updating with crash log. – Ari Dec 07 '18 at 07:49
  • addCigaretteNameDialog.setTargetFragment(Purchase.this,0) add this line before addCigaretteNameDialog.show(getFragmentManager(), "add_cigarette_name"); – MathanG Dec 07 '18 at 07:54
  • 1
    https://stackoverflow.com/questions/13733304/callback-to-a-fragment-from-a-dialogfragment?rq=1 refer this if you have more doubt – MathanG Dec 07 '18 at 07:59
  • I'll just try it in couple of minutes. AS is being opened. :) – Ari Dec 07 '18 at 08:02
  • It is showing error saying "getTarhetFragment() in Fragment can't be applied to (com.example.android.mybusiness.Purchase, int)" – Ari Dec 07 '18 at 08:12
  • Bro you know what, the reference of yours worked! But not the checked answer. Thanks for your help! N I checked it before posting my question. Since I couldn't understand it, I left it. Really need to work hard to be a good developer. Don't I? – Ari Dec 07 '18 at 08:27
  • it will come with experience bro. cool. glad it helps. cheers – MathanG Dec 07 '18 at 09:17

1 Answers1

0

If you use custom dialog class with custom view this would solve your problem. I am posting a code below which shows a custom dialog with views. first of all create a layout file for your dialog design by name dlg_add_cigarette_name inside layout folder

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:background="@drawable/dialog_background_inset"
android:layout_height="wrap_content">

<TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_marginTop="20dp"
    android:layout_marginLeft="16dp"
    android:text="Cigrate Name:"
    android:id="@+id/tv_label_total"
    />
<LinearLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:id="@+id/layout_total_update"
    android:orientation="horizontal"
    android:layout_below="@+id/tv_label_total"
    android:weightSum="2"
    >
    <EditText
        android:layout_width="0dp"
        android:layout_height="wrap_content"
        android:layout_weight="1.5"
        android:layout_marginLeft="16dp"
        android:layout_marginRight="10dp"
        android:id="@+id/et_cgrate_name"
        android:inputType="text"
        />
</LinearLayout>



<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/layout_total_update"
    android:layout_alignParentRight="true"
    android:background="@android:color/transparent"

    android:layout_marginRight="70dp"
    android:text="OK"
    android:layout_marginBottom="20dp"
    android:layout_marginTop="20dp"
    android:id="@+id/button_ok"
    android:textColor="@color/colorPrimaryDark"
    />

<Button
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_below="@+id/layout_total_update"
    android:layout_toLeftOf="@+id/button_ok"
    android:layout_marginRight="30dp"
    android:text="Cancel"
    android:layout_marginBottom="20dp"
    android:layout_marginTop="20dp"
    android:background="@android:color/transparent"
    android:id="@+id/button_cancel"
    android:textColor="@color/colorPrimaryDark"
    />

Now inside drawable folder create a new file by name dialog_background_insetand update the file by following code

<?xml version="1.0" encoding="utf-8"?>
<inset
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:drawable="@color/white"
    android:insetRight="15dp"
    android:insetLeft="15dp">
</inset>

and now in your AddCigaretteNameDialog file update the class by following code

import android.annotation.SuppressLint;
import android.app.Dialog;
import android.content.Context;
import android.content.res.Resources;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;

import com.drinkwater.reminder.watertracker.R;

import butterknife.BindView;
import butterknife.ButterKnife;
import butterknife.OnClick;
import butterknife.Unbinder;


public class AddCigaretteNameDialog extends DialogFragment {
    private static final float DIM_AMOUNT = 0.4f;
    @BindView(R.id.tv_label_total)
    TextView tvLabelTotal;
    @BindView(R.id.et_cgrate_name)
    EditText etCgrateName;
    @BindView(R.id.layout_total_update)
    LinearLayout layoutTotalUpdate;
    @BindView(R.id.button_ok)
    Button buttonOk;
    @BindView(R.id.button_cancel)
    Button buttonCancel;

    @OnClick(R.id.button_cancel)
    public void onClick(){
        dismiss();
    }
    @OnClick(R.id.button_ok)
    public void onClickOkay(){
        unitCallBack.addCigarette(cigaretteName);
    }



    private Unbinder mUnbinder;
    private AddCigaretteName unitCallBack;

    private String cigaretteName;



    public AddCigaretteNameDialog() {
    }

    public static AddCigaretteNameDialog newInstance(String title) {
        AddCigaretteNameDialog frag = new AddCigaretteNameDialog();
        Bundle args = new Bundle();
        args.putString("title", title);
        frag.setArguments(args);
        return frag;
    }

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        if (unitCallBack == null && context instanceof AddCigaretteName) {
            unitCallBack = (AddCigaretteName) context;
        }
    }

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

    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        final Dialog dialog = super.onCreateDialog(savedInstanceState);

        final Window window = dialog.getWindow();
        if (window != null) {
            window.requestFeature(Window.FEATURE_NO_TITLE);
            window.setBackgroundDrawableResource(android.R.color.transparent);

            WindowManager.LayoutParams windowLayoutParams = window.getAttributes();
            windowLayoutParams.dimAmount = DIM_AMOUNT;
        }
        dialog.setCancelable(false);
        dialog.setCanceledOnTouchOutside(false);
        return dialog;
    }


    @SuppressLint("NewApi")
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.test, container, false);
        mUnbinder = ButterKnife.bind(this, view);

        etCgrateName.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {

                cigaretteName=s.toString();
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        return view;
    }

    @Override
    public void onResume() {
        super.onResume();
        final int width = getScreenWidth();
        int height = getScreenHeight() / 2;
        changeWindowSizes(width, ViewGroup.LayoutParams.WRAP_CONTENT);
    }

    private void changeWindowSizes(int width, int height) {
        final Window window = getDialog().getWindow();
        if (window != null) {
            window.setLayout(width, height);
        }
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        mUnbinder.unbind();
    }

    @Override
    public int getTheme() {
        return R.style.CustomDialog;
    }

    public interface AddCigaretteName {
        void addCigarette(String name);
    }

    public static int getScreenWidth() {
        return Resources.getSystem().getDisplayMetrics().widthPixels;
    }

    public static int getScreenHeight() {
        return Resources.getSystem().getDisplayMetrics().heightPixels;
    }
}

create a custom theme inside styles file

 <style name="CustomDialog" parent="Theme.AppCompat.Light.Dialog">
        <item name="android:windowAnimationStyle">@style/CustomDialogAnimation</item>
    </style>

    <style name="CustomDialogAnimation">
        <item name="android:windowEnterAnimation">@anim/translate_left_side</item>
        <item name="android:windowExitAnimation">@anim/translate_right_side</item>
    </style>

create a package anim inside resfolder and create two files translate_left_sideand translate_right_side translate_right_side

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:fromXDelta="0%" android:toXDelta="100%"
    android:fromYDelta="0%" android:toYDelta="0%"
    android:duration="600"/>

translate_left_side

<?xml version="1.0" encoding="utf-8"?>
<translate xmlns:android="http://schemas.android.com/apk/res/android"
    android:duration="600"
    android:fromXDelta="100%"
    android:toXDelta="0%"/>

Your AddCigrateDialog is ready to appear.Now its time to call this dialog from button click.Inside Purchase make an object of AddCigaretteDialog class globally

AddCigaretteNameDialog addCigaretteNameDialog;

@Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
            case R.id.add_cigarette:
                 addCigaretteNameDialog= new AddCigaretteNameDialog();            
                 addCigaretteNameDialog= AddCigaretteNameDialog.newInstance("AddCigrateNameDialog");
                 addCigaretteNameDialog.show(manager,"show");
                return true;
            case R.id.remove_cigarette:
                RemoveCigaretteNameDailog RemoveCigaretteNameDailog = new RemoveCigaretteNameDailog();
                RemoveCigaretteNameDailog.show(getFragmentManager(), "add_cigarette_name");
            ;    return true;
            default:
                return super.onOptionsItemSelected(item);
        }

    }

And yes don't forget to implement the interface of AddCigaretteDialog inside Purchase dialog

As I am using ButterKnife dependency add the following lines inside your app's gradle file

inal BUTTER_KNIFE_VERSION = "8.8.1"
implementation "com.jakewharton:butterknife:$BUTTER_KNIFE_VERSION"
annotationProcessor "com.jakewharton:butterknife-compiler:$BUTTER_KNIFE_VERSION"
Raza
  • 791
  • 7
  • 22
  • Actually comments right below my question worked. Thanks for your help though. I really appreciate it buddy. – Ari Dec 07 '18 at 15:07