Android - 如何从 FragmentActivity 访问 Fragment

Posted

技术标签:

【中文标题】Android - 如何从 FragmentActivity 访问 Fragment【英文标题】:Android - How to access a Fragment from a FragmentActivity 【发布时间】:2015-08-20 12:33:57 【问题描述】:

当我尝试从我的主要活动中访问片段时,我总是得到NullPointerException。不管我做什么。

问题是我使用了TabsPagerAdapterViewPager,但我不知道如何获取膨胀视图(片段的onCreate() 方法已经返回膨胀视图)。

目标是访问片段内的元素并通过单个后台线程动态隐藏或显示它,这也应该为更多片段执行此操作。

MainActivity.java

public class MainActivity extends FragmentActivity implements
        ActionBar.TabListener

/* swipe view */
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private android.app.ActionBar actionBar;

// tab titles
private String[] tabs =  "Basic", "Advanced", "Settings";


@Override
protected void onCreate(Bundle savedInstanceState) 

    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    /* init swipe views */
    mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
    viewPager = (ViewPager) findViewById(R.id.pager);
    viewPager.setAdapter(mAdapter);

    actionBar = getActionBar();
    actionBar.setHomeButtonEnabled(false);
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);

    /* addTab returns void, how to geht my fragements and their views???*/
    for (String tabName : tabs)
        actionBar.addTab(actionBar.newTab().setText(tabName).setTabListener(this));

    viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() 

        @Override
        public void onPageSelected(int position) 
            actionBar.setSelectedNavigationItem(position);
        

        @Override
        public void onPageScrolled(int arg0, float arg1, int arg2) 

        @Override
        public void onPageScrollStateChanged(int arg0) 
    );


public void testFunction()

    FragmentPage1 fragmentPage1 = (FragmentPage1) getSupportFragmentManager().findFragmentById(R.layout.fragment_page1);
    GridLayout gridlayout = (GridLayout) fragmentPage1.getRootView().findViewById(R.id.adBannerBasicLayout);
    gridlayout.setVisibility(GridLayout.VISIBLE); /* THATS MY GOAL */

FragmentPage1.java

public class FragmentPage1 extends Fragment 

    private View rootView;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) 

        rootView = inflater.inflate(R.layout.fragment_page1, container, false);


        /* HERE IT IS WORKING FINE, 
        but later I want to make it visible again 
        from code OUTSIDE FragmentPage1 ??? */

        GridLayout gridlayout = (GridLayout) rootView.findViewById(R.id.adBannerBasicLayout);
        gridlayout.setVisibility(GridLayout.GONE);

        return rootView;
        


    /* so I tried this, but also get always NullPointerException */
    public View getRootView()
    
        return rootView;
    

fragment_page1.xml

<?xml version="1.0" encoding="utf-8"?>

<ScrollView
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:ads="http://schemas.android.com/apk/res-auto"
    android:id="@+id/settings_scroll_view"
    android:layout_
    android:layout_
    >


    <GridLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        xmlns:ads="http://schemas.android.com/apk/res-auto"
        android:layout_
        android:layout_
        android:background="#000000"
        android:orientation="vertical"
        android:rowCount="12"
        android:columnCount="5"
        >

        <!-- Some Banner Ads I want to hide show -->           
        <!-- I want to access this from everywhere! --> 
        <GridLayout
            xmlns:android="http://schemas.android.com/apk/res/android"
            xmlns:ads="http://schemas.android.com/apk/res-auto"
            android:id="@+id/adBannerBasicLayout"
            android:orientation="vertical"
            android:layout_
            android:layout_
            android:layout_gravity="fill"
            android:rowCount="3"
            android:columnCount="1"
            android:layout_row="0"
            android:layout_column="0"
            android:layout_columnSpan="5">

            <Space
                android:layout_
                android:layout_
                android:layout_row="0"
                android:layout_column="0"
                />

            <com.google.android.gms.ads.AdView
                android:id="@+id/adBannerBasic"
                android:layout_
                android:layout_
                android:layout_gravity="fill"
                ads:adSize="BANNER"
                ads:adUnitId="xxxxxxxxxxxxxxxxxxxxxxxxxx"
                android:layout_row="1"
                android:layout_column="0"
                >
            </com.google.android.gms.ads.AdView>

            <Space
                android:layout_
                android:layout_
                android:layout_row="2"
                android:layout_column="0"
                />

        </GridLayout>

        <!-- more stuff... -->
  </GridLayout>
</ScrollView>

请帮帮我,我完全被卡住了!

谢谢!

编辑:

testFunction() 中的第二行抛出 NullPointerException:

 GridLayout gridlayout = (GridLayout) fragmentPage1.getRootView().findViewById(R.id.adBannerBasicLayout);

因为getSupportFragmentManager() 总是返回null:

