How to display R.xml files in a fragment? - android

I was having this doubt of how to display a R.xml file in a fragment.
public class initSettings extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstance)
{
return inflater.inflate(R.layout.init_settings,container,false);
}
}
Now, if I wanted to display R.xml.init_settings getting an error
Expected resource of type layout.

Expected resource of type layout.
You are trying to inflate xml file which it should be layout in this case in here:
(R.layout.init_settings, container, false);
So try to inflate this in the layout directory (just like your current codes):
R.layout.init_settings
And use layout to inflate and not an xml.
And like I said in my comments, onCreateView() won't import xml files. Instead, use addPreferencesFromResource() to inflate xml settings:
public class SettingsActivity extends PreferenceActivity {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.init_settings);
}
}
Read the documentation: https://developer.android.com/guide/topics/ui/settings#java
For the newest APIs (AndroidX) use PreferenceFragmentCompat().
Kotlin code:
class SettingsFragment : PreferenceFragmentCompat() {
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
addPreferencesFromResource(R.xml.settingspreference)
}
}

Related

Why do you pass LayoutInflater to inflate()?

Here's the code:
private lateinit var binding: ResultProfileBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ResultProfileBinding.**inflate(layoutInflater)**
val view = binding.root
setContentView(view)
}
Why layoutInflater? Isn't inflate supposed to inflate an xml file?
Doing a Ctr+Q on inflate gives this public static #NonNull com.example.ActivityMainBinding inflate(#NonNull android.view.LayoutInflater inflater)
I can't find this function on the android developer website. It's either in LayoutInflater or View. Where can you find this function?
Isn't inflate supposed to inflate an xml file?
Yes. A LayoutInflater inflates layout resources.
I can't find this function on the android developer website
It is code-generated in your project. That generated code is not significantly different than if you had typed it in yourself, which is why it uses a LayoutInflater to inflate the associated layout resource.
Where can you find this function?
On your computer. Specifically, it will be in one of the subdirectories off of your module's build/ directory.

Where is layoutInflater defined in this android app tutorial code?

This Android tutorial introduces the concept of view binding, with this section demonstrating how to use it. In this case, the view binding is set up using the following code.
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
The explanation for the call to ActivityMainBinding.inflate() is as follows:
This line initializes the binding object which you'll use to access
Views in the activity_main.xml layout.
What this does not explain is where the variable layoutInflater is defined.
When using Android Studio, the code completion suggests that the variable "comes from getLayoutInflater()":
getLayoutInflater() seems to be a method in Activity, but this doesn't help me understand what the reference to layoutInflater is doing, where it is defined, and how it is in scope at this point of the code. Can someone help me to understand this please?
ActivityMainBinding.java is the generated class by data binding which has a static method inflate(). When you pass the layoutInflater(it retrieve a standard LayoutInflater instance that is already hooked up to the current context) to inflate() it generates the same code under the code as we usually do while inflating the views and it fetches the layout name automatically.
So, the whole method is like
public static ActivityMainBinding inflate(#NonNull LayoutInflater inflater,
#Nullable ViewGroup parent, boolean attachToParent) {
View root = inflater.inflate(R.layout.activity_main, parent, false);
if (attachToParent) {
parent.addView(root);
}
return bind(root);
}
I hope this is what you are looking and sure can help you. Thanks
I came at the same question and found layoutInflater was declared in NavigationMenuPresenter.java.
package com.google.android.material.internal;
...
public class NavigationMenuPresenter implements MenuPresenter {
...
LayoutInflater layoutInflater;
...
public void initForMenu(#NonNull Context context, #NonNull MenuBuilder menu) {
layoutInflater = LayoutInflater.from(context);
...
In my case, full path to the java script was
%HOMEDRIVE%%HOMEPATH%\.gradle\caches\modules-2\files-2.1\com.google.android.material\material\1.7.0\289bbb3a7fea52532f1163487f9469217ee608a9\material-1.7.0-sources.jar!\com\google\android\material\internal\NavigationMenuPresenter.java
After a little digging I found out that the getLayoutInflater comes from the Activity class. Activity class is parent of androidx.core.app.ComponentActivity that is parent of ComponentActivity that is parent of FragmentActivity that is parent of AppCompatActivity that is parent of your Activity class.

Passing data between fragments through ViewModel

let say for example I have 1 Activity that contains 5 Fragments and those Fragments presents a 1 flow of payment process so each Fragment depends on the previous Fragment by passing data of what the user chooses
I'm planning to make 1 ViewModel in the Activity that handles the data between fragments but I've read that it is a bad idea to expose MutableLiveData outside of the view model. so I can't say viewModel.setdata(example) in the Activity the best solution was is to use navigation component with safe args and create a ViewModel for each Fragment and create ViewModelFactory for each fragment too.
but this will make me write too many classes.
is there an optimal way to pass data between views using 1 ViewModel without violating the MVVM architecture rules?
Yes, this is good decision to use ViewModel to share data between fragments. Look this
SharedViewModel
public class SharedViewModel extends ViewModel {
private final MutableLiveData<Item> selected = new MutableLiveData<Item>();
public void select(Item item) {
selected.setValue(item);
}
public LiveData<Item> getSelected() {
return selected;
}
}
MasterFragment
public class MasterFragment extends Fragment {
private SharedViewModel model;
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
itemSelector.setOnClickListener(item -> {
model.select(item);
});
}
}
DetailFragment
public class DetailFragment extends Fragment {
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
SharedViewModel model = new ViewModelProvider(requireActivity()).get(SharedViewModel.class);
model.getSelected().observe(getViewLifecycleOwner(), item -> {
// Update the UI.
});
}
}
Here is the black magic:
val viewModel: MyViewModel by activityViewModels()

