วิธีจัดการ startActivityForResult บน Android


969

startActivityForResultในกิจกรรมของฉันฉันเรียกกิจกรรมที่สองจากกิจกรรมหลักโดย ในกิจกรรมที่สองของฉันมีบางวิธีที่ทำกิจกรรมนี้ให้เสร็จ (อาจไม่มีผลลัพธ์) อย่างไรก็ตามมีเพียงหนึ่งในวิธีที่ส่งคืนผลลัพธ์

ตัวอย่างเช่นจากกิจกรรมหลักฉันเรียกอันที่สอง ในกิจกรรมนี้ฉันกำลังตรวจสอบคุณสมบัติบางอย่างของโทรศัพท์มือถือเช่นมีกล้อง หากไม่มีแล้วฉันจะปิดกิจกรรมนี้ นอกจากนี้ในระหว่างการเตรียมการMediaRecorderหรือMediaPlayerหากมีปัญหาเกิดขึ้นฉันจะปิดกิจกรรมนี้

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

ฉันจะตรวจสอบผลลัพธ์จากกิจกรรมหลักได้อย่างไร


คำตอบ:


2446

จากการFirstActivityโทรของคุณวิธีการSecondActivityใช้ startActivityForResult()

ตัวอย่างเช่น:

int LAUNCH_SECOND_ACTIVITY = 1
Intent i = new Intent(this, SecondActivity.class);
startActivityForResult(i, LAUNCH_SECOND_ACTIVITY);

ของคุณในชุดข้อมูลที่คุณต้องการที่จะกลับไปSecondActivity FirstActivityหากคุณไม่ต้องการกลับมาอย่าตั้งค่าใด ๆ

ตัวอย่างเช่น: SecondActivityหากคุณต้องการส่งข้อมูลกลับ:

Intent returnIntent = new Intent();
returnIntent.putExtra("result",result);
setResult(Activity.RESULT_OK,returnIntent);
finish();

หากคุณไม่ต้องการส่งคืนข้อมูล:

Intent returnIntent = new Intent();
setResult(Activity.RESULT_CANCELED, returnIntent);
finish();

ตอนนี้ในFirstActivityชั้นเรียนของคุณเขียนรหัสต่อไปนี้สำหรับonActivityResult()วิธีการ

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (requestCode == LAUNCH_SECOND_ACTIVITY) {
        if(resultCode == Activity.RESULT_OK){
            String result=data.getStringExtra("result");
        }
        if (resultCode == Activity.RESULT_CANCELED) {
            //Write your code if there's no result
        }
    }
}//onActivityResult

หากต้องการใช้ข้อมูลที่ผ่านระหว่างสองกิจกรรมในวิธีที่ดีขึ้นมากใน Kotlin โปรดไปที่ลิงก์นี้ ' วิธีที่ดีกว่าในการส่งผ่านข้อมูลระหว่างกิจกรรม '


1
วัตถุประสงค์ของการวางเจตนาเมื่อ RESUT_CANCELLED ใน setResult (RESULT_CANCELED, returnIntent) คืออะไร;
Ismail Sahin

4
@ismail สมมติว่าSecondActivityมีข้อยกเว้นบางอย่างเกิดขึ้นในกรณีนั้นคุณต้องส่งคืนผลลัพธ์ไปที่FirstActivityดังนั้นคุณจึงสามารถตั้งค่าผลลัพธ์เป็น"RESULT_CANCELLED"catch catch และกลับไปที่FirstActivtyและFirstActivity's' 'onActivityResult()คุณสามารถตรวจสอบว่าคุณได้รับผลสำเร็จหรือล้มเหลว
Nishant

10
ดังนั้นขึ้นอยู่กับคุณหากคุณไม่จำเป็นต้องทราบเหตุผลในการยกเลิกคุณสามารถใช้ setResult (RESULT_CANCELED) ได้เพียง; โดยไม่มีเจตนาใด ๆ
Ismail Sahin

2
@Lei Leyba ไม่เสร็จสิ้น () ไม่ถูกเรียกหลังจากเรียก startActivityForResult () First Actvity จะย้ายไปที่สถานะหยุดชั่วคราว
Nishant

6
สำหรับฉันมันไม่ทำงาน -.- นี่คือสิ่งที่ฉันเกลียดมากเกี่ยวกับ Android - ระบบนี้ไม่น่าเชื่อถือดังนั้น: - /
Martin Pfeffer

50

จะตรวจสอบผลลัพธ์จากกิจกรรมหลักได้อย่างไร?

