Có phương pháp nào hoạt động như đoạn bắt đầu cho kết quả không?


91

Tôi hiện có một mảnh trong lớp phủ. Đây là để đăng nhập vào dịch vụ. Trong ứng dụng điện thoại, mỗi bước tôi muốn hiển thị trong lớp phủ là màn hình và hoạt động của riêng chúng. Có 3 phần của quá trình đăng nhập và mỗi phần có hoạt động riêng được gọi với startActivityForResult ().

Bây giờ tôi muốn làm điều tương tự bằng cách sử dụng các mảnh và lớp phủ. Lớp phủ sẽ hiển thị một phân đoạn tương ứng với mỗi hoạt động. Vấn đề là những đoạn này được lưu trữ trong một hoạt động trong API tổ ong. Tôi có thể làm cho phân đoạn đầu tiên hoạt động, nhưng sau đó tôi cần startActivityForResult (), điều này không thể thực hiện được. Có thứ gì đó dọc theo dòng startFragmentForResult () nơi tôi có thể bắt đầu một phân đoạn mới và khi nó hoàn thành, nó có trả về kết quả cho phân đoạn trước đó không?

Câu trả lời:


57

Tất cả các Fragment đều nằm bên trong các Activity. Việc bắt đầu một Fragment cho một kết quả không có nhiều ý nghĩa, bởi vì Activity chứa nó luôn có quyền truy cập vào nó và ngược lại. Nếu Fragment cần chuyển một kết quả, nó có thể truy cập Activity của nó và thiết lập kết quả của nó và kết thúc nó. Trong trường hợp hoán đổi các Phân đoạn trong một Hoạt động duy nhất, cả hai Phân đoạn vẫn có thể truy cập Hoạt động và tất cả thông điệp của bạn được truyền qua có thể chỉ cần đi qua Hoạt động.

Chỉ cần nhớ rằng bạn luôn có giao tiếp giữa Fragment và Activity của nó. Bắt đầu và kết thúc với kết quả là cơ chế giao tiếp giữa các Hoạt động - Các Hoạt động sau đó có thể ủy thác bất kỳ thông tin cần thiết nào cho các Phân đoạn của chúng.


11
Ngoài ra, khi tải một phân đoạn này từ một phân đoạn khác, bạn có thể đặt phân đoạn đích và gọi lại onActivityResultphương thức của phân đoạn mẹ nếu bạn muốn.
PJL

4
Câu trả lời của bạn chưa hoàn chỉnh, các phân đoạn đang sử dụng vòng đời như các hoạt động. Vì vậy, chúng ta không thể biện minh cho việc truyền các đối số thông qua các phương thức nhưng chúng ta nên sử dụng các gói để truyền các giá trị. Ngay cả khi mảnh vỡ đặt một giá trị ở đâu đó, chúng ta cần biết khi nào anh ta kết thúc. Các phân đoạn trước có nên nhận giá trị khi nó bắt đầu / tiếp tục không? Đây là một ý tưởng. Nhưng không có cách thích hợp để lưu trữ giá trị, phân đoạn có thể được gọi bởi nhiều phân mảnh / hoạt động khác.
Loenix

59

Nếu bạn muốn, có một số phương pháp để giao tiếp giữa các Fragment,

setTargetFragment(Fragment fragment, int requestCode)
getTargetFragment()
getTargetRequestCode()

Bạn có thể gọi lại bằng cách sử dụng chúng.

Fragment invoker = getTargetFragment();
if(invoker != null) {
    invoker.callPublicMethod();
}

Tôi đã không xác nhận nó, nhưng có lẽ nó hoạt động. vì vậy, bạn nên quan tâm đến việc rò rỉ bộ nhớ do tham chiếu vòng tròn do lạm dụng setTargetFragment().
nagoya0

3
Giải pháp tốt nhất cho đến nay! Hoạt động tuyệt vời khi có một Hoạt động và một đống phân mảnh trong đó cố gắng tìm đúng mảnh để thông báo sẽ là một cơn ác mộng. Cảm ơn
Damien Praca

1
@ userSeven7s điều này sẽ không làm việc cho thay thế trường hợp nhưng nên làm việc cho hide trường hợp
Muhammad Babar

1
@ nagoya0 có làm tốt hơn không nếu chúng ta sử dụng WeakReference cho phân mảnh đích
quangson91

11

Chúng tôi chỉ có thể chia sẻ cùng một ViewModel giữa các phân đoạn

SharedViewModel

import android.arch.lifecycle.MutableLiveData
import android.arch.lifecycle.ViewModel

class SharedViewModel : ViewModel() {

    val stringData: MutableLiveData<String> by lazy {
        MutableLiveData<String>()
    }

}