FragmentPage1 fragmentPage1 = (FragmentPage1) getSupportFragmentManager().findFragmentById(R.layout.fragment_page1);

Logcat 输出

java.lang.IllegalStateException: Could not execute method of the activity
            at android.view.View$1.onClick(View.java:3969)
            at android.view.View.performClick(View.java:4633)
            at android.view.View$PerformClick.run(View.java:19330)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:157)
            at android.app.ActivityThread.main(ActivityThread.java:5356)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
            at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.reflect.InvocationTargetException
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at android.view.View$1.onClick(View.java:3964)
            at android.view.View.performClick(View.java:4633)
            at android.view.View$PerformClick.run(View.java:19330)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:157)
            at android.app.ActivityThread.main(ActivityThread.java:5356)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
            at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
            at dalvik.system.NativeStart.main(Native Method)
     Caused by: java.lang.NullPointerException
            at org.tzapp.smote.MainActivity.testFuntion(MainActivity.java:393)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at android.view.View$1.onClick(View.java:3964)
            at android.view.View.performClick(View.java:4633)
            at android.view.View$PerformClick.run(View.java:19330)
            at android.os.Handler.handleCallback(Handler.java:733)
            at android.os.Handler.dispatchMessage(Handler.java:95)
            at android.os.Looper.loop(Looper.java:157)
            at android.app.ActivityThread.main(ActivityThread.java:5356)
            at java.lang.reflect.Method.invokeNative(Native Method)
            at java.lang.reflect.Method.invoke(Method.java:515)
            at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:1265)
            at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1081)
            at de.robv.android.xposed.XposedBridge.main(XposedBridge.java:132)
            at dalvik.system.NativeStart.main(Native Method)

【问题讨论】:

您在哪一行得到 NullPointerException ?向我们展示 stackTrace 请发布您的 logcat 错误输出。 将片段添加到 Activity 时的行在哪里? 我已经添加了 logcat 输出。 testFunction() 中的第二行抛出 NullPointerException。 GridLayout gridlayout = (GridLayout) fragmentPage1.getRootView().findViewById(R.id.adBannerBasicLayout);fragmentPage1 始终为空... 【参考方案1】:

您使用了自动生成的 R 文件中的错误静态值!

而不是使用引用 XML 资源的 R.layout.fragment_page1 您应该使用 R.id.fragment_page1 这应该是您的 R.layout.activity_main XML 文件位于 /res/layout/activity_main.xml

R.layout 引用 XML 布局文件 R.id 引用单个 XML 节点(视图、片段等)

简而言之,改变:

FragmentPage1 fragmentPage1 = (FragmentPage1) getSupportFragmentManager().findFragmentById(R.layout.fragment_page1);

收件人:

FragmentPage1 fragmentPage1 = (FragmentPage1) getSupportFragmentManager().findFragmentById(R.id.fragment_page1);

并确保 /res/layout/activity_main.xml 中的片段 ID 设置为 R.id.fragment_page1,如下所示:

<fragment android:name="com.example.yourpackage.FragmentPage1"
              android:id="@+id/fragment_page1"
              android:layout_weight="1"
              android:layout_
              android:layout_ />

【讨论】:

【参考方案2】:

您使用 getSupportFragmentManager() 来获取片段。 请仔细检查您的 FragmentPage1 是否扩展了 android.support.v4.app.Fragment; 不是 android.Fragment;

【讨论】:

