在主/细节流中切换碎片

tgr*_*ger 5 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();
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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());
            }
        });
    }
}
Run Code Online (Sandbox Code Playgroud)

在电话上时,使用包装器活动来保存片段

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 {
            ...
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

C B*_*B J 1

对此有很多观点,也有很多实现方法。我认为在这种情况下,问题是“谁负责更改片段?” 从表面上看,按钮上的侦听器似乎是明显的地方,但是片段不应该知道它托管在什么中(其症状是从 getParent() 获得像 null 这样的不良结果)。

在你的情况下,我建议你在父级中实现一个“监听器”接口,并从片段中“通知”。当通知父级时,它会更改片段。这样片段就不会改变自身(所以不需要知道如何改变)..所以..对于你的情况..

添加新接口:

public interface FragmentChangeListener {
  void onFragmentChangeRequested(Fragment newFragment);
}
Run Code Online (Sandbox Code Playgroud)

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

}
Run Code Online (Sandbox Code Playgroud)

添加监听器到详细片段

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);
      }
    }
}
Run Code Online (Sandbox Code Playgroud)

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

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

}
Run Code Online (Sandbox Code Playgroud)

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

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

问候,CJ