【问题标题】:How to implement OnFragmentInteractionListener如何实现 OnFragmentInteractionListener
【发布时间】:2014-09-06 19:35:31
【问题描述】:

我在 android studio 0.8.2 中有一个带有导航抽屉的向导生成应用程序

我创建了一个片段并使用 newInstance() 添加了它,但出现此错误:

com.domain.myapp E/AndroidRuntime: FATAL EXCEPTION: main java.lang.ClassCastException: com.domain.myapp.MainActivity@422fb8f0 必须实现 OnFragmentInteractionListener

我在任何地方都找不到如何实现这个 OnFragmentInteractionListener ? 即使在android sdk文档中也找不到!

MainActivity.java

import android.app.Activity;

import android.app.ActionBar;
import android.app.Fragment;
import android.app.FragmentManager;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.support.v4.widget.DrawerLayout;


public class MainActivity extends Activity
    implements NavigationDrawerFragment.NavigationDrawerCallbacks {

/**
 * Fragment managing the behaviors, interactions and presentation of the navigation drawer.
 */
private NavigationDrawerFragment mNavigationDrawerFragment;

/**
 * Used to store the last screen title. For use in {@link #restoreActionBar()}.
 */
private CharSequence mTitle;

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

    mNavigationDrawerFragment = (NavigationDrawerFragment)
            getFragmentManager().findFragmentById(R.id.navigation_drawer);
    mTitle = getTitle();

    // Set up the drawer.
    mNavigationDrawerFragment.setUp(
            R.id.navigation_drawer,
            (DrawerLayout) findViewById(R.id.drawer_layout));
}

@Override
public void onNavigationDrawerItemSelected(int position) {
    // update the main content by replacing fragments
    FragmentManager fragmentManager = getFragmentManager();

    switch (position) {
        case 0: fragmentManager.beginTransaction()
                .replace(R.id.container, PlaceholderFragment.newInstance(position + 1))
                .commit(); break; 
        case 1: fragmentManager.beginTransaction() 
                .replace(R.id.container, AboutFragment.newInstance("test1", "test2"))
                .commit(); break; // this crashes the app
        case 2: fragmentManager.beginTransaction()
                .replace(R.id.container, BrowseQuotesFragment.newInstance("test1", "test2"))
                .commit(); break; // this crashes the app
    }
}


public void onSectionAttached(int number) {
    switch (number) {
        case 1:
            mTitle = getString(R.string.title_section1);
            break;
        case 2:
            mTitle = getString(R.string.title_section2);
            break;
        case 3:
            mTitle = getString(R.string.title_section3);
            break;
    }
}

public void restoreActionBar() {
    ActionBar actionBar = getActionBar();
    actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
    actionBar.setDisplayShowTitleEnabled(true);
    actionBar.setTitle(mTitle);
}


@Override
public boolean onCreateOptionsMenu(Menu menu) {
    if (!mNavigationDrawerFragment.isDrawerOpen()) {
        // Only show items in the action bar relevant to this screen
        // if the drawer is not showing. Otherwise, let the drawer
        // decide what to show in the action bar.
        getMenuInflater().inflate(R.menu.main, menu);
        restoreActionBar();
        return true;
    }
    return super.onCreateOptionsMenu(menu);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    // Handle action bar item clicks here. The action bar will
    // automatically handle clicks on the Home/Up button, so long
    // as you specify a parent activity in AndroidManifest.xml.
    int id = item.getItemId();
    if (id == R.id.action_settings) {
        return true;
    }
    return super.onOptionsItemSelected(item);
}

/**
 * A placeholder fragment containing a simple view.
 */
public static class PlaceholderFragment extends Fragment {
    /**
     * The fragment argument representing the section number for this
     * fragment.
     */
    private static final String ARG_SECTION_NUMBER = "section_number";

    /**
     * Returns a new instance of this fragment for the given section
     * number.
     */
    public static PlaceholderFragment newInstance(int sectionNumber) {
        PlaceholderFragment fragment = new PlaceholderFragment();
        Bundle args = new Bundle();
        args.putInt(ARG_SECTION_NUMBER, sectionNumber);
        fragment.setArguments(args);
        return fragment;
    }

