【问题标题】:Fragment tab not attached to a context片段选项卡未附加到上下文
【发布时间】:2021-01-08 23:42:24
【问题描述】:

我目前正在android studio上制作一个有大量片段的音板应用程序。我的选项卡布局的第一页只是一个普通的按钮视图,但我的第二个 tabView 里面有另一个 fragmentView。目标是第一页只显示一些正常的声音,第二个选项卡有多个操作员来选择特定的音板。

无论如何,该应用程序似乎运行良好。我可以从主选项卡切换到操作员选项卡,甚至可以选择操作员音板页面并移动到它们的片段。但是,一旦我尝试切换片段(通过我的 tabview),我的应用程序就会崩溃并且我收到错误消息:

"Fragment operatorTab{4623fc9} (312d4e58-458c-4f47-8fa3-794fe15f0536)} not attached to a context."**
**at com.jkcarraher.rainbowsixsoundboard.operatorTab.resetAllButtons(operatorTab.java:113)
        at com.jkcarraher.rainbowsixsoundboard.operatorTab.access$000(operatorTab.java:31)
        at com.jkcarraher.rainbowsixsoundboard.operatorTab$2.onScrollChanged(operatorTab.java:107)

我在下面附上了我的代码,如果您有任何想法,请告诉我!

MainActivity.java

import android.graphics.Color;
import android.os.Bundle;
import android.text.SpannableString;
import android.text.Spanned;
import android.text.style.ForegroundColorSpan;
import android.widget.Adapter;
import android.widget.TextView;

import com.google.android.gms.ads.MobileAds;
import com.google.android.gms.ads.initialization.InitializationStatus;
import com.google.android.gms.ads.initialization.OnInitializationCompleteListener;
import com.google.android.material.tabs.TabLayout;
import androidx.viewpager.widget.ViewPager;
import androidx.appcompat.app.AppCompatActivity;

public class MainActivity extends AppCompatActivity {

    private SectionsPageAdapter mSectionsPageAdapter;
    private ViewPager mViewPager;
    private TabLayout tabLayout;


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

        initializeAds();
        makeSixYellow();

        //Create ViewPager (that houses all fragments)
        mSectionsPageAdapter = new SectionsPageAdapter(getSupportFragmentManager());
        mViewPager = (ViewPager) findViewById(R.id.view_pager);
        setUpViewPager(mViewPager);

        //Add & customize tabs
        tabLayout = (TabLayout) findViewById(R.id.tabs);
        tabLayout.setTabTextColors(Color.WHITE, Color.WHITE);
        tabLayout.setupWithViewPager(mViewPager);
        tabLayout.getTabAt(0).setText("Utility");
        tabLayout.getTabAt(1).setText("Voice Lines");
        checkPageChange();
    }

    private void setUpViewPager(ViewPager viewPager) {
        SectionsPageAdapter adapter = new SectionsPageAdapter(getSupportFragmentManager());
        adapter.addFragment(new utilityTab(), "Utility");
        adapter.addFragment(new voiceLinesView(), "Voice Lines");

        viewPager.setAdapter(adapter);
    }

    private void makeSixYellow(){
        TextView textView = findViewById(R.id.titleText);
        String text = "R6 Soundboard";
        SpannableString ss = new SpannableString(text);
        ForegroundColorSpan fcsYellow = new ForegroundColorSpan(Color.rgb(255,236,141));
        ss.setSpan(fcsYellow, 1,2, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
        textView.setText(ss);
    }

    private void initializeAds(){
        MobileAds.initialize(this, new OnInitializationCompleteListener() {
            @Override
            public void onInitializationComplete(InitializationStatus initializationStatus) {
            }
        });
    }

    private void checkPageChange(){
        mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
                utilityTab.resetAllButtons();
            }

            @Override
            public void onPageSelected(int position) {
                utilityTab.resetAllButtons();
            }

            @Override
            public void onPageScrollStateChanged(int state) {
                utilityTab.resetAllButtons();
            }
        });
        }

}

activity_main.xml

<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

    <com.google.android.material.appbar.AppBarLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/colorHeader"
        android:theme="@style/AppTheme.AppBarOverlay">

        <TextView
            android:id="@+id/titleText"
            android:layout_marginLeft="20dp"
            android:layout_marginTop="20dp"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:fontFamily="@font/avenir"
            android:gravity="center_horizontal"
            android:text="R6 Soundboard"
            android:textColor="#FFF"
            android:textSize="30sp" />

        <com.google.android.material.tabs.TabLayout
            android:id="@+id/tabs"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            app:tabTextColor="#fff"
            android:background="@color/colorHeader" />
    </com.google.android.material.appbar.AppBarLayout>

    <androidx.viewpager.widget.ViewPager
        android:id="@+id/view_pager"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@color/colorBody"
        app:layout_behavior="@string/appbar_scrolling_view_behavior" />