Mvvmcross fragments

I am trying to implement my navigation drawer with MVVMCross but I cannot get the fragments to show. (This is not a problem with the navigation drawer but with fragments and MVVMCross).
This is the code I have in my sample (found on the github of MVVMCross), see github links below!
I have one activity extending the MvxCachingFragmentCompatActivity<MainViewModel>, this is the MainActivity containing the FrameLayout (Called Resource.Id.content_frame)
I have an MvxFragment called FirstFragment:
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame, true)]
[Register(nameof(FirstFragment))]
public class FirstFragment : MvxFragment<FirstViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(Resource.Layout.FirstView, container, false);
return view;
}
}
I also have the following code added in my MainViewModel:
public class MainViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public MainViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public override async Task Initialize()
{
await _navigationService.Navigate<FirstViewModel>();
}
}
Github links:
My sample is visible on the following github!
And the is the mvvmcross sample!
I found the issue => apparently the MvxNavigationService doesn't like MvxCachingFragmentCompatActivity for some reason.
First Sample (Working)
In the first sample I do RegisterAppStart<> directly on the MainViewModel which extend from MvxCachingFragmentCompatActivity. This is working perfectly
Some code samples (for full code see link)
public class App : MvvmCross.Core.ViewModels.MvxApplication
{
public override void Initialize()
{
RegisterAppStart<MainViewModel>();
}
}
MainViewModel : MvxViewModel
public MainViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
Init();
}
public async void Init()
{
await _navigationService.Navigate<FirstViewModel>();
}
Second Sample (Issue)
In the second sample I first call another activity(StartActivity) and then go to the MainActivity. This gives problems because the MainViewModel is not called with RegisterAppStart<> but with the IMvxNavigationService.Navigate<MainViewModel>()
Some code samples (for full code see link)
public class App : MvvmCross.Core.ViewModels.MvxApplication
{
public override void Initialize()
{
RegisterAppStart<StartViewModel>();
}
}
StartViewModel:
public class StartViewModel : MvxViewModel
{
private readonly IMvxNavigationService _navigationService;
public ICommand StartCommand => new MvxCommand(ExecuteStart);
public StartViewModel()
{
_navigationService = Mvx.Resolve<IMvxNavigationService>();
}
private async void ExecuteStart()
{
await _navigationService.Navigate<MainViewModel>();
}
}
Difference:
First ViewModel: RegisterAppStart<StartViewModel>();
Called from the navigation service: await _navigationService.Navigate<MainViewModel>();

MvvmCross fragments resolving

I'm using Xamarin with MvvmCross, and have problem with fragments usage.
I call ShowViewModel so:
public class MainViewModel : MvxViewModel
{
public override void Start()
{
ShowViewModel<MainMenuViewModel>();
}
}
Where MainMenuViewModel it's class:
public class MainMenuViewModel : MvxViewModel
{
}
Implemented fragment as follows:
[MvxFragment(typeof(MainMenuViewModel), Resource.Id.navigation_frame)]
[Register("mvvm.droid.views.MainMenuView")]
public class MainMenuView : MvxFragment<MainMenuViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var ignore = base.OnCreateView(inflater, container, savedInstanceState);
return this.BindingInflate(Resource.Layout.MainMenuView, null);
}
}
But on runtime it throws error:
Android.Content.ActivityNotFoundException: Unable to find explicit
activity class
{Mvvm.Droid/md5f67dcc55ddb5809d2766dd0c42c8b3bb.MainMenuView};
have you declared this activity in your AndroidManifest.xml?
For figuring out this, i implemented CustomPresenter, taken from here.
And in Setup registered this presenter for fragments:
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
var mvxFragmentsPresenter = new MvxCustomFragmentsPresenter(AndroidViewAssemblies);
Mvx.RegisterSingleton<IMvxAndroidViewPresenter>(mvxFragmentsPresenter);
return mvxFragmentsPresenter;
}
It seems like presenter found fragments, but at Show(Intent) method call it's still crushing. In decompiled sources there is a strange check if it's an activity.
Tryed to implement drawerLayout based on many implementations, but the same result. What i'm missing?
The issue is in your MvxFragment attribute:
[MvxFragment(typeof(MainMenuViewModel), Resource.Id.navigation_frame)]
The first parameter needs to be the MvxViewModel associated to your Activity that you want to place the menu fragment in. In your case I believe this may be MainViewModel?
Mvvmcross description of MvxFragment attribute:
public MvxFragmentAttribute(
Type parentActivityViewModelType,
int fragmentContentId,
bool addToBackStack = false);

Categories

Resources