FirstFragment

import android.arch.lifecycle.Observer
import android.os.Bundle
import android.arch.lifecycle.ViewModelProviders
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup

class FirstFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)

        sharedViewModel.stringData.observe(this, Observer { dateString ->
            // get the changed String
        })

    }

}

SecondFragment

import android.arch.lifecycle.ViewModelProviders
import android.os.Bundle
import android.support.v4.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGrou

class SecondFragment : Fragment() {

    private lateinit var sharedViewModel: SharedViewModel

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        activity?.run {
            sharedViewModel = ViewModelProviders.of(activity).get(SharedViewModel::class.java)
        }
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?,
                              savedInstanceState: Bundle?): View? {
        return inflater.inflate(R.layout.fragment_first, container, false)
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        changeString()
    }

    private fun changeString() {
        sharedViewModel.stringData.value = "Test"
    }

}

3
Xin chào Levon, tôi nghĩ rằng đoạn mã trên sẽ không chia sẻ cùng một phiên bản mô hình chế độ xem. Bạn đang chuyển (phân đoạn) này cho ViewModelProviders.of (this) .get (SharedViewModel :: class.java). Điều này sẽ tạo ra hai trường hợp riêng biệt cho các phân đoạn. Bạn cần phải vượt qua các hoạt động ViewModelProviders.of (hoạt động) .get (SharedViewModel :: class.java)
Shailendra Patil

@ShailendraPatil Bắt rất tốt, tôi sẽ sửa nó ngay bây giờ.
Levon Petrosyan

4

2 xu của tôi.

Tôi chuyển đổi giữa các phân đoạn bằng cách hoán đổi một phân đoạn cũ với một phân đoạn mới bằng cách sử dụng ẩn và hiện / thêm (hiện có / mới). Vì vậy, câu trả lời này dành cho những nhà phát triển sử dụng mảnh vỡ như tôi.

Sau đó, tôi sử dụng onHiddenChangedphương pháp để biết rằng phân đoạn cũ đã được chuyển đổi trở lại từ phân đoạn mới. Xem mã bên dưới.

Trước khi rời khỏi phân đoạn mới, tôi đặt một kết quả là tham số toàn cục sẽ được phân đoạn cũ truy vấn. Đây là một giải pháp rất ngây thơ.

@Override
public void onHiddenChanged(boolean hidden) {
    super.onHiddenChanged(hidden);
    if (hidden) return;
    Result result = Result.getAndReset();
    if (result == Result.Refresh) {
        refresh();
    }
}

public enum Result {
    Refresh;

    private static Result RESULT;

    public static void set(Result result) {
        if (RESULT == Refresh) {
            // Refresh already requested - no point in setting anything else;
            return;
        }
        RESULT = result;
    }

    public static Result getAndReset() {
        Result result = RESULT;
        RESULT = null;
        return result;
    }
}

getAndReset()Phương pháp là gì?
EpicPandaForce

Không phải cũng onResume()được gọi trên phân đoạn đầu tiên khi phân đoạn thứ hai bị loại bỏ?
PJ_Finnegan

4

Gần đây, Google vừa bổ sung một khả năng mới giúp FragmentManagerFragmentManagercó thể hoạt động như một cửa hàng trung tâm cho các kết quả phân mảnh. Chúng ta có thể truyền dữ liệu qua lại giữa các Fragment một cách dễ dàng.

Phân đoạn bắt đầu.

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    // Use the Kotlin extension in the fragment-ktx artifact
    setResultListener("requestKey") { key, bundle ->
        // We use a String here, but any type that can be put in a Bundle is supported
        val result = bundle.getString("bundleKey")
        // Do something with the result...
    }
}

Một Fragment mà chúng ta muốn trả lại kết quả.

button.setOnClickListener {
    val result = "result"
    // Use the Kotlin extension in the fragment-ktx artifact
    setResult("requestKey", bundleOf("bundleKey" to result))
}

Đoạn mã được lấy từ các tài liệu chính thức của Google. https://developer.android.com/training/basics/fragment/pass-data-between#kotlin

Tại dữ liệu của câu trả lời này được viết, tính năng này vẫn ở alphatrạng thái. Bạn có thể thử nó bằng cách sử dụng phụ thuộc này.

androidx.fragment:fragment:1.3.0-alpha05

2

Trong phân đoạn của bạn, bạn có thể gọi getActivity (). Điều này sẽ cung cấp cho bạn quyền truy cập vào hoạt động đã tạo ra phân mảnh. Từ đó, bạn có thể gọi phương thức tùy chỉnh của mình để đặt giá trị hoặc chuyển giá trị.


1

Có một thư viện Android - FlowR cho phép bạn bắt đầu các phân đoạn để có kết quả.