คุณต้องแทนที่Activity.onActivityResult()แล้วตรวจสอบพารามิเตอร์ของมัน:

  • requestCodeระบุว่าแอปใดส่งคืนผลลัพธ์เหล่านี้ startActivityForResult()นี้จะถูกกำหนดโดยคุณเมื่อคุณโทร
  • resultCode แจ้งให้คุณทราบว่าแอปนี้ประสบความสำเร็จล้มเหลวหรือมีอะไรที่แตกต่างออกไป
  • dataเก็บข้อมูลใด ๆ ที่ส่งคืนโดยแอปนี้ nullนี้อาจจะเป็น

หมายความว่า requestCode ใช้เฉพาะในกิจกรรมแรกเท่านั้นและไม่เคยใช้สำหรับกิจกรรมที่สองใช่หรือไม่ หากกิจกรรมที่ 2 มีวิธีการที่แตกต่างกันมันจะเปลี่ยนไป แต่ขึ้นอยู่กับความตั้งใจพิเศษและไม่ใช่โดย requestCode ใช่มั้ย แก้ไข:ใช่stackoverflow.com/questions/5104269/…
JCarlosR

44

การเติมคำตอบจาก @ Nishant วิธีที่ดีที่สุดในการส่งคืนผลกิจกรรมคือ:

Intent returnIntent = getIntent();
returnIntent.putExtra("result",result);
setResult(RESULT_OK,returnIntent);
finish();

ฉันมีปัญหากับ

new Intent();

จากนั้นฉันก็พบว่าวิธีที่ถูกต้องใช้อยู่

getIntent();

เพื่อรับเจตนาปัจจุบัน


รู้สึกแปลกเล็กน้อยที่จะสร้างใหม่Intentที่มีอยู่เพื่อเก็บBundleและไม่มีค่าปกติเช่นการกระทำหรือส่วนประกอบ แต่มันก็รู้สึกแปลก ๆ เล็กน้อย (และอาจเป็นอันตรายหรือไม่?) ในการปรับเปลี่ยนสิ่งIntentที่เคยใช้เพื่อเริ่มกิจกรรมปัจจุบัน ดังนั้นฉันจึงค้นหาแหล่งที่มาสำหรับ Android ของตัวเองและพบว่าพวกเขาสร้างใหม่Intentเพื่อใช้เป็นผล ตัวอย่างเช่นgithub.com/aosp-mirror/platform_frameworks_base/blob/…
spaaarky21

สวัสดี spaaarky21 ขอขอบคุณสำหรับความคิดเห็นของคุณ ฉันขอโทษที่ฉันไม่ได้อธิบายอย่างชัดเจนว่าฉันลงเอยด้วยวิธีแก้ปัญหานั้นได้อย่างไร เมื่อสามปีก่อนและฉันจำได้เพียงว่าแอปของฉันพังเพราะ "เจตนาใหม่" นั่นคือสิ่งที่ฉันหมายถึงเมื่อฉันพูดว่า "ฉันกำลังมีปัญหา" ที่จริงฉันเพิ่งลอง "getIntent" เพราะมันสมเหตุสมผลในเวลาและมันก็ใช้ได้! เพราะเหตุนี้ฉันจึงตัดสินใจแบ่งปันวิธีแก้ปัญหาของฉัน อาจไม่ใช่คำที่ดีที่สุดในการพูดคำว่า "วิธีที่ดีที่สุด" หรือ "วิธีที่ถูกต้อง" แต่ฉันยืนหยัดด้วยวิธีแก้ปัญหาของฉัน มันเป็นสิ่งที่แก้ไขปัญหาของฉันและคนอื่น ๆ ด้วยเช่นกัน ขอบคุณ
Julian Alberto

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

43

ตัวอย่าง

หากต้องการดูกระบวนการทั้งหมดในบริบทนี่คือคำตอบเพิ่มเติม ดูคำตอบแบบเต็มของฉันสำหรับคำอธิบายเพิ่มเติม

ป้อนคำอธิบายรูปภาพที่นี่

MainActivity.java

public class MainActivity extends AppCompatActivity {

    // Add a different request code for every activity you are starting from here
    private static final int SECOND_ACTIVITY_REQUEST_CODE = 0;

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

    // "Go to Second Activity" button click
    public void onButtonClick(View view) {

        // Start the SecondActivity
        Intent intent = new Intent(this, SecondActivity.class);
        startActivityForResult(intent, SECOND_ACTIVITY_REQUEST_CODE);
    }

    // This method is called when the second activity finishes
    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        // check that it is the SecondActivity with an OK result
        if (requestCode == SECOND_ACTIVITY_REQUEST_CODE) {
            if (resultCode == RESULT_OK) { // Activity.RESULT_OK

                // get String data from Intent
                String returnString = data.getStringExtra("keyName");

                // set text view with string
                TextView textView = (TextView) findViewById(R.id.textView);
                textView.setText(returnString);
            }
        }
    }
}

