简体   繁体   中英

Activity is being destroyed on replacing Fragment

I am new to Android and am trying to write a simple Dummy App to learn something about creating own View classes and so on. Why am i doing this? I wanted to have a UI that consists of several Pages which can be navigated through by pressing buttons on the action bar. I now wanted to add the feature of navigating via touch gestures. I then faced the problem which i was trying to solve with this dummy app, which does the same as my original app, just a bit simpler.

The ( former ) problem: The Fragments do have a ScrollView on one side, which eats the touch gesture, when you click on them.

I read about this and figured out I would have to create my own FrameLayout and @Override the onInterceptTouchEvent and onTouchEvent methods. I do now use this new FrameLayout as my Fragment Container. Yet it's still not working properly when i test the app with a ScrollView in the FragmentLayout instead of a LinearLayout(as in this codesample). But at this point this is not my biggest problem, I think I will only have to work a bit on the two methods to get them work as i want.

At this point my App crashes when the Fragments are being swapped via touch gestures... The buttons still work.

I am getting this Error(LogCat):

06-18 14:34:03.295: W/dalvikvm(2983): threadid=1: thread exiting with uncaught exception (group=0x409f51f8)
06-18 14:34:03.305: E/AndroidRuntime(2983): FATAL EXCEPTION: main
06-18 14:34:03.305: E/AndroidRuntime(2983): java.lang.IllegalStateException: Activity has been destroyed
06-18 14:34:03.305: E/AndroidRuntime(2983):     at android.app.FragmentManagerImpl.enqueueAction(FragmentManager.java:1280)
06-18 14:34:03.305: E/AndroidRuntime(2983):     at android.app.BackStackRecord.commitInternal(BackStackRecord.java:541)
06-18 14:34:03.305: E/AndroidRuntime(2983):     at android.app.BackStackRecord.commit(BackStackRecord.java:525)
06-18 14:34:03.305: E/AndroidRuntime(2983):     at com.example.trysomething.MainActivity.switchFragment(MainActivity.java:97)
06-18 14:34:03.305: E/AndroidRuntime(2983):     at com.example.trysomething.MainActivity.navForward(MainActivity.java:74)

This is my MainActivity:

public class MainActivity extends Activity {

    int pos;

    TouchInterceptingLayout mtil;

    CharSequence mTitle;
    String[] mTitles;

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

        mtil = (TouchInterceptingLayout) findViewById(R.id.fragment_container);

        pos = 0;
        mTitle = getTitle();
        mTitles = getResources().getStringArray(R.array.dummy_seiten);

        if(savedInstanceState == null){
            switchFragment(0);
        }
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu){
        MenuInflater inflater = getMenuInflater();
        inflater.inflate(R.menu.main, menu);
        return super.onCreateOptionsMenu(menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item){

        switch(item.getItemId()){
        case R.id.action_nav_back:
            navBackwards();
            break;
        case R.id.action_nav_forward:
            navForward();
            break;
        }

        return super.onOptionsItemSelected(item);
    }

    public void setTitle(CharSequence title){
        mTitle = title;
        getActionBar().setTitle(mTitle);
    }

    public void navForward(){
        if(pos == 2){
            pos = 0;
            switchFragment(pos);
        } else {
            pos += 1;
            switchFragment(pos);
        }
    }

    public void navBackwards(){
        if(pos == 0){
            pos = 2;
            switchFragment(pos);
        } else {
            pos -= 1;
            switchFragment(pos);
        }
    }

    public void switchFragment(int position){
        Fragment newFrag;
        Bundle args = new Bundle();
        args.putInt(DummyFragmentSimpleView.ARG_POSITION, position);
        newFrag = new DummyFragmentSimpleView();
        newFrag.setArguments(args);
        android.app.FragmentManager fm = getFragmentManager();
        FragmentTransaction ft = fm.beginTransaction();
        ft.replace(R.id.fragment_container, newFrag);
        ft.commit();
    }
}

This is my DummyFragmentSimpleView (Java):

public class DummyFragmentSimpleView extends Fragment {

public static String ARG_POSITION = "position";

Bundle args;

String[] dummyStringArray;
String[] nonsenseArray;

LayoutParams params;

public void onCreate(Bundle savedInstanceState){
    super.onCreate(savedInstanceState);

    dummyStringArray = getResources().getStringArray(R.array.dummy_array);
    nonsenseArray = getResources().getStringArray(R.array.nonsense_array);
    params = new LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
    args = getArguments();
}

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

    View mySimpleFrag = inflater.inflate(R.layout.fragment_notscrolling, container, false);

    ImageView image = (ImageView) mySimpleFrag.findViewById(R.id.image_container);
    String bild = getResources().getStringArray(R.array.dummy_seiten)[args.getInt(ARG_POSITION)];
    int bildId = getResources().getIdentifier(bild.toLowerCase(Locale.getDefault()), "drawable", getActivity().getPackageName());
    ((ImageView) image).setImageResource(bildId);

    LinearLayout dummyLayout = (LinearLayout) mySimpleFrag.findViewById(R.id.content_container);

    TextView t = (TextView) mySimpleFrag.findViewById(R.id.nonsense_text);
    t.setText(nonsenseArray[args.getInt(ARG_POSITION)]);