Bắt đầu một phân đoạn cho kết quả.

Flowr.open(RequestFragment.class)
    .displayFragmentForResults(getFragmentId(), REQUEST_CODE);

Xử lý kết quả trong phân đoạn cuộc gọi.

@Override
protected void onFragmentResults(int requestCode, int resultCode, Bundle data) {
    super.onFragmentResults(requestCode, resultCode, data);

    if (requestCode == REQUEST_CODE) {
        if (resultCode == Activity.RESULT_OK) {
            demoTextView.setText("Result OK");
        } else {
            demoTextView.setText("Result CANCELED");
        }
    }
}

Đặt kết quả trong Fragment.

Flowr.closeWithResults(getResultsResponse(resultCode, resultData));

1

Một giải pháp sử dụng giao diện (và Kotlin). Ý tưởng cốt lõi là xác định một giao diện gọi lại, triển khai nó trong hoạt động của bạn, sau đó gọi nó từ phân đoạn của bạn.

Đầu tiên, hãy tạo một giao diện ActionHandler:

interface ActionHandler {
    fun handleAction(actionCode: String, result: Int)
}

Tiếp theo, gọi điều này từ con bạn (trong trường hợp này là phân mảnh của bạn):

companion object {
    const val FRAGMENT_A_CLOSED = "com.example.fragment_a_closed"
}

fun closeFragment() {
    try {
        (activity as ActionHandler).handleAction(FRAGMENT_A_CLOSED, 1234)
    } catch (e: ClassCastException) {
        Timber.e("Calling activity can't get callback!")
    }
    dismiss()
}

Cuối cùng, triển khai điều này trong cha mẹ của bạn để nhận lệnh gọi lại (trong trường hợp này là Hoạt động của bạn):

class MainActivity: ActionHandler { 
    override fun handleAction(actionCode: String, result: Int) {
        when {
            actionCode == FragmentA.FRAGMENT_A_CLOSED -> {
                doSomething(result)
            }
            actionCode == FragmentB.FRAGMENT_B_CLOSED -> {
                doSomethingElse(result)
            }
            actionCode == FragmentC.FRAGMENT_C_CLOSED -> {
                doAnotherThing(result)
            }
        }
    }

0

Cách dễ nhất để chuyển dữ liệu trở lại là setArgument (). Ví dụ: bạn có phân mảnh1 gọi phân mảnh2 gọi phân mảnh3, phân mảnh1 -> framgnet2 -> phân mảnh3

Trong phân mảnh1

public void navigateToFragment2() {
    if (fragmentManager == null) return;

    Fragment2 fragment = Fragment2.newInstance();
    String tag = "Fragment 2 here";
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .add(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commitAllowingStateLoss();
}

Tronggment2, chúng tôi gọi làgment3 như thường lệ

private void navigateToFragment3() {
    if (fragmentManager == null) return;
    Fragment3 fragment = new Fragment3();
    fragmentManager.beginTransaction()
            .setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
            .replace(R.id.flContent, fragment, tag)
            .addToBackStack(null)
            .commit();
}

Khi chúng tôi hoàn thành nhiệm vụ của mình trong mảnh 3 bây giờ chúng tôi gọi như thế này:

FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
if (fragmentManager == null) return;
fragmentManager.popBackStack();
Bundle bundle = new Bundle();
bundle.putString("bundle_filter", "data");
fragmentManager.findFragmentByTag("Fragment 2 here").setArguments(bundle);

Bây giờ trong phân đoạn 2, chúng ta có thể dễ dàng gọi các đối số

@Override
public void onResume() {
    super.onResume();
    Bundle rgs = getArguments();
    if (args != null) 
        String data = rgs.getString("bundle_filter");
}

Hãy cẩn thận với điều này, nó có thể hoạt động trong một số trường hợp, nhưng nếu phân đoạn của bạn có các đối số ban đầu ('phân đoạn 2' trong ví dụ này), điều này sẽ ghi đè các đối số ban đầu được sử dụng để tạo phân đoạn, điều này có thể dẫn đến trạng thái không nhất quán nếu mảnh vỡ bị phá hủy và tái tạo.
Juan

0

Một điều khác bạn có thể làm tùy thuộc vào kiến ​​trúc của bạn là sử dụng ViewModel được chia sẻ giữa các phân đoạn. Vì vậy, trong trường hợp của tôi, FragmentA là một biểu mẫu và FragmentB là một dạng xem lựa chọn mục nơi người dùng có thể tìm kiếm và chọn một mục, lưu trữ nó trong ViewModel. Sau đó, khi tôi quay lại FragmentA, thông tin đã được lưu trữ!

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.