결과에 대한 시작 조각처럼 작동하는 방법이 있습니까?


95

현재 오버레이에 조각이 있습니다. 서비스에 로그인하기위한 것입니다. 전화 앱에서 오버레이에 표시하려는 각 단계는 자체 화면과 활동입니다. 로그인 프로세스에는 세 부분이 있으며 각 부분에는 startActivityForResult ()로 호출 된 자체 활동이 있습니다.

이제 조각과 오버레이를 사용하여 동일한 작업을 수행하고 싶습니다. 오버레이는 각 활동에 해당하는 조각을 표시합니다. 문제는 이러한 프래그먼트가 Honeycomb API의 활동에서 호스팅된다는 것입니다. 첫 번째 조각이 작동하도록 할 수는 있지만 가능하지 않은 startActivityForResult ()가 필요합니다. startFragmentForResult () 줄을 따라 새 조각을 시작할 수 있고 완료되면 이전 조각에 결과를 반환 할 수있는 것이 있습니까?

답변:


58

모든 조각은 활동 내에 있습니다. 결과에 대한 조각을 시작하는 것은 그다지 의미가 없습니다. 왜냐하면 그것을 수용하는 활동은 항상 그것에 접근 할 수 있고 그 반대도 마찬가지이기 때문입니다. Fragment가 결과를 전달해야하는 경우 활동에 액세스하여 결과를 설정하고 완료 할 수 있습니다. 단일 활동에서 프래그먼트를 교환하는 경우에도 두 프래그먼트 모두에서 활동에 계속 액세스 할 수 있으며 모든 메시지 전달은 단순히 활동을 통과 할 수 있습니다.

프래그먼트와 액티비티간에 항상 커뮤니케이션이 있다는 것을 기억하십시오. 결과를 시작하고 끝내는 것은 활동 간의 통신 메커니즘입니다. 활동은 필요한 정보를 프래그먼트에 위임 할 수 있습니다.


11
또한 한 조각을 다른 조각에서로드 할 때 대상 조각을 설정하고 onActivityResult원하는 경우 부모 조각의 메서드를 다시 호출 할 수 있습니다 .
PJL

4
귀하의 대답은 불완전하고 조각은 활동과 같은 수명주기를 사용하고 있습니다. 따라서 메서드를 통해 인수를 전달할 수는 없지만 번들을 사용하여 값을 전달해야합니다. 조각이 어딘가에 값을 설정하더라도 그가 언제 끝났는지 알아야합니다. 이전 조각이 시작 / 재개 할 때 값을 가져와야합니까? 이것은 아이디어입니다. 그러나 값을 저장하는 적절한 방법이 없습니다. 조각은 여러 다른 조각 / 활동에 의해 호출 될 수 있습니다.
Loenix

1
글쎄, 그것은 훨씬 더 많은 유연성을 제공하기 때문에 '결과에 대한 시작 조각'을 호출하는 것이 합리적 일 것입니다 .-> 왜 조각이 부모에 대해 알아야 하는가? 프래그먼트가 작업을 수행하고 그가 살고있는 활동에 관계없이 결과를 반환하는 것은 그다지 깨끗하지 않을 것입니다. 특히 단일 활동 앱의 맥락에서.
daneejela

60

원한다면 Fragment 간의 통신을위한 몇 가지 방법이 있습니다.

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

이것을 사용하여 콜백 할 수 있습니다.

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

나는 그것을 확인하지 않았지만 아마도 작동 할 것입니다. 따라서 .NET의 남용으로 인한 순환 참조로 인한 메모리 누수에주의해야합니다 setTargetFragment().
nagoya0

3
지금까지 최고의 솔루션! 알림에 적합한 조각을 찾으려고하는 것이 악몽 인 하나의 활동과 조각 스택이있을 때 훌륭하게 작동합니다. 감사합니다
Damien Praca 2013-10-04

1
@ userSeven7s이하지 않습니다에 대한 작업을 대체 할 경우 만 작동한다 숨기기 경우
무하마드 바바

1
@ nagoya0은 우리가 대상 조각에 WeakReference를 사용하면 더 잘합니다
quangson91

작동하는 동안 setTargetFragment현재 사용되지 않습니다. 보기 setResultListenerstackoverflow.com/a/61881149/2914140 여기.
CoolMind

11