    for(int i = 0; i < dummyStringArray.length; i++){
        TextView text = new TextView(getActivity());
        text.setText(dummyStringArray[i]);
        text.setId(i);
        text.setLayoutParams(params);
        text.setTextSize(20);
        ((LinearLayout) dummyLayout).addView(text);         
    }

    getActivity().setTitle(bild);

    return mySimpleFrag;
}
}

This is the XML for the Simple View:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="horizontal" 
    android:baselineAligned="false">

    <LinearLayout 
        android:id="@+id/left_panel"
        android:layout_weight="1"
        android:layout_width="0dp"
        android:layout_height="match_parent"
        android:orientation="vertical"
        android:background="#C00000"
        android:gravity="center">

        <ImageView
            android:id="@+id/image_container"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"/>

    </LinearLayout>



        <LinearLayout
            android:id="@+id/right_panel"
            android:layout_weight="2"
            android:layout_width="0dp"
            android:layout_height="wrap_content"
            android:orientation="vertical">

            <TextView 
                android:id="@+id/nonsense_text"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:textSize="30sp"
                android:textStyle="bold"/>

            <LinearLayout 
                android:id="@+id/content_container"
                android:layout_width="match_parent"
                android:layout_height="wrap_content"
                android:orientation="vertical"/>

        </LinearLayout>



</LinearLayout>

And this is my TouchInterceptingLayout class:

public class TouchInterceptingLayout extends FrameLayout{

    boolean mIsNavigating = false;

    MotionEventHandler handler = new MotionEventHandler();

    public TouchInterceptingLayout(Context context) {
        super(context);
    }

    public TouchInterceptingLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TouchInterceptingLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        for(int i = 0; i < getChildCount(); i++){
            getChildAt(i).layout(left, top, right, bottom);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event){

        float x1 = 0;
        float x2 = 0;

        final int action = event.getAction();

        if(action == MotionEvent.ACTION_CANCEL || action == MotionEvent.ACTION_UP){
            mIsNavigating = false;
            return false;
        }

        switch (action){
        case MotionEvent.ACTION_DOWN:
            x1 = event.getX();
            break;
        case MotionEvent.ACTION_MOVE:
            return false;
        case MotionEvent.ACTION_UP:
            x2 = event.getX();
            if(Math.abs(x2 - x1) > 150){
                mIsNavigating = true;
                return true;
            } else {
                return false;
            }
        }
        return false;
    }

    @Override
    public boolean onTouchEvent(MotionEvent event){
        switch(event.getAction()){
        case MotionEvent.ACTION_DOWN:
            handler.setXOne(event.getX());
            return true;

        case MotionEvent.ACTION_UP:
            handler.setXTwo(event.getX());
            handler.setDelta();
            handler.performAction();
            break;
        }
        return super.onTouchEvent(event);
    }
}

MotionEventHandler class:

public class MotionEventHandler {

    static final int MIN_X_DISTANCE = 150;
    public float x1 = 0;
    public float x2 = 0;
    public float deltaX = 0;
    MainActivity myBoss = new MainActivity();

    public MotionEventHandler(){
    }

    public void setXOne(float x){
        x1 = x;
    }

    public void setXTwo(float x){
        x2 = x;
    }

    public float getXOne(){
        return x1;
    }

    public float getXTwo(){
        return x2;
    }

    public void setDelta(){
        deltaX = x2 - x1;
    }

    public float getDelta(){
        return deltaX;
    }

    public boolean isPageBeingSwapped(){
        if (Math.abs(deltaX)<MIN_X_DISTANCE){
            return false;
        } else {
            return true;
        }
    }

    public void performAction(){
        if(isPageBeingSwapped()){
            if(deltaX < 0){
                myBoss.navBackwards();
            } else {
                myBoss.navForward();
            }
        }
    }
}

activity_main.xml:

<?xml version="1.0" encoding="utf-8"?>
    <com.example.trysomething.TouchInterceptingLayout
        xmlns:android="http://schemas.android.com/apk/res/android"
        android:id="@+id/fragment_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>

I hope someone can help, and that my problem description is not too confusing, and my problem not too dumb... ;s

Thanks in advance!

Your problem occurs right here in your MotionEventHandler

public class MotionEventHandler {

    static final int MIN_X_DISTANCE = 150;
    public float x1 = 0;
    public float x2 = 0;
    public float deltaX = 0;

    // -- Right here --
    MainActivity myBoss = new MainActivity();

    // The rest of your code
}

The error you are getting is slightly misleading because it's not that your MainActivity has been destroyed. This issue is that the one you are using isn't the one that is visible on screen . In nearly every case, you never want to instantiate an Activity by calling its constructor.


Fortunately, the fix for this is relatively easy. Your MotionEventHandler should have a constructor that takes in a MainActivity like the following:

public MotionEventHandler(MainActivity mainAct) {
    myBoss = mainAct;
}

And in your TouchInterceptingLayout you should instantiate the handler inside the constructors.

public class TouchInterceptingLayout extends FrameLayout{
    MotionEventHandler handler;

    public TouchInterceptingLayout(Context context) {
        super(context);
        if(context instanceof MainActivity) {
            handler =  = new MotionEventHandler((MainActivity) context)
        }
        else {
            // Not a valid context
        }
    }
    public TouchInterceptingLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public TouchInterceptingLayout(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        if(context instanceof MainActivity) {
            handler =  = new MotionEventHandler((MainActivity) context)
        }
        else {
            // Not a valid context
        }
    }

    // The rest of your code
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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