In my app I am using a fragment and inside the fragment I am trying to use
view.FindViewById<TextView>(Resource.Id.tv1).Typeface = Typeface.CreateFromAsset(Assets, "fonts/Montserrat_Light.ttf");
to change the typeface of a textview. But I am getting an error that 'The name 'assets' does not exist in the current context'
This is the fragment code
public class xFragment : Fragment
{
const string ARG_PAGE = "ARG_PAGE";
private int mPage;
public static xFragment newInstance(int page)
{
var args = new Bundle();
args.PutInt(ARG_PAGE, page);
var fragment = new xFragment ();
fragment.Arguments = args;
return fragment;
}
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
mPage = Arguments.GetInt(ARG_PAGE);
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.AgeLayout, container, false);
view.FindViewById<TextView>(Resource.Id.tv1).Typeface = Typeface.CreateFromAsset(Assets, "fonts/Montserrat_Light.ttf");
if (mPage == 2)
{
view = inflater.Inflate(Resource.Layout.AgeERLayout, container, false);
}
return view;
}
}
I found the solution and was so easy. I just had to use Activity.Assets
Related
Within my fragment code, i was trying to attach on click listener to this button so i can use it to navigate through a navigation graph.
When using viewbinding the app crashes with a null object exception, using find view by id fixes the problem,
I wanna know what's wrong !
java.lang.NullPointerException: Attempt to read from field 'android.widget.TextView com.example.teleport.databinding.FragmentLoginBinding.registerLink' on a null object reference
that's the error i get and here's the fragment code
public class LoginFragment extends Fragment implements View.OnClickListener {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;
private FragmentLoginBinding binding;
private NavController navController;
private TextView registerLink;
public LoginFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment LoginFragment.
*/
// TODO: Rename and change types and number of parameters
public static LoginFragment newInstance(String param1, String param2) {
LoginFragment fragment = new LoginFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_login, container, false);
}
#Override
public void onViewCreated(#NonNull #NotNull View view, #Nullable #org.jetbrains.annotations.Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
navController = Navigation.findNavController(view);
//registerLink = (TextView) view.findViewById(R.id.register_link);
//registerLink.setOnClickListener(this);
registerLink = (TextView) binding.registerLink;
registerLink.setOnClickListener(this);
}
#Override
public void onClick(View v) {
switch (v.getId()){
case R.id.register_link:
navController.navigate(R.id.nav_register);
break;
}
}
}
You need to create a binding instance in the onCreateView() method.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// create instance of binding here
binding = FragmentLoginBinding.inflate(inflater, container, false);
return binding.getRoot();
}
Also, explicitly you do not need to get the reference of the views, like
registerLink = (TextView) binding.registerLink;
registerLink.setOnClickListener(this);
You can just directly us the binding instance as
binding.registerLink.setOnClickListener(this);
For more details, check the documentation here.
I have a fragment, with only a list view. I am loading the favourites from my database like below. How do I load them async? Currently I am forced to make the async method synchronised while loading the view. I would like it so the view loads, then when the results are ready they are populated so that it is faster. Its not just micro optimisation, I would like to learn how to do this properly.
public class FavoriteFragment : Fragment
{
public event EventHandler<Favorite> FavoriteSelected;
private FavoriteAdapter _lstAdapter;
private List<Favorite> _favorites;
private Repository _repo;
public override void OnCreate(Bundle savedInstanceState) {
base.OnCreate(savedInstanceState);
_repo = TinyIoCContainer.Current.Resolve<Repository>();
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
var view = inflater.Inflate(Resource.Layout.fragment_favorite, container, false);
var lstFavorites = view.FindViewById<ListView>(Resource.Id.lstFavorites);
_favorites = Task.Run(async () => await _repo.GetFavoritesAsync()).Result;
_lstAdapter = new FavoriteAdapter(Activity, _favorites);
lstFavorites.Adapter = _lstAdapter;
lstFavorites.ItemClick += (s, e) => FavoriteSelected?.Invoke(this, _favorites[e.Position]);
return view;
}
From this thread onActivityCreated(), you can use OnActivityCreated() to load your data.
public override async void OnActivityCreated(Bundle savedInstanceState)
{
base.OnActivityCreated(savedInstanceState);
await LoadData();
}
private async Task LoadData()
{
//get data
}
I have a problem with await async method. I have a fragment (SalesFragment) which contains TabLayout with two fragments (FirstSalesFragment and SecondSalesFragment). When I showing SalesFragment and there is in TabLayout selected FirstSalesFragment I have there in ViewModel async method which call API and gives me some data. But problem is in FirstSalesFragment I have Microcharts layout and I want show there Chart, but main problem is my entries for chart are from async method from Api and OnCreateView method just doesnt wait for my async method in ViewModel.
My FirstSales view just show but chart is empty because OnCreateView doesnt wait for Entries... I tried OnActivityCreated or OnStart method but its same...
My SalesFragment
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_view = base.OnCreateView(inflater, container, savedInstanceState);
var viewPager = _view.FindViewById<ViewPager>(Resource.Id.viewpager);
if (viewPager != null)
{
var fragments = new List<MvxViewPagerFragmentInfo>
{
new MvxViewPagerFragmentInfo(ViewModel.FirstSalesTab, typeof(FirstSalesFragment), typeof(FirstSalesViewModel)),
new MvxViewPagerFragmentInfo(ViewModel.SecondSalesTab, typeof(SecondSalesFragment), typeof(SecondSalesViewModel))
};
viewPager.Adapter = new MvxCachingFragmentStatePagerAdapter(Activity, ChildFragmentManager, fragments);
}
var tabLayout = _view.FindViewById<TabLayout>(Resource.Id.tabs);
tabLayout.SetupWithViewPager(viewPager);
return _view;
}
FirstSalesViewModel:
public class FirstSalesViewModel : BaseViewModel
{
private readonly ISessionInfo _session;
private readonly IInfoMessageReporter _infoMessageReporter;
public FirstSalesViewModel()
{
_session = Mvx.Resolve<ISessionInfo>();
_infoMessageReporter = Mvx.Resolve<IInfoMessageReporter>();
}
public async Task Initialize()
{
}
public void Init()
{
Task.Run(InitializeHourlySales);
}
private async Task InitializeHourlySales()
{
// Call API async service... initialize Entries here....
}
private List<Entry> _entries;
public List<Entry> Entries
{
get { return _entries; }
set
{
_entries = value;
RaisePropertyChanged(() => Entries);
}
}
}
}
FirstSalesFragment
public class FirstSalesFragment : BaseFragment<FirstSalesViewModel>
{
protected override int FragmentId => Resource.Layout.fragment_firstsales;
private View _view;
private ChartView _chartView;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_view = base.OnCreateView(inflater, container, savedInstanceState);
_chartView = _view.FindViewById<ChartView>(Resource.Id.chartView);
if (_chartView != null)
{
InitializeChart();
}
return _view;
}
private void InitializeChart()
{
var tempEntries = ViewModel.Entries;
if (tempEntries == null || tempEntries.Count == 0)
{
_chartView.Chart = null;
return;
}
var tempChart = new LineChart
{
Entries = tempEntries,
LineMode = LineMode.Straight,
LineSize = 5,
LabelTextSize = 25,
AnimationDuration = new TimeSpan(0, 0, 5),
BackgroundColor = SKColor.Parse("#EEEEEE")
};
_chartView.Chart = tempChart;
}
}
Do your call to your async code in OnResume which supports using async.
I've modified your code sample a bit and made a call to your ViewModel.Init() in OnResume() for FirstSalesFragment.
FirstSalesViewModel
public class FirstSalesViewModel : BaseViewModel
{
...
public async Task Init()
{
await Task.Run(InitializeHourlySales);
}
private async Task InitializeHourlySales()
{
// Call API async service... initialize Entries here....
}
.....
FirstSalesFragment
...
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
_view = base.OnCreateView(inflater, container, savedInstanceState);
_chartView = _view.FindViewById<ChartView>(Resource.Id.chartView);
return _view;
}
public async override void OnResume()
{
base.OnResume();
await ViewModel.Init();
if (_chartView != null)
{
InitializeChart();
}
}
...
I am trying to implement tabs through MvvmCross in Xamarin. I came across MvxTabActivity in Android and MvxTabBarViewController in IOS. Both are working well. The problem is MvxTabActivity is obselete. Are there any alternatives for MvxTabActivity?
I found another way to implement this, which uses TabLayout and a ViewPager.
The solution asks to use fragments within a fragment. I have pasted the code for this approach. The problem here is on swiping the tabs, all the data in previous tabs is lost.
I tried using RetainInstance = true, that gave following exception : "Can't retain fragements that are nested in other fragments."
Product Detail Activity :
[Activity(Label = "ProductDetailView")]
public class ProductDetailView : MvxAppCompatActivity<ProductDetailViewModel>
{
private FrameLayout _mainFrame;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.product_detail_view);
if (FindViewById<FrameLayout>(Resource.Id.frame_Detail) != null)
{
var frag = new NutritionCategoryView();
frag.ViewModel = ViewModel.NutritionCategoryModel;
var trans = SupportFragmentManager.BeginTransaction();
trans.Replace(Resource.Id.frame_Detail, frag);
trans.AddToBackStack(null);
trans.Commit();
}
}
}
Nutrition Category View Fragment :
public class NutritionCategoryView : MvxFragment
{
public NutritionCategoryViewModel vm
{
get { return (NutritionCategoryViewModel) ViewModel; }
}
private TabLayout _tablayout;
private ViewPager _viewPager;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.EnsureBindingContextIsSet(inflater);
var view = this.BindingInflate(Resource.Layout.nutrition_category_view, container, false);
SetViewPager(view);
return view;
}
private void SetViewPager(View view)
{
_viewPager = view.FindViewById<Android.Support.V4.View.ViewPager>(Resource.Id.viewpager);
if (_viewPager != null)
{
var fragments = new List<CategoryTabsAdapter.FragmentInfo>
{
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Proximates",
ViewModel = vm.Category1
},
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Minerals",
ViewModel = vm.Category2
},
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Fats",
ViewModel = vm.Category3
},
new CategoryTabsAdapter.FragmentInfo
{
FragmentType = typeof(CategoryView),
Title = "Vitamins",
ViewModel = vm.Category4
}
};
_viewPager.Adapter = new CategoryTabsAdapter(Activity, ChildFragmentManager, fragments);
}
_tablayout = view.FindViewById<TabLayout>(Resource.Id.sliding_tabs);
_tablayout.SetBackgroundColor(Android.Graphics.Color.Black);
_tablayout.SetupWithViewPager(_viewPager);
}
}
Category Tabs Adapter :
public class CategoryTabsAdapter : FragmentStatePagerAdapter
{
private readonly Context _context;
public IEnumerable<FragmentInfo> Fragments { get; private set; }
public CategoryTabsAdapter(Context context, FragmentManager fragmentManager, IEnumerable<FragmentInfo> fragments) : base(fragmentManager)
{
_context = context;
Fragments = fragments;
}
public override int Count
{
get { return Fragments.Count(); }
}
public override Fragment GetItem(int position)
{
var fragmentInfo = Fragments.ElementAt(position);
var fragment = Fragment.Instantiate(_context, Java.Lang.Class.FromType(fragmentInfo.FragmentType).Name);
((MvxFragment)fragment).ViewModel = fragmentInfo.ViewModel;
return fragment;
}
public override ICharSequence GetPageTitleFormatted(int position)
{
return new Java.Lang.String(Fragments.ElementAt(position).Title);
}
public class FragmentInfo
{
public string Title { get; set; }
public Type FragmentType { get; set; }
public IMvxViewModel ViewModel { get; set; }
}
}
Category View Fragment
public class CategoryView : MvxFragment<CategoryViewModel>
{
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
this.EnsureBindingContextIsSet(inflater);
var view = this.BindingInflate(Resource.Layout.category_view, container, false);
//Exception caused here :
//RetainInstance = true;
return view;
}
}
I am new to Xamarin and MvvmCross, so could come up with this much research only. Any solution for either approaches would be of great help.
P.S. This is my first question on Stackoverflow.
I think you need to use the MvxCachingFragmentStatePagerAdapter which is in the MvvmCross.Droid.Support.V4 Nuget package. Then hook it up to your TabLayout with SetupWithViewPager().
var viewPager = view.FindViewById<ViewPager>(Resource.Id.viewpager);
if (viewPager != null)
{
var fragments = new List<MvxCachingFragmentStatePagerAdapter.FragmentInfo>
{
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"TitleA",
typeof (YourFragmentA),
typeof (YourViewModelA)),
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"TitleB",
typeof (YourFragmentB),
typeof (YourViewModelB)),
new MvxCachingFragmentStatePagerAdapter.FragmentInfo(
"TitleC",
typeof (YourFragmentC),
typeof (YourViewModelC))
};
viewPager.Adapter = new MvxCachingFragmentStatePagerAdapter(Activity, ChildFragmentManager, fragments);
viewPager.OffscreenPageLimit = fragments.Count;
var tabLayout = view.FindViewById<TabLayout>(Resource.Id.tabs);
tabLayout.SetupWithViewPager(viewPager);
}
Your tabs are being recreated each time because you are using FragmentStatePagerAdapter instead of FragmentPagerAdapter. From the docs, FragmentPagerAdapter:
This version of the pager is best for use when there are a handful of
typically more static fragments to be paged through, such as a set of
tabs. The fragment of each page the user visits will be kept in
memory, though its view hierarchy may be destroyed when not visible.
This can result in using a significant amount of memory since fragment
instances can hold on to an arbitrary amount of state. For larger sets
of pages, consider FragmentStatePagerAdapter.
FragmentStatePagerAdapter:
This version of the pager is more useful when there are a large number
of pages, working more like a list view. When pages are not visible to
the user, their entire fragment may be destroyed, only keeping the
saved state of that fragment. This allows the pager to hold on to much
less memory associated with each visited page as compared to
FragmentPagerAdapter at the cost of potentially more overhead when
switching between pages.
So use FragmentPagerAdapter, but I think it will have to be at the Activity level, and not a fragment within a fragment.
I have two fragments which are VolleyFragment and AnotherFragment, setting VolleyFragment as the initial fragment. In this fragment, the bundle data is displaying ok. However, when I go to AnotherFragment, the app crashes. It says this error:
java.lang.NullPointerException: Attempt to invoke virtual method 'java.lang.String[] android.os.Bundle.getStringArray(java.lang.String)' on a null object reference
at com.example.rendell.volleyfragment.AnotherFragment.onCreateView(AnotherFragment.java:28)
MainActivity
public class MainActivity extends AppCompatActivity {
public static final String JSON_URL = "http://192.168.0.102/musicmania/music/getMF";
TextView id,name,email;
String[] ids, names, emails;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
id = (TextView) findViewById(R.id.id);
name = (TextView) findViewById(R.id.name);
email = (TextView) findViewById(R.id.email);
sendRequest();
}
private void sendRequest(){
StringRequest stringRequest = new StringRequest(JSON_URL,
new Response.Listener<String>() {
#Override
public void onResponse(String response) {
showJSON(response);
}
},
new Response.ErrorListener() {
#Override
public void onErrorResponse(VolleyError error) {
Toast.makeText(MainActivity.this, error.getMessage(), Toast.LENGTH_LONG).show();
}
});
RequestQueue requestQueue = Volley.newRequestQueue(this);
requestQueue.add(stringRequest);
}
private void showJSON(String json){
ParseJSON pj = new ParseJSON(json);
pj.parseJSON();
id.setText(ParseJSON.ids[0]);
name.setText(ParseJSON.titles[0]);
email.setText(ParseJSON.artists[0]);
FragmentManager fragmentTransaction = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentTransaction.beginTransaction();
VolleyFragment squadFragment = new VolleyFragment();
AnotherFragment anotherFragment = new AnotherFragment();
Bundle bundle = new Bundle();
bundle.putStringArray("id", ParseJSON.ids);
bundle.putStringArray("name", ParseJSON.titles);
bundle.putStringArray("email", ParseJSON.artists);
squadFragment.setArguments(bundle);
anotherFragment.setArguments(bundle);
transaction.replace(R.id.containerView, squadFragment);
transaction.commit();
}
public void changeFragment(View view) {
FragmentManager fragmentTransaction = getFragmentManager();
android.app.FragmentTransaction transaction = fragmentTransaction.beginTransaction();
AnotherFragment anotherFragment = new AnotherFragment();
transaction.replace(R.id.containerView, anotherFragment);
transaction.commit();
}
}
VolleyFragment
public class VolleyFragment extends Fragment {
TextView id,name,email;
String[] ids,names,emails;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.volley_fragment, container, false);
id = (TextView) view.findViewById(R.id.id);
name = (TextView) view.findViewById(R.id.name);
email = (TextView) view.findViewById(R.id.email);
ids = getArguments().getStringArray("id");
names = getArguments().getStringArray("name");
emails = getArguments().getStringArray("email");
//jsonArray.setIds(id);
id.setText(/*jsonArray.getIds()*/ids[0]);
name.setText(names[0]);
email.setText(emails[0]);
return view;
}
}
AnotherFragment
public class AnotherFragment extends Fragment {
TextView id,name,email;
String[] ids,names,emails;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.another_fragment, container, false);
id = (TextView) view.findViewById(R.id.id);
name = (TextView) view.findViewById(R.id.name);
email = (TextView) view.findViewById(R.id.email);
ids = getArguments().getStringArray("id");
names = getArguments().getStringArray("name");
emails = getArguments().getStringArray("email");
//jsonArray.setIds(id);
id.setText(/*jsonArray.getIds()*/ids[0]);
name.setText(names[0]);
email.setText(emails[0]);
return view;
}
}
I'm having trouble following exactly what you're doing here, but I see a number of problems:
In showJSON, you new up a ParseJSON object and assign it to pj, but never make use of pj. Instead you reach statically into ParseJSON for the values to populate a Bundle. That seems really odd. I'd expect the pj instance to contain the data.
Typically one does not "new" up a fragment. The usual pattern is to use a static builder in the class of the fragment itself.
You're passing the same Bundle to two different fragments. Unless you know for a fact that's OK, try instead using a different Bundle for each one.
in showJSON, your AnotherFragment instance doesn't seem to be used at all
I don't see any place where changeFragment is called
You're passing string arrays in the Bundle, but you only ever use the first element in the array in the fragment. Consider just passing a single string instead?