How to pass XML widget id to ViewModel? - android-databinding

The target is when a button clicked, a chart refresh data.
I used MPAndroidChart, data binding layout.
The question is how can I get 'lineChart' from XML widget id?
And I think maybe it is not the right way to achieve this, what is the better way to refresh a chart when a button clicked?
Code in xxxViewModel.class:
fun onButtonClicked() {
// how can I get 'lineChart' from XML widget id?
plotChart(lineChart)
}
fun plotChart(lineChart: LineChart){
//detail code
}

We should not pass the view reference to the view model, this will break the MVVM architecture pattern.
so view will call the viewmodel and perform some action VM and return the data via Live data.
<Button
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_vertical"
android:layout_marginLeft="10dp"
android:layout_marginStart="10dp"
android:onClick="onButtonClicked"
android:paddingEnd="5dp"
android:paddingRight="5dp"
/>
in the above code there is a xml attribute android:onClick. Map the name of the function, which is defined (action for click of button) in the activity/fragment.
fun onButtonClicked() {
viewModel.plotChart(lineChart)
}
in the above method, view will call viewModel to plotChart method to perform action.

Related

Can I define a XAML element in the ViewModel and use it directly in the View using C++/WinRT and WinUI 3?

Many questions have been asked about using a XAML element from the View in the ViewModel. But what about defining a XAML element in the ViewModel and using that in the XAML markup of the View? So that the ViewModel knows about the element from the get-go(and owns the element).
I was trying to do this thinking it would be more MVVM-compliant--the ViewModel doesn't have to know about the entire View in order to use this component. But I recognize it's still not really in the spirit of MVVM to define a XAML element in the ViewModel. But at this point, I just want to know if this technique is possible.
I know I can define a property in the ViewModel, and then bind that to a property of a XAML element defined in the View. E.g. defining a string in the ViewModel and binding that to the Text property of a TextBlock XAML element. But is it possible to simply define the XAML element itself in the ViewModel and use that in the XAML markup of the View?
Example code. I'm using C++/WinRT with WinUI 3:
MainWindowViewModel.idl
namespace MyApp
{
runtimeclass FooViewModel
{
MainWindowViewModel();
Int32 IntProperty; // I can bind this to a XAML element's property
Microsoft.UI.Xaml.Controls.Frame Frame; // Can I use this in the View?
}
}
MainWindow.idl (View)
import "ViewModel/MainWindowViewModel.idl";
namespace MyApp
{
runtimeclass MainWindow : Micorosoft.UI.Xaml.Window
{
MainWindow();
MainWindowViewModel MainWindowViewModel{ get; };
}
}
MainWindow.xaml (View's XAML)
<Window
x:Class="MyApp.MainWindow"
//...
>
<StackPanel>
// I can bind to a property of MainWindowViewModel as such:
<Slider Minimum="0" Maximum="{x:Bind MainWindowViewModel.IntProperty}" />
// Is there a way to use the Frame defined in MainWindowViewModel here, or can you only create a new Frame instance?
<Frame/>
</StackPanel
</Window>
Is it possible to simply define the XAML element itself in the
ViewModel and use that in the XAML markup of the View?
Yes, It will be work.
I tested it in WinUI3 C# and the code works fine.

Kotlin App wide change TextView Color Programatically

How does one change the TextView default color throughout the entire app programmatically?
Not individual textView but every TextView in all activity layouts
Either getting the current theme and setting the default TextView Color, if <TextView has textColor="" in the xml, then leave it alone.
But if <TextView does not reference a textColor it's getting the default (for the theme)
How do I set this based on code/logic
Thanks in advance
Here is the object's code:
object Example {
fun getColor(c: Context) : Int {
if (someCondition) {
return (c.getColor(R.color.colorAccent))
} else {
return (c.getColor(R.color.colorPrimaryDark))
}
}
}
you call the get method every time you need to alter the view (you can store the TextViews inside a list)
Alternatively you can use the meterial theme feature that is explained here: https://proandroiddev.com/dark-mode-on-android-app-with-kotlin-dc759fc5f0e1

Kotlin download and display a PDF

Hi I would like to download and display a pdf using Kotlin. I want to download and display a progress bar with the first page of the pdf display while we are downloading.Then display the PDF. I did it in swift with PDFKit it was very simple but I can't find an equivalent in Kotlin.
I made many research to display a pdf in Kotlin but I didn't got much result, it's seem that this subject is not really so first I looked at PdfRenderer that is native but most exemple are in java and not Kotlin and I don't get what is : documented.getSeekableFileDescriptor().
Then I looked at pdfView lib that is really nice to display a pdf but only from asset, the pdfView.fromStream doesn't seem to work and I can't get any exemple to how it's work.More over I would like to avoid downloading the pdf I want to display it directly to avoid long loading.
Finaly I used okhttp and retrofit to download the pdf but I can't use pdfView to display it because in the from asset the pdf has to be already in the project.
I found that downloading pdf from and url and display it with Kotlin is very difficult and not very documented.
So if anyone has some suggestion I'll take it.
here is my sample of code using pdfView.fromStream this only load a blanc page
private fun loadpdf(){
println("pdfview")
//PDF View
Thread(Runnable {
val input = URL(pdf_url).openStream()
val pdfView = this.findViewById<PDFView>(com.example.mylibrary.R.id.pdfView)
//pdfView.fromFile(file)
pdfView.fromStream(input)
.enableSwipe(true) // allows to block changing pages using swipe
.swipeHorizontal(true)
.enableDoubletap(true)
.defaultPage(0)
.enableAnnotationRendering(false) // render annotations (such as comments, colors or forms)
.password(null)
.scrollHandle(null)
.enableAntialiasing(true) // improve rendering a little bit on low-res screens
// spacing between pages in dp. To define spacing color, set view background
.spacing(0)
.pageFitPolicy(FitPolicy.WIDTH)
.load()
println("testpdf")
})
}
And here is my sample of code using pdfView.fromAsset this one work but only if the file is already on the project however I want to get my pdf from and url
private fun loadpdf(){
//PDF View
Thread(Runnable {
val pdfView = this.findViewById<PDFView>(com.example.mylibrary.R.id.pdfView)
pdfView.fromAsset("url")
.enableSwipe(true) // allows to block changing pages using swipe
.swipeHorizontal(true)
.enableDoubletap(true)
.defaultPage(0)
.enableAnnotationRendering(false) // render annotations (such as comments, colors or forms)
.password(null)
.scrollHandle(null)
.enableAntialiasing(true) // improve rendering a little bit on low-res screens
// spacing between pages in dp. To define spacing color, set view background
.spacing(0)
.pageFitPolicy(FitPolicy.WIDTH)
.load()
})
}
If anyone got this problem, I solved it by using the coroutine :
Add this dependency to your gradle build :
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.2")
then import this line :
import kotlinx.coroutines.*
in your file.
Then use launch coroutine methods as follow :
private fun loadpdf(pdfView: PDFView){
//PDF View
GlobalScope.launch{
val input : InputStream
input = URL(pdf_url).openStream()
pdfView.fromStream(input)
.enableSwipe(true) // allows to block changing pages using swipe
.swipeHorizontal(true)
.enableDoubletap(true)
.defaultPage(0)
.enableAnnotationRendering(false) // render annotations (such as comments, colors or forms)
.password(null)
.scrollHandle(null)
.enableAntialiasing(true) // improve rendering a little bit on low-res screens
// spacing between pages in dp. To define spacing color, set view background
.spacing(0)
.pageFitPolicy(FitPolicy.WIDTH)
.load()
}
You have to pass the pdfView cos you can use the global view in asyctask.
Now to add a progress bar you can add to your xml :
<ProgressBar
android:id="#+id/Progressbar"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:indeterminate="true"
android:minWidth="200dp"
android:minHeight="50dp" />
then you need to override the function loadComplete to get the call back for your progress bar and implement OnLoadCompleteListener interface for your activity.
And btw if you want to display page per page with the pdfView lib add :
.swipeHorizontal(true)
.pageSnap(true)
.autoSpacing(true)
.pageFling(true)

how can I manipulate the View (XAML) from my ViewModel? (Xamarin Forms)

how can I manipulate the View (XAML) from my ViewModel?
For example, I have a detail view for an object. The view have a grid. Depending of the object, the Grid should have different count of rows and cols. The number of cols and rows is set in the object details. Is it possible to do that from the ViewModel or I have to do that in the View .cs?
I open the detail view from a ListView (OnItemSelect):
await Navigation.PushAsync(new AlgoDetailPage(new AlgoDetailViewModel(algo)));
AlgoDetailPage - Here I want to add the cols and rows, depending of the object.
<?xml version="1.0" encoding="utf-8" ?>
<ContentPage xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
x:Class="NotsanHessen.Views.AlgoDetailPage"
Title="{Binding Algo.Title}">
<StackLayout>
<Grid>
</Grid>
</StackLayout>
</ContentPage>
The AlgoDetailPage.cs:
public partial class AlgoDetailPage : ContentPage
{
AlgoDetailViewModel viewModel;
public AlgoDetailPage(AlgoDetailViewModel viewModel)
{
InitializeComponent();
BindingContext = this.viewModel = viewModel;
}
public AlgoDetailPage()
{
InitializeComponent();
BindingContext = viewModel;
}
}
The ViewModel:
public class AlgoDetailViewModel : BaseViewModel
{
public Algo Algo { get; set; }
public AlgoDetailViewModel(Algo algo = null)
{
this.Algo = algo;
// Rows: algo.Rows
// Cols: algoCols
}
}
you don't manipulate the View from the VM. Instead, the View should use the VM's properties to determine it's layout. In this case, you would add the rows and cols in the View based on the data from the VM.
Technically, you don't have to do that from View itself. If you want to be exactly orthodox you can create the custom control that will handle the binding for you. Personally I wouldn't recommend this level of following some theory, but if you want it it is possible.
Other than that you may try to take a look at CollectionView that will appear in Xamarin 4.0 it may be close to what you have requested (you haven't specified exactly how column width is to be handled). Also there could be some 3rd party components that can handle this.
If it is a complex grid, have two different content views. In your xaml, based on the condition show or hide the content views. In this way, you can keep the code manageable any time.
And if you REALLY necessarily want to access the View from ViewModel, you can Bind View's Load Event to a Command and pass the View itself as a Command Parameter.

Android Spinner text difference

I'm adding two different spinners to my XML layout, one thats filled through an XML file and one thats filled programmatically.
<Spinner
android:id="#+id/spHome"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textAlignment="textEnd" />
<Spinner
android:id="#+id/spDevice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:entries="#array/spinner_data"
android:textAlignment="textEnd" />
So the one Spinner is filled through this XML file, which goes great and gives a good clear text quality.
<resources>
<string-array name="spinner_data">
<item>Alle apparaten</item>
<item>Light</item>
<item>Door Sensor</item>
<item>Socket</item>
<item>Smart meter</item>
<item>Multi sensor</item>
</string-array>
<string-array name="solo_data">
<item>Hele huis</item>
</string-array>
The other spinner gets filled programmatically, with the following code:
private var roomDtoList: ArrayList<RoomItemDto> = ArrayList()
private var spRoomData : ArrayList<String> = ArrayList<String>()
private fun fillSpRooms(){
spRoomData.clear()
for(item in roomDtoList){
spRoomData.add(item.name)
}
var spAdapter : ArrayAdapter<String> = ArrayAdapter<String>(this.context, R.layout.spinner_drop_layout, R.id.spinnerTextview, spRoomData)
spHome!!.adapter = spAdapter
}
So whenever I fill this spinner with the following code, the filled text looks ugly and has no margin/spacing between every line. Even when I add margin and textStyling into the R.id.spinnerTextview, it won't make the text look clear.
Anyone knows a fix?
Try doing this. Hope this will help.
for(item in roomDtoList){
spRoomData.add(item.name)
}
spHome!!.setOnItemSelectedListener(this)
// Create an ArrayAdapter using a simple spinner layout and languages array
val aa = ArrayAdapter(this, android.R.layout.simple_spinner_item, spRoomData)
// Set layout to use when the list of choices appear
aa.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Set Adapter to Spinner
spHome!!.setAdapter(aa)