如何从活动中调用片段方法?

Posted

技术标签:

【中文标题】如何从活动中调用片段方法?【英文标题】:How to call fragments method from activity? 【发布时间】:2021-12-23 01:25:38 【问题描述】:

我希望每个人都做得很好, 我是一名初学者级别的 android 开发人员,我正在制作一个计数器 Android 应用程序,它可以使用我们设备上的音量键递增和递减。无论用户按下音量增大或减小按钮(长按)多长时间,应用都会增加或减少一次。

我已经在一个活动中实现了这个功能,几天后我想添加底部导航栏,所以我添加了片段并在 android.app.Fragment 包中实现了这个,但由于某种原因在我的代码中我不得不在这个包中转移androidx.fragment.app.Fragment。

所以我的问题是 如何从我的主 Activity 调用来自 androidx.fragment.app.Fragment 包的 Fragment 方法?我已经尝试了很多解决方案,即使它在几天前工作但是我猜一些在我的应用程序中添加更多代码破坏了这部分。希望大家帮帮我。

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

One Fragment 的布局很简单,所以我认为我不必分享它 而且java中的代码也很简单,所以我只分享增量代码,减量方法同样只是减少。

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);

一旦按下音量按钮,应用程序就会崩溃,我会得到这个 NullPointerException,如果不能正常使用,这在使用片段时很常见。递增一会出现递增问题,递减会出现递减问题##

