Cách triển khai OnFragmentInteractionListener


151

Tôi có một ứng dụng được tạo bằng thuật sĩ với ngăn kéo điều hướng trong studio android 0.8.2

Tôi đã tạo một đoạn và thêm nó với newInstance () và tôi gặp lỗi này:

com.domain.myapp E / AndroidR.78

Tôi không thể tìm thấy bất cứ nơi nào để triển khai OnFragmentInteractionListener này ?? Nó không thể được tìm thấy ngay cả trong tài liệu sdk Android!

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

}

Câu trả lời:


120

Câu trả lời được đăng ở đây không giúp được gì, nhưng liên kết sau đây đã làm:

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

Xác định giao diện

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

    ...
}

Ví dụ, phương thức sau trong đoạn được gọi khi người dùng nhấp vào một mục danh sách. Đoạn này sử dụng giao diện gọi lại để phân phối sự kiện đến hoạt động chính.

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

Thực hiện giao diện

Ví dụ, hoạt động sau đây thực hiện giao diện từ ví dụ trên.

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

Cập nhật cho API 23: 8/31/2015

Phương thức ghi đè onAttach(Activity activity)hiện không được chấp nhận android.app.Fragment, mã phải được nâng cấp lênonAttach(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");
    }
}

7
xin lưu ý rằng onAttach(Activity activity);không được chấp nhận và được thay thế bằngonAttach(Context context)
EpicPandaForce

1
@EpicPandaForce Quả thực bạn đúng, tôi đã cập nhật bài viết của mình để phản ánh điều đó
meda

1
Có bất kỳ lý do tại sao nó chuyển từ onAttach sang onStart không? Tôi vẫn có thể đặt nó trong onAttach chỉ bằng cách sử dụng bối cảnh thay vì hoạt động chứ?
Louis Tsai

Tôi nghĩ chỉ cần sử dụng onAttach(context)sẽ hoạt động tốt
EpicPandaForce

212

Đối với những bạn vẫn chưa hiểu sau khi đọc câu trả lời @meda, đây là lời giải thích ngắn gọn và đầy đủ của tôi cho vấn đề này:

Giả sử bạn có 2 Mảnh vỡ Fragment_AFragment_Bđược tạo tự động từ ứng dụng. Ở phần dưới cùng của các đoạn được tạo của bạn, bạn sẽ tìm thấy mã này:

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

Để khắc phục vấn đề, bạn phải thêm onFragmentInteractionphương thức vào hoạt động của mình, trong trường hợp của tôi được đặt tên MainActivity2. Sau đó, bạn cần implementstất cả các mảnh trong MainActivitynhư thế này:

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

PS: Nói tóm lại, phương pháp này có thể được sử dụng để liên lạc giữa các mảnh. Đối với những người bạn muốn biết thêm về phương pháp này, vui lòng tham khảo liên kết này .


10
Với phiên bản hiện tại của SDK trong Android Studio, nó yêu cầu bạn thực hiện onFragmentIntereactior(Uri)phương thức không được đề cập trong bất kỳ câu trả lời nào khác. +1

2
Cảm ơn rât nhiều!
ofir_aghai

Cảm ơn đã đề cập đến điều này. Giúp tôi rất nhiều.
hottaema

2
Đòi hỏi? Bạn chỉ có thể xóa mã liên quan đến người nghe ... Nếu bạn tình cờ có những đoạn không cần phải tương tác với các đoạn khác, những người nghe đó là vô dụng.
Truy tìm

4
Dễ dàng theo dõi và hiểu hơn nhiều so với câu trả lời được chấp nhận.
jerrythebum

44

Xem tự động tạo của bạn được Fragmenttạo bởi Android Studio. Khi bạn tạo mới Fragment, Studio đã khai thác một loạt mã cho bạn. Ở dưới cùng của mẫu được tạo tự động có một định nghĩa giao diện bên trong được gọi là OnFragmentInteractionListener. ActivityNhu cầu của bạn để thực hiện giao diện này. Đây là mẫu được đề xuất để bạn Fragmentthông báo cho Activitycác sự kiện của mình để sau đó nó có thể thực hiện hành động thích hợp, chẳng hạn như tải một mẫu khác Fragment. Xem trang này để biết chi tiết, hãy tìm phần "Tạo cuộc gọi lại sự kiện cho Hoạt động": http://developer.android.com/guide/components/fragments.html


Trong tài liệu cho thấy cách triển khai trình nghe trong đoạn (đã được tạo bởi trình hướng dẫn), nhưng không phải trong tính năng chính khiến ứng dụng bị sập.
Mario M

3
Không chính xác. Tài liệu (và mã được tạo) xác định giao diện và kiểm tra giao diện đó khi Activityđược gắn vào Fragment. Sự cố bạn đang thấy là do bạn Activitychưa thực hiện giao diện. Bạn cần phải đi vào Activityvà thêm một implements YourFragment.OnFragmentInteractionListenersau đó thêm một triển khai phương thức được xác định trong giao diện.
Larry Schiefer

ok, nhưng tôi không biết làm thế nào để thêm cách triển khai đó vì nó không có trong tài liệu sdk của Android
Mario M

