จะส่งตัวแปรจาก Activity ไปยัง Fragment และส่งกลับได้อย่างไร?


140

ฉันกำลังสร้างแอพ Android และฉันต้องการส่งต่อวันที่ระหว่างกิจกรรมและส่วนย่อย กิจกรรมของฉันมีปุ่มซึ่งเปิดแฟรกเมนต์: DatePickerFragment

ในกิจกรรมของฉันฉันแสดงวันที่ซึ่งฉันต้องการแก้ไขด้วยส่วน ดังนั้นฉันต้องการส่งวันที่ไปยัง datepicker และส่งกลับไปที่กิจกรรม

ฉันได้ลองวิธีแก้ปัญหามากมาย แต่ไม่มีวิธีใดได้ผล วิธีง่ายๆจะส่งผ่านข้อโต้แย้ง แต่ไม่สามารถทำได้ด้วยเศษ


ปัญหาของฉันเหมือนกับของคุณทุกประการ ฉันสงสัยว่าทำไมตัวอย่างจึงถือว่าส่วนย่อยนั้นเป็น "ผู้ใช้" ของเครื่องมือเลือกวันที่ไม่ใช่กิจกรรมที่เริ่มต้น
Claudio

คำตอบ:


216

ในการส่งผ่านข้อมูลไปยังแฟรกเมนต์คุณ setArguments เมื่อคุณสร้างมันและคุณสามารถดึงอาร์กิวเมนต์นี้ได้ในภายหลังโดยใช้เมธอด onCreate หรือ onCreateView ของแฟรกเมนต์ของคุณ

ในฟังก์ชัน newInstance ของแฟรกเมนต์ของคุณคุณเพิ่มอาร์กิวเมนต์ที่คุณต้องการส่งไป:

/**
 * Create a new instance of DetailsFragment, initialized to
 * show the text at 'index'.
 */
public static DetailsFragment newInstance(int index) {
    DetailsFragment f = new DetailsFragment();
    // Supply index input as an argument.
    Bundle args = new Bundle();
    args.putInt("index", index);
    f.setArguments(args);
    return f;
}

จากนั้นภายในส่วนของวิธีการonCreateหรือonCreateViewคุณสามารถดึงข้อโต้แย้งเช่นนี้:

Bundle args = getArguments();
int index = args.getInt("index", 0);

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

การสอนเอกสาร:

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


12
ฉันคิดว่าประเด็นของคนที่ถามคำถามนี้ครั้งแล้วครั้งเล่าก็คือมันอธิบายไม่ดีในเอกสาร
Michael Alan Huff

1
ฉันยังสับสนว่าวิธีใดดีที่สุดที่จะใช้ของคุณหรือวิธีการด้านล่างนี้ตอบโดย @Elenasys
Yoann Hercouet