2021-11-10 19:19:15.927 26403-26403/com.myapp E/InputEventSender: 异常调度完成信号。 2021-11-10 19:19:15.928 26403-26403/com.myapp E/MessageQueue-JNI:MessageQueue 中的异常 回调:handleReceiveCallback 2021-11-10 19:19:15.929 26403-26403/com.myapp E/MessageQueue-JNI: java.lang.NullPointerException:尝试调用虚拟方法'void com.app.fragments.OneFragment.increment()' 在空对象引用上 在 com.myapp.MainActivity.onKeyUp(MainActivity.java:53) 在 android.view.KeyEvent.dispatch(KeyEvent.java:2885) 在 android.app.Activity.dispatchKeyEvent(Activity.java:4175) 在 androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:122) 在 androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84) 在 androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:140) 在 androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:599) 在 androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59) 在 androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3068) 在 com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:410) 在 android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:6091) 在 android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5959) 在 android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5452) 在 android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) 在 android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) 在 android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5627) 在 android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) 在 android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5684) 在 android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) 在 android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) 在 android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) 在 android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) 在 android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) 在 android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) 在 android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) 在 android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5660) 在 android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:5820) 在 android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3210) 在 android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2752) 在 android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2743) 在 android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3187) 在 android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:143) 在 android.os.MessageQueue.nativePollOnce(本机方法) 在 android.os.MessageQueue.next(MessageQueue.java:335) 在 android.os.Looper.loop(Looper.java:193) 在 android.app.ActivityThread.main(ActivityThread.java:7861) 在 java.lang.reflect.Method.invoke(本机方法) 在 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967) 2021-11-10 19:19:15.929 26403-26403/com.myapp D/AndroidRuntime: 关闭 VM 2021-11-10 19:19:15.932 26403-26403/com.myapp E/AndroidRuntime: 致命异常: main 进程:com.myapp,PID:26403 java.lang.NullPointerException:尝试在空对象上调用虚拟方法“void com.myapp.fragments.OneFragment.increment()” 参考 在 com.myapp.MainActivity.onKeyUp(MainActivity.java:53) 在 android.view.KeyEvent.dispatch(KeyEvent.java:2885) 在 android.app.Activity.dispatchKeyEvent(Activity.java:4175) 在 androidx.core.app.ComponentActivity.superDispatchKeyEvent(ComponentActivity.java:122) 在 androidx.core.view.KeyEventDispatcher.dispatchKeyEvent(KeyEventDispatcher.java:84) 在 androidx.core.app.ComponentActivity.dispatchKeyEvent(ComponentActivity.java:140) 在 androidx.appcompat.app.AppCompatActivity.dispatchKeyEvent(AppCompatActivity.java:599) 在 androidx.appcompat.view.WindowCallbackWrapper.dispatchKeyEvent(WindowCallbackWrapper.java:59) 在 androidx.appcompat.app.AppCompatDelegateImpl$AppCompatWindowCallback.dispatchKeyEvent(AppCompatDelegateImpl.java:3068) 在 com.android.internal.policy.DecorView.dispatchKeyEvent(DecorView.java:410) 在 android.view.ViewRootImpl$ViewPostImeInputStage.processKeyEvent(ViewRootImpl.java:6091) 在 android.view.ViewRootImpl$ViewPostImeInputStage.onProcess(ViewRootImpl.java:5959) 在 android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5452) 在 android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) 在 android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) 在 android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5627) 在 android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) 在 android.view.ViewRootImpl$AsyncInputStage.apply(ViewRootImpl.java:5684) 在 android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) 在 android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) 在 android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) 在 android.view.ViewRootImpl$InputStage.apply(ViewRootImpl.java:5483) 在 android.view.ViewRootImpl$InputStage.deliver(ViewRootImpl.java:5456) 在 android.view.ViewRootImpl$InputStage.onDeliverToNext(ViewRootImpl.java:5509) 在 android.view.ViewRootImpl$InputStage.forward(ViewRootImpl.java:5475) 在 android.view.ViewRootImpl$AsyncInputStage.forward(ViewRootImpl.java:5660) 在 android.view.ViewRootImpl$ImeInputStage.onFinishedInputEvent(ViewRootImpl.java:5820) 在 android.view.inputmethod.InputMethodManager$PendingEvent.run(InputMethodManager.java:3210) 在 android.view.inputmethod.InputMethodManager.invokeFinishedInputEventCallback(InputMethodManager.java:2752) 在 android.view.inputmethod.InputMethodManager.finishedInputEvent(InputMethodManager.java:2743) 在 android.view.inputmethod.InputMethodManager$ImeInputEventSender.onInputEventFinished(InputMethodManager.java:3187) 在 android.view.InputEventSender.dispatchInputEventFinished(InputEventSender.java:143) 在 android.os.MessageQueue.nativePollOnce(本机方法) 在 android.os.MessageQueue.next(MessageQueue.java:335) 在 android.os.Looper.loop(Looper.java:193) 在 android.app.ActivityThread.main(ActivityThread.java:7861) 在 java.lang.reflect.Method.invoke(本机方法) 在 com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:600) 在 com.android.internal.os.ZygoteInit.main(ZygoteInit.java:967) 2021-11-10 19:19:15.970 26403-26403/com.duadhikr I/Process: 发送 信号。 PID:26403 SIG:9

【问题讨论】:

"this part" 很好,但请确保您使用"OneFragment" 创建/添加Fragment。 “打破这部分”是什么意思。显示日志 使用 instanceOf 函数代替智能转换来确保所需的片段当前处于活动状态。 我的fragment是活跃的,两个fragment的名字是OneFragment和SecondFragment,虽然在app里的名字用我自己的语言不一样,但是我已经改了,方便大家理解 【参考方案1】:

如果你想要一个好的设计,你需要:

    创建一个 ViewModel。 请求 Fragment 中的 Activity ViewModel,这样您将拥有与 Activity 中的 ViewModel 相同的 ViewModel。 从 Activity 调用 ViewModel 中的方法,这将更新 LiveData 观察片段中的 LiveData。

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

这是两个片段之间共享的示例。 https://developer.android.com/topic/libraries/architecture/viewmodel#sharing

【讨论】:

我是初学者,这对我来说有点难以理解,请提供其他解决方案。

以上是关于如何从活动中调用片段方法?的主要内容,如果未能解决你的问题,请参考以下文章

从意图活动访问片段方法

从活动中调用片段事件

从片段中调用父母的活动

从片段调用活动方法

从片段内部调用活动方法[重复]

从活动中调用片段方法