Skip to content

Condividere i dati tra i frammenti usando il nuovo componente dell'architettura ViewModel

Vogliamo mostrarti la migliore soluzione che abbiamo trovato online. Vogliamo che ti aiuti e se vuoi condividere eventuali miglioramenti, fallo liberamente.

Soluzione:

Aggiornato il 6/12/2017,

Android Official fornisce un esempio semplice e preciso di come funziona il ViewModel sul modello Master-Detail, si dovrebbe dare un'occhiata prima.Condividere i dati tra i frammenti

Come hanno detto @CommonWare, @Quang Nguyen, non è lo scopo di Yigit fare la chiamata dal master al dettaglio, ma è meglio usare il modello Middle man. Ma se si vuole effettuare una transazione su un frammento, è necessario farlo nell'attività. In quel momento, la classe ViewModel dovrebbe essere una classe statica nell'attività e potrebbe contenere qualche brutta callback per richiamare l'attività per effettuare la transazione del frammento.

Ho cercato di implementare questo aspetto e ho realizzato un semplice progetto al riguardo. Potete dargli un'occhiata. La maggior parte del codice proviene da Google IO 2017, anche la struttura.
https://github.com/charlesng/SampleAppArch

Non uso Master Detail Fragment per implementare il componente, ma il vecchio (comunicazione tra frammenti in ViewPager.) La logica dovrebbe essere la stessa.

Ma ho trovato qualcosa di importante usando questi componenti

  1. Ciò che si desidera inviare e ricevere nel Middle man, dovrebbe essere inviato e ricevuto solo nel View Model
  2. La modifica non sembra eccessiva nella classe del frammento. Poiché cambia solo l'implementazione da "Interface callback" a "Listening and responding ViewModel".
  3. L'inizializzazione del View Model sembra importante e probabilmente sarà chiamata nell'attività.
  4. Usare MutableLiveData per rendere la sorgente sincronizzata solo nell'attività.

1. Attività Pager

public class PagerActivity extends AppCompatActivity {
    /**
     * The pager widget, which handles animation and allows swiping horizontally to access previous
     * and next wizard steps.
     */
    private ViewPager mPager;
    private PagerAgentViewModel pagerAgentViewModel;
    /**
     * The pager adapter, which provides the pages to the view pager widget.
     */
    private PagerAdapter mPagerAdapter;
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_pager);
        FloatingActionButton fab = (FloatingActionButton) findViewById(R.id.fab);
        fab.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View view) {
                Snackbar.make(view, "Replace with your own action", Snackbar.LENGTH_LONG)
                        .setAction("Action", null).show();
            }
        });
        mPager = (ViewPager) findViewById(R.id.pager);
        mPagerAdapter = new ScreenSlidePagerAdapter(getSupportFragmentManager());
        mPager.setAdapter(mPagerAdapter);
        pagerAgentViewModel = new ViewModelProvider(this).get(PagerAgentViewModel.class);
        pagerAgentViewModel.init();
    }

    /**
     * A simple pager adapter that represents 5 ScreenSlidePageFragment objects, in
     * sequence.
     */
    private class ScreenSlidePagerAdapter extends FragmentStatePagerAdapter {
       ...Pager Implementation
    }

}

2.PagerAgentViewModel (Meritava un nome migliore di questo)

public class PagerAgentViewModel extends ViewModel {
    private final SavedStateHandle state;
    private final MutableLiveData messageContainerA;
    private final MutableLiveData messageContainerB;

    public PagerAgentViewModel(SavedStateHandle state) {
        this.state = state;

        messageContainerA = state.getLiveData("Default Message");
        messageContainerB = state.getLiveData("Default Message");
    }

    public void sendMessageToB(String msg)
    {
        messageContainerB.setValue(msg);
    }
    public void sendMessageToA(String msg)
    {
        messageContainerA.setValue(msg);

    }
    public LiveData getMessageContainerA() {
        return messageContainerA;
    }

    public LiveData getMessageContainerB() {
        return messageContainerB;
    }
}

3.BlankFragmentA

public class BlankFragmentA extends Fragment {

    private PagerAgentViewModel viewModel;