มีวิธีใหม่ในการแบ่งปันข้อมูลระหว่างชิ้นส่วนโดยใช้ส่วนประกอบสถาปัตยกรรม (I / O '17): developer.android.com/topic/libraries/architecture/…
jpardogo

แล้วส่วนที่สร้างขึ้นภายในเค้าโครง XML ล่ะ
ralphgabb

ฉันไม่ต้องการใช้วิธีคงที่อีกต่อไป
Ahamadullah Saikat

90

การส่งข้อมูลจากActivityไปยังไฟล์Fragment

กิจกรรม:

Bundle bundle = new Bundle();
String myMessage = "Stackoverflow is cool!";
bundle.putString("message", myMessage );
FragmentClass fragInfo = new FragmentClass();
fragInfo.setArguments(bundle);
transaction.replace(R.id.fragment_single, fragInfo);
transaction.commit();

ส่วน:

การอ่านค่าในส่วนย่อย

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

แต่ถ้าคุณต้องการส่งค่าจาก Fragment ไปยัง Activity อ่านคำตอบของ jpardogo คุณต้องมีอินเทอร์เฟซข้อมูลเพิ่มเติม: การสื่อสารกับ Fragment อื่น ๆ


2
จะส่งผ่านวัตถุที่กำหนดเองได้อย่างไร? ฉันใช้Parcelableแต่ที่ให้ฉันclass cast exception
งูพิษ

โซลูชันนี้ใช้ไม่ได้สำหรับฉันไม่พบว่าจะนำเข้าธุรกรรมจาก
ที่ไหน

ถ้าคุณโหลด Fragment ลงในกิจกรรมของคุณคุณสามารถส่งค่าผ่านบันเดิลที่กำหนดไว้ในธุรกรรมของคุณ อธิบายว่าสถานการณ์ของคุณคืออะไร?
Jorgesys

9

ขอบคุณ @ ρяσѕρєя K และ Terry ... มันช่วยฉันได้มากและทำงานได้อย่างสมบูรณ์แบบ

จากกิจกรรมคุณส่งข้อมูลโดยมีเจตนาเป็น:

Bundle bundle = new Bundle(); 
bundle.putString("edttext", "From Activity"); 
// set Fragmentclass Arguments
Fragmentclass fragobj = new Fragmentclass();
fragobj.setArguments(bundle);

และใน Fragment onCreateView method:

@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
        Bundle savedInstanceState) {
    // get arguments
    String strtext = getArguments().getString("edttext");    
    return inflater.inflate(R.layout.fragment, container, false);
}

การอ้างอิง: ส่งข้อมูลจากกิจกรรมไปยังส่วนย่อยใน Android


7

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

https://github.com/greenrobot/EventBus


4
ฉันไม่สามารถพูดได้ว่านี่ไม่ใช่วิธีแก้ปัญหาที่ดี แต่มีคำเตือนอยู่ในสถานที่ แม้ว่า EventBus จะใช้งานง่ายอย่างมาก แต่ก็มีอันตรายเช่นกัน หากคุณเริ่มเพิ่มสัญญาณมากเกินไปลักษณะการทำงานร่วมกันอย่างหลวม ๆ อาจทำให้ยากที่จะติดตามว่าใครโทรหาใครและเกิดเหตุการณ์ต่างๆขึ้นที่ใด
Johan Paul

2

การส่งข้อมูลจากกิจกรรมไปยัง Fragments ที่เชื่อมโยงโดย XML

หากคุณสร้างแฟรกเมนต์ใน Android Studio โดยใช้เทมเพลตอย่างใดอย่างหนึ่งเช่น File> New> Fragment> Fragment (List) ดังนั้นแฟรกเมนต์จะถูกเชื่อมโยงผ่าน XML เมธอด newInstance ถูกสร้างขึ้นในแฟรกเมนต์ แต่ไม่เคยถูกเรียกดังนั้นจึงไม่สามารถใช้เพื่อส่งผ่านอาร์กิวเมนต์ได้

แทนในกิจกรรมแทนที่เมธอด onAttachFragment

@Override
public void onAttachFragment(Fragment fragment) {
    if (fragment instanceof DetailsFragment) {
        Bundle args = new Bundle();
        args.putInt("index", index);
        f.setArguments(args);        
     }
}

จากนั้นอ่านอาร์กิวเมนต์ในเมธอด Fragment onCreate ตามคำตอบอื่น ๆ


2

สำหรับนักพัฒนา Kotlin ทั้งหมด:

นี่คือโซลูชันที่เสนอโดย Android Studio เพื่อส่งข้อมูลไปยัง Fragment ของคุณ (= เมื่อคุณสร้าง Blank-Fragment ด้วย File -> New -> Fragment -> Fragment (Blank) และคุณเลือก "include Fragment factory method")

ใส่สิ่งนี้ไว้ใน Fragment ของคุณ:

class MyFragment: Fragment {

...

    companion object {

            @JvmStatic
            fun newInstance(isMyBoolean: Boolean) = MyFragment().apply {
                arguments = Bundle().apply {
                    putBoolean("REPLACE WITH A STRING CONSTANT", isMyBoolean)
                }
            }
     }
}

