การส่งผ่านข้อมูลระหว่างแฟรกเมนต์และกิจกรรมคอนเทนเนอร์


176

ฉันจะส่งผ่านข้อมูลระหว่างส่วนและกิจกรรมภาชนะได้อย่างไร มีบางสิ่งที่คล้ายคลึงกับการส่งผ่านข้อมูลระหว่างกิจกรรมผ่านจุดประสงค์หรือไม่

ฉันอ่านสิ่งนี้แล้ว แต่ก็ไม่ได้ช่วยอะไรมาก:
http://developer.android.com/guide/topics/fundamentals/fragments.html#CommunicatingWithActivity


นั่นไม่เพียงพอหรือ คุณสามารถส่งสิ่งที่คุณต้องการหรือคุณสามารถอธิบายเพิ่มเติมสิ่งที่คุณต้องการจะทำอย่างไร
Marcin Milejski

จากเอกสารที่ฉันไม่เข้าใจเช่นฉันจะส่งผ่านค่าหรือสตริงจากกิจกรรมไปยังส่วนหรือในทางกลับกัน ขอบคุณ
tyb

2
วิธีที่ดีที่สุดในการจัดการการโทรแบบอะซิงโครนัสจากชิ้นส่วนและส่งคืนข้อมูลคือการใช้ BroadcastReceivers ทั้งสองด้าน คุณควรทำแบบอะซิงโครนัสหากคุณทำงานกับจำนวนแฟรกเมนต์ที่ไม่เฉพาะเจาะจง
Marek Sebera

@punisher_malade ไม่เหมาะกับฉัน
Vadim Kotov

คำตอบ:


211

ในส่วนของคุณคุณสามารถโทรหาgetActivity()

สิ่งนี้จะทำให้คุณสามารถเข้าถึงกิจกรรมที่สร้างชิ้นส่วนได้ จากตรงนั้นคุณสามารถเรียกใช้เมธอด accessor ใด ๆ ที่อยู่ในกิจกรรมได้

เช่นสำหรับวิธีการที่เรียกgetResult()ในกิจกรรมของคุณ:

((MyActivity) getActivity()).getResult();

86
เนื่องจากคุณกำลังเข้าถึงฟังก์ชั่นภายในกิจกรรมของคุณ (ไม่ใช่กิจกรรม Android) คุณจะต้องโทรหา getActivity () ของคุณ: ((MyActivity) getActivity ()). getResult ();
นิค

3
อาจจะเป็นวิธีการย้อนกลับจากชิ้นส่วนไปยังกิจกรรม?
Vasil Valchev

6
@VasilValchev คุณสามารถสร้างอินเทอร์เฟซและบังคับให้กิจกรรมใช้งานได้แล้วเรียกวิธีการจากภายในส่วนของคุณเพื่อส่งผ่านข้อมูล ใช้เมธอด onAttach เพื่อตรวจสอบว่ากิจกรรมนั้นใช้ส่วนต่อประสานหรือไม่
Ivan Nikolov

3
คำตอบที่ยอดเยี่ยมจาก @IvanNikolov คุณสามารถอ่านคำอธิบายได้อย่างละเอียดที่ลิงก์การฝึกอบรมชิ้นส่วน
bogdan

8
นี่ไม่ใช่การปฏิบัติหรือรูปแบบที่ดี คำตอบที่มีส่วนต่อประสานนั้นถูกต้อง
moictab

303

ลองใช้อินเตอร์เฟส

ส่วนใด ๆ ที่ควรส่งผ่านข้อมูลกลับไปยังกิจกรรมที่บรรจุควรประกาศอินเตอร์เฟสเพื่อจัดการและส่งผ่านข้อมูล จากนั้นตรวจสอบให้แน่ใจว่ากิจกรรมที่มีของคุณใช้อินเทอร์เฟซเหล่านั้น ตัวอย่างเช่น:

JAVA

ในส่วนของคุณประกาศอินเทอร์เฟซ ...

public interface OnDataPass {
    public void onDataPass(String data);
}

จากนั้นเชื่อมต่อการใช้งานคลาสที่มีอยู่ของอินเทอร์เฟซกับส่วนในวิธี onAttach เช่น:

OnDataPass dataPasser;

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

ภายในแฟรกเมนต์ของคุณเมื่อคุณต้องการจัดการกับการส่งผ่านข้อมูลเพียงแค่เรียกมันบนวัตถุ dataPasser:

public void passData(String data) {
    dataPasser.onDataPass(data);
}

ในที่สุดในกิจกรรมที่มีของคุณซึ่งใช้ OnDataPass ...

@Override
public void onDataPass(String data) {
    Log.d("LOG","hello " + data);
}

Kotlin

ขั้นตอนที่ 1 สร้างส่วนต่อประสาน

interface OnDataPass {
    fun onDataPass(data: String)
}

ขั้นตอนที่ 2 จากนั้นเชื่อมต่อการใช้งานคลาสที่มีอยู่ของอินเทอร์เฟซกับส่วนในวิธี onAttach (YourFragment) เช่น:

lateinit var dataPasser: OnDataPass

override fun onAttach(context: Context) {
    super.onAttach(context)
    dataPasser = context as OnDataPass
}

ขั้นตอน 3. ภายในแฟรกเมนต์ของคุณเมื่อคุณต้องการจัดการกับการส่งผ่านข้อมูลเพียงแค่เรียกมันบนวัตถุ dataPasser:

fun passData(data: String){
    dataPasser.onDataPass(data)
}

ขั้นตอน 4. ในที่สุดในกิจกรรมของคุณใช้ OnDataPass

class MyActivity : AppCompatActivity(), OnDataPass {}

override fun onDataPass(data: String) {
    Log.d("LOG","hello " + data)
}

1
ขอบคุณคำตอบที่ดีจนถึงตอนนี้ และนี่ก็เป็นอย่างมากอธิบายที่นี่ สิ่งที่ฉันไม่ทราบก็คือคุณสามารถใช้หลาย ๆ อินเทอร์เฟซได้ฉันใช้งานแล้วActionBar.TabListenerและต้องเพิ่มอินเทอร์เฟซเพิ่มเติม
Eugene van der Merwe

5
นี่เป็นวิธีที่จะไปและในความคิดของฉันเป็นวิธีเดียวที่จะไป และให้การโต้ตอบแบบสองทางระหว่างกิจกรรมและชิ้นส่วน นอกจากนี้จะอนุญาตให้มีวิธีการสื่อสารระหว่างแฟรกเมนต์เมื่อคุณมีแฟรกเมนต์จำนวนมากในกิจกรรมเดียวไม่ว่าจะผ่านแท็บหรือโครงร่างแฟรกเมนต์หลายชิ้น
คริสโตเฟอร์

1
มีบางอย่างที่ฉันสงสัยว่า ... นี่คือคำตอบอย่างเป็นทางการและจากเว็บไซต์นักพัฒนาอย่างเป็นทางการที่พวกเขากล่าวว่านี่เป็นวิธีที่เหมาะสมในการเชื่อมต่อ Frag-Act-Frag แต่ทำไมถึงเป็นไปได้ที่จะทำได้ผ่านการคัดเลือก getActivity ( )?
unmultimedio

3
ทำไมบางคนต้องการอินเทอร์เฟซพิเศษเพื่อทำสิ่งนั้น เหตุใดคุณจึงพิจารณาโซลูชันต่อไปนี้ไม่ดีหรือโซลูชันของคุณดีกว่า ((MyActivity) getActivity) .myMethod (... )
spacifici

3
onAttach (กิจกรรมกิจกรรม) เลิกใช้แล้วโปรดใช้ onAttach (บริบทบริบท) แทน
Chris.C

23

วิธีการที่ง่ายที่สุด แต่ไม่แนะนำ

คุณสามารถเข้าถึงข้อมูลกิจกรรมได้จากส่วน:

กิจกรรม:

public class MyActivity extends Activity {

    private String myString = "hello";

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

    public String getMyData() {
        return myString;
    }
}

Fragment:

public class MyFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        MyActivity activity = (MyActivity) getActivity();
        String myDataFromActivity = activity.getMyData();
        return view;
    }
}

5
สิ่งนี้จะเพิ่มการมีเพศสัมพันธ์สูงในรหัสของคุณและเป็นการปฏิบัติที่ไม่ดี ตอนนี้คุณไม่สามารถใช้MyActivity
แฟรกเมนต์

เหตุใดวิธีนี้จึงถือว่าเป็นวิธีปฏิบัติที่ไม่ดีและอินเทอร์เฟซเป็นเพียงทางออกของคำถามนี้
androidXP

