I'm trying to binding some data with mvvmcross recyclerView but i'm facing on an issue...Recyclerview always empty.This is my code. I'm pretty sur taht all stup file is correct
HomeView(xml file) :
<!-- language: xml -->
<ScrollView
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<MvvmCross.Droid.Support.V7.RecyclerView.MvxRecyclerView
android:id="#+id/product_recycler"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clipToPadding="false"
android:scrollbars="vertical"
app:MvxItemTemplate="#layout/product_item_row"
app:MvxBind="ItemsSource Products"/>
</ScrollView>
product_item_row:
<!-- language: xml -->
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="#dimen/card_margin"
android:clickable="true"
android:elevation="3dp"
android:foreground="?attr/selectableItemBackground">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:id="#+id/thumbnail"
android:layout_width="match_parent"
android:layout_height="#dimen/card_cover_height"
android:background="?attr/selectableItemBackgroundBorderless"
android:clickable="true"
android:src="#drawable/ic_store_24"
android:scaleType="fitXY" />
<TextView
android:id="#+id/name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/thumbnail"
android:lines="2"
android:paddingLeft="#dimen/card_name_padding"
android:paddingRight="#dimen/card_name_padding"
android:paddingTop="#dimen/card_name_padding"
android:textColor="#111"
android:textSize="11dp"
android:text="Name"
app:MvxBind="Text Name"/>
<TextView
android:id="#+id/price"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/name"
android:layout_marginRight="10dp"
android:gravity="right"
android:paddingBottom="#dimen/card_price_padding_bottom"
android:textColor="#color/colorAccent"
android:textSize="11dp"
android:text="Price"
app:MvxBind="Text Price"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
Ma viewModel(HomeViewModel):
<!-- language: c# -->
public class HomeViewModel : MvxViewModel
{
private readonly ICarouselService _carouselService;
private readonly IProductDataService _productDataService;
private MvxObservableCollection<Carousel> _carousels;
private MvxObservableCollection<Product> _products;
public MvxObservableCollection<Carousel> Carousels
{
get { return _carousels; }
set
{
_carousels = value;
RaisePropertyChanged(() => Carousels);
}
}
public MvxObservableCollection<Product> Products
{
get { return _products; }
set
{
_products = value;
RaisePropertyChanged(() => Products);
}
}
public HomeViewModel(ICarouselService carouselService, IProductDataService productDataService)
{
_carouselService = carouselService;
_productDataService = productDataService;
}
public override async Task Initialize()
{
Products = new MvxObservableCollection<Product>(await _productDataService.GetProducts());
Carousels = new MvxObservableCollection<Carousel>(await _carouselService.GetImages());
}
}
My fragment:
<!-- language: c# -->
[MvxFragmentPresentation(typeof(MainViewModel), Resource.Id.content_frame, false)]
public class HomeView : BaseFragment<HomeViewModel>, IImageListener
{
protected override int FragmentId => Resource.Layout.HomeView;
CarouselView _carouselView;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = inflater.Inflate(Resource.Layout.HomeView, container, false);
//var view = this.BindingInflate(Resource.Layout.HomeView, container, false);
_carouselView = view.FindViewById<CarouselView>(Resource.Id.carouselView);
var recyclerView = view.FindViewById<MvxRecyclerView>(Resource.Id.product_recycler);
_carouselView.PageCount = ViewModel.Carousels.Count;
_carouselView.SetImageListener(this);
if (recyclerView != null)
{
recyclerView.HasFixedSize = true;
var layoutManager = new GridLayoutManager(Activity, 2, LinearLayoutManager.Vertical, false);
recyclerView.SetLayoutManager(layoutManager);
}
return view;
}
public void SetImageForPosition(int position, ImageView imageView)
{
Picasso.With(Context).Load(ViewModel.Carousels[position].Image)
//.Placeholder(Resource.Mipmap.ic_launcher)
//.Error(Resource.Mipmap.ic_launcher)
//.Resize(500, 300)
.Fit()
.Into(imageView);
}
}
Did i misse something? Any help would be very much appreciated.
In your HomeView fragment the following line is commented out:
var view = this.BindingInflate(Resource.Layout.HomeView, container, false);
And it appears to be using the system provided LayoutInflater.
In order for MvvmCross to recognize the bindings you defined in the axml layout files, you need to use the MvxLayoutInflater via the this.BindingInflate extension method.
Try modifying your code as such:
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
var view = this.BindingInflate(Resource.Layout.HomeView, null);
_carouselView = view.FindViewById<CarouselView>(Resource.Id.carouselView);
var recyclerView = view.FindViewById<MvxRecyclerView>(Resource.Id.product_recycler);
_carouselView.PageCount = ViewModel.Carousels.Count;
_carouselView.SetImageListener(this);
if (recyclerView != null)
{
recyclerView.HasFixedSize = true;
var layoutManager = new GridLayoutManager(Activity, 2, LinearLayoutManager.Vertical, false);
recyclerView.SetLayoutManager(layoutManager);
}
return view;
}
More context:
https://stackoverflow.com/a/53894136/2754727
Related
I need to Navigate from one app to another in Xamarin.Forms.
Tried this but didn't work
await Xamarin.Essentials.Launcher.OpenAsync("myapp://com.companyname.myapp1");
Suppose the package name of the app you want to open is com.xamarin.secondapp.
Then you need to add IntentFilter and Exported =true to MainActivity.cs of the app you want to open(com.xamarin.secondapp).
Just as follows.
[Activity(Label = "SearchBarDemos", Icon = "#mipmap/icon", Theme = "#style/MainTheme", MainLauncher = true,Exported =true, ConfigurationChanges = ConfigChanges.ScreenSize | ConfigChanges.Orientation)]
[
IntentFilter
(
new[] { Android.Content.Intent.ActionView },
Categories = new[]
{
Android.Content.Intent.CategoryDefault,
Android.Content.Intent.CategoryBrowsable
},
DataSchemes = new[] { "myapp" }
)
]
public class MainActivity : global::Xamarin.Forms.Platform.Android.FormsAppCompatActivity
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
global::Xamarin.Forms.Forms.Init(this, savedInstanceState);
LoadApplication(new App());
}
}
Note: remember to add code DataSchemes = new[] { "myapp" }.
For the current app, you need to add queries tag for the app you want to open in file manifest.xml.
For example:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" android:versionCode="1" android:versionName="1.0" package="com.companyname.openappapp1">
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
<application android:label="OpenappApp1.Android" android:theme="#style/MainTheme">
</application>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<queries>
<package android:name="com.xamarin.secondapp" />
</queries>
</manifest>
And the code is:
private void Button_Clicked(object sender, EventArgs e)
{
OpenSecondApp();
}
public async void OpenSecondApp()
{
var supportsUri = await Launcher.CanOpenAsync("myapp://");
if (supportsUri)
await Launcher.OpenAsync("myapp://com.xamarin.secondapp");
}
Try this:
public class Test
{
public async Task OpenRideShareAsync()
{
var supportsUri = await Launcher.CanOpenAsync("myapp://");
if (supportsUri)
await Launcher.OpenAsync("myapp://com.companyname.myapp1");
}
}
I created mobile application with Xamarin. I can login with nfc card and i can access true page. but i cant open file chooser with html file upload button. if i can use webchromeclient its working. but i should upload file with mobile application. File chooser is not opening when i press upload file button
namespace MYPROJECTMOBILE
{
[Activity(Label = "#string/app_name", Theme = "#style/AppTheme.NoActionBar")]
public class MainActivity : Activity
{
WebView webView;
static MainActivity context;
static string cardNo = "";
static string pernr = "";
public static IValueCallback mUploadCallbackAboveL;
public static Android.Net.Uri imageUri;
public static MainActivity Instance;
public static int PHOTO_REQUEST = 10023;
public static IValueCallback mUploadMessage;
public static int FILECHOOSER_RESULTCODE = 1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
SetContentView(Resource.Layout.activity_main);
Instance = this;
context = this;
cardNo = Intent.GetStringExtra("CARD_ID") ?? string.Empty;
pernr = Intent.GetStringExtra("PERNR") ?? string.Empty;
webView = FindViewById<WebView>(Resource.Id.webView1);
webView.SetWebViewClient(new ExtendWebViewClient());
webView.SetWebChromeClient(new WebChromeClient());
webView.ClearCache(true);
webView.ClearFormData();
CookieManager.Instance.RemoveAllCookie();
webView.Settings.JavaScriptEnabled = true;
webView.Settings.DomStorageEnabled = true;
webView.Settings.AllowFileAccess = true;
webView.Settings.AllowFileAccessFromFileURLs = true;
webView.Settings.AllowUniversalAccessFromFileURLs = true;
webView.Settings.AllowContentAccess = true;
webView.LoadUrl("https://website.com.tr/");
}
public override bool OnCreateOptionsMenu(IMenu menu)
{
MenuInflater.Inflate(Resource.Menu.menu_main, menu);
return true;
}
public override bool OnOptionsItemSelected(IMenuItem item)
{
int id = item.ItemId;
if (id == Resource.Id.action_settings)
{
return true;
}
return base.OnOptionsItemSelected(item);
}
public override void OnRequestPermissionsResult(int requestCode, string[] permissions, [GeneratedEnum] Android.Content.PM.Permission[] grantResults)
{
Xamarin.Essentials.Platform.OnRequestPermissionsResult(requestCode, permissions, grantResults);
base.OnRequestPermissionsResult(requestCode, permissions, grantResults);
}
public override void OnBackPressed()
{
}
protected override void OnActivityResult(int requestCode, Result resultCode, Intent intent)
{
if (requestCode == FILECHOOSER_RESULTCODE)
{
if (null == mUploadMessage) return;
Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
mUploadMessage.OnReceiveValue(result);
mUploadMessage = null;
}
else if (requestCode == PHOTO_REQUEST)
{
Android.Net.Uri result = intent == null || resultCode != Result.Ok ? null : intent.Data;
if (mUploadCallbackAboveL != null)
{
onActivityResultAboveL(requestCode, resultCode, intent);
}
else if (mUploadMessage != null)
{
mUploadMessage.OnReceiveValue(result);
mUploadMessage = null;
}
}
}
private void onActivityResultAboveL(int requestCode, Result resultCode, Intent data)
{
if (requestCode != PHOTO_REQUEST || mUploadCallbackAboveL == null)
{
return;
}
Android.Net.Uri[] results = null;
if (resultCode == Result.Ok)
{
results = new Android.Net.Uri[] { imageUri };
results[0] = MainActivity.imageUri;
}
mUploadCallbackAboveL.OnReceiveValue(results);
mUploadCallbackAboveL = null;
}
public class ExtendWebViewClient : WebViewClient
{
bool isLoggedIn;
bool redirected;
[Obsolete]
public override bool ShouldOverrideUrlLoading(WebView view, string url)
{
view.LoadUrl(url);
return true;
}
public override void OnPageFinished(WebView view, string url)
{
if (url.Contains("https://website.com.tr/Login"))
{
if (pernr != "") {
view.EvaluateJavascript("javascript:{" +
"WaitScreen.Show();" +
"ins=document.getElementsByTagName('input');" +
"ins[0].value='" + pernr + "';" +
"ins[1].value='" + cardNo + "';" +
"ins[2].value=false;" +
"document.getElementsByTagName('button')[0].click();" +
"};", new JavascriptResult());
}
else
{
Intent activityIntent = new Intent(context, typeof(NfcActivity));
context.StartActivity(activityIntent);
}
pernr = "";
cardNo = "";
}
}
}
internal class JavascriptResult : Java.Lang.Object, IValueCallback
{
public string result;
public void OnReceiveValue(Java.Lang.Object value)
{
result = ((Java.Lang.String)value).ToString();
}
}
}
}
" i can upload photo in web with this part
<input type="file" id="myFile" name="filename">
<input type="submit">
"
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/activity_main"
android:minWidth="25px"
android:minHeight="25px">
<android.webkit.WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/webView1" />
</RelativeLayout>
"
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.NFC" />
<uses-permission android:name="android.permission.NFC_TRANSACTION_EVENT" />
<uses-permission android:name="android.permission.BIND_NFC_SERVICE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
<uses-permission android:name="android.permission.CAMERA" />
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />
In your custom WebChromeClient you have to override OnShowFileChooser(...) as here
https://learn.microsoft.com/en-us/answers/questions/198451/problem-opening-camera-with-webview-and-input-type.html
i check also Camera permission and i ask to user before use it in this way:
var activity = Plugin.CurrentActivity.CrossCurrentActivity.Current.Activity;
int CAMERA_PERMISSION_REQUEST_CODE = 2323;
if (activity.PackageManager.CheckPermission(Android.Manifest.Permission.Camera, activity.PackageName) != Android.Content.PM.Permission.Granted)
{
var permissions = new string[] { Android.Manifest.Permission.Camera }; activity.RequestPermissions(permissions, CAMERA_PERMISSION_REQUEST_CODE);
}
I created a CustomTextInputLayout class and set the style to Resource.Style.Widget_MaterialComponents_TextInputLayout_OutlinedBox and BoxBackgroundMode to BoxBackgroundOutline but the OutlinedBox is still not showing. I'm using Xamarin.Android.
public class CustomTextInputLayout : TextInputLayout
{
public CustomTextInputLayout(Context context) : base(context, null, Resource.Style.Widget_MaterialComponents_TextInputLayout_OutlinedBox)
{
Init(context);
}
public CustomTextInputLayout(Context context, IAttributeSet attrs) : base(context, attrs, Resource.Style.Widget_MaterialComponents_TextInputLayout_OutlinedBox)
{
Init(context);
}
public CustomTextInputLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
Init(context);
}
public void Init(Context context)
{
var textInputEditText = new TextInputEditText(context) { LayoutParameters = new LayoutParams(-1, -2) };
AddView(textInputEditText);
BoxBackgroundMode = BoxBackgroundOutline;
}
}
This is the content_main.xml:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:showIn="#layout/activity_main">
<MaterialTest.CustomTextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</RelativeLayout>
And this is the AppTheme
<style name="AppTheme" parent="Theme.MaterialComponents.Light.DarkActionBar">
This is what it looks like (no OutlinedBox):
Any help would be appreciated.
I modify your code, you can take a look the following code:
public class CustomTextInputLayout : TextInputLayout
{
public CustomTextInputLayout(Context context) : base(context, null, Resource.Style.Widget_MaterialComponents_TextInputLayout_OutlinedBox)
{
Init(context);
}
public CustomTextInputLayout(Context context, IAttributeSet attrs) : base(context, attrs, Resource.Style.Widget_MaterialComponents_TextInputLayout_OutlinedBox)
{
Init(context);
}
public CustomTextInputLayout(Context context, IAttributeSet attrs, int defStyleAttr) : base(context, attrs, defStyleAttr)
{
Init(context);
}
public void Init(Context context)
{
SetBoxBackgroundMode(TextInputLayout.BoxBackgroundOutline);
SetBoxCornerRadii(5, 5, 5, 5);
BoxStrokeColor = Android.Graphics.Color.Red;
Hint = "Please Enter Email Address";
var textInputEditText = new TextInputEditText(context);
AddView(textInputEditText);
}
}
<textinputlayout.CustomTextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
The screenshot:
But I suggest you can use TextInputLayout and TextInputEditText in xml.
<com.google.android.material.textfield.TextInputLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/userIDTextInputLayout"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox"
android:layout_width="match_parent"
app:boxStrokeColor="#color/boxcolor"
android:layout_height="wrap_content"
android:layout_margin="10dp">
<com.google.android.material.textfield.TextInputEditText
android:id="#+id/userIDTextInputEditText"
android:layout_width="match_parent"
android:hint="Enter User Name"
android:textColorHint="#color/colorAccent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
You can also add TextInputLayout and TextInputEditText by code behind.
private LinearLayout linearlayout1;
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
Xamarin.Essentials.Platform.Init(this, savedInstanceState);
// Set our view from the "main" layout resource
SetContentView(Resource.Layout.activity_main);
linearlayout1 = (LinearLayout)FindViewById(Resource.Id.linearLayout1);
TextInputLayout emailTextInputLayout = new TextInputLayout(this, null, Resource.Style.Widget_MaterialComponents_TextInputLayout_OutlinedBox);
emailTextInputLayout.Hint = "Please Enter Email Address";
emailTextInputLayout.SetBoxBackgroundMode(TextInputLayout.BoxBackgroundOutline);
emailTextInputLayout.SetBoxCornerRadii(5, 5, 5, 5);
TextInputEditText edtEmail = new TextInputEditText(emailTextInputLayout.Context);
emailTextInputLayout.AddView(edtEmail);
linearlayout1.AddView(emailTextInputLayout);
}
You need to add android:id="#+id/linearLayout1" firstly.
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/linearLayout1"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
One thread that you can also take a look:
How to create TextInputLayout with OutlineBox programmatically
I'm trying to figure out how to properly set the localized string as Tab Title (or dynamically change the Tab Title) with MVVMCross 6.2+ in Xamarin Android.
How should I set the title of the tab in the simple example app?
Thanks in advance for your help.
Here is the simple example app:
MvvmCrossTabs.Core
HomeViewModel.cs
using System.Collections.Generic;
using System.Threading.Tasks;
using MvvmCross.Commands;
using MvvmCross.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
namespace MvvmCrossTabs.Core.ViewModels
{
public class HomeViewModel : MvxNavigationViewModel
{
public IMvxAsyncCommand ShowInitialViewModelsCommand { get; private set; }
public HomeViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
{
ShowInitialViewModelsCommand = new MvxAsyncCommand(ShowInitialViewModels);
}
private async Task ShowInitialViewModels()
{
await Task.WhenAll(new List<Task>
{
NavigationService.Navigate<Tab1ViewModel>(),
NavigationService.Navigate<Tab2ViewModel>(),
NavigationService.Navigate<Tab3ViewModel>()
});
}
}
}
Tab1ViewModel.cs (Tab2ViewModel.cs, Tab3ViewModel.cs)
using MvvmCross.Logging;
using MvvmCross.Navigation;
using MvvmCross.ViewModels;
namespace MvvmCrossTabs.Core.ViewModels
{
public class Tab1ViewModel : MvxNavigationViewModel
{
public Tab1ViewModel(IMvxLogProvider logProvider, IMvxNavigationService navigationService) : base(logProvider, navigationService)
{
}
}
}
App.cs
using MvvmCross.IoC;
using MvvmCross.ViewModels;
using MvvmCrossTabs.Core.ViewModels;
namespace MvvmCrossTabs.Core
{
public class App : MvxApplication
{
public override void Initialize()
{
CreatableTypes()
.EndingWith("Service")
.AsInterfaces()
.RegisterAsLazySingleton();
RegisterAppStart<HomeViewModel>();
}
}
}
MvvmCrossTabs.Android
MainApplication.cs
using System;
using Android.App;
using Android.Runtime;
using MvvmCross.Droid.Support.V7.AppCompat;
using MvvmCrossTabs.Core;
namespace MvvmCrossTabs.Android
{
[Application]
public class MainApplication : MvxAppCompatApplication<MvxAppCompatSetup<App>, App>
{
public MainApplication() : base() { }
public MainApplication(IntPtr javaReference, JniHandleOwnership transfer) : base(javaReference, transfer) { }
}
}
home.axml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/maincontent"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways" />
<android.support.design.widget.TabLayout
android:id="#+id/tabs"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_scrollFlags="enterAlways"
app:tabGravity="fill"
app:tabMaxWidth="0dp" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior" />
</android.support.design.widget.CoordinatorLayout>
tab1.axml (tab2.axml, tab3.axml)
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:id="#+id/main_frame"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
</LinearLayout>
styles.xml
<resources>
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.AppCompat">
<item name="windowActionBar">false</item>
<item name="windowNoTitle">true</item>
<!-- Customize your theme here. -->
<item name="colorPrimary">#color/colorPrimary</item>
<item name="colorPrimaryDark">#color/colorPrimaryDark</item>
<item name="colorAccent">#color/colorAccent</item>
</style>
</resources>
HomeView.cs
using Android.App;
using Android.Content.PM;
using Android.OS;
using Android.Support.V7.Widget;
using MvvmCross.Droid.Support.V7.AppCompat;
using MvvmCross.Platforms.Android.Presenters.Attributes;
using MvvmCrossTabs.Core.ViewModels;
namespace MvvmCrossTabs.Android
{
[Activity(Label = "#string/app_name", LaunchMode = LaunchMode.SingleTask, Theme = "#style/AppTheme", MainLauncher = true)]
[MvxActivityPresentation]
public class HomeView : MvxAppCompatActivity<HomeViewModel>
{
protected override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
SetContentView(Resource.Layout.home);
// Replaces Action Bar with new Toolbar.
var toolbar = FindViewById<Toolbar>(Resource.Id.toolbar);
SetSupportActionBar(toolbar);
ViewModel.ShowInitialViewModelsCommand.Execute();
}
}
}
Tab1View.cs (Tab2View.cs, Tab3View.cs)
using Android.OS;
using Android.Runtime;
using Android.Views;
using MvvmCross.Droid.Support.V4;
using MvvmCross.Platforms.Android.Binding.BindingContext;
using MvvmCross.Platforms.Android.Presenters.Attributes;
using MvvmCrossTabs.Core.ViewModels;
namespace MvvmCrossTabs.Android.Views
{
[MvxTabLayoutPresentation(TabLayoutResourceId = Resource.Id.tabs, ViewPagerResourceId = Resource.Id.viewpager, Title = "Tab 1", ActivityHostViewModelType = typeof(HomeViewModel))]
[Register(nameof(Tab1View))]
public class Tab1View : MvxFragment<Tab1ViewModel>
{
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
base.OnCreateView(inflater, container, savedInstanceState);
return this.BindingInflate(Resource.Layout.tab1, null);
}
}
}
When your tabs are created using the ShowInitialViewModelsCommand, the built-in presenter creates a list of MvxViewPagerFragmentInfo objects, passing in the Title value from the attributes. You can see that happening in the ShowViewPagerFragment method in the MvvmCross source code.
The list of MvxViewPagerFragmentInfo objects is then passed into the MvxCachingFragmentStatePagerAdapter that is created for the ViewPager. You can see that happening here
MvxCachingFragmentStatePagerAdapter inherits from the MvxFragmentPagerAdapter class. Inside the MvxFragmentPagerAdapter class is finally where the Title you provided is actually used. You can see it being used in the GetPageTitleFormatted method here
So in order to change the Title at runtime, you could do the following:
Subclass the default presenter and override the ShowViewPagerFragment method (it is marked virtual) and provide the correct localized title string instead of the one defined in the attribute
Here is an example of how to accomplish this:
1.) Create a custom presenter and override ShowViewPagerFragment
public class LocalizedTabPresenter : MvxAppCompatViewPresenter
{
public LocalizedTabPresenter(IEnumerable<Assembly> androidViewAssemblies) : base(androidViewAssemblies)
{
}
protected override Task<bool> ShowViewPagerFragment(Type view, MvxViewPagerFragmentPresentationAttribute attribute, MvxViewModelRequest request)
{
if (attribute.ViewModelType == typeof(Tab1ViewModel)) {
attribute.Title = "My Localized Title for Tab 1"
}
return base.ShowViewPagerFragment(view, attribute, request);
}
}
2.) In your Setup.cs class, let MvvmCross know to use the custom presenter instead
protected override IMvxAndroidViewPresenter CreateViewPresenter()
{
return new LocalizedTabPresenter(AndroidViewAssemblies);
}
Note:
This will only work if you need to set the title only once when the
app launches and is setting up the tabs for the first time.
If you are in a situation where the Title can change multiple times while the app is running, you need to subclass MvxCachingFragmentStatePagerAdapter and override the GetPageTitleFormatted method and provide a more custom implementation that fits your use case.
Hope that helps.
Another possible solution would be to use the IMvxOverridePresentationAttribute interface.
This way you can change these values at runtime from the fragment itself.
To do it, simply implement IMvxOverridePresentationAttribute in your tab fragment, and then return something like this:
public MvxBasePresentationAttribute PresentationAttribute(MvxViewModelRequest request)
{
return new MvxTabLayoutPresentationAttribute(title: _("Results"), viewPagerResourceId: Resource.Id.viewPager, tabLayoutResourceId: Resource.Id.tab_layout, fragmentHostViewType: typeof(HostFragment));
}
I like this approach because I don't need to change anything at app level and I have all the code related to the fragment contained in that fragment specifically.
I've been using this and it has been working perfectly.
Be aware that this.ViewModel property will be null during PresentationAttribute
If you want a more detailed explanation on how it works, have a look at this
You have to do something like that:
if (fragments == null || fragments.Count == 0)
{
_firstFragment= (YourFragmentType)Activator.CreateInstance(typeof(YourFragmentType));
_firstFragment.ViewModel = YourVM;
_secondFragment= (YourFragmentType)Activator.CreateInstance(typeof(YourFragmentType));
_secondFragment.ViewModel = YourVM;
_thridFragment= (YourFragmentType)Activator.CreateInstance(typeof(YourFragmentType));
_thridFragment.ViewModel = YourVM;
// Strings from RESX Localization
fragments = new Dictionary<string, Fragment>
{
{ Strings.first_localized_string, _firstFragment},
{ Strings.second_localized_string, _secondFragment},
{ Strings.thrid_localized_string, _thridFragment}
};
}
viewPager = View.FindViewById<ViewPager>(Resource.Id.viewpager);
adapter = new TabsFragmentPagerAdapter(ChildFragmentManager, fragments);
viewPager.Adapter = adapter;
var tabLayout = View.FindViewById<TabLayout>(Resource.Id.tabs);
tabLayout.SetupWithViewPager(viewPager);
Your adapter:
public class TabsFragmentPagerAdapter : FragmentPagerAdapter
{
private readonly Fragment[] fragments;
private readonly string[] titles;
public TabsFragmentPagerAdapter(FragmentManager fm, Dictionary<string, Fragment> fragments) : base(fm)
{
this.fragments = fragments.Values.ToArray();
this.titles = fragments.Keys.ToArray();
}
public override int Count => fragments.Length;
private String GetCharSeuenceFromString(string s)
{
return new String(s);
}
public override Object InstantiateItem(ViewGroup container, int position)
{
return base.InstantiateItem(container, position);
}
public override void SetPrimaryItem(ViewGroup container, int position, Object #object)
{
base.SetPrimaryItem(container, position, #object);
}
public override Fragment GetItem(int position)
{
return fragments[position];
}
public override ICharSequence GetPageTitleFormatted(int position)
{
return GetCurrentPageTitle(position);
}
private ICharSequence GetCurrentPageTitle(int position)
{
return GetCharSeuenceFromString(titles[position]);
}
}
Happy codding!
P.S. Don't use fragments with generics.
I just want to change my resource.drawable.image into URL, but I don't know how I can do that. I try using bitmap method but I don't know how I can make it array using this method. The purpose of this is I want to get the data from the database and store it into my List<> or array so the output will be like this picture. It's working fine in resource.drawable.image but I want to change into into url value.
By the way, I already stored the column ID from the database. I also try to store the image_link column from the database into my while loop and call it into my List<> but I get error. See my code at below --> Fragment1.cs
All of these codes are working.
fragment_layout.axml:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout
android:padding="16dp"
android:layout_width="fill_parent"
android:layout_height="wrap_content">
<GridView
android:id="#+id/grid_view_image_text"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:columnWidth="110dp"
android:gravity="center"
android:numColumns="auto_fit"/>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:layout_gravity="center" />
</FrameLayout>
gridview_layout.axml:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:layout_marginTop="15dp"
android:id="#+id/imageViewGrid"/>
<TextView
android:layout_marginTop="5dp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/textViewGrid"/>
</LinearLayout>
CustomGridViewAdapter.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Views;
using Android.Widget;
using Java.Lang;
namespace testing_code
{
public class CustomGridViewAdapter : BaseAdapter
{
private Context context;
private string[] gridViewString;
private int[] gridViewImage;
public CustomGridViewAdapter(Context context, string[] gridViewstr, int[] gridViewImage)
{
this.context = context;
gridViewString = gridViewstr;
this.gridViewImage = gridViewImage;
}
public override int Count
{
get
{
return gridViewString.Length;
}
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return 0;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view;
LayoutInflater inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
if(convertView == null)
{
view = new View(context);
view = inflater.Inflate(Resource.Layout.gridview_layout, null);
TextView txtview = view.FindViewById<TextView>(Resource.Id.textViewGrid);
ImageView imgview = view.FindViewById<ImageView>(Resource.Id.imageViewGrid);
txtview.Text = gridViewString[position];
imgview.SetImageResource(gridViewImage[position]);
}
else
{
view = (View)convertView;
}
return view;
}
//private Bitmap GetImageBitmapFromUrl(string url)
//{
// Bitmap imageBitmap = null;
// using (var webClient = new WebClient())
// {
// var imageBytes = webClient.DownloadData(url);
// if (imageBytes != null && imageBytes.Length > 0)
// {
// imageBitmap = BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
// }
// }
// return imageBitmap;
//}
}
}
Fragment1.cs:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Android.App;
using Android.Content;
using Android.OS;
using Android.Runtime;
using Android.Util;
using Android.Views;
using Android.Widget;
using SupportFragment = Android.Support.V4.App.Fragment;
using Android.Support.Design.Widget;
using Android.Support.V7.App;
using Android.Support.V4.Widget;
using Android.Support.V4.App;
using MySql.Data.MySqlClient;
using System.Data;
namespace testing_code
{
public class Fragment1 : SupportFragment
{
MySqlConnection conn = new MySqlConnection();
string query = "server=localhost;port=3306;database=dbsample;user id=root;password=123";
GridView gridview;
//string[] gridviewstring = {
// "location", "sound", "note"
//};
//int[] imgview =
//{
// Resource.Drawable.ic_dashboard, Resource.Drawable.ic_headset, Resource.Drawable.ic_dashboard
//};
List<string> gridviewstring = new List<string>();
List<int> imgview = new List<int>();
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// Use this to return your custom view for this Fragment
// return inflater.Inflate(Resource.Layout.YourFragment, container, false);
View view = inflater.Inflate(Resource.Layout.fragment_layout, container, false);
conn.ConnectionString = query;
//
MySqlCommand cmd = new MySqlCommand("Select * from wp_kdskli23jkposts where ID in (1,2,4)", conn);
try
{
conn.Open();
MySqlDataReader reader = cmd.ExecuteReader();
//Read, get and loop the data value from the database
while (reader.Read())
{
string asd = reader["ID"].ToString();
gridviewstring.Add(asd);
}
}
catch(MySqlException ex)
{
Android.Support.V7.App.AlertDialog.Builder except = new Android.Support.V7.App.AlertDialog.Builder(Activity);
except.SetTitle("Please report this to our website(error server timeout)");
except.SetMessage(ex.ToString());
except.SetPositiveButton("Ok", (senderAlert, args) =>
{
except.Dispose();
});
except.Show();
}
finally
{
conn.Close();
}
//gridviewstring.Add("location");
//gridviewstring.Add("music");
//gridviewstring.Add("book");
imgview.Add(Resource.Drawable.ic_dashboard);
imgview.Add(Resource.Drawable.ic_headset);
imgview.Add(Resource.Drawable.ic_dashboard);
string[] GridViewStringArray = gridviewstring.ToArray();
int[] GridImgViewArray = imgview.ToArray();
CustomGridViewAdapter adapter = new CustomGridViewAdapter(Activity, GridViewStringArray, GridImgViewArray);
gridview = view.FindViewById<GridView>(Resource.Id.grid_view_image_text);
gridview.Adapter = adapter;
return view;
}
}
}
I edited your adapter code to use a list of strings
namespace testing_code
{
public class CustomGridViewAdapter : BaseAdapter
{
private Context context;
private List<string> gridViewString;
private List<string> gridViewImage;
public CustomGridViewAdapter(Context context, List<string> gridViewstr, List<string> gridViewImage)
{
this.context = context;
gridViewString = gridViewstr;
this.gridViewImage = gridViewImage;
}
public override int Count
{
get
{
return gridViewString.Count;
}
}
public override Java.Lang.Object GetItem(int position)
{
return null;
}
public override long GetItemId(int position)
{
return 0;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
View view;
LayoutInflater inflater = (LayoutInflater)context.GetSystemService(Context.LayoutInflaterService);
if (convertView == null)
{
view = new View(context);
view = inflater.Inflate(Resource.Layout.gridview_layout, null);
TextView txtview = view.FindViewById<TextView>(Resource.Id.textViewGrid);
ImageView imgview = view.FindViewById<ImageView>(Resource.Id.imageViewGrid);
txtview.Text = gridViewString[position];
imgview.SetImageBitmap(GetImageBitmapFromUrl(gridViewImage[position]));
}
else
{
view = (View)convertView;
}
return view;
}
private Android.Graphics.Bitmap GetImageBitmapFromUrl(string url)
{
Android.Graphics.Bitmap imageBitmap = null;
using (var webClient = new System.Net.WebClient())
{
var imageBytes = webClient.DownloadData(url);
if (imageBytes != null && imageBytes.Length > 0)
{
imageBitmap = Android.Graphics.BitmapFactory.DecodeByteArray(imageBytes, 0, imageBytes.Length);
}
}
return imageBitmap;
}
}
}
Then your fragment to use list of URL strings
public class Fragment1 : SupportFragment
{
MySqlConnection conn = new MySqlConnection();
string query = "server=localhost;port=3306;database=dbsample;user id=root;password=123";
GridView gridview;
List<string> gridviewstring = new List<string>();
List<string> imgview = new List<string>();
public override void OnCreate(Bundle savedInstanceState)
{
base.OnCreate(savedInstanceState);
// Create your fragment here
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.Inflate(Resource.Layout.fragment_layout, container, false);
conn.ConnectionString = query;
//
MySqlCommand cmd = new MySqlCommand("Select * from wp_kdskli23jkposts where ID in (1,2,4)", conn);
try
{
conn.Open();
MySqlDataReader reader = cmd.ExecuteReader();
//Read, get and loop the data value from the database
while (reader.Read())
{
string asd = reader["ID"].ToString();
gridviewstring.Add(asd);
}
}
catch (MySqlException ex)
{
Android.Support.V7.App.AlertDialog.Builder except = new Android.Support.V7.App.AlertDialog.Builder(Activity);
except.SetTitle("Please report this to our website(error server timeout)");
except.SetMessage(ex.ToString());
except.SetPositiveButton("Ok", (senderAlert, args) =>
{
except.Dispose();
});
except.Show();
}
finally
{
conn.Close();
}
imgview.Add(" https://www.bellanaija.com/wp-content/uploads/2013/11/Remy-Martins-Pacesetters-VIP-Party-BellaNaija-November2013023-600x398.jpg");
imgview.Add("http://www.allevents.ng/media/2018/at-the-club-with-remy-martins.png?width=375&mode=crop");
imgview.Add("https://i.ytimg.com/vi/iLZz9Becq98/maxresdefault.jpg");
CustomGridViewAdapter adapter = new CustomGridViewAdapter(Activity, gridviewstring, imgview);
gridview = view.FindViewById<GridView>(Resource.Id.grid_view_image_text);
gridview.Adapter = adapter;
return view;
}
}
}
Hope this helps