Databinding generate included layouts as view in aar file - android

My project contains multiple modules, and I am using aar files of other modules contains custom views and components. so I have an XML in .aar and I want to use it in my project.
Of course, I can but DataBinding doesn't generate it in the generated file, so I don't have access to XML's components and widgets.
My fragment's XML is:
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<!-- Comes from AAR file -->
<include
android:id="#+id/toolbarLayout"
layout="#layout/simple_tool_bar" />
Simple toolbar is:
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appbar"
android:layout_width="match_parent"
android:layout_height="#dimen/simple_tool_bar_height"
app:elevation="0dp">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:navigationIcon="?attr/homeAsUpIndicator"
app:navigationIconTint="#color/primary" />
</com.google.android.material.appbar.AppBarLayout>
And Databinding generated file is:
public abstract class FragmentTestBinding extends ViewDataBinding {
#NonNull
public final View toolbarLayout;
So as you can see in the generated file toolbarLayout is an instance of View but it should be SimpleToolBarBinding.
When you are working on a project that contains modules instead of .aar it everything is working well, but after generating the .aar file and use it in another project it doesn't.

Conclude:
The Android Gradle Plugin (version: 7.2.0) doesn't support to generate the matched type of XxxBinding when the layout file is in class (aar).
Reason:
I compared the gradle tasks of using module's layout file with using aar's layout file.
We missed following gradle tasks:
:module:dataBindingMergeDependencyArtifactsDebug
:module:dataBindingMergeGenClassesDebug
:module:dataBindingGenBaseClassesDebug
And analyzed the related ViewBinding AGP source code, in the function of android.databinding.tool.writer.ViewBinderKt#toViewBinder
// Check to make sure that the ID matches a binding. Ignored tags like <merge> or <fragment>
// might have an ID but not have an actual binding. Only use ID if a match was found.
val rootBinding = bindings.singleOrNull { it.id == id }
if (rootBinding != null) {
return RootNode.Binding(rootBinding)
}
If the binding doesn't in the binding list, it would be regarded as View.
Workaround
Using the function of ViewBinding.bind(view: View) to inflate the XxxBinding manually.
layout file
<!-- activity_awesome.xml -->
<androidx.constraintlayout.widget.ConstraintLayout>
<include android:id="#+id/includes" layout="#layout/included_buttons" />
</androidx.constraintlayout.widget.ConstraintLayout>
<!-- included_buttons.xml 在aar中-->
<androidx.constraintlayout.widget.ConstraintLayout>
<Button android:id="#+id/include_me" />
</androidx.constraintlayout.widget.ConstraintLayout>
Generated Code
// Activity.kt
class Activity {
private val includedButtonsBinding :IncludedButtonsBinding
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
val rootBinding = ActivityAwesomeBinding.inflate(inflater, container, false)
includedButtonsBinding = IncludedButtonsBinding.bind(ActivityAwesomeBinding.includes)
return rootBinding.root
}
}

Related

Trying to Change src of an ImageButton in another layout folder other than activity_main

to sum up I am trying to change the src of ImageButtons inside my dialog_colors.xml file from MainActivity. However whatever I do I couldnt manage to change it. I tried the same code with ImageButtons inside my activity_main.xml but it doesnt work for buttons inside dialog_colors.xml file.
activity_main.xlm
<androidx.constraintlayout.widget.ConstraintLayout 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"
tools:context=".MainActivity">
<include
android:id="#+id/dialog_colors"
layout="#layout/dialog_colors"/> ...
dialog_colors.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<LinearLayout
android:id="#+id/ll_paint_colors"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<ImageButton
android:id="#+id/red"
android:layout_width="40sp"
android:layout_height="40sp"
android:src="#drawable/pallet_normal"
android:background="#color/Red"
android:layout_margin="1sp"
android:tag="redTag"
android:clickable="true"
android:onClick="selectColor"
/>...
MainActivity.kt
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
colorDialog=findViewById<LinearLayout>(R.id.dialog_colors)
firstRowColors=colorDialog.findViewById<LinearLayout>(R.id.ll_paint_colors)
secondRowColors=colorDialog.findViewById<LinearLayout>(R.id.ll_paint_colors2)
drawingView=findViewById<DrawingView>(R.id.drawingView)
pressedColor=secondRowColors[0] as ImageButton
pressedColor!!.setImageResource(R.drawable.pallet_pressed)
}...
I tried the same thing with TextViews etc too. It seems like I cannot change anything inside the dialog_colors.xml file.
Do not get confused about the firstRows, secondRows etc, there are many ImageButtons, it doesnt work for any of them.
I found a solution to define an attribute in the MainActivity.kt through activity_main.xml to content_main.xml (included layout). The key word here is DataBinding. The project is completely reproducible and I provide first Kotlin and at the very end the JAVA files.
KOTLIN:
To enable DataBinding you need to go to your build.gradle(Module) and add following code:
//...
dataBinding{
enabled true
}
//...
You define a container called DrawableContainer as a Kotlin class. There you define a Drawable called customDrawable.
Thus DrawableContainer.kt:
import android.graphics.drawable.Drawable
data class DrawableContainer(val customDrawable: Drawable)
Now we will define our MainActivity.kt which will bind our chosen Drawable and pass it through our Container (DrawableContainer).
Our MainActivity.kt:
import android.app.Activity
import android.os.Bundle
import androidx.databinding.DataBindingUtil
import com.example.imagebuttonexperiment.databinding.ActivityMainBinding
class MainActivity : Activity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val binding: ActivityMainBinding = DataBindingUtil.setContentView(
this, R.layout.activity_main)
binding.drawable = DrawableContainer(resources.getDrawable(R.drawable.my_image))
}
}
The missing parts are our XML files. The code below shows our content_main.xml. It contains a variable (in <data>) we will define named drawable. The type guides to our DrawableContainer. So this is the first bridge between our Container and our layout we will <include. In the ImageButton you can see that as android:src we refer over our variable to our Drawable in our Container. That's why android:src="#{drawable.customDrawable}".
Thus content_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android">
<data>
<variable
name="drawable"
type="com.example.imagebuttonexperiment.DrawableContainer" />
</data>
<ImageButton
android:layout_width="match_parent"
android:layout_height="100dp"
android:layout_margin="10dp"
android:src="#{drawable.customDrawable}"/>
</layout>
Now it's important to build our second bridge. Yet, we have DrawableContainer -> content_main. This will be the bridge content_main -> MainActivity. Therefor we have our <data/> and variable defined again. As you can see in <include we bind:drawable the exact same variable which is in both XML files.
The missing piece in our puzzle is the activity_main.xml:
<?xml version="1.0" encoding="utf-8"?>
<layout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:bind="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context=".MainActivity">
<data>
<variable
name="drawable"
type="com.example.imagebuttonexperiment.DrawableContainer" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/include_content"
layout="#layout/content_main"
bind:drawable = "#{drawable}"/>
</LinearLayout>
</layout>
RESULT:
The final result is an ImageButton in the MainActivity that was included by another Layout. But the Drawable was chosen from the MainActivity and passed a Container to be shown in the included Layout. Magic isn't it!? ;)
JAVA:
And I will keep my word and provide you the JAVA version also.
The XML files and gradle will be the same, you only need to use MainActivity.java instead of MainActivity.kt, the same for DrawableContainer.java instead of DrawableContainer.kt.
Here is the same in green, DrawableContainer.java:
import android.graphics.drawable.Drawable;
public class DrawableContainer {
public final Drawable customDrawable;
public DrawableContainer(Drawable customDrawable) {
this.customDrawable = customDrawable;
}
}
And of course our MainActivity.java:
import androidx.appcompat.app.AppCompatActivity;
import androidx.databinding.DataBindingUtil;
import android.annotation.SuppressLint;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import com.example.imagebuttonexperiment.databinding.ActivityMainBinding;
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ActivityMainBinding mainBinding = DataBindingUtil.setContentView(this,R.layout.activity_main);
mainBinding.setDrawable(new DrawableContainer(getDrawable(R.drawable.my_image)));
}
}
DOWNLOAD PROJECT:
In addition I provide the project with both, JAVA and Kotlin classes in it. You just need to comment the content of the 2 classes grey you don't want. In example, if you want to use Kotlin, grey the content of .java files.
Github Project
Documentation & Tutorial:
Databinding Android Documentation
Youtube Tutorial: How to bind data?
NOTE: That's somewhat a solution of my problem:
I have tried this:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
colorDialog2=Dialog(this)
colorDialog2.setContentView(R.layout.dialog_colors)
colorDialog2.setTitle("Colors")
firstRowColors=colorDialog2.findViewById<LinearLayout>(R.id.ll_paint_colors)
secondRowColors=colorDialog2.findViewById<LinearLayout>(R.id.ll_paint_colors2)
drawingView=findViewById<DrawingView>(R.id.drawingView)
pressedColor= secondRowColors[0] as ImageButton
pressedColor!!.setImageResource(R.drawable.pallet_pressed)
}
intead of this in Main.Activity
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
colorDialog=findViewById<LinearLayout>(R.id.dialog_colors)
firstRowColors=colorDialog.findViewById<LinearLayout>(R.id.ll_paint_colors)
secondRowColors=colorDialog.findViewById<LinearLayout>(R.id.ll_paint_colors2)
drawingView=findViewById<DrawingView>(R.id.drawingView)
pressedColor=secondRowColors[0] as ImageButton
pressedColor!!.setImageResource(R.drawable.pallet_pressed)}
And it worked, I managed to change the src however I wanted. (Only did this change didnt do the changes that the others provided) However, I am not even sure if this is a good practice. It works as a quick fix for sure tho.
I will look into DEX7RA's answer more later on.

MainActivity.kt doesn't recognise button by ID

I'm struggling with very common problem, I think.
I've created a button in xml file and tagged it with ID. Then I wanted to make onClickListener in MainActivity.kt. But when I'm typing button's ID, it's marked red and it seems like Android Studio doesn't recognise it. I've tried cleaning and rebuilding project, but the problem still exist. Invalidate Caches/Restart didn't help as well.
Here's XML Code
<?xml version="1.0" encoding="utf-8"?>
<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:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/backgroundColor"
android:fadingEdge="vertical"
android:fadingEdgeLength="80dp"
android:gravity="center_horizontal"
android:orientation="vertical"
android:padding="16dp"
tools:context=".MainActivity"
tools:viewBindingIgnore="true">
<Button
android:id="#+id/btnDateBicker"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="25dp"
android:backgroundTint="#3D446C"
android:text="Select Date"
android:textSize="25sp"
android:textStyle="bold" />
</LinearLayout>
And here's kotlin code
package com.example.ageinminutes
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val mybtn = findViewById<btnDateBicker>()
}
}
If you simply want to fetch by id, use findViewById method.
val myBtn: Button = findViewById(R.id.btnDateBicker)
or
val myBtn = findViewById<Button>(R.id.btnDateBicker)
Another way to play with views in the kotlin file:
Try with synthetic binding, like just start writing few words of XML id in your kotlin file and android studio will attach that view for you. [Deprecated after Android 11]
Try with view binding or data binding, you just have to enable these settings in build.gradle file. [More Robust way]
Try this:
val mybtn = findViewById<Button>(R.id.btnDateBicker)

View must have a tag error in android data binding

I've two layouts for a screen. Activity works fine while setting a layout for Mobile device but it's causing error while setting layout for tablet device. The main issue is:
Caused by: java.lang.RuntimeException: view must have a tag at
com.mypackage.DataBinderMapperImpl.getDataBinder(DataBinderMapperImpl.java:941)
Though, I don't face the problem when I install app on mobile device.
This way I'm setting layout on activity:
val resetPasswordActivityBinding = DataBindingUtil.setContentView<ResetPasswordActivityBinding>(this,
R.layout.reset_password_activity)
resetPasswordActivityBinding.resetPasswordViewModel = resetPasswordViewModel
Here is my XML layout for tablet screen:
<?xml version="1.0" encoding="utf-8"?>
<layout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<data>
<variable
name="resetPasswordViewModel"
type="com.bhi.salesarchitect.user.password.reset.ResetPasswordViewModel" />
</data>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/toolbar"
layout="#layout/app_toolbar_layout"
app:appTheme="#{resetPasswordViewModel.appTheme}"
app:appToolbar="#{resetPasswordViewModel.appToolbar}" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/bg_splash"
android:contentDescription="#null"
android:scaleType="centerCrop" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/imv_builder_logo_change_pswd"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/dp_135"
android:layout_marginBottom="#dimen/space_xxlarge"
android:layout_weight=".5"
android:contentDescription="#null"
android:src="#drawable/ic_logo" />
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginEnd="#dimen/dp_80"
android:layout_weight=".4">
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginStart="#dimen/space_normal"
android:layout_marginEnd="#dimen/space_normal"
android:contentDescription="#null"
android:scaleType="fitXY"
android:src="#drawable/bg_white_shadow" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:paddingStart="#dimen/space_xxxlarge"
android:paddingEnd="#dimen/space_xxxlarge">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/dp_55"
android:layout_marginBottom="#dimen/space_small"
android:gravity="center"
android:text="#string/a_one_time_password_reset_code_has_been_sent_to_your_email"
android:textColor="#color/blue_dark_main"
android:textSize="#dimen/text_size_normal" />
<EditText
android:id="#+id/et_otp"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/space_normal"
android:background="#drawable/shape_rounded_white"
android:drawablePadding="#dimen/space_small"
android:hint="#string/password_reset_code"
android:inputType="textPersonName"
android:paddingStart="#dimen/space_small"
android:paddingTop="#dimen/space_xsmall"
android:paddingEnd="#dimen/space_xxsmall"
android:paddingBottom="#dimen/space_xsmall"
android:textColor="#android:color/black"
android:textSize="#dimen/sp_15" />
<EditText
android:id="#+id/et_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/space_small"
android:background="#drawable/shape_rounded_white"
android:drawableStart="#drawable/ic_password"
android:drawablePadding="#dimen/space_small"
android:hint="#string/new_password"
android:inputType="textPassword"
android:maxLength="#integer/max_password_length"
android:paddingStart="#dimen/space_small"
android:paddingTop="#dimen/space_xsmall"
android:paddingEnd="#dimen/space_xxsmall"
android:paddingBottom="#dimen/space_xsmall"
android:textColor="#android:color/black"
android:textSize="#dimen/sp_15" />
<EditText
android:id="#+id/et_confirm_password"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/space_small"
android:background="#drawable/shape_rounded_white"
android:drawableStart="#drawable/ic_password"
android:drawablePadding="#dimen/space_small"
android:hint="#string/confirm_new_password"
android:imeOptions="actionDone"
android:inputType="textPassword"
android:maxLength="#integer/max_password_length"
android:paddingStart="#dimen/space_small"
android:paddingTop="#dimen/space_xsmall"
android:paddingEnd="#dimen/space_xxsmall"
android:paddingBottom="#dimen/space_xsmall"
android:textColor="#android:color/black"
android:textSize="#dimen/sp_15" />
<Button
android:id="#+id/bt_submit"
style="#style/ButtonNormal"
android:layout_marginTop="#dimen/space_normal"
android:backgroundTint="#color/blue_dark_main"
android:onClick="#{()-> resetPasswordViewModel.onSubmitClick()}"
android:text="#string/submit"
android:textColor="#android:color/white" />
</LinearLayout>
</FrameLayout>
</LinearLayout>
</FrameLayout>
</LinearLayout>
</layout>
I ran into this when I had:
A library module defining a layout resource
An app module that depended upon that library module defining the same layout resource
The library layout resource was set up for data binding (e.g., root <layout> element), but the app module's edition of that layout resource was not
In my case, the app module's layout was left over from when I created the project. Removing it cleared up the problem.
Keeping two layouts, one with layout tag of data-binding and another without it, is the common cause of this issue.
I got stuck when I renamed my two normal layout files with same name (/layout and /layout-sw720dp) and used tag. Then, it worked for mobile device but not for tablet. So, after cleaning project, it all started working.
I was having this problem when using an array adapter, having a crash due to a missing tag on convertView. In my getView(), I was doing:
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
if (convertView == null) {
DataBindingUtil.inflate<ItemSpinnerDropDownWorkPackageFilterBinding>(
LayoutInflater.from(parent.context),
R.layout.item_spinner_drop_down_work_package_filter,
parent,
false
)
} else {
binding = ItemSpinnerDropDownWorkPackageFilterBinding.bind(convertView)
binding.text1.setText(getItem(position))
setDividerVisibility(binding.divider, position)
return convertView
}
}
Which was crashing. The solution was to set the tag on the first run through getView:
override fun getView(position: Int, convertView: View?, parent: ViewGroup): View {
val binding = if (convertView == null) {
DataBindingUtil.inflate<ItemSpinnerDropDownWorkPackageFilterBinding>(
LayoutInflater.from(parent.context),
R.layout.item_spinner_drop_down_work_package_filter,
parent,
false
)
} else {
convertView.tag as ItemSpinnerDropDownWorkPackageFilterBinding
}
binding.text1.setText(getItem(position))
setDividerVisibility(binding.divider, position)
binding.root.tag = binding
return binding.root
}
Using viewStub with databinding:
class MyFragment {
lateinit var binding: MyFragmentBinding
lateinit var viewStubBinding: MyViewStubBinding
private fun setViewStub() {
binding.myViewStub.setOnInflateListener { viewStub, view ->
viewStubBinding = binding.myViewStub.binding as MyViewStubBinding // property viewStubBinding is finally inflated
}
binding.myViewStub.viewStub?.inflate() // inflate viewStub with defined layout file in XML
}
}
When I tried to use MyViewStubBinding.bind(view / viewStub) it crash on View must have a tag. This happen because ViewStubProxy try to inflate binding class by itself, so when I try to call bind() by myself on same Binding class, it was already bound and crashed with this error.
You need to add layout tag at start of your app_toolbar_layout layout file
You must have <layout> tag into your all XML views (portrait, landscape, tablet, etc.). Even you have to include <layout> tag into included views ("#layout/app_toolbar_layout").
i was facing similar error so my work around was
DataBindingUtil.bind(holder.itemView)?.apply
{
item = items[position]
}
I just needed to clean and rebuild after deleting a resource file.
May be you have multiple xml layout files.
if you already use layout tag in your xml file, and you still give error, just rename your layout file name and clean/rebuild the project again it will fix
I had this error trying to bind separately as my view is inflated within a library but I supply the layout.
The doc says you can do this:
val binding: MyLayoutBinding = MyLayoutBinding.bind(viewRoot)
The viewRoot I was passing was derived from the custom class but ended up being a parent of my layout file (the one that included the layout tag). So I had to specify the view as the root of my layout file via findViewById
I encounter this problem, the root case is there are duplicate layout resource file in different module. delete the redundant one to resolve the issue.
In my case worked this code to set content view with data binding.
binding = GenericItemDetailsListBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot());

iOS equivalent for Android's include control

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.

DataBindingUtil.setContentView(this, resource) return null

I started using Android Data binding but without success.I have done everything as proposed in the documentation but when I have to set method value I get null.
I am using Android Studio 2.1.2 and I put in gradle
dataBinding {
enabled = true
}
in layout I do exactly da same put layout and inside I put tag data:
<data>
<variable name="order" type="com.example.Order"/>
</data>
and in code when I want to have binding variable
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
ActivityOrderOnePaneBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_order_one_pane);
binding.setOrder(mOrder);
Binding is null,I don't have compile errors.
Since you're overriding setContentView in your Activity, you need to replace:
ActivityOrderOnePaneBinding binding = DataBindingUtil.setContentView(this, R.layout.activity_order_one_pane);
with
ActivityOrderOnePaneBinding binding = DataBindingUtil.inflate(getLayoutInflater(), R.layout.activity_order_one_pane, getContentFrame(), false);
setContentView(binding.getRoot());
I had the same problem because I overrode setContentView in my base Activity and that fixed it.
If you overrode setContentView, getContentFrame() is the ViewGroup that contains your content, exclusive of the AppBarLayout and Toolbar. Here's an example of what getContentFrame() would look like if you had a base layout similar to what's below:
<?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:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.design.widget.AppBarLayout
...>
<android.support.design.widget.CollapsingToolbarLayout
...>
<android.support.v7.widget.Toolbar
.../>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
<android.support.v7.widget.ContentFrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</android.support.v7.widget.ContentFrameLayout>
</android.support.design.widget.CoordinatorLayout>
getContentFrame() just returns the FrameLayout in the above layout.
protected ViewGroup getContentFrame() {
return (ViewGroup) findViewById(R.id.content_frame);
}
I hope this helps.
Try to rebuild gradle and clean project

Categories

Resources