AmeyaB หากกิจกรรมทั้งหมดยื่นออกมาจาก BaseActivity หรือและเราใช้มันเหมือน BaseActivity activity = (BaseActivity) getActivity (); จากนั้นมันจะทำงานกับกิจกรรมทั้งหมด
Zar E Ahmer

21
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    Bundle b = getActivity().getIntent().getExtras();
            wid = b.getString("wid");
            rid = b.getString("rid");
            View view = inflater.inflate(R.layout.categoryfragment, container, false);
    return view;
 }

14
นี่คือวิธีรับในแฟรกเมนต์ แต่คุณจะตั้งค่าอย่างไรในคลาสที่เรียกแฟรกเมนต์ถ้าคุณมีแฟรกเมนต์ของคุณในไฟล์โครงร่าง xml
Zapnologica

17

การส่งผ่านข้อมูลระหว่างแฟรกเมนต์และกิจกรรมคอนเทนเนอร์

กิจกรรม:

        Bundle bundle = new Bundle();
        bundle.putString("message", "Alo Elena!");
        FragmentClass fragInfo = new FragmentClass();
        fragInfo.setArguments(bundle);
        transaction.replace(R.id.fragment_single, fragInfo);
        transaction.commit();

Fragment:

อ่านค่าในแฟรกเมนต์

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        String myValue = this.getArguments().getString("message");
        ...
        ...
        ...
        }

สวัสดี @Elenasys ฉันจะส่งข้อมูลบันเดิลกลับไปที่แฟรกเมนต์จากกิจกรรมได้อย่างไร ฉันไปจากส่วนหนึ่งไปยังกิจกรรมและฉันต้องกลับไปที่ส่วนที่สร้างขึ้นแล้ว (โทรเฉพาะ onStart, onResume, ... ) และต้องการข้อมูลที่ฉันสามารถรับได้ในกิจกรรม มีวิธีทำหรือฉันทำอะไรผิดหรือเปล่า?
Michal Moravik

7

ฉันไม่รู้ว่านี่เป็นวิธีที่ดีที่สุดหรือไม่ Bu ฉันค้นหาบน Google มาระยะหนึ่งแล้วหาว่าฉันจะส่ง Bundle จากส่วนหนึ่งไปยังกิจกรรมที่เก็บได้อย่างไร แต่ทั้งหมดที่ฉันพบคือการส่งข้อมูลจากกิจกรรมไปยังส่วนย่อยแทน (ซึ่งค่อนข้างสับสนสำหรับฉันเพราะฉันเป็นมือใหม่)

ต่อมาฉันลองทำบางสิ่งเป็นของตัวเองที่เหมาะกับฉันอย่างที่ฉันต้องการ ดังนั้นฉันจะโพสต์ไว้ที่นี่กรณีที่มีคนอย่างฉันกำลังมองหาสิ่งเดียวกัน

// การส่งผ่านข้อมูลจากแฟรกเมนต์

Bundle gameData = new Bundle();
        gameData.putStringArrayList(Constant.KEY_PLAYERS_ARR,players);
        gameData.putString(Constant.KEY_TEAM_NAME,custom_team_name);
        gameData.putInt(Constant.KEY_REQUESTED_OVER,requestedOver);

        Intent intent = getActivity().getIntent();
        intent.putExtras(gameData);

// การรับข้อมูลจากบันเดิลจากกิจกรรมคอนเทนเนอร์

Bundle gameData = getIntent().getExtras();
        if (gameData != null)
        {
            int over = gameData.getInt(Constant.KEY_REQUESTED_OVER);
            ArrayList<String> players = gameData.getStringArrayList(Constant.KEY_PLAYERS_ARR);
            String team = gameData.getString(Constant.KEY_TEAM_NAME);

        }
        else if (gameData == null)
        {
            Toast.makeText(this, "Bundle is null", Toast.LENGTH_SHORT).show();
        }

6

อินเทอร์เฟซเป็นหนึ่งในโซลูชันที่ดีที่สุด:

อินเตอร์เฟซกาว:

public interface DataProviderFromActivity {

    public String getName();
    public String getId);

}  

MyActivity:

public class MyActivity implements DataProviderFromActivity{

    String name = "Makarov";
    String id = "sys533";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    public String getName(){
        return name;
    };
    public String getId(){
        return id;
    };
}

MyFragment:

public class MyFragment extends Fragment{

    String fragName = "";
    String fragId = "";

