I'm having a tough time getting a basic MvvmCross Android example working where the BackgroundColor of the RelativeLayout is bound to the ViewModel.
The app runs, some text appears, and I'm expecting my background to turn Yellow. The background color, however, remains unchanged.
I have included the Hot Tuna starter pack in both my Core and Droid projects as well as the MvvmCross - Color Plugin. My Droid project was automatically given ColorPluginBootstrap.cs
Layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:local="http://schemas.android.com/apk/res-auto"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="BackgroundColor NativeColor(BackgroundColor)">
<TextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:textSize="20sp"
android:gravity="center"
android:text="Text to make sure the layout inflates" />
</RelativeLayout>
ViewModel
public class ViewModel : MvxViewModel
{
RGBAColorConverter _rgbaConverter;
public ViewModel()
{
var color = "#ffedff00";
_rgbaConverter = new RGBAColorConverter();
BackgroundColor = _rgbaConverter.Convert(color);
}
private MvxColor _backgroundColor;
public MvxColor BackgroundColor
{
get { return _backgroundColor; }
set
{
_backgroundColor = value;
RaisePropertyChanged(() => BackgroundColor);
}
}
}
Binding works - I've tried making other ViewModel properties that were string to do simple text binding. All of that seems just fine.
I've placed debugging break points on the getter of the BackgroundColor ViewModel property and I can see the MvxColor as expected.
What am I missing for my color binding scenario?
I haven't done anything extra in the Setup.cs
I haven't created any other wiring up classes in my Droid project
I haven't created any Android-specific color converter implementations
I've just written a test app and it seems to work for me - using 3.0.14 nuget binaries.
Also, the ValueConverters test app seemed to work OK - https://github.com/MvvmCross/MvvmCross-Tutorials/tree/master/ValueConversion
Looking at your sample, the only thing I can think of is that maybe you are only testing transparent colours (RGBA #ffedff00 has Alpha=0)
If that isn't it, can you post more - perhaps a full sample somewhere?
Related
I am developing an Android application using Kotlin. I am writing instrumented tests for my application. Now, I am having a problem testing ImageButton because it is not working as expected. I want to test that an ImageButton view is set with the right drawable when the activity is opened.
In my activity XML layout file, I have an ImageButton with the following code
<ImageButton
android:id="#+id/camera_image_btn_capture"
android:adjustViewBounds="true"
android:scaleType="centerInside"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"
android:background="#null"
android:src="#drawable/camera_capture"
android:layout_width="85dp"
android:layout_height="85dp" />
I have written a test to assert that the image button view is set with the right drawable resource. Following is the implementation of my test.
#RunWith(AndroidJUnit4::class)
#LargeTest
class CameraTest: TestBuilder()
{
#get:Rule
var cameraActivityRule: ActivityTestRule<CameraActivity> = ActivityTestRule<CameraActivity>(CameraActivity::class.java, true, false)
#get:Rule
var permissionRule: GrantPermissionRule = GrantPermissionRule.grant(android.Manifest.permission.CAMERA, android.Manifest.permission.WRITE_EXTERNAL_STORAGE, android.Manifest.permission.READ_EXTERNAL_STORAGE)
#Test
fun cameraCaptureImageButtonIsInitiallySetWithCaptureImage() {
this.cameraActivityRule.launchActivity(null)
val imageButtonCapture: ImageButton = this.cameraActivityRule.activity.findViewById<ImageButton>(R.id.camera_image_btn_capture)
Assert.assertEquals(imageButtonCapture.drawable, this.cameraActivityRule.activity.getDrawable(R.drawable.camera_capture))
}
}
As you can see in the test method, I am asserting that if two drawable resources are equal. When I run the test, the test always fails. But it is supposed to pass. What is missing in my code and how can I fix it?
After struggling a bit, I found the issue. I am posting the solution in case anyone else is having the same issue. The problem was that I was not using the right way to check if two drawable objects are equal. To compare two drawable objects, we have to compare the constant states of them instead. I wrote a test helper function to assert that.
This is the code.
fun assertDrawablesEqual(drawable1: Drawable, drawable2: Drawable) {
var drawable1ConstantState: Drawable.ConstantState? = drawable1.constantState
var drawable2ConstantState: Drawable.ConstantState? = drawable2.constantState
Assert.assertTrue(drawable1ConstantState!!.equals(drawable2ConstantState))
}
I am designing an cross-platform app for iOS aswell Android with a shared logic using MVVMCross (5.1.1).
Throughout my app I have a fixed toolbar at the top displaying the current view's title aswell a button. Below the bar the interface is changing from view to view
The Android part:
On Android I created a reuseable layout which I embed in my current layout using include.
In my portable project I have a BaseViewModel which has the properties the reuseable toolbar layout binds to. Every other ViewModel derives from this base class. This way I can have all bindable properties of a displayed screen in one ViewModel without the need of nesting but see for yourself:
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar_login" />
<RelativeLayout
android:id="#+id/parentLoginLayout"
android:clickable="true"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_below="#id/toolbar">
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
app:MvxBind="Text Pin"
/>
</RelativeLayout>
</RelativeLayout>
toolbar_login.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.Toolbar xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/toolbar_login"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
android:elevation="0dp"
android:theme="#style/ThemeOverlay.AppCompat.Dark.ActionBar"
app:popupTheme="#style/ThemeOverlay.AppCompat.Light"
android:minWidth="25px"
android:minHeight="25px">
<TextView
app:MvxBind="Click ToolbarMenuCommand"
/>
<!-- some other -->
</android.support.v7.widget.Toolbar>
ViewModels.cs
using System.Threading.Tasks;
using Mobile.Helpers;
using ViewModels.Base;
using MvvmCross.Core.Navigation;
using MvvmCross.Core.ViewModels;
using Plugin.MessageBox;
namespace Mobile.ViewModels
{
public abstract class BaseViewModel : MvxViewModel
{
protected void NavigateToMainView()
{
NavigateTo<MainViewModel>();
}
private readonly IMvxNavigationService _navigationService;
protected BaseViewModel(IMvxNavigationService navigationService)
{
_navigationService = navigationService;
}
public IMvxCommand ToolbarMenuCommand => new MvxCommand(OnMenuButtonClick);
protected abstract void OnMenuButtonClick();
}
public class LoginViewModel : BaseViewModel
{
private bool _menuVisibility;
private string _pin;
public LoginViewModel(IMvxNavigationService navigationService) : base(navigationService)
{
}
public bool MenuVisibility
{
get => _menuVisibility;
set => SetProperty(ref _menuVisibility, value);
}
public string Pin
{
get => _pin;
set => SetProperty(ref _pin, value);
}
protected override void OnMenuButtonClick()
{
MenuVisibility = !MenuVisibility;
}
}
}
The iOS part:
I am not entirely sure how to realise above behavior on iOS. I hope someone has a good idea or a good example project for me which I can take a look at. In general it is no problem to refactorise the ViewModels incase my idea is just not possible at iOS.
A few facts about the iOS project:
I am not using storyboards but single .xib's being independent
from each other
In my .xib's files I use autolayout constraints for positioning and
sizing entirely
A few ideas I already had (cant test them right now):
1. idea:
Create a base .xib with the above bar, the constraints aswell the
outlets
Create each new xib Design based on the previously created file
This would mean I need to adjust every view incase I decide to change something about the toolbar but so far I found no other way to embed a .xib in another .xib without having two different ViewControllers. Also I read that inheritance cause problems with outlets.
2. idea
Each .xib has an empty view at the top which acts as a container for
the toolbar
Have a Base ViewController which constructs the toolbar from code and
adds it as a child to the container view, and binds the properties
from the BaseViewModel
In a previous iOS project I noticed that adding views to the layout can cause problems with autolayout. Probably also a not that good solution?
3. idea
Create a xib with the toolbar and a container below and use it as a master page which would probably mean having a MasterViewModel with the toolbar properties and a nested ChildViewModel.
This is probably the way to go but I have to admit that I have no clue what is the best way to approach it (stil pretty new to iOS and MVVMCross).
Does someone have a few useful hints for me? Thanks a lot!
From what I understood I think you should try to use ScrollView for iOS part and try to imitate the ViewPager's behavior from Android, an example.
I have a MvxListView with custom template defined. The binding in general works well. I see labels bound. However not BackgroundColor property when binding:
<LinearLayout
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
local:MvxBind="Text Name;BackgroundColor
NativeColor(HexColor(Color))">
The Color property I tried to have as stringn as
This is the most "sophisticated" version I tried the last (HexColor is my custom ValueConverter). As there was like 20 different ones before and none of them worked. Color is I tried Color(Color) and NativeColor(Color) (accordingly to this), and without the Converter, and background, Background, backgroundColor, but nothing helps.
I also checked and it looks like the plugin is setup well for Color.
So, any ideas? Thanks!
In order the Color plugin to work make sure you have the Bootstrap class ColorPluginBootstrap in the Bootstrap folder of your native project:
using MvvmCross.Platform.Plugins;
namespace MyProject.Mobile.Droid.Bootstrap
{
public class ColorPluginBootstrap
: MvxPluginBootstrapAction<MvvmCross.Plugins.Color.PluginLoader>
{
}
}
If you have that setup correctly then you can make your own color converters based on MvxColorValueConverter<T>, e.g.:
public class BooleanToBicolorConverter : MvxColorValueConverter<bool>
{
protected override MvxColor Convert (bool value, object parameter, System.Globalization.CultureInfo culture)
{
// returns gray or white depending whether value is true or false.
return value ? new MvxColor (215, 215, 215) : new MvxColor (255, 255, 255);
}
}
And you use that like this:
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxBind="Text Name; BackgroundColor BooleanToBicolor(MyBoolProperty)">
If you want to just bind an MvxColor of your ViewModel to your View you can use MvxNativeColorValueConverter like this:
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
local:MvxBind="Text Name; BackgroundColor NativeColor(MyMvxColorProperty)">
You can find the other Mvx base color converters here
HIH
In Android project I'm trying to add databindings using CrossLight part of MvvmCross.
Bindings to standard TextView/Buttons work great. But simplest markup with Mvx.Control:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
<Mvx.MvxListView />
</LinearLayout>
Gives an error
"Binary XML file line #1: Error inflating class Mvx.MvxListView"
The same thing is with Mvx.Spinner.
However, when instantiating it from code in Activity.OnCreate:
_bindingContext = new MvxAndroidBindingContext(this, new LayoutInflaterProvider(LayoutInflater), _viewModel);
var view = (LinearLayout)_bindingContext.BindingInflate(Resource.Layout.Main, null);
SetContentView(view);
var spinner = new MvxSpinner(this, null, new MvxAdapter(this, _bindingContext));
view.AddView(spinner);
Everything works great (including bindings). What am I doing wrong? Is this scenario supported in general?
Or maybe I should reference anything else except nuget MvvmCross.HotTuna.CrossCore?
P.S. Haven't found any samples with custom controls and CrossLight neither on github, nor on N+1 videos
If you want to use namespace abbreviations within your non-MvvmCross application, then you'll need to add those abbreviations. This can be done using a custom binding builder or using a 'light' setup step like:
var viewResolver = Mvx.Resolve<IMvxAxmlNameViewTypeResolver>();
viewResolver.ViewNamespaceAbbreviations["Mvx"] = "Cirrious.MvvmCross.Binding.Droid.Views";
viewResolver.ViewNamespaceAbbreviations["MyApp"] = "MyApp.Controls";
When doing this within a full MvvmCross application, then you can override the Setup property ViewNamespaceAbbreviations
protected override IDictionary<string, string> ViewNamespaceAbbreviations
{
get
{
var toReturn = base.ViewNamespaceAbbreviations;
toReturn["MyApp"] = "MyApp.UI.Droid.Controls";
return toReturn;
}
}
When markup was changed to using the full namespace and layout_width and layout_height attribute was added it started to work!
<Cirrious.MvvmCross.Binding.Droid.Views.MvxSpinner
android:id="#+id/MySpinner"
android:layout_width="fill_parent"
android:layout_height="20dp"
/>
It was found when I switched to default Android inflater and it was complaining about missing layout_width in Exceptions.
I am currently trying to draw a graph within an Android application. The library I found is called GraphView (http://www.jjoe64.com/p/graphview-library.html). I am currently using version 2, which is available on GitHub.
Drawing graphs works really nicely. The code necessary to get a graph is the following:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Map<String,List<GraphEntry>> graphData = (...)
if (graphData != null) {
List<GraphEntry> entries = graphData.get("temperature");
GraphView.GraphViewData[] data = new GraphView.GraphViewData[entries.size()];
int i = 0;
for (GraphEntry entry : entries) {
data[i++] = new GraphView.GraphViewData(entry.getDate().getTime(), entry.getValue());
}
GraphView.GraphViewSeries graphViewSeries = new GraphView.GraphViewSeries("temperature", 0xffff0000, data);
LineGraphView graphView = new LineGraphView(this, "temperature");
graphView.addSeries(graphViewSeries);
graphView.setShowLegend(true);
LinearLayout graphLayout = (LinearLayout) findViewById(R.id.layout);
graphLayout.addView(graphView);
}
}
This will produce a normal graph. Unfortunately, all kinds of labels are missing. The documentation tells that for the normal use case, one does not have to care about labels, as the library does this automatically. What am I doing wrong? I only get the plain graph, without any labels.
For the completeness, I am adding the graph to a linear layout. The appropriate layout file has the following contents:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:padding="5dp"
android:id="#+id/layout"
android:orientation="vertical"
></LinearLayout>
The GraphEntry class is only a container with a java.util.Date attribute and a double value attribute.
Thank you very much for any help,
Matthias
I switched to another charting engine: AChartEngine. This one works out of the box.
I had the same problem. This can be solved by removing the following line from the manifest file.
android:theme="#style/AppTheme"
I know this is quite vague, but worked for me. I don't the exact reason why this happens. If u guys come across the better solution please do share it.
You should use the latest version from github and include that in your project. This will allow you to set various colours using
graphView.getGraphViewStyle().setGridColor(Color.GREEN);
graphView.getGraphViewStyle().setHorizontalLabelsColor(Color.YELLOW);
graphView.getGraphViewStyle().setVerticalLabelsColor(Color.RED);