SecondActivity.java

public class SecondActivity extends AppCompatActivity {

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

    // "Send text back" button click
    public void onButtonClick(View view) {

        // get the text from the EditText
        EditText editText = (EditText) findViewById(R.id.editText);
        String stringToPassBack = editText.getText().toString();

        // put the String to pass back into an Intent and close this activity
        Intent intent = new Intent();
        intent.putExtra("keyName", stringToPassBack);
        setResult(RESULT_OK, intent);
        finish();
    }
}

ทำได้โดยแอพสองตัวที่ต่างกัน A และแอพ b stackoverflow.com/questions/52975645/…
Jerry Abraham

12

สำหรับผู้ที่มีปัญหากับrequestCode ผิดใน onActivityResult

หากคุณโทรstartActivityForResult()จากคุณFragmentRequestCode จะถูกเปลี่ยนโดยกิจกรรมที่เป็นเจ้าของแฟรกเมนต์

หากคุณต้องการได้รับ resultCode ที่ถูกต้องในกิจกรรมของคุณลองสิ่งนี้:

เปลี่ยนแปลง:

startActivityForResult(intent, 1); ถึง:

getActivity().startActivityForResult(intent, 1);


10

หากคุณต้องการอัปเดตส่วนต่อประสานผู้ใช้ด้วยผลลัพธ์กิจกรรมคุณไม่สามารถใช้การthis.runOnUiThread(new Runnable() {} ทำสิ่งนี้ได้ UI จะไม่รีเฟรชด้วยค่าใหม่ คุณสามารถทำสิ่งนี้แทน:

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
    super.onActivityResult(requestCode, resultCode, data);

    if (resultCode == RESULT_CANCELED) {
        return;
    }

    global_lat = data.getDoubleExtra("LATITUDE", 0);
    global_lng = data.getDoubleExtra("LONGITUDE", 0);
    new_latlng = true;
}

@Override
protected void onResume() {
    super.onResume();

    if(new_latlng)
    {
        PhysicalTagProperties.this.setLocation(global_lat, global_lng);
        new_latlng=false;
    }
}

ดูเหมือนว่าโง่ แต่ทำงานได้ดี


2

แรกที่คุณใช้startActivityForResult()กับพารามิเตอร์ในครั้งแรกActivityและถ้าคุณต้องการที่จะส่งข้อมูลจากสองActivityไปก่อนActivityแล้วส่งค่าใช้IntentกับsetResult()วิธีการและได้รับข้อมูลที่อยู่ภายในวิธีการในครั้งแรกonActivityResult()Activity


1

ปัญหาที่พบบ่อยมากใน Android
มันสามารถแบ่งออกเป็น 3 ชิ้น
1) เริ่มกิจกรรม B (เกิดขึ้นในกิจกรรม A)
2) ตั้งค่าข้อมูลที่ร้องขอ (เกิดขึ้นในกิจกรรม B)
3) รับข้อมูลที่ร้องขอ (เกิดขึ้นในกิจกรรม A)

1) startActivity B

Intent i = new Intent(A.this, B.class);
startActivity(i);

2) ตั้งค่าข้อมูลที่ร้องขอ

ในส่วนนี้คุณตัดสินใจว่าคุณต้องการส่งข้อมูลกลับหรือไม่เมื่อมีเหตุการณ์ใดเกิดขึ้น
เช่นในกิจกรรม B จะมี EditText และปุ่มสองปุ่ม b1, b2
การคลิกที่ปุ่ม b1 จะส่งข้อมูลกลับไปที่กิจกรรม A
การคลิกที่ปุ่ม b2 จะไม่ส่งข้อมูลใด ๆ

กำลังส่งข้อมูล

b1......clickListener
{
   Intent resultIntent = new Intent();
   resultIntent.putExtra("Your_key","Your_value");
   setResult(RES_CODE_A,resultIntent);
   finish();
}

ไม่ส่งข้อมูล

b2......clickListener
    {
       setResult(RES_CODE_B,new Intent());
       finish();
    }

ผู้ใช้คลิกปุ่มย้อนกลับ
โดยค่าเริ่มต้นผลลัพธ์จะถูกกำหนดด้วย Activity.RESULT_CANCEL รหัสการตอบสนอง

3) รับผล

สำหรับแทนที่ onActivityResult วิธีการที่

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RES_CODE_A) {

     // b1 was clicked 
   String x = data.getStringExtra("RES_CODE_A");

}
else if(resultCode == RES_CODE_B){

   // b2 was clicked

}
else{
   // back button clicked 
}
}

