在主/细节流中切换碎片

时间:2013-04-11 03:28:52

标签: android android-fragments android-fragmentactivity

我正在尝试创建一个使用Fragments具有Master / Detail流程的应用程序。选择一个项目将打开一个细节片段,然后可以“打开”另一个片段并将其添加到后面的堆栈。

我已重命名课程以帮助说明他们的工作。

public class ListOfDetails extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        ...
    }

    //Callback method indicating that an item with the given ID was selected.
    public void onItemSelected(String id) {
        // Performing logic to determine what fragment to start omitted

        if (ifTwoPanes()) {
            Fragment fragment = new DetailFragmentType1();
            getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
        } else {
            Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class);
            newIntent.putExtra("id", id);
            startActivity(newIntent);
        }
    }

    // My attempt at making it possible to change displayed fragment from within fragments
    public void changeDetailFragment(Fragment fragment) {
        FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
        transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
        transaction.addToBackStack(null);
        transaction.replace(R.id.aContainer, fragment);
        transaction.commit();
    }
}

其中一个细节片段的示例。在不同的情况下可以创建许多不同的碎片。

public class DetailFragmentType1 extends Fragment {
    private ListOfDetails parent;

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

        Activity a = getActivity();
        if (a instanceof ListOfDetails) {
            parent = (ListOfDetails) a;
        }
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        Button aButton = (Button) getActivity().findViewById(R.id.aButton);
        aButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                parent.changeDetailFragment(new SubDetailFragment());
            }
        });
    }
}

在电话上,包装活动用于保存片段

public class SinglePaneFragmentWrapper extends FragmentActivity {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Duplicate logic must be performed to start fragment
        // Performing logic to determine what fragment to start omitted
        String id = getIntent().getStringExtra("id");
        if(id == "DetailFragmentType1") {
            Fragment fragment = new DetailFragmentType1();
            getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
        } else {
            ...
        }
    }
}

在这种情况下,更改详细信息窗格中打开的片段的正确方法是什么?我的方法在使用两个窗格时感觉就像是一个黑客,而且在只使用一个窗格时甚至不起作用,因为SinglePaneFragmentWrapper中的getParent()返回null,使我无法调用parent.changeDetailFragment()

这是一个复杂的问题,希望我能很好地解释。如果我错过了什么,请告诉我。感谢

1 个答案:

答案 0 :(得分:1)

关于这方面有很多意见,有很多方法可以做到这一点。我认为在这种情况下问题是"谁负责更改片段?"从表面上看,按钮上的监听器似乎是显而易见的位置,但是片段不应该知道它所托管的内容(这种情况的一个症状是从getParent()获得一个不合理的结果,如null)。

在你的情况下,我建议你实施一个"听众"父母的接口和"通知"从片段..当通知父级时,它会更改片段。这样片段就不会改变自己(所以不需要知道如何)..所以...对于你的情况......

添加新界面:

public interface FragmentChangeListener {
  void onFragmentChangeRequested(Fragment newFragment);
}

在ListOfDetails活动中实现接口

public class ListOfDetails extends FragmentActivity implements FragmentChangeListener {
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    ...
}

//Callback method indicating that an item with the given ID was selected.
public void onItemSelected(String id) {
    // Performing logic to determine what fragment to start omitted

    if (ifTwoPanes()) {
        Fragment fragment = new DetailFragmentType1();
        getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
    } else {
        Intent newIntent = new Intent(this, SinglePaneFragmentWrapper.class);
        newIntent.putExtra("id", id);
        startActivity(newIntent);
    }
}

// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    transaction.addToBackStack(null);
    transaction.replace(R.id.aContainer, fragment);
    transaction.commit();
}

// This is the interface implementation that will be called by your fragments
void onFragmentChangeRequested(Fragment newFragment) {
    changeDetailFragment(newFragment);
}

}

为细节片段添加了监听器

public class DetailFragmentType1 extends Fragment {

    private FragmentChangeListener fragmentChangeListener;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // Actually you might not have an activity here.. you should probably be 
        // doing this in onAttach
        //Activity a = getActivity();
        //if (a instanceof ListOfDetails) {
        //    parent = (ListOfDetails) a;
        //}
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        Button aButton = (Button) getActivity().findViewById(R.id.aButton);
        aButton.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
               // parent.changeDetailFragment(new SubDetailFragment());
               notifyFragmentChange(new SubDetailFragment());
            }
        });
    }

    @Override
    public void onAttach(Activity activity) {
      // This is called when the fragment is attached to an activity..
      if (activity instanceof FragmentChangeListener) {
          fragmentChangeListener = (FragmentChangeListener) activity;
      } else {
         // Find your bugs early by making them clear when you can...
         if (BuildConfig.DEBUG) {
           throw new IllegalArgumentException("Fragment hosts must implement FragmentChangeListener");
         }
      }
    }

    private void notifyFragmentChange(Fragment newFragment) {
      FragmentChangeListener listener = fragmentChangeListener;
      if (listener != null) {
         listener.onFragmentChangeRequested(newFragment);
      }
    }
}

并为您的单一窗格活动实现相同的界面...

public class SinglePaneFragmentWrapper extends FragmentActivity implements FragmentChangeListener {
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        // Duplicate logic must be performed to start fragment
        // Performing logic to determine what fragment to start omitted
        String id = getIntent().getStringExtra("id");
        if(id == "DetailFragmentType1") {
            Fragment fragment = new DetailFragmentType1();
            getSupportFragmentManager().beginTransaction().replace(R.id.aContainer, fragment).commit();
        } else {
            ...
        }
    }
// My attempt at making it possible to change displayed fragment from within fragments
public void changeDetailFragment(Fragment fragment) {
    FragmentTransaction transaction = getSupportFragmentManager().beginTransaction();
    transaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
    transaction.addToBackStack(null);
    transaction.replace(R.id.aContainer, fragment);
    transaction.commit();
}

// This is the interface implementation that will be called by your fragments
void onFragmentChangeRequested(Fragment newFragment) {
    changeDetailFragment(newFragment);
}

}

请注意单个窗格与多窗格活动之间的相似性。这表明您可以将所有重复的代码(changefragment等)放入一个它们都扩展的活动中,或者它们可能是相同的不同布局的活动......

我希望有所帮助,祝你好运。

此致 CJ