1
Đây không phải là một phần của SDK, nhưng là một cách thực hành tốt nhất. Mã mẫu được tạo bởi trình hướng dẫn chỉ là nền tảng cho bạn. Giao diện onFragmentInteraction(Uri uri)chỉ là một sơ khai. Bạn có thể thực hiện phương pháp này bất cứ điều gì bạn muốn và Activitynhu cầu của bạn để thực hiện nó. Xem nếu điều này giúp.
Larry Schiefer

3
Gợi ý này tiết kiệm nhiều giờ. Trong khi tạo phân đoạn, kiểm tra UN "bao gồm các phương thức nhà máy phân mảnh" và "bao gồm các cuộc gọi lại giao diện". Và bạn không phải thực hiện OnFragmentInteractionListener. Tôi đang sử dụng Android studio 1.3.2 với Java sdk 8. Android 6.0 (API 23) và sdk-platform 23 là. Cảm ơn bạn Larry Schiefer.
người học

28

Đối với những bạn truy cập trang này để tìm hiểu rõ hơn về lỗi này, trong trường hợp của tôi, hoạt động thực hiện cuộc gọi đến đoạn cần có 2 thực hiện trong trường hợp này, như sau:

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

9

Bạn nên thử xóa đoạn mã sau khỏi đoạn của bạn

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

Giao diện / trình nghe là mặc định được tạo để hoạt động và các đoạn của bạn có thể giao tiếp dễ dàng hơn


2
Đây là một điểm rất tốt vì người nghe này không cần thiết trong hầu hết các ứng dụng mới bắt đầu.
Code-Apprentice

5

Ngoài câu trả lời của @ user26409021, Nếu bạn đã thêm ItemFragment, thông báo trong ItemFragment là;

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

Và bạn nên thêm vào hoạt động của bạn;

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
}

Ở đây mục giả là những gì bạn có ở dưới cùng của ItemFragment của bạn


5

Với tôi, nó hoạt động xóa mã này:

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

Kết thúc như thế này:

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

4

OnFragmentInteractionListenerlà triển khai mặc định để xử lý phân đoạn để truyền thông hoạt động. Điều này có thể được thực hiện dựa trên nhu cầu của bạn. Giả sử nếu bạn cần một chức năng trong hoạt động của mình được thực thi trong một hành động cụ thể trong đoạn của bạn, bạn có thể sử dụng phương thức gọi lại này. Nếu bạn không cần phải có sự tương tác giữa lưu trữ của mình activityfragment, bạn có thể xóa triển khai này.

Nói tóm lại, bạn nên implementlắng nghe trong hoạt động lưu trữ phân đoạn của mình nếu bạn cần tương tác hoạt động phân đoạn như thế này

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

và đoạn của bạn nên được định nghĩa như thế này

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

cũng cung cấp định nghĩa cho void onFragmentInteraction(Uri uri);trong hoạt động của bạn

hoặc nếu không, chỉ xóa phần listenerkhởi tạo khỏi đoạn của onAttachbạn nếu bạn không có bất kỳ tương tác hoạt động đoạn nào


3

Thay vì Activity sử dụng bối cảnh. Nó hoạt động với tôi.

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

3

Chỉ là một phụ lục:

OnFragmentInteractionListener xử lý giao tiếp giữa Activity và Fragment bằng giao diện (OnFragmentInteractionListener) và được Android Studio tạo mặc định, nhưng nếu bạn không cần giao tiếp với hoạt động của mình, bạn có thể sử dụng giao diện đó.

Mục tiêu là bạn có thể đính kèm đoạn của mình vào nhiều hoạt động và vẫn sử dụng lại phương thức giao tiếp tương tự (Mỗi hoạt động có thể có OnFragmentInteractionListener riêng cho từng phân đoạn).

Nhưng và nếu tôi chắc chắn đoạn của tôi sẽ chỉ được gắn vào một loại hoạt động và tôi muốn giao tiếp với hoạt động đó?

Sau đó, nếu bạn không muốn sử dụng OnFragmentInteractionListener vì tính dài dòng của nó, bạn có thể truy cập các phương thức hoạt động của mình bằng cách sử dụng:

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

Mặc dù điều này sẽ hoạt động trong hầu hết các trường hợp, đôi khi getActivity () có thể trả về null nếu các đoạn bị tách ra khỏi hoạt động trong khi điều này được gọi, ví dụ như trong phương thức postExecute của một asynctask, nếu bạn gọi hàm hoạt động nhưng đã rời khỏi đoạn trước khi asyncTask hoàn thành thì bạn sẽ có một ngoại lệ con trỏ null. Vì lý do này, các tài liệu Android nói cụ thể là sử dụng giao diện trình nghe tương tác phân đoạn
MichaelStoddart

2

Chỉ cần đi đến Hoạt động phân đoạn của bạn và xóa tất cả phương thức ..... thay vào đó trên phương thức createview.

đoạn của bạn chỉ có trên phương thức oncreateview đó là nó.

// chỉ có phương thức này thực hiện xóa phương thức khác

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

và chắc chắn rằng bố cục của bạn là bản demo cho u.


Cảm ơn bạn ... Nếu cần một cái gì đó kalpeshnikam1080 @ gmail gửi thư
Kalpesh A. Nikam

1

Tôi muốn thêm sự hủy diệt của người nghe khi đoạn bị tách ra khỏi hoạt động hoặc bị phá hủy.

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

và khi sử dụng phương thức onStart () mới với Ngữ cảnh

@Override
public void onDestroy() {
    super.onDestroy();
    mListener = null;
}
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.