    ... ... ... ... ... .... .... 
    ... ... ... ... ... .... .... 

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        DataProviderFromActivity myActivity= (DataProviderFromActivity) getActivity();
        fragName = myActivity.getName();
        fragId = myActivity.getId();

        ... ... ... ... ... .... .... 
        ... ... ... ... ... .... .... 

        updateFragmentView();
    }
}

สวัสดี @HassanMakarov ฉันชอบอินเตอร์เฟสของคุณเรียบง่ายและสะอาด ฉันมีหนึ่งปัญหาแม้ว่า ฉันไม่ทราบว่าเป็นข้อมูลเฉพาะสำหรับผู้ดูของฉันหรือไม่ แต่ฉันได้รับข้อมูลในจำนวนไม่เกิน 2 รายการในแต่ละครั้ง สตริง "Makarov" & "sys533" จะปรากฏในส่วนที่ 1 และ 2 เมื่อกิจกรรมถูกสร้างขึ้น แต่สตริงจะไม่ปรากฏในส่วนที่ 3 จนกว่าจะเลือกส่วนที่ 2 หรือ 3 หรือไม่ ความคิดใด ๆ
JC23

@ JC23 ฉันเดาว่าปัญหาเกี่ยวข้องกับการทำธุรกรรมแฟรกเมนต์ แฟรกเมนต์ใดถูกตั้งค่าเมื่อเปิดใช้งาน MainActivity
Hassan Tareq

@ JC23 ฉันต้องทำอะไร: แทน Tab / ViewPager ฉันใช้ Toolbar & Navigation View ใน onNavigationItemSelected () ฉันทำธุรกรรมส่วนเพื่อตั้งค่าชิ้นส่วนตาม
Hassan Tareq

2

ฉันใช้ AppCompatActivity ที่ใช้ Date Date แฟรกเมนต์มาเป็นสิ่งจำเป็นเนื่องจากฉันต้องการรหัสตัวเลือกช่วงวันที่ และฉันยังต้องการตู้คอนเทนเนอร์เพื่อรับวันที่ที่เลือกเพื่อส่งกลับไปยังกิจกรรมหลัก

สำหรับกิจกรรม container นี่คือการประกาศคลาส:

public class AppCompatDateRange extends AppCompatActivity implements
    DateIniRangeFragment.OnDateIniSelectedListener, DateFimRangeFragment.OnDateFimSelectedListener

และอินเทอร์เฟซสำหรับการเรียกกลับ:

@Override
public void onDateIniSelected(String dataIni) {
    Log.i("data inicial:", dataIni);
}

@Override
public void onDateFimSelected(String dataFim) {
    Log.i("data final:", dataFim);
}

การเรียกกลับเป็นสตริงเนื่องจากวันที่เป็นพารามิเตอร์ในแบบสอบถามเลือก

รหัสสำหรับแฟรกเมนต์ (อิงตามแฟรกเมนต์วันที่เริ่มต้น):

public class DateIniRangeFragment extends Fragment {
OnDateIniSelectedListener callbackIni;

private DatePicker startDatePicker;

public DateIniRangeFragment() {
    ///required empty constructor
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
}

///through this interface the fragment sends data to the container activity
public interface OnDateIniSelectedListener {
    void onDateIniSelected(String dataIni);
}

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
    ///layout for the fragment
    View v = inflater.inflate(R.layout.date_ini_fragment, container, false);

    ///initial date for the picker, in this case, current date
    startDatePicker = (DatePicker) v.findViewById(R.id.start_date_picker_appcompat);
    Calendar c = Calendar.getInstance();
    int ano = c.get(Calendar.YEAR);
    int mes = c.get(Calendar.MONTH);
    int dia = c.get(Calendar.DAY_OF_MONTH);
    startDatePicker.setSpinnersShown(false);
    startDatePicker.init(ano, mes, dia, dateSetListener);

    return v;
}

///listener that receives the selected date
private DatePicker.OnDateChangedListener dateSetListener = new DatePicker.OnDateChangedListener() {
    public void onDateChanged(DatePicker view, int year, int monthOfYear, int dayOfMonth) {
        if (view.isShown()) { ///if the datepicker is on the screen
            String sDataIni = year + "-" + (monthOfYear + 1) + "-" + dayOfMonth;
            callbackIni.onDateIniSelected(sDataIni); //apply date to callback, string format
        }
    }
};