</androidx.coordinatorlayout.widget.CoordinatorLayout>

voiceLinesView.java(包含片段,因此我可以选择一个操作员,它会将我带到他们的音板片段)

package com.jkcarraher.rainbowsixsoundboard;

import android.os.Bundle;

import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentTransaction;
import androidx.viewpager.widget.ViewPager;

import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;

import com.google.android.material.tabs.TabLayout;

public class voiceLinesView extends Fragment {

    private SectionsPageAdapter voiceLinesSectionsPageAdapter;
    private ViewPager voiceLinesViewPager;

    public voiceLinesView() {
        // Required empty public constructor
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_voice_lines_view, container, false);

        FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
        fragmentTransaction.add(R.id.voiceLinesFrame, new operatorTab());
        fragmentTransaction.commit();


        return view;
    }

}

fragment_voice_lines_view.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".voiceLinesView">

    <FrameLayout
        android:id="@+id/voiceLinesFrame"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

</FrameLayout>

operatorTab.java

package com.jkcarraher.rainbowsixsoundboard;

import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.media.MediaPlayer;
import android.net.Uri;
import android.os.Bundle;

import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;

import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.RelativeLayout;
import android.widget.ScrollView;

import com.google.android.gms.ads.AdRequest;
import com.google.android.gms.ads.AdView;

import soup.neumorphism.NeumorphCardView;
import soup.neumorphism.ShapeType;

import static android.view.MotionEvent.ACTION_MOVE;

public class operatorTab extends Fragment {
    private ScrollView scrollView;
    public static RelativeLayout KapkanButton;
    public static RelativeLayout GlazButton;
    public static RelativeLayout FuzeButton;
    public static RelativeLayout TachankaButton;

    voiceLinesView voiceLinesView = new voiceLinesView();


    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.fragment_operator_tab, container, false);

        //Initialize ScrollView
        scrollView = view.findViewById(R.id.operatorScrollView);

        //Initialize buttons 1-4
        KapkanButton = view.findViewById(R.id.kapkanButton);
        GlazButton = view.findViewById(R.id.glazButton);
        FuzeButton = view.findViewById(R.id.fuzeButton);
        TachankaButton = view.findViewById(R.id.tachankaButton);

        //Make buttons 1-6 pressable
        initPressableButton(KapkanButton);
        initPressableButton(GlazButton);
        initPressableButton(FuzeButton);
        initPressableButton(TachankaButton);

        scrollResetListener();
        return view;
    }

    @SuppressLint("ClickableViewAccessibility")
    private void initPressableButton(final RelativeLayout relativeLayout) {
        relativeLayout.setBackgroundColor(getResources().getColor(R.color.colorBody));
        final Rect r = new Rect();

        relativeLayout.setOnTouchListener(new View.OnTouchListener() {
            @Override
            public boolean onTouch(View view, MotionEvent event) {
                // get the View's Rect relative to its parent
                view.getHitRect(r);
                // offset the touch coordinates with the values from r
                // to obtain meaningful coordinates
                final float x = event.getX() + r.left;
                final float y = event.getY() + r.top;

                if(event.getAction() == MotionEvent.ACTION_DOWN) {
                    relativeLayout.setBackgroundColor(getResources().getColor(R.color.colorButtonPressed));
                    return true;
                } else if(event.getAction() == MotionEvent.ACTION_UP) {
                    if (r.contains((int) x, (int) y)) {
                        relativeLayout.setBackgroundColor(getResources().getColor(R.color.colorBody));
                        //On Click Up
                        FragmentTransaction fr = getFragmentManager().beginTransaction();
                        fr.replace(R.id.voiceLinesFrame, new kapkanVoiceLines());
                        fr.commit();
                    }

                }else if(event.getAction() == ACTION_MOVE){
                    if (!r.contains((int) x, (int) y)) {
                        relativeLayout.setBackgroundColor(getResources().getColor(R.color.colorBody));
                    }
                    return true;
                }
                return false;
            }
        });
    }

    private void scrollResetListener(){
        scrollView.getViewTreeObserver().addOnScrollChangedListener(new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                resetAllButtons();
            }
        });
    }

    public void resetAllButtons(){
        KapkanButton.setBackgroundColor(getResources().getColor(R.color.colorBody));
        GlazButton.setBackgroundColor(getResources().getColor(R.color.colorBody));
        FuzeButton.setBackgroundColor(getResources().getColor(R.color.colorBody));
        TachankaButton.setBackgroundColor(getResources().getColor(R.color.colorBody));
    }
}