我使用import android.support.v4.app.Fragment; 并尝试过public class FragmentPage1 extends android.support.v4.app.Fragment 但同样的错误:(【参考方案3】:

更新 SDK 和仓库后,又出现了新的问题。我不能再构建了,我不想在 23 级编译:(

Information:Gradle tasks [clean, :app:generateDebugSources, :app:generateDebugTestSources]
:app:clean
:app:preBuild
:app:preDebugBuild
:app:checkDebugManifest
:app:preReleaseBuild
:app:prepareComAndroidSupportAppcompatV72300Library
:app:prepareComAndroidSupportSupportV42300Library
:app:prepareComGoogleAndroidGmsPlayServicesAds750Library
:app:prepareComGoogleAndroidGmsPlayServicesAnalytics750Library
:app:prepareComGoogleAndroidGmsPlayServicesBase750Library
:app:prepareComInstabugLibraryInstabugcore161Library
:app:prepareComInstabugLibraryInstabugsupport161Library
:app:prepareDebugDependencies
:app:compileDebugAidl
:app:compileDebugRenderscript
:app:generateDebugBuildConfig
:app:generateDebugAssets UP-TO-DATE
:app:mergeDebugAssets
:app:generateDebugResValues UP-TO-DATE
:app:generateDebugResources
:app:mergeDebugResources
:app:processDebugManifest
:app:processDebugResources
C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\exploded-aar\com.android.support\appcompat-v7\23.0.0\res\values-v23\values-v23.xml
Error:(1) Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse'.
Error:(1) Error retrieving parent for item: No resource found that matches the given name 'android:Widget.Material.Button.Colored'.
Error:Execution failed for task ':app:processDebugResources'.
> com.android.ide.common.internal.LoggedErrorException: Failed to run command:
    C:\Users\Tom\AppData\Local\Android\sdk\build-tools\21.1.2\aapt.exe package -f --no-crunch -I C:\Users\Tom\AppData\Local\Android\sdk\platforms\android-21\android.jar -M C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\manifests\full\debug\AndroidManifest.xml -S C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\res\debug -A C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\assets\debug -m -J C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\generated\source\r\debug -F C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\res\resources-debug.ap_ --debug-mode --custom-package org.tzapp.smote -0 apk --output-text-symbols C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\symbols\debug
Error Code:
    1
Output:
    C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\res\debug\values-v23\values.xml:5: error: Error retrieving parent for item: No resource found that matches the given name 'android:TextAppearance.Material.Widget.Button.Inverse'.
    C:\Users\Tom\AndroidStudioProjects\SSMote\app\build\intermediates\res\debug\values-v23\values.xml:20: error: Error retrieving parent for item: No resource found that matches the given name 'android:Widget.Material.Button.Colored'.
Information:BUILD FAILED
Information:Total time: 40.157 secs
Information:3 errors
Information:0 warnings

【讨论】:

所以,许多小时后,现在我终于用 SDK 级别 23 编译了 ^^ 但这并没有解决问题......不幸的是 NullPointerException 与 SDK 或存储库无关。但是在将我的整个代码对齐到 Lv 23 之后,我终于在我的应用程序中找到了导致问题的错误代码:)【参考方案4】:

我还找到了另一个答案,我认为这是最令人满意的:)

我忘了说还有一门课。这一切都来自于 developer.google.com 上某处可用的 Swipe Views 的那段愚蠢的示例代码......

每次我尝试使用愚蠢的 getItem(int i) 方法通过我的 TabPagerAdapter 类访问 FragmentPages 时,我都会创建一个新的 Fragment() :-/ 非常烦人!

来自不良 google 示例的类 TabsPagerAdapter

public class TabsPagerAdapter extends FragmentPagerAdapter     

    public TabsPagerAdapter(FragmentManager fm, MainActivity mainActivity)
        super(fm);
    

    // page index, fragment selector
    @Override
    public Fragment getItem(int index) 

        switch (index)
        
            case 0: return new FragmentPage1(); // bad practice
            case 1: return new FragmentPage2(); // why should one do that?
            case 2: return new FragmentPage3(); 
        
        return null;
    

    @Override
    public int getCount() 
        return 3;
    

真是废话^^我只是复制了它然后完全忘记了它的存在;)

将片段的实例化移动到构造函数并保留它们以供以后使用。现在完美运行。

优化后的类 TabsPagerAdapter

    public class TabsPagerAdapter extends FragmentPagerAdapter 

    // hosted fragments
    private FragmentPage fragmentPageBasic;
    private FragmentPage fragmentPageAdvanced;
    private FragmentPage fragmentPageSettings;

    //constructor
    public TabsPagerAdapter(FragmentManager fm, MainActivity mainActivity)
        super(fm);

        fragmentPageBasic = new FragmentPage();
        fragmentPageBasic.setMainActivity(mainActivity);
        fragmentPageBasic.setLayoutResource(R.layout.fragment_page1);
        fragmentPageBasic.setAdBannerResource(R.id.adBannerBasic);
        fragmentPageBasic.setAdBannerLayoutResource(R.id.adBannerBasicLayout);

        fragmentPageAdvanced = new FragmentPage();
        fragmentPageAdvanced.setMainActivity(mainActivity);
        fragmentPageAdvanced.setLayoutResource(R.layout.fragment_page2);
        fragmentPageAdvanced.setAdBannerResource(R.id.adBannerAdvanced);
        fragmentPageAdvanced.setAdBannerLayoutResource(R.id.adBannerAdvancedLayout);

        fragmentPageSettings = new FragmentSettingsPage();
        fragmentPageSettings.setMainActivity(mainActivity);
        fragmentPageSettings.setLayoutResource(R.layout.fragment_page3);
        fragmentPageSettings.setAdBannerResource(R.id.adBannerSettings);
        fragmentPageSettings.setAdBannerLayoutResource(R.id.adBannerSettingsLayout);
    

    // page index, fragment selector
    @Override
    public Fragment getItem(int index) 

        switch (index)
        
            case 0: return fragmentPageBasic;
            case 1: return fragmentPageAdvanced;
            case 2: return fragmentPageSettings;
        
        return null;
    

    @Override
    public int getCount() 
        return 3;
    

    // GETTERS
    public FragmentPage getFragmentPageBasic() return fragmentPageBasic;
    public FragmentPage getFragmentPageAdvanced() return fragmentPageAdvanced;
    public FragmentPage getFragmentPageSettings() return fragmentPageSettings;