@Override
public void onAttach(Activity activity) {
    super.onAttach(activity);

    /*
    * this function guarantees that the container activity implemented the callback interface
    * */
    try {
        callbackIni = (OnDateIniSelectedListener) activity;
    } catch (ClassCastException e) {
        throw new ClassCastException(activity.toString() + " deve implementar OnDateIniSelectedListener");
    }
}

}

ในการเขียน container + fragments ฉันใช้ ViewPager (AppCompat) กับคลาสที่กำหนดเองที่ขยาย FragmentPagerAdapter ไม่มีกล่องโต้ตอบ


2

เพียงแค่คุณสามารถใช้EventBusมันเป็นเรื่องง่ายและทำงานได้ดี

EventBus ใน 3 ขั้นตอน

  1. กำหนดกิจกรรม:

    public static class MessageEvent { /* Additional fields if needed */ }

  2. จัดเตรียมผู้สมัครสมาชิก: ประกาศและใส่คำอธิบายประกอบวิธีการสมัครสมาชิกของคุณระบุโหมดเธรดหรือไม่ก็ได้:

    @Subscribe(threadMode = ThreadMode.MAIN)
    public void onMessageEvent(MessageEvent event) {/* Do something */};

ลงทะเบียนและยกเลิกการลงทะเบียนสมาชิกของคุณ ตัวอย่างเช่นบน Android กิจกรรมและชิ้นส่วนควรลงทะเบียนตามวงจรชีวิต:

 @Override
 public void onStart() {
     super.onStart();
     EventBus.getDefault().register(this);
 }

 @Override
 public void onStop() {
     super.onStop();
     EventBus.getDefault().unregister(this);
 }
  1. โพสต์เหตุการณ์:

    EventBus.getDefault().post(new MessageEvent());


1

ฉันรู้ว่ามันอาจจะสาย แต่ฉันก็หลงทางกับคำถามนี้อยู่เสมอ ฉันแบ่งปันลิงค์นี้ ... เพราะนี่อาจเป็นคำอธิบายที่ดีที่สุดที่ฉันเคยพบบนเว็บสำหรับเรื่องนี้ วิธีนี้จะแก้ปัญหาส่วนของกิจกรรมและชิ้นส่วนเป็นส่วน !

แก้ไขและอธิบายได้ดีจริงๆ


0

มันใช้งานได้สำหรับฉัน ..

ในกิจกรรมเพิ่มวิธีนี้

    public void GetData(String data)
     {
        // do something with your data        
     }

และในส่วนเพิ่มบรรทัดนี้

((YourActivity)getContext).GetData("your data here");

0
public class Fragmentdemo extends Fragment {

  public interface onDemoEventListener {
    public void demoEvent(String s);
  }

  onDemoEventListener demoEventListener;

  @Override
  public void onAttach(Activity activity) {
    super.onAttach(activity);
        try {
          demoEventListener = (onDemoEventListener) activity;
        } catch (ClassCastException e) {
            throw new ClassCastException(activity.toString() + " must implement onDemoEventListener");
        }
  }

  final String LOG_TAG = "TAG";

  public View onCreateView(LayoutInflater inflater, ViewGroup container,
      Bundle savedInstanceState) {
    View v = inflater.inflate(R.layout.fragmentdemo, null);

    Button button = (Button) v.findViewById(R.id.button);
    button.setOnClickListener(new OnClickListener() {
      public void onClick(View v) {
        demoEventListener.someEvent("Test text to Fragment1");
      }
    });
    enter code here
    return v;
  }
}

-12

อีกวิธีง่ายๆในการรับข้อมูลที่ส่งผ่านจากกิจกรรมอื่น ๆ ในส่วนในกิจกรรมภาชนะ: ตัวอย่างเช่น:

Activity_A => Activity_B (ส่วน)

ใน Activity_A ของคุณคุณสร้างเจตนาเหมือนคุณกำลังส่งข้อมูล (สตริงที่นี่) ไปยังกิจกรรมอื่น:

Intent intent = new Intent(getBaseContext(),Activity_B.class);
intent.putExtra("NAME", "Value");
startActivity(intent);

ในแฟรกเมนต์ของคุณมีอยู่ใน Activity_B ของคุณ:

String data = getActivity().getIntent().getExtras();

getBaseContext()ทำให้ฉันมีข้อผิดพลาดดังต่อไปนี้:The method getBaseContext() is undefined for the type new View.OnClickListener(){}
Si8
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.