fragment_operator_tab.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    tools:context=".operatorTab">

    <ScrollView
        android:id="@+id/operatorScrollView"
        android:layout_width="match_parent"
        android:layout_height="wrap_content">
        <RelativeLayout
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:background="@color/colorBody">
            <RelativeLayout
                android:id="@+id/kapkanButton"
                android:layout_width="match_parent"
                android:layout_height="60sp"
                android:orientation="horizontal">
                <ImageView
                    android:id="@+id/kapkanIcon"
                    android:layout_width="50sp"
                    android:layout_height="50sp"
                    android:layout_marginTop="5sp"
                    android:layout_marginLeft="10sp"
                    android:src="@drawable/ic_kapkan"/>
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_toRightOf="@id/kapkanIcon"
                    android:layout_marginLeft="10sp"
                    android:layout_marginTop="15sp"
                    android:layout_gravity="center"
                    android:fontFamily="@font/avenir"
                    android:text="Kapkan"
                    android:textColor="#ffffff"
                    android:textSize="25dp" />

                <ImageView
                    android:layout_width="40sp"
                    android:layout_height="40sp"
                    android:layout_alignParentEnd="true"
                    android:layout_marginTop="10sp"
                    android:layout_marginEnd="10dp"
                    android:src="@drawable/ic_arrow_right" />
            </RelativeLayout>
            <RelativeLayout
                android:id="@+id/glazButton"
                android:layout_below="@+id/kapkanButton"
                android:layout_width="match_parent"
                android:layout_height="60sp"
                android:orientation="horizontal">
                <ImageView
                    android:id="@+id/glazIcon"
                    android:layout_width="50sp"
                    android:layout_height="50sp"
                    android:layout_marginTop="5sp"
                    android:layout_marginLeft="10sp"
                    android:src="@drawable/ic_glaz"/>
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_toRightOf="@id/glazIcon"
                    android:layout_marginLeft="10sp"
                    android:layout_marginTop="15sp"
                    android:layout_gravity="center"
                    android:fontFamily="@font/avenir"
                    android:text="Glaz"
                    android:textColor="#ffffff"
                    android:textSize="25dp" />

                <ImageView
                    android:layout_width="40sp"
                    android:layout_height="40sp"
                    android:layout_alignParentEnd="true"
                    android:layout_marginTop="10sp"
                    android:layout_marginEnd="10dp"
                    android:src="@drawable/ic_arrow_right" />
            </RelativeLayout>
            <RelativeLayout
                android:id="@+id/fuzeButton"
                android:layout_below="@+id/glazButton"
                android:layout_width="match_parent"
                android:layout_height="60sp"
                android:orientation="horizontal">
                <ImageView
                    android:id="@+id/fuzeIcon"
                    android:layout_width="50sp"
                    android:layout_height="50sp"
                    android:layout_marginTop="5sp"
                    android:layout_marginLeft="10sp"
                    android:src="@drawable/ic_fuze"/>
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_toRightOf="@id/fuzeIcon"
                    android:layout_marginLeft="10sp"
                    android:layout_marginTop="15sp"
                    android:layout_gravity="center"
                    android:fontFamily="@font/avenir"
                    android:text="Fuze"
                    android:textColor="#ffffff"
                    android:textSize="25dp" />

                <ImageView
                    android:layout_width="40sp"
                    android:layout_height="40sp"
                    android:layout_alignParentEnd="true"
                    android:layout_marginTop="10sp"
                    android:layout_marginEnd="10dp"
                    android:src="@drawable/ic_arrow_right" />
            </RelativeLayout>
            <RelativeLayout
                android:id="@+id/tachankaButton"
                android:layout_below="@+id/fuzeButton"
                android:layout_width="match_parent"
                android:layout_height="60sp"
                android:orientation="horizontal">
                <ImageView
                    android:id="@+id/tachankaIcon"
                    android:layout_width="50sp"
                    android:layout_height="50sp"
                    android:layout_marginTop="5sp"
                    android:layout_marginLeft="10sp"
                    android:src="@drawable/ic_tachanka"/>
                <TextView
                    android:layout_width="wrap_content"
                    android:layout_height="wrap_content"
                    android:layout_toRightOf="@id/tachankaIcon"
                    android:layout_marginLeft="10sp"
                    android:layout_marginTop="15sp"
                    android:layout_gravity="center"
                    android:fontFamily="@font/avenir"
                    android:text="Tachanka"
                    android:textColor="#ffffff"
                    android:textSize="25dp" />

                <ImageView
                    android:layout_width="40sp"
                    android:layout_height="40sp"
                    android:layout_alignParentEnd="true"
                    android:layout_marginTop="10sp"
                    android:layout_marginEnd="10dp"
                    android:src="@drawable/ic_arrow_right" />
            </RelativeLayout>

        </RelativeLayout>
    </ScrollView>

</FrameLayout>

【问题讨论】:

    标签: java android android-fragments android-viewpager


    【解决方案1】:

    尝试在片段被销毁时删除ViewTreeObserver.OnScrollChangedListener,这将避免任何可以附加到已销毁片段上下文的侦听器在您离开并返回此片段时不会被触发。

    首先:为监听器创建一个全局字段

    public class operatorTab extends Fragment {
    
        ...
    
        ViewTreeObserver.OnScrollChangedListener  mScrollListener = new ViewTreeObserver.OnScrollChangedListener() {
            @Override
            public void onScrollChanged() {
                resetAllButtons();
            }
        };
    

    然后用创建的字段设置监听器

    private void scrollResetListener(){
        scrollView.getViewTreeObserver().addOnScrollChangedListener(mScrollListener);
    }
    

    然后删除监听器。我相信它通常应该在onDestroyView() >> 但如果它不起作用,也可以在onStop()onPause 中尝试

    这也将解决这个监听器在 Fragment 的视图被销毁后触发它的回调时的内存泄漏

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        scrollView.getViewTreeObserver().removeOnScrollChangedListener(mScrollListener);
    }
    

    更新

    onStop()/onPause() 为我工作

    它不适用于onDestroyView() 的原因是OperatorTab 片段是ViewPager 页面之一的一部分;默认情况下ViewPager在后台加载一些关闭页面,为下一页滚动做好准备;加载此页面包括一些片段生命周期回调,如onCreateViewonStart,但不包括onResume

    当您通过滚动ViewPager 到下一个选项卡/页面离开OperatorTab 时;如果ViewPager 决定OperatorTab 是用户稍后可能返回的缓存/关闭页面的一部分,则不会创建onDestroyView();因此侦听器仍然存在,并且在几次滑动寻呼机后,OperatorTab 片段可以从上下文中分离出来,使侦听器留在那里内存泄漏......

    因此,ViewPager 片段(或任何提前加载其片段的视图)中的最佳方法是在您离开页面后停止任何侦听器,即在onPauseonStop 回调中,而不是等到它们被销毁了。

    【讨论】:

    • 非常感谢!你绝对立足。 onStop()/onPause() 为我工作
    • 嘿@JohnCarraher 刚刚弄清楚为什么这在onViewCreated 中不起作用.. 请查看答案的更新部分.. 快乐编码:)
    【解决方案2】:

    此错误的一个可能原因是您在未使用上下文的情况下在 Activity 中调用 getResources()。因此,您需要使用 context.getResources() 代替 context 可能是这些 here 之一。在你的例子中,我认为 getContext() 会起作用。

    所以,我认为你应该搜索你调用 getResources 的所有时间,看看你是否使用了上下文。

    对这个错误的简单解释是,你试图在片段实例化之前访问 getResources() 中所需的上下文。

    【讨论】:

    • 我添加了这个,但是我得到一个不同的错误:“java.lang.NullPointerException: Attempt to invoke virtual method 'android.content.res.Resources android.content.Context.getResources()' on一个空对象引用”在与上面相同的行中
    • 请尝试使用 getApplicationContext() 而不是 getContext(),也许这会有所帮助。和 getActivity()。
    • 不幸的是,这也没有成功。我仍然收到错误:“java.lang.NullPointerException:尝试在空对象引用上调用虚拟方法 'android.content.Context androidx.fragment.app.FragmentActivity.getApplicationContext()'”
    猜你喜欢
    • 2021-09-28
    • 1970-01-01
    • 2020-07-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-04
    • 1970-01-01
    相关资源
    最近更新 更多