不再有多余的 FragmentPage1、Fragment2、FragmentPage3 类,当然现在只有 FragmentPage 和 FragmentSettingsPage(扩展 FragmentPage 以覆盖 OnCreateView 方法),这导致代码更加简洁易懂。

类片段页面

public class FragmentPage extends Fragment 

    protected MainActivity mainActivity;

    // layout resource
    protected int layoutResource;

    // view
    protected View rootView;

    //Admob
    protected AdView adBanner;
    protected int adBannerResource;
    protected int adBannerLayoutResource;


    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 

        onCreate(inflater, container);

        return rootView;
    

    // manage common onCreate things
    protected void onCreate(LayoutInflater inflater, ViewGroup container)
    
        rootView = inflater.inflate(layoutResource, container, false);

        // admob banner
        adBanner = (AdView) rootView.findViewById(adBannerResource);
        adBanner.setAdListener(new AdListener() 

            @Override
            public void onAdLoaded() 
            

            @Override
            public void onAdOpened() 
            

            @Override
            public void onAdClosed() 
                newAdBannerRequest();
            

            @Override
            public void onAdFailedToLoad(int errorCode) 
                newAdBannerRequest();
            

            @Override
            public void onAdLeftApplication() 
            
        );

        if(mainActivity.showAdBanners())
        
            newAdBannerRequest();
            showAdBanner();
        
        else
            hideAdBanner();
    


    // ADMOB
    public void newAdBannerRequest()
    
        AdRequest request = new AdRequest.Builder()
                .addTestDevice(AdRequest.DEVICE_ID_EMULATOR)        // All emulators
                .addTestDevice("XXXXXXXXXXXXXXXXXXXXXXXXXX")  // My Galaxy Nexus test phone
                .build();
        adBanner.loadAd(request);
    

    public void showAdBanner()
    
        GridLayout adBannerLayout = (GridLayout) rootView.findViewById(adBannerLayoutResource);
        if(adBannerLayout.getVisibility() == GridLayout.GONE)
            adBannerLayout.setVisibility(GridLayout.VISIBLE);
    

    public void hideAdBanner()
    
        GridLayout adBannerLayout = (GridLayout) rootView.findViewById(adBannerLayoutResource);
        if(adBannerLayout.getVisibility() == GridLayout.VISIBLE)
            adBannerLayout.setVisibility(GridLayout.GONE);
    


    // GETTERS
    public AdView getAdBanner()  return adBanner;
    public View getRootView()  return rootView;


    // SETTERS
    public void setMainActivity(MainActivity mainActivity) this.mainActivity = mainActivity;
    public void setLayoutResource(int layoutResource)this.layoutResource = layoutResource;
    public void setAdBannerResource(int adBannerResource)this.adBannerResource = adBannerResource;
    public void setAdBannerLayoutResource(int adBannerLayoutResource)this.adBannerLayoutResource = adBannerLayoutResource;

类片段设置页面

public class FragmentSettingsPage extends FragmentPage 

    // Preferences
    public static final String PREFS_NAME = "Preferences";

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) 

        super.onCreate(inflater, container);

        SharedPreferences preferences = mainActivity.getSharedPreferences(PREFS_NAME, 0);
        // do some other top secret stuff here ;)


        return rootView;
    

有什么想法可以进一步改进吗?

【讨论】:

【参考方案5】:

使用 import android.support.v4.app.Fragment 代替 import android.app.Fragment

请将此添加到您的 gradle 依赖项中

compile 'com.android.support:support-v4:22.1.1' 

编辑:请使用 findFragmentById(R.id.fragment_page1) 代替 R.layout

FragmentPage1 fragmentPage1 = (FragmentPage1) getSupportFragmentManager().findFragmentById(R.id.fragment_page1);

【讨论】:

compile 'com.android.support:support-v4:22.1.1' 包含到应用程序 build.gradle 文件中。但仍然是同样的错误。 请更新您的 sdk 和存储库

以上是关于Android - 如何从 FragmentActivity 访问 Fragment的主要内容,如果未能解决你的问题,请参考以下文章

如何从Android调用javascript?

android: 如何从 Android Support Lib 版本 4 向 PagerTabStrip 添加图标/drawables?

Android:从片段调用时如何从活动中获取返回结果?

Android:如何在 android 中从服务器打开 pdf 文件 [关闭]

如何从 android 活动启动 Unity 应用程序?

Android -- 如何从 https 获取图片