    public PlaceholderFragment() {
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
            Bundle savedInstanceState) {
        View rootView = inflater.inflate(R.layout.fragment_main, container, false);
        return rootView;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        ((MainActivity) activity).onSectionAttached(
                getArguments().getInt(ARG_SECTION_NUMBER));
    }
}

}

【问题讨论】:

    标签: android android-fragments


    【解决方案1】:

    对于那些看了@meda的回答还是不明白的人,这里是我对这个问题的简明完整的解释:

    假设您有 2 个片段,Fragment_AFragment_B,它们是从应用程序自动生成的。在生成的片段的底部,您将找到以下代码:

    public class Fragment_A extends Fragment {
    
        //rest of the code is omitted
    
        public interface OnFragmentInteractionListener {
            // TODO: Update argument type and name
            public void onFragmentInteraction(Uri uri);
        }
    }
    
    public class Fragment_B extends Fragment {
    
        //rest of the code is omitted
    
        public interface OnFragmentInteractionListener {
            // TODO: Update argument type and name
            public void onFragmentInteraction(Uri uri);
        }
    }
    

    要解决这个问题,您必须将onFragmentInteraction 方法添加到您的活动中,在我的例子中,它被命名为MainActivity2。之后,你需要像这样implementsMainActivity中的所有片段:

    public class MainActivity2 extends ActionBarActivity
            implements Fragment_A.OnFragmentInteractionListener, 
                       Fragment_B.OnFragmentInteractionListener, 
                       NavigationDrawerFragment.NavigationDrawerCallbacks {
        //rest code is omitted
    
        @Override
        public void onFragmentInteraction(Uri uri){
            //you can leave it empty
        }
    }
    

    P.S.:简而言之,这种方法可以用于片段之间的通信。想详细了解此方法的朋友,请参考link

    【讨论】:

    • 在 Android Studio 中使用当前版本的 SDK,它要求您实现 onFragmentIntereactior(Uri) 方法,其他答案中均未提及该方法。 +1
    • 感谢您提及这一点。对我帮助很大。
    • 很高兴它可以帮助很多人.. :)
    • 需要吗?您可以只删除与侦听器相关的代码...如果您恰好有不需要与其他片段交互的片段,那么这些侦听器是无用的。
    • 比公认的答案更容易理解和理解。
    【解决方案2】:

    此处发布的答案没有帮助,但以下链接有帮助:

    http://developer.android.com/training/basics/fragments/communicating.html

    定义接口

    public class HeadlinesFragment extends ListFragment {
        OnHeadlineSelectedListener mCallback;
    
        // Container Activity must implement this interface
        public interface OnHeadlineSelectedListener {
            public void onArticleSelected(int position);
        }
    
        @Override
        public void onAttach(Activity activity) {
            super.onAttach(activity);
    
            // This makes sure that the container activity has implemented
            // the callback interface. If not, it throws an exception
            try {
                mCallback = (OnHeadlineSelectedListener) activity;
            } catch (ClassCastException e) {
                throw new ClassCastException(activity.toString()
                        + " must implement OnHeadlineSelectedListener");
            }
        }
    
        ...
    }
    

    例如,当用户单击列表项时,会调用片段中的以下方法。 Fragment 使用回调接口将事件传递给父 Activity。

    @Override
    public void onListItemClick(ListView l, View v, int position, long id) {
        // Send the event to the host activity
        mCallback.onArticleSelected(position);
    }
    

    实现接口

    例如,以下活动实现了上例中的接口。

    public static class MainActivity extends Activity
            implements HeadlinesFragment.OnHeadlineSelectedListener{
        ...
    
        public void onArticleSelected(int position) {
            // The user selected the headline of an article from the HeadlinesFragment
            // Do something here to display that article
        }
    }
    

    API 23 更新:2015 年 8 月 31 日

    重写方法onAttach(Activity activity) 现在在android.app.Fragment 中已弃用,代码应升级到onAttach(Context context)

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
    }
    
    
    @Override
    public void onStart() {
        super.onStart();
        try {
            mListener = (OnFragmentInteractionListener) getActivity();
        } catch (ClassCastException e) {
            throw new ClassCastException(getActivity().toString()
                    + " must implement OnFragmentInteractionListener");
        }
    }
    

    【讨论】:

    • 请注意onAttach(Activity activity); 已被弃用,取而代之的是onAttach(Context context)
    • @EpicPandaForce 确实你是对的,我更新了我的帖子以反映这一点
    • 它从 onAttach 移动到 onStart 有什么原因吗?我仍然可以仅使用上下文而不是活动将其放入 onAttach 吗?
    • 我认为只使用onAttach(context) 就可以了
    【解决方案3】:

    查看由 Android Studio 创建的自动生成的Fragment。当您创建新的Fragment 时,Studio 会为您存根一堆代码。在自动生成的模板底部有一个名为OnFragmentInteractionListener 的内部接口定义。你的Activity 需要实现这个接口。这是您的Fragment 的推荐模式,用于通知您的Activity 事件,以便它可以采取适当的操作,例如加载另一个Fragment。有关详细信息,请参阅此页面,查找“为 Activity 创建事件回调”部分:http://developer.android.com/guide/components/fragments.html

    【讨论】:

    • 在文档中展示了如何在片段中实现监听器(已由向导生成),而不是在导致应用崩溃的 mainactivity 中。
    • 不完全是。文档(和生成的代码)定义接口并在Activity 附加到Fragment 时对其进行检查。您看到的崩溃是因为您的 Activity 没有实现该接口。您需要进入您的Activity 并添加implements YourFragment.OnFragmentInteractionListener,然后添加接口中定义的方法的实现。
    • 好的,但我不知道如何添加该实现,因为它不在 android sdk 文档中
    • 它不是 SDK 的一部分,而是一个最佳实践。向导生成的模板代码只是为您奠定了基础。接口的onFragmentInteraction(Uri uri) 只是一个存根。你可以让这个方法随心所欲,你的Activity 需要实现它。看看this 是否有帮助。
    • 这个提示节​​省了很多时间。在创建片段时 UN-check “包含片段工厂方法”和“包含接口回调”。而且您不必实现 OnFragmentInteractionListener。我正在使用带有 Java sdk 8 的 Android Studio 1.3.2。Android 6.0 (API 23) 和 sdk-platform 23 是。谢谢拉里·希弗。
    【解决方案4】:

    对于那些访问此页面以进一步澄清此错误的人,在我的情况下,调用片段的活动在这种情况下需要有 2 个实现,如下所示:

    public class MyActivity extends Activity implements 
        MyFragment.OnFragmentInteractionListener, 
        NavigationDrawerFragment.NaviationDrawerCallbacks {
        ...// rest of the code
    }
    

    【讨论】:

      【解决方案5】:

      您应该尝试从片段中删除以下代码

          try {
              mListener = (OnFragmentInteractionListener) activity;
          } catch (ClassCastException e) {
              throw new ClassCastException(activity.toString()
                      + " must implement OnFragmentInteractionListener");
          }
      

      接口/侦听器是默认创建的,以便您的活动和片段可以更轻松地通信

      【讨论】:

      • 这是一个很好的观点,因为在大多数初学者应用程序中都不需要这个监听器。
      【解决方案6】:

      对我来说,删除这段代码是有效的:

      @Override
          public void onAttach(Context context) {
              super.onAttach(context);
              if (context instanceof OnFragmentInteractionListener) {
                  mListener = (OnFragmentInteractionListener) context;
              } else {
                  throw new RuntimeException(context.toString()
                          + " must implement OnFragmentInteractionListener");
              }
          }
      

      这样结束:

      @Override
      public void onAttach(Context context) {
          super.onAttach(context);
      }
      

      【讨论】:

        【解决方案7】:

        除了@user26409021的回答,如果你添加了ItemFragment,ItemFragment中的消息是;

        Activities containing this fragment MUST implement the {@link OnListFragmentInteractionListener} interface.
        

        你应该添加你的活动;

        public class MainActivity extends AppCompatActivity
            implements NavigationView.OnNavigationItemSelectedListener, ItemFragment.OnListFragmentInteractionListener {
        
        //the code is omitted
        
         public void onListFragmentInteraction(DummyContent.DummyItem uri){
            //you can leave it empty
        }
        

        这里的虚拟项目是你在 ItemFragment 底部的东西

        【讨论】:

          【解决方案8】:

          OnFragmentInteractionListener 是处理片段到活动通信的默认实现。这可以根据您的需要实施。假设如果您需要在片段中的特定操作期间执行活动中的函数,则可以使用此回调方法。如果您不需要在托管 activityfragment 之间进行此交互,则可以删除此实现。

          简而言之,如果您需要像这样的片段-活动交互,您应该在片段托管活动中implement 监听器

          public class MainActivity extends Activity implements 
          YourFragment.OnFragmentInteractionListener {..}
          

          你的片段应该像这样定义它

          public interface OnFragmentInteractionListener {
              // TODO: Update argument type and name
              void onFragmentInteraction(Uri uri);
          }
          

          还在您的活动中提供void onFragmentInteraction(Uri uri); 的定义

          或者,如果您没有任何片段活动交互,则只需从片段的 onAttach 中删除 listener 初始化

          【讨论】:

            【解决方案9】:

            使用上下文代替 Activity。它对我有用。

            @Override
                public void onAttach(Context context) {
                    super.onAttach(context);
                    try {
                        mListener = (OnFragmentInteractionListener) context;
                    } catch (ClassCastException e) {
                        throw new ClassCastException(context.toString()
                                + " must implement OnFragmentInteractionListener");
                    }
            }
            

            【讨论】:

              【解决方案10】:

              只是一个附录:

              OnFragmentInteractionListener 使用接口处理 Activity 和 Fragment 之间的通信(OnFragmentInteractionListener),默认由 Android Studio 创建,但如果你不需要与你的 Activity 通信,你可以直接获取骑它。

              目标是您可以将片段附加到多个 Activity 并仍然重用相同的通信方法(每个 Activity 可以为每个片段具有自己的 OnFragmentInteractionListener)。

              但是,如果我确定我的片段将仅附加到一种类型的活动并且我想与该活动进行通信?

              然后,如果您不想使用 OnFragmentInteractionListener 因为它的冗长,您可以使用以下方式访问您的活动方法:

              ((MyActivityClass) getActivity()).someMethod()
              

              【讨论】:

              • 虽然这在大多数情况下都有效,但有时如果片段在调用它时已从活动中分离,getActivity() 可能会返回 null,例如在异步任务的 postExecute 方法中,如果你调用获取活动但在 asyncTask 完成之前已经离开片段,那么您将获得空指针异常。出于这个原因,android文档特别说要使用片段交互侦听器接口
              【解决方案11】:

              只需转到您的片段 Activity 并删除所有方法.....而不是在 createview 方法上。

              您的片段只有 oncreateview 方法。

              //只有这个方法实现其他方法删除

               @Override
              public View onCreateView(LayoutInflater inflater, ViewGroup container,
                      Bundle savedInstanceState) {
                  View rootView = inflater.inflate(R.layout.fragment_main, container, false);
                  return rootView;
              }
              

              并确保你的布局是你的演示版。

              【讨论】:

              • 谢谢...如果需要的话 kalpeshnikam1080@gmail.com drop mail
              【解决方案12】:

              我想在片段与活动分离或销毁时添加监听器的销毁。

              @Override
              public void onDetach() {
                  super.onDetach();
                  mListener = null;
              }
              

              当使用新的 onStart() 方法和 Context 时

              @Override
              public void onDestroy() {
                  super.onDestroy();
                  mListener = null;
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多