    public BlankFragmentA() {
        // Required empty public constructor
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        viewModel = new ViewModelProvider(getActivity()).get(PagerAgentViewModel.class);

        textView = (TextView) view.findViewById(R.id.fragment_textA);
        // set the onclick listener
        Button button = (Button) view.findViewById(R.id.btnA);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewModel.sendMessageToB("Hello B");
            }
        });

        //setup the listener for the fragment A
        viewModel.getMessageContainerA().observe(getViewLifecycleOwner(), new Observer() {
            @Override
            public void onChanged(@Nullable String msg) {
                textView.setText(msg);
            }
        });

    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank_a, container, false);
        return view;
    }

}

4.FrammentoB vuoto

public class BlankFragmentB extends Fragment {

    public BlankFragmentB() {
        // Required empty public constructor
    }

    @Override
    public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);

        viewModel = new ViewModelProvider(getActivity()).get(PagerAgentViewModel.class);

        textView = (TextView) view.findViewById(R.id.fragment_textB);
        //set the on click listener
        Button button = (Button) view.findViewById(R.id.btnB);
        button.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                viewModel.sendMessageToA("Hello A");
            }
        });

        //setup the listener for the fragment B
        viewModel.getMessageContainerB().observe(getViewLifecycleOwner(), new Observer() {
            @Override
            public void onChanged(@Nullable String msg) {
                textView.setText(msg);

            }
        });
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container,
                             Bundle savedInstanceState) {
        // Inflate the layout for this fragment
        View view = inflater.inflate(R.layout.fragment_blank_b, container, false);
        return view;
    }

}

Ho trovato una soluzione simile a quella degli altri secondo l'esempio di google codelabs.
Ho due frammenti in cui uno di essi attende un cambiamento di oggetto nell'altro e continua il suo processo con l'oggetto aggiornato.

Per questo approccio è necessaria una classe ViewModel come quella che segue:

import android.arch.lifecycle.MutableLiveData;
import android.arch.lifecycle.ViewModel;
import yourPackage.YourObjectModel;

public class SharedViewModel extends ViewModel {

   public MutableLiveData item = new MutableLiveData<>();

   public YourObjectModel getItem() {
      return item.getValue();
   }

   public void setItem(YourObjectModel item) {
      this.item.setValue(item);
   }

}

e il frammento ascoltatore dovrebbe avere questo aspetto:

public class ListenerFragment extends Fragment{
   private SharedViewModel model;
  @Override
  public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);

    model.item.observe(getActivity(), new Observer(){

        @Override
        public void onChanged(@Nullable YourObjectModel updatedObject) {
            Log.i(TAG, "onChanged: recieved freshObject");
            if (updatedObject != null) {
                // Do what you want with your updated object here. 
            }
        }
    });
}
}

infine, il frammento di aggiornamento può essere come questo:

public class UpdaterFragment extends DialogFragment{
    private SharedViewModel model;
    @Override
    public void onCreate(@Nullable Bundle savedInstanceState) {
       super.onCreate(savedInstanceState);
       model = ViewModelProviders.of(getActivity()).get(SharedViewModel.class);
   }
   // Call this method where it is necessary
   private void updateViewModel(YourObjectModel yourItem){
      model.setItem(yourItem);
   }
}

È bene ricordare che il frammento di aggiornamento può essere qualsiasi forma di frammento (non solo DialogFragment) e che per usare questi componenti dell'architettura si devono avere queste righe di codice nel file build.gradle dell'applicazione. source

dependencies {
  def lifecycle_version = "1.1.1"
  implementation "android.arch.lifecycle:extensions:$lifecycle_version"
}

Come scritto nel tutorial ufficiale di Google, ora si può ottenere un modello di vista condiviso con by activityViewModels()

// Use the 'by activityViewModels()' Kotlin property delegate
// from the fragment-ktx artifact
private val model: SharedViewModel by activityViewModels()

Commenti e valutazioni

Successivamente puoi trovare le recensioni di altri programmatori, hai ancora la libertà di mostrare la tua se ti piace.



Utilizzate il nostro motore di ricerca

Ricerca
Generic filters

Lascia un commento

Il tuo indirizzo email non sarà pubblicato.