프래그먼트간에 동일한 ViewModel을 공유 할 수 있습니다

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
안녕하세요 Levon, 위의 코드는 동일한 뷰 모델 인스턴스를 공유하지 않을 것이라고 생각합니다. ViewModelProviders.of (this) .get (SharedViewModel :: class.java)에 대해 this (fragment)를 전달합니다. 이렇게하면 조각에 대해 두 개의 개별 인스턴스가 생성됩니다. 당신은 활동 ViewModelProviders.of (활동) 갔지 (SharedViewModel :: class.java) 통과해야
쉐일 렌드 라 파틸

@ShailendraPatil 좋은 캐치, 지금 고칠 것입니다.
Levon Petrosyan

6

최근 구글은 단지에 새로운 기능을 추가했습니다 FragmentManager(가) 만든 FragmentManager조각 결과를위한 중앙 저장소 역할을 할 수 있습니다. Fragment간에 데이터를 쉽게주고받을 수 있습니다.

조각을 시작합니다.

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

결과를 되돌리려는 Fragment.

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

스 니펫은 Google의 공식 문서에서 가져온 것입니다. https://developer.android.com/training/basics/fragments/pass-data-between#kotlin

작성된이 답변의 데이터에서이 기능은 여전히 alpha상태입니다. 이 종속성을 사용하여 시도해 볼 수 있습니다.

androidx.fragment:fragment:1.3.0-alpha05

4

내 2 센트.

숨기기 및 표시 / 추가 (기존 / 신규)를 사용하여 이전 조각을 새 조각으로 교체하여 조각간에 전환합니다. 그래서이 대답은 나처럼 조각을 사용하는 개발자를위한 것입니다.

그런 다음 onHiddenChanged방법을 사용하여 이전 조각이 새 조각에서 다시 전환되었음을 알 수 있습니다. 아래 코드를 참조하십시오.

새 조각을 떠나기 전에 이전 조각에서 쿼리 할 전역 매개 변수에 결과를 설정했습니다. 이것은 매우 순진한 솔루션입니다.

@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()방법은?
EpicPandaForce

onResume()두 번째 조각이 해제 될 때 첫 번째 조각 에서도 호출 되지 않습니까?
PJ_Finnegan

2

조각에서 getActivity ()를 호출 할 수 있습니다. 이렇게하면 조각을 만든 활동에 액세스 할 수 있습니다. 여기에서 사용자 정의 메소드를 호출하여 값을 설정하거나 값을 전달할 수 있습니다.


1

결과에 대한 조각을 시작할 수 있는 Android 라이브러리 -FlowR 이 있습니다.

결과에 대한 조각을 시작합니다.

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

호출 조각에서 결과를 처리합니다.

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

조각에 결과를 설정합니다.

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

1

인터페이스 (및 Kotlin)를 사용하는 솔루션입니다. 핵심 아이디어는 콜백 인터페이스를 정의하고 활동에 구현 한 다음 조각에서 호출하는 것입니다.

먼저 인터페이스를 만듭니다 ActionHandler.

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

다음으로, 자녀로부터 이것을 호출하십시오 (이 경우에는 귀하의 조각).

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

마지막으로 부모에서이를 구현하여 콜백을 수신합니다 (이 경우에는 활동).

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

데이터를 다시 전달하는 가장 쉬운 방법은 setArgument ()입니다. 예를 들어 fragment3, fragment1-> framgnet2-> fargment3을 호출하는 fragment2를 호출하는 fragment1이 있습니다.

fragment1에서

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

fragment2에서는 평소처럼 fragment3을 호출합니다.

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

fragment3에서 작업을 마치면 다음과 같이 호출합니다.

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

이제 fragment2에서 쉽게 인수를 호출 할 수 있습니다.

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

이 항목에주의하세요. 어떤 경우에는 작동 할 수 있지만 프래그먼트에 원래 인수 (이 예에서는 'fragment 2')가있는 경우 프래그먼트를 만드는 데 사용 된 원래 인수를 재정 의하여 다음과 같은 경우 불일치 상태로 이어질 수 있습니다. 조각이 파괴되고 재생성됩니다.
Juan

0

아키텍처에 따라 할 수있는 또 다른 일은 프래그먼트간에 공유 된 ViewModel을 사용하는 것입니다. 따라서 제 경우에는 FragmentA가 양식이고 FragmentB는 사용자가 항목을 검색하고 선택하여 ViewModel에 저장할 수있는 항목 선택 뷰입니다. 그런 다음 FragmentA로 돌아 오면 정보가 이미 저장되어 있습니다!

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.