web-dev-qa-db-ja.com

Android ViewModelの追加の引数

アプリケーションコンテキスト以外の追加の引数をカスタムAndroidViewModelコンストラクターに渡す方法はありますか。例:

public class MyViewModel extends AndroidViewModel {
    private final LiveData<List<MyObject>> myObjectList;
    private AppDatabase appDatabase;

    public MyViewModel(Application application, String param) {
        super(application);
        appDatabase = AppDatabase.getDatabase(this.getApplication());

        myObjectList = appDatabase.myOjectModel().getMyObjectByParam(param);
    }
}

そして、カスタムViewModelクラスを使用したい場合、次のコードをフラグメントで使用します。

MyViewModel myViewModel = ViewModelProvider.of(this).get(MyViewModel.class)

したがって、追加の引数String paramをカスタムViewModelに渡す方法がわかりません。アプリケーションコンテキストのみを渡すことができますが、追加の引数は渡せません。私は本当に助けていただければ幸いです。ありがとうございました。

編集:いくつかのコードを追加しました。私はそれが今より良いことを願っています。

55
Mario Rudman

ViewModelのファクトリクラスが必要です。

public class MyViewModelFactory implements ViewModelProvider.Factory {
    private Application mApplication;
    private String mParam;


    public MyViewModelFactory(Application application, String param) {
        mApplication = application;
        mParam = param;
    }


    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        return (T) new MyViewModel(mApplication, mParam);
    }
}

ビューモデルをインスタンス化するときは、次のようにします。

MyViewModel myViewModel = ViewModelProviders.of(this, new MyViewModelFactory(this.getApplication(), "my awesome param")).get(MyViewModel.class);
126
mlyko

複数の異なるビューモデル間で共有される1つのファクトリについて、mlykoの答えを次のように拡張します。

public class MyViewModelFactory extends ViewModelProvider.NewInstanceFactory {
    private Application mApplication;
    private Object[] mParams;

    public MyViewModelFactory(Application application, Object... params) {
        mApplication = application;
        mParams = params;
    }

    @Override
    public <T extends ViewModel> T create(Class<T> modelClass) {
        if (modelClass == ViewModel1.class) {
            return (T) new ViewModel1(mApplication, (String) mParams[0]);
        } else if (modelClass == ViewModel2.class) {
            return (T) new ViewModel2(mApplication, (Integer) mParams[0]);
        } else if (modelClass == ViewModel3.class) {
            return (T) new ViewModel3(mApplication, (Integer) mParams[0], (String) mParams[1]);
        } else {
            return super.create(modelClass);
        }
    }
}

ビューモデルのインスタンス化:

ViewModel1 vm1 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), "something")).get(ViewModel1.class);
ViewModel2 vm2 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123)).get(ViewModel2.class);
ViewModel3 vm3 = ViewModelProviders.of(this, new MyViewModelFactory(getApplication(), 123, "something")).get(ViewModel3.class);

異なるコンストラクタを持つ異なるビューモデル。

9
rzehan

(KOTLIN)私のソリューションでは、少しのReflectionを使用しています。

いくつかの引数を必要とする新しいViewModelクラスを作成するたびに、同じ外観のFactoryクラスを作成したくないとしましょう。これはReflectionを介して実現できます。

たとえば、2つの異なるアクティビティがあります。

class Activity1 : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val args = Bundle().apply { putString("NAME_KEY", "Vilpe89") }
        val viewModel = ViewModelProviders.of(this, ViewModelWithArgumentsFactory(args))
            .get(ViewModel1::class.Java)
    }
}

class Activity2 : FragmentActivity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val args = Bundle().apply { putInt("AGE_KEY", 29) }
        val viewModel = ViewModelProviders.of(this, ViewModelWithArgumentsFactory(args))
            .get(ViewModel2::class.Java)
    }
}

そして、それらのアクティビティのViewModels:

class ViewModel1(private val args: Bundle) : ViewModel()

class ViewModel2(private val args: Bundle) : ViewModel()

次に、魔法の部分、Factoryクラスの実装:

class ViewModelWithArgumentsFactory(private val args: Bundle) : NewInstanceFactory() {
    override fun <T : ViewModel?> create(modelClass: Class<T>): T {
        try {
            val constructor: Constructor<T> = modelClass.getDeclaredConstructor(Bundle::class.Java)
            return constructor.newInstance(args)
        } catch (e: Exception) {
            Timber.e(e, "Could not create new instance of class %s", modelClass.canonicalName)
            throw e
        }
    }
}
0
vilpe89

Daggerが依存関係として提供できるViewModel引数をシームレスに使用しながら、これをより簡単でわかりやすく、マルチバインディングやファクトリボイラープレートを必要としないライブラリを作成しました: https://github.com/radutopor/ViewModelFactory

@ViewModelFactory
class UserViewModel(@Provided repository: Repository, userId: Int) : ViewModel() {

    val greeting = MutableLiveData<String>()

    init {
        val user = repository.getUser(userId)
        greeting.value = "Hello, $user.name"
    }    
}

ビューで:

class UserActivity : AppCompatActivity() {
    @Inject
    lateinit var userViewModelFactory2: UserViewModelFactory2

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_user)
        appComponent.inject(this)

        val userId = intent.getIntExtra("USER_ID", -1)
        val viewModel = ViewModelProviders.of(this, userViewModelFactory2.create(userId))
            .get(UserViewModel::class.Java)

        viewModel.greeting.observe(this, Observer { greetingText ->
            greetingTextView.text = greetingText
        })
    }
}
0
Radu Topor

作成済みのオブジェクトが渡されるクラスにしました。

private Map<String, ViewModel> viewModelMap;

public ViewModelFactory() {
    this.viewModelMap = new HashMap<>();
}

public void create(ViewModel viewModel) {
    viewModelMap.put(viewModel.getClass().getSimpleName(), viewModel);
    create(viewModel.getClass());
}

@NonNull
@Override
public <T extends ViewModel> T create(@NonNull Class<T> modelClass) {
    for (Map.Entry<String, ViewModel> viewModel : viewModelMap.entrySet()) {
        if (viewModel.getKey().equals(modelClass.getSimpleName())) {
            return (T) viewModel.getValue();
        }
    }
    return null;
}

その後

ViewModelFactory viewModelFactory = new ViewModelFactory();
viewModelFactory.create(new SampleViewModel(Args1, Args2));
SampleViewModel sampleViewModel = ViewModelProviders.of(this, viewModelFactory).get(SampleViewModel.class);
0
Danil