.applyเป็นเคล็ดลับที่ดีในการตั้งค่าข้อมูลเมื่อสร้างวัตถุหรือตามที่ระบุไว้ที่นี่ :

เรียกใช้ฟังก์ชันที่ระบุ [บล็อก] โดยมีthisค่าเป็นตัวรับและส่งกลับthisค่า

จากนั้นในกิจกรรมหรือส่วนของคุณทำ:

val fragment = MyFragment.newInstance(false)
... // transaction stuff happening here

และอ่านอาร์กิวเมนต์ใน Fragment ของคุณเช่น:

private var isMyBoolean = false

override fun onAttach(context: Context?) {
    super.onAttach(context)
    arguments?.getBoolean("REPLACE WITH A STRING CONSTANT")?.let {
        isMyBoolean = it
    }
}

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

(activity as? YourActivityClass)?.callYourFunctionLikeThis(date) // your function will not be called if your Activity is null or is a different Class

เพลิดเพลินไปกับความมหัศจรรย์ของ Kotlin!


0

คุณสามารถสร้างอินสแตนซ์ส่วนของคุณด้วยบันเดิล:

Fragment fragment = Fragment.instantiate(this, RolesTeamsListFragment.class.getName(), bundle);

-2

การประกาศตัวแปรสาธารณะในคลาสเป็นวิธีที่ง่ายที่สุด:

ในคลาสเป้าหมาย:

public class MyFragment extends Fragment {
    public MyCallerFragment caller; // Declare the caller var
...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
           Bundle savedInstanceState) {
        // Do what you want with the vars
        caller.str = "I changed your value!";
        caller.i = 9999;
        ...
        return inflater.inflate(R.layout.my_fragment, container, false);
    }
...
}

ในคลาสผู้โทร:

public class MyCallerFragment extends Fragment {
    public Integer i; // Declared public var
    public String str; // Declared public var
        ...
            FragmentManager fragmentManager = getParentFragmentManager();
            FragmentTransaction transaction = fragmentManager.beginTransaction();

            myFragment = new MyFragment();
            myFragment.caller = this;
            transaction.replace(R.id.nav_host_fragment, myFragment)
                    .addToBackStack(null).commit();
        ...
}

หากคุณต้องการใช้กิจกรรมหลักก็ทำได้ง่ายเช่นกัน:

ในชั้นเรียนกิจกรรมหลัก:

public class MainActivity extends AppCompatActivity {
    public String str; // Declare public var
    public EditText myEditText; // You can declare public elements too.
                                // Taking care that you have it assigned
                                // correctly.
...
}

ในชั้นเรียน:

public class MyFragment extends Fragment {
    private MainActivity main; // Declare the activity var
...
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
           Bundle savedInstanceState) {
        // Assign the main activity var
        main = (MainActivity) getActivity();

        // Do what you want with the vars
        main.str = "I changed your value!";
        main.myEditText.setText("Wow I can modify the EditText too!");
        ...
        return inflater.inflate(R.layout.my_fragment, container, false);
    }
...
}

หมายเหตุ: โปรดใช้ความระมัดระวังเมื่อใช้เหตุการณ์ (onClick, onChanged ฯลฯ ) เนื่องจากคุณสามารถอยู่ในสถานการณ์ "ต่อสู้" ที่มีมากกว่าหนึ่งตัวกำหนดตัวแปร ผลลัพธ์ที่ได้คือบางครั้งตัวแปรจะไม่เปลี่ยนหรือจะกลับไปเป็นค่าสุดท้ายอย่างน่าอัศจรรย์

สำหรับการผสมผสานเพิ่มเติมให้ใช้ความคิดสร้างสรรค์ของคุณ :)


1
นี่เป็นการเชื่อมโยงกิจกรรมและชิ้นส่วนเข้าด้วยกันอย่างแน่นหนาดังนั้นจึง จำกัด การใช้ซ้ำของชิ้นส่วน เราควรหาวิธีการสื่อสารควบคู่กันให้น้อยลง
Zorayr
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.