5

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

我已重命名类以帮助说明它们的作用。

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 {
            ...
        }
    }
}

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

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

4

1 回答 1

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

于 2014-03-15T22:11:53.780 回答