从片段调用活动方法

382
尝试从片段中调用我的活动中的方法。我希望片段提供方法数据并在方法返回时获取数据。我想实现类似于调用静态方法,但不使用静态方法是因为它会在活动中创建问题。
对片段新手,需要简单易懂的解释!
谢谢!

https://dev59.com/fmgv5IYBdhLWcg3wBMSg#73796270 - Zahid Iqbal
18个回答

941

从片段到活动:

((YourActivityClassName)getActivity()).yourPublicMethod();

从Activity到Fragment:

FragmentManager fm = getSupportFragmentManager();

//if you added fragment via layout xml
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentById(R.id.your_fragment_id);
fragment.yourPublicMethod();
如果您通过代码添加了片段,并在添加片段时使用了tag字符串,则应改用findFragmentByTag
YourFragmentClass fragment = (YourFragmentClass)fm.findFragmentByTag("yourTag");

10
请注意,如果类型转换不起作用,可能会发生意外情况。:S - Ewoks
57
为避免类型转换问题,请使用以下代码:Activity act = getActivity(); if (act instanceof YourActivityClassName) ((YourActivityClassName) act).yourPublicMethod(); - ericharlow
17
将Activity转换为Fragment是不良设计且存在安全隐患。而Fragment则不受限于特定的Activity。 - Aviv Ben Shabat
2
片段旨在重复使用并在任何活动中设置。如果我有5个活动使用相同的片段怎么办?Marco的答案是正确的,并且对于片段间和活动-片段通信是一个好的实践。 - Karan
3
不一定。碎片可以被用作任何被分解的更大活动的“碎片”,例如为了创建响应式用户界面。我很少使用同一个碎片并将其附加到不同的活动主机上。 - Richard
显示剩余8条评论

233

如果您想在其他地方使用它,最好将片段与活动解耦。您可以通过创建一个接口来实现这一点,而该接口由您的活动实现。

因此,您可以定义以下接口:

例如,假设您想给该活动一个字符串并让其返回一个整数:

public interface MyStringListener{
    public Integer computeSomething(String myString);
}

这可以在片段或单独的文件中定义。

然后你可以让你的活动实现该接口。

public class MyActivity extends FragmentActivity implements MyStringListener{

  @Override
  public Integer computeSomething(String myString){
   /** Do something with the string and return your Integer instead of 0 **/ 
   return 0;
  }

}

然后在您的片段中,您将拥有一个MyStringListener变量,并且您将在片段的onAttach(Activity activity)方法中设置监听器。

public class MyFragment {

        private MyStringListener listener;

        @Override
        public void onAttach(Context context) {
            super.onAttach(context);
            try {
                listener = (MyStringListener) context;
            } catch (ClassCastException castException) {
                /** The activity does not implement the listener. */
            }
        }

    }

编辑(2015年12月17日):onAttach(Activity activity)已过时,改用onAttach(Context context),它能正常工作。

第一个答案肯定有效,但它会将您当前的片段与宿主活动耦合在一起。最好的做法是将片段与宿主活动解耦,以防您想在另一个活动中使用它。


71
对于其他查看此内容的人,虽然已被接受的答案显然可行,但从设计角度来看,这是更好且更安全的方法。 - Paul Richter
10
从代码设计的角度来看,这个答案要好得多。而且如果活动被错误地转换,它也不会导致崩溃。 - David T.
3
+1,但我不会在onAttach中使用try-catch。让它失败。如果监听器是可选的(即失败不合适),请向片段添加set/addListener方法。 - ataulm
在MyFragment.OnAttach方法中,您需要将其转换为Activity。针对@Kay提到的情况,假设我有5个使用相同片段的Activity,我认为转换代码应该更改,对吗? 为了支持多个Activity,OnAttach中的代码应该是什么样子的?我应该为每个Activity使用switch-case吗? - Raymond Lukanta
1
我认为MyFragment重写onDetach并将监听器设置为null以避免内存泄漏也非常重要。 - Mateus Gondim
显示剩余7条评论

97

对于Kotlin开发者

(activity as YourActivityClassName).methodName()

针对Java开发人员

((YourActivityClassName) getActivity()).methodName();

如果我们运行这段代码,它会在 Kotlin 中出现错误.. 有其他的方法吗? - Jakegarbo
1
当我运行它时,ActivityClass 的值为 null,我认为这不是在 Kotlin 中正确的做法,即使没有错误。或者可能是一个 bug? - Jakegarbo
@JakeGarbo 这是正确的方式,否则就有12个人没有为它投票。另外,有时 getActivity() 会返回 null,请在 SO 上检查这些问题。 - Wasim K. Memon
这里有一件重要的事情。在你的片段中,你可以直接调用父活动。如果你尝试在片段中调用其他活动(非父活动)的方法,你可能会得到空引用错误或无法转换错误。 - Abdullah

16

在我更深入了解片段(fragments)的工作原理后,更新此内容。每个片段都属于一个父活动(parent activity)。因此只需使用:

getActivity().whatever
在片段内部,这是更好的答案,因为您可以避免不必要的转换。如果您无法使用此解决方案避免转换,请使用下面的解决方案。
你需要做的是将其转换为外部活动。
((MainActivity) getActivity()).Method();

创建一个新实例将会混淆安卓框架,导致无法识别。另请参见:

https://dev59.com/e2fWa4cB1Zd3GeqPg2kT#12014834

https://dev59.com/cnI-5IYBdhLWcg3wCztn#2042829


10
尽管我完全喜欢Marco的回答,但我认为指出你也可以使用基于发布/订阅的框架来实现相同的结果是公平的,例如如果您选择事件总线,您可以执行以下操作。 片段:
EventBus.getDefault().post(new DoSomeActionEvent()); 

活动:

 @Subscribe
onSomeActionEventRecieved(DoSomeActionEvent doSomeActionEvent){
//Do something

}

使用单例模式(不依赖任何依赖注入库),并使该类扩展DefaultLifecycleObserver,将其附加到活动上以在onResume()onPause()中订阅/取消订阅它,这个“事件总线”可以是您想要的任何类。 - Eduardo

9

这是我完成此任务的步骤:

首先创建一个接口

interface NavigationInterface {
    fun closeActivity()
}

接下来确保activity实现了接口并覆盖了接口方法

class NotesActivity : AppCompatActivity(), NavigationInterface {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_notes)
        setSupportActionBar(findViewById(R.id.toolbar))
    }

    override fun closeActivity() {
        this.finish()
    }
}

请确保在片段中创建接口监听器。

private lateinit var navigationInterface: NavigationInterface

override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
): View? {
    //establish interface communication
    activity?.let {
        instantiateNavigationInterface(it)
    }
    // Inflate the layout for this fragment
    return inflater.inflate(R.layout.fragment_notes_info, container, false)
}

private fun instantiateNavigationInterface(context: FragmentActivity) {
    navigationInterface = context as NavigationInterface
}

那么你可以像这样进行调用:

view.findViewById<Button>(R.id.button_second).setOnClickListener {
    navigationInterface.closeActivity()
}

优秀的人 @Droid Chris - kamal douma

6

从它们各自的fragment中调用activity方法的最佳方式

(activity as YourActivity).activtiyMethod()

在您的活动中使用此行。例如

假设您有活动A和方法add(),以及您的碎片ABC,您想从碎片ABC调用方法add,

(activity as A).add()

6
在 Kotlin 中,您可以像以下示例一样从片段中调用活动方法:
var mainActivity: MainActivity = activity as MainActivity
        mainActivity.showToast() //Calling show toast method of activity

4

感谢@BIJAY_JHA和@Manaus。我使用了Kotlin版本来调用位于Activity中的signIn()方法,并从Fragment中调用它。我在Android中使用导航架构,因此Listener接口模式不在Fragment中:

 (activity as MainActivity).signIn() 

3

如果你想要通过片段访问在你的Activity中声明的函数,请使用接口,如marco所示的答案。

如果你没有标签或ID,你可以使用此方法来通过你的Activity访问在片段中声明的函数。

private void setupViewPager(ViewPager viewPager) {
    //fragmentOne,fragmentTwo and fragmentThree are all global variables
    fragmentOne= new FragmentOne();
    fragmentTwo= new FragmentTwo();
    fragmentThree = new FragmentThree();

    viewPagerAdapteradapter = new ViewPagerAdapter(getSupportFragmentManager());
    viewPagerAdapteradapter.addFragment(fragmentOne, "Frag1");
    viewPagerAdapteradapter.addFragment(fragmentTwo, "Frag2");
    viewPagerAdapteradapter.addFragment(fragmentThree, "Frag3");

    //viewPager has to be instantiated when you create the activity:
    //ViewPager viewPager = (ViewPager)findViewById(R.id.pager);
    //setupViewPager(viewPager);
    //Where R.id.pager is the id of the viewPager defined in your activity's xml page.

    viewPager.setAdapter(viewPagerAdapteradapter);


    //frag1 and frag2 are also global variables
    frag1 = (FragmentOne)viewPagerAdapteradapter.mFragmentList.get(0);
    frag2 = (FragmentTwo)viewPagerAdapteradapter.mFragmentList.get(1);;


    //You can use the variable fragmentOne or frag1 to access functions declared in FragmentOne


}

这是ViewpagerAdapter类。
    class ViewPagerAdapter extends FragmentPagerAdapter {
    public final List<Fragment> mFragmentList = new ArrayList<>();
    private final List<String> mFragmentTitleList = new ArrayList<>();

    public ViewPagerAdapter(FragmentManager manager) {
        super(manager);
    }

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

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

    public void addFragment(Fragment fragment, String title) {
        mFragmentList.add(fragment);
        mFragmentTitleList.add(title);
    }

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

这篇答案是为像我一样的新手准备的。祝你有美好的一天。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接