【问题标题】:Switching Fragments in Master/Detail Flow在主/从流中切换片段
【发布时间】:2013-04-11 03:28:52
【问题描述】:

我正在尝试使用 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();
    }
}

细节片段之一的示例。在不同的情况下可能会创建许多不同的 Fragment。

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,甚至在只使用一个窗格时都不起作用,因为 SinglePaneFragmentWrapper 中的 getParent() 返回 null,使我无法调用 parent.changeDetailFragment()

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

【问题讨论】:

    标签: android android-fragments android-fragmentactivity


    【解决方案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

    【讨论】:

      猜你喜欢
      • 2015-11-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-11-17
      • 1970-01-01
      相关资源
      最近更新 更多