1

ActivityResultRegistry เป็นแนวทางที่แนะนำ

ComponentActivityตอนนี้ให้สิ่งActivityResultRegistryที่ช่วยให้คุณจัดการstartActivityForResult()+ onActivityResult()รวมถึงrequestPermissions()+ onRequestPermissionsResult()โฟลว์โดยไม่ต้องใช้วิธีการแทนที่ในของคุณActivityหรือFragmentเพิ่มความปลอดภัยให้กับประเภทผ่านทางActivityResultContractและเพิ่มตะขอสำหรับการทดสอบกระแสเหล่านี้

ขอแนะนำอย่างยิ่งให้ใช้ API ผลลัพธ์ของกิจกรรมที่แนะนำใน AndroidX Activity 1.2.0-alpha02 และ Fragment 1.3.0-alpha02

เพิ่มไปยังของคุณ build.gradle

def activity_version = "1.2.0-alpha03"

// Java language implementation
implementation "androidx.activity:activity:$activity_version"
// Kotlin
implementation "androidx.activity:activity-ktx:$activity_version"

จะใช้สัญญาที่สร้างไว้ล่วงหน้าได้อย่างไร

API ใหม่นี้มีฟังก์ชันที่สร้างไว้ล่วงหน้าดังต่อไปนี้

  1. TakeVideo
  2. PickContact
  3. GetContent
  4. GetContents
  5. OpenDocument
  6. โอเพนด็อกคิวเมนต์
  7. OpenDocumentTree
  8. CreateDocument
  9. หมุน
  10. ถ่ายภาพ
  11. RequestPermission
  12. RequestPermissions

ตัวอย่างที่ใช้สัญญา takePicture:

private val takePicture = prepareCall(ActivityResultContracts.TakePicture()) 
     { bitmap: Bitmap? ->
        // Do something with the Bitmap, if present
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        button.setOnClickListener { takePicture() }
       }

แล้วเกิดอะไรขึ้นที่นี่? มาทำลายมันกันหน่อย takePictureเป็นเพียงการเรียกกลับซึ่งส่งกลับบิตแมปที่ nullable - หรือไม่ว่ามันเป็นโมฆะขึ้นอยู่กับว่าonActivityResultกระบวนการประสบความสำเร็จหรือไม่ prepareCallจากนั้นลงทะเบียนการโทรนี้ในคุณลักษณะใหม่ที่ComponentActivityเรียกว่าActivityResultRegistry- เราจะกลับมาที่นี่ในภายหลัง ActivityResultContracts.TakePicture()เป็นหนึ่งในผู้ช่วยเหลือในตัวที่ Google ได้สร้างสำหรับเราและในที่สุดก็กล่าวอ้างจริงเรียกเจตนาในลักษณะเดียวกับที่คุณจะก่อนหน้านี้ด้วยtakePictureActivity.startActivityForResult(intent, REQUEST_CODE)

จะเขียนสัญญาที่กำหนดเองได้อย่างไร

สัญญาง่าย ๆ ที่ใช้ Int เป็นอินพุตและส่งคืนสตริงที่ร้องขอกิจกรรมส่งคืนในผลลัพธ์ของเจตนา

    class MyContract : ActivityResultContract<Int, String>() {

    companion object {
        const val ACTION = "com.myapp.action.MY_ACTION"
        const val INPUT_INT = "input_int"
        const val OUTPUT_STRING = "output_string"
    }

    override fun createIntent(input: Int): Intent {
        return Intent(ACTION)
            .apply { putExtra(INPUT_INT, input) }
    }

    override fun parseResult(resultCode: Int, intent: Intent?): String? {
        return when (resultCode) {
            Activity.RESULT_OK -> intent?.getStringExtra(OUTPUT_STRING)
            else -> null
        }
    }
}



    class MyActivity : AppCompatActivity() {

    private val myActionCall = prepareCall(MyContract()) { result ->
        Log.i("MyActivity", "Obtained result: $result")
    }

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        ...
        button.setOnClickListener {
            myActionCall(500)
        }
    }
}

ตรวจสอบเอกสารอย่างเป็นทางการนี้สำหรับข้อมูลเพิ่มเติม


0
You need to override Activity.onActivityResult()

@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
super.onActivityResult(requestCode, resultCode, data);

if (resultCode == RESULT_CODE_ONE) {


   String a = data.getStringExtra("RESULT_CODE_ONE");

}
else if(resultCode == RESULT_CODE_TWO){

   // b was clicked

}
else{

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