简体   繁体   中英

How to call fragments method from activity?

I hope everyone's doing great, I am a beginner level Android developer and I am making a counter android app which can increment and decrement using the volume keys on our device. No matter the how long the user presses the volume up or down button(which is long press), the app will increment or decrement once.

I have achieved to make this feature in an activity, also a few days later I wanted to add bottom nav bar so I had added fragments and also achieved this in android.app.Fragment package but for some reason in my code I had to shift in this package androidx.fragment.app.Fragment.

So my question is How do I call fragments method which is from androidx.fragment.app.Fragment package from my main Activity? I have tried many many solutions, even it was working some days before but some adding more code in my app broke this part I guess. I hope you guys help me.

MainActivity.java

import android.os.Bundle;
import android.view.KeyEvent;
import android.view.MenuItem;

import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;

import com.myapp.fragments.OneFragment;
import com.myapp.fragments.TwoFragment;
import com.google.android.material.bottomnavigation.BottomNavigationView;


public class MainActivity extends AppCompatActivity implements BottomNavigationView.OnNavigationItemSelectedListener {

    final Fragment fragment1 = new OneFragment();
    final Fragment fragment2 = new TwoFragment();
    final FragmentManager fm = getSupportFragmentManager();
    BottomNavigationView bottomNavigationView;
    Fragment active = fragment1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);


        fm.beginTransaction().add(R.id.frame_layout, fragment2, "2").hide(fragment2).commit();
        fm.beginTransaction().add(R.id.frame_layout, fragment1, "1").commit();

        bottomNavigationView = findViewById(R.id.bottom_navigation_bar);
        bottomNavigationView.setOnNavigationItemSelectedListener(this);

        //End of onCreate()
    }


    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        return true;
    }


    @Override
    public boolean onKeyUp(int keyCode, KeyEvent event) {
        FragmentManager fm =getSupportFragmentManager();
        OneFragment oneFragment = (OneFragment) fm.findFragmentByTag("OneFragment");
        if (keyCode == KeyEvent.KEYCODE_VOLUME_UP) {
            oneFragment.increment();
        }
        if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN) {
            oneFragment.decrement();
        }
        return true;
    }

    @Override
    public boolean onNavigationItemSelected(@NonNull MenuItem item) {
        int itemId = item.getItemId();
        if (itemId == R.id.one_fragment) {
            item.setChecked(true);
            fm.beginTransaction().hide(active).show(fragment1).commit();
            active = fragment1;
        } else if (itemId == R.id.two_fragment) {
            item.setChecked(true);
            fm.beginTransaction().hide(active).show(fragment2).commit();
            active = fragment2;
        }
        return false;

    }

    //End of MainActivity
}

The layout of the One Fragment is simple so I don't think I have to share it and the code in java for it is simple too so I am sharing the code for increment only, decrement method same just decreases.

OneFragment.java

public void increment() {
    //if progress is less than or greater than goal - 1 than only increment the progress and update the progress bar
    if (progress <= goal - 1) {
        progress++;
        updateProgressBar();
    }

    //If quantity goes beyond the maximum limit of the count which is 9,999 to 10,000 return with this toast
    if (quantity > MAXIMUM_LIMIT) {
        Toast.makeText(getActivity(), "Cannot go beyond 10,000! Take a break", Toast.LENGTH_SHORT).show();
        return;
    }

    //If all the above condition does meet and not meet then increment quantity by 1
    quantity++;

    //If the user has switch on for vibration then do vibrate else don't
    if (vibrateToggleButton.isChecked()) {
        // this type of vibration requires API 29
        final VibrationEffect vibrationEffect;

        //This will vibrate in a modern way in devices which is greater or equal to Android 10 version Q API level 29
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.Q) {

            // create vibrator effect with the constant EFFECT_CLICK
            vibrationEffect = VibrationEffect.createPredefined(VibrationEffect.EFFECT_CLICK);
            // it is safe to cancel other vibrations currently taking place
            vibrator.cancel();
            vibrator.vibrate(vibrationEffect);
        } else {
            //This will vibrate in a old way in devices which is lesser to Android 10 version Q API level 29
            //minimum requirement of this app is marshmallow version API 23 so this old vibration will run in those devices

            // it is safe to cancel other vibrations currently taking place
            vibrator.cancel();
            vibrator.vibrate(30);
        }
    }
    //Update the views
    display(quantity, goal);
}

As soon as the volume button is pressed the app crashes and I get this NullPointerException which is common while using fragments if not worked properly with it. The increment one will give increment problem and decrement will give decrement problem##

2021-11-10 19:19:15.927 26403-26403/com.myapp E/InputEventSender: Exception dispatching finished signal. 2021-11-10 19:19:15.928 26403-26403/com.myapp E/MessageQueue-JNI: Exception in MessageQueue callback: handleReceiveCallback 2021-11-10 19:19:15.929 26403-26403/com.myapp E/MessageQueue-JNI: java.lang.NullPointerException: Attempt to invoke virtual method 'void com.app.fragments.OneFragment.increment()' on a null object reference at com.myapp.MainActivity.onKeyUp(MainActivity.java:53) at android.view.KeyEvent.dispatch(KeyEvent.java:2885) at android.app.Activity.dispatchKeyEvent(Activity.java:4175) at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:122) at androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84) at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:140) at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:599) at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59) at androidx.appcompat.app.AppCompatDelegateImpl$AppCo mpatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3068) at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:410) at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:6091) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5959) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5452) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5627) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5684) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) at andr oid.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5660) at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:5820) at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3210) at android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2752) at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2743) at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3187) at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:143) at android.os.MessageQueue.nativePollOnce(N ative Method) at android.os.MessageQueue.next(MessageQueue.java:335) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:7861) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967) 2021-11-10 19:19:15.929 26403-26403/com.myapp D/AndroidRuntime: Shutting down VM 2021-11-10 19:19:15.932 26403-26403/com.myapp E/AndroidRuntime: FATAL EXCEPTION: main Process: com.myapp, PID: 26403 java.lang.NullPointerException: Attempt to invoke virtual method 'void com.myapp.fragments.OneFragment.increment()' on a null object reference at com.duadhikr.MainActivity.onKeyUp(MainActivity.java:53) at android.view.KeyEvent.dispatch(KeyEvent.java:2885) at android.app.Activity.dispatchKeyEvent(Activity.java:4175) at androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:122) at androidx.core.view.Ke yEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84) at androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:140) at androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:599) at androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59) at androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3068) at com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:410) at android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:6091) at android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5959) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5452) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.ja va:5627) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) at android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5684) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) at android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) at android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) at android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) at android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) at android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5660) at android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:5820) at android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3210) at android.view.inputmethod.InputMethodManager.invokeFinishedInputEve ntCallback(InputMethodManager.java:2752) at android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2743) at android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3187) at android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:143) at android.os.MessageQueue.nativePollOnce(Native Method) at android.os.MessageQueue.next(MessageQueue.java:335) at android.os.Looper.loop(Looper.java:193) at android.app.ActivityThread.main(ActivityThread.java:7861) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967) 2021-11-10 19:19:15.970 26403-26403/com.duadhikr I/Process: Sending signal. PID: 26403 SIG: 9

If you want a good design you need to:

  1. Create a ViewModel.
  2. Ask for the Activity ViewModel in the Fragment so you will have the same ViewModel as the one from the Activity.
  3. Call a method in the ViewModel, from the Activity, which will update a LiveData
  4. Observe the LiveData in the Fragment.

https://developer.android.com/topic/libraries/architecture/viewmodel https://developer.android.com/topic/libraries/architecture/livedata

This is an example of sharing between two fragments. https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM