简体   繁体   English

片段选项卡未附加到上下文

[英]Fragment tab not attached to a context


I'm currently making a soundboard app on android studio which has a ton of fragments. 我目前正在 android 工作室制作一个音板应用程序,它有大量的片段。 The first page on my tab layout is just a normal button view, but my second tabView has another fragmentView within it. 我的标签布局的第一页只是一个普通的按钮视图,但我的第二个 tabView 里面有另一个 fragmentView。 The goal is for the first page to just show some normal sounds, and the second tab to have multiple operators to choose specific soundboards for. 目标是第一页只显示一些正常的声音,第二个选项卡有多个操作员来选择特定的音板。

Anyways, the app seems to work fine.无论如何,该应用程序似乎运行良好。 I can switch from the main tab to the operator tab, and even select operator soundboard pages and be moved to their fragments.我可以从主选项卡切换到操作员选项卡,甚至 select 操作员音板页面并移动到它们的片段。 However as soon as I try to switch fragments (through my tabview) my app crashes and I get the error:但是,一旦我尝试切换片段(通过我的 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)

I attached my code below, please let me know if you have any ideas!我在下面附上了我的代码,如果您有任何想法,请告诉我!

MainActivity.java MainActivity.java

<?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>

activity_main.xml activity_main.xml

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

voiceLinesView.java (contains fragments so I can select an operator and it will take me to their soundboard fragment) voiceLinesView.java (包含片段,所以我可以 select 操作员,它会带我到他们的音板片段)

<?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>

fragment_voice_lines_view.xml 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运算符Tab.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 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>

Try to remove the ViewTreeObserver.OnScrollChangedListener when the fragment is destroyed this will avoid any listener that can be attached to a destroyed fragment context not to be triggered whenever you leave and come back to this fragment.尝试在片段被销毁时删除ViewTreeObserver.OnScrollChangedListener这将避免任何可以附加到已销毁片段上下文的侦听器在您离开并返回此片段时不会被触发。

First: make a global field for the listener第一:为监听器创建一个全局字段

public class operatorTab extends Fragment {

    ...

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

Then set the listener wit the created field然后用创建的字段设置监听器

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

Then remove the listener.. I believe it's typically should be in onDestroyView() >> but also try it in onStop() or onPause if it didn't work然后删除监听器。我相信它通常应该在onDestroyView() >> 但如果它不起作用,也可以在onStop()onPause中尝试

This also will solve memory leak of this listener whenever its callback is triggered after the Fragment's view is destroyed这也将解决此侦听器的 memory 泄漏,只要在 Fragment 的视图被销毁后触发其回调

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

UPDATE更新

onStop()/onPause() worked for me onStop()/onPause() 为我工作

The reason that it didn't work with onDestroyView() because the OperatorTab fragment is a part of one of the ViewPager pages;它不能与onDestroyView()一起使用的原因是OperatorTab片段是ViewPager页面之一的一部分; and by default ViewPager loads a number of close pages in the background to be ready for the next page scroll;默认情况下, ViewPager会在后台加载一些关闭页面,为下一页滚动做好准备; loading this pages include some of the fragment lifecycle callbacks like onCreateView , onStart , but not onResume .加载此页面包括一些片段生命周期回调,如onCreateViewonStart ,但不包括onResume

When you leave the OperatorTab by scrolling the ViewPager to the next tab/page;当您通过滚动ViewPager到下一个选项卡/页面离开OperatorTab时; onDestroyView() won't be created if the ViewPager decides that OperatorTab is a part of the cached/close pages that the user might return back to it later;如果ViewPager决定OperatorTab是用户稍后可能返回的缓存/关闭页面的一部分,则不会创建onDestroyView() and therefore the listener still there and after a few swipes of the pager, the OperatorTab fragment can be detached from the context leaving the listener there with memory leaks...因此侦听器仍然存在,并且在寻呼机滑动几次之后, OperatorTab片段可以从上下文中分离出来,使侦听器留在那里 memory 泄漏...

So, the best way in ViewPager fragment (or any View that loads its fragments in advance) is to stop any listeners once you leave the page, ie in onPause or onStop callbacks and not waiting until they are destroyed.因此,在ViewPager片段(或任何预先加载其片段的视图)中的最佳方法是在您离开页面后停止任何侦听器,即在onPauseonStop回调中,而不是等到它们被销毁。

A possible reason for this error is that you are calling getResources() NOT in an Activity without using a context.此错误的一个可能原因是您在没有使用上下文的情况下在 Activity 中调用 getResources() 。 So, you need to use context.getResources() instead where context may be one of these here .因此,您需要使用context.getResources()而不是 context 可能是其中之一 In you instance, I think getContext() will work.在你的例子中,我认为 getContext() 会起作用。

So, I think you should search for all the times you are calling getResources and see if you are using a context or not.所以,我认为你应该搜索你调用 getResources 的所有时间,看看你是否使用了上下文。

The explanation for this error in a simple way is that you are trying to access the context required in getResources() before the fragment is instantiated.对此错误的简单解释是,您试图在片段实例化之前访问 getResources() 中所需的上下文。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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