I have one imageView animation, so when i rotate screen it start again. How do i use that with viewModel.
Here is code
lateinit var img = ImageView
img = findViewById(R.id.img)
Img.animate().translationY(600F).setDuration(2000).setStartDelay(2000)
ViewModel holds data, but it should not hold a reference to a View.
You could place a boolean in the ViewModel that is true, and after the animation you set its value to false. If you then turn your phone and the boolean value remains false, so it would not animate again.
You will have something like this:
img = findViewById(R.id.img);
if (vm.getBooleanValue()) {
img.animate().translationY(600F).setDuration(2000).setStartDelay(2000);
vm.setBooleanValue(false);
}
There are two ways you can solve this problem:
Use the viewmodel
Use onSaveInstanceState ()
Is. The second method is not suitable for heavy and large data and it is better to use the first method for your purpose.
viewmodels are not rebuilt when your UI is restarted, which is why you see the same UI as the previous UI in the output.
You can see how to implement viewmodels in Android by viewing the codelab below:
https://developer.android.com/codelabs/kotlin-android-training-view-model?index=..%2F..android-kotlin-fundamentals#4
You can read more information about viewmodels from the link below:
https://developer.android.com/topic/libraries/architecture/viewmodel
Related
HomeFragment
Everything seems to be done correctly, but when you flip the screen, the resulting value from the EditText disappears from the TextView. What to do tell me pls
HomeViewModel
MainActivity
what you are doing wrong is that you start observing the value from the viewmodel only after you click btnSend.
This creates two problems. The first is the obvious one you have found already. You lose the value upon rotating because you have a new View.
The second one is that every time you click on btnSend you are re-observing your LiveData with a new observer object.
You have to move the folowing code inside your OnCreateView after setting the binding value
viewModel.myValue.observe(viewLifecycleOwner) {
binding.txtResult.text = viewModel.myValue.value
}
You can also replace the second part of the assignment with just it as it is suggested in the answer from #JustSightseeing
Try replacing:
viewModel.myValue.observe(viewLifecycleOwner) {
binding.txtResult.text = viewModel.myValue.value
}
with
viewModel.myValue.observe(viewLifecycleOwner) {
binding.txtResult.text = it
}
I have a fragment screen where there is a form that is used to create Questions and Answers for my app. For this fragment, I use data binding, then I created many functions to validate the form, and check other stuffs.
Now, I'm creating a different fragment screen, where I'll be able to edit this Questions and Answers that were created, and for this, I want to use the same functions that were used when I created on the other fragment, for example to validate the fields that the user is editing.
I thought about implementing an Interface, and put these common functions there, so I could use it on both fragments. However, in these functions I use DataBinding, and I don't know how I can use it on the interface, so it would get the correct XML variables regarding to one fragment, or the other one.
On the screenshot bellow, it shows that I'm trying to use the binding, however I can't specify which one I'm using, otherwise the code will work only for a fragment, and not for both. Consequently, I tried to declare as DataBindingUtil but it didn't work.
Screenshot interface
If you want to go with this approach you could add the views as fields in the interface:
interface IQuestionForm {
var newQuestionTextInput: EditText
var answer1TextField: EditText
fun validateAllParametersToCreateNewQuestion(){
var allTextInputSet = true
if (newQuestionTextInput.text.isNullOrEmpty()){
newQuestionTextInput.error = "You have to enter the question"
allTextInputSet = false
}
if (answer1TextField.text.isNullOrEmpty()){
answer1TextField.error = "You have to enter an answer"
allTextInputSet = false
}
....
}
}
Then initialise those fields after creating the binding in the fragment.
I want to optimize my Android application, but i don't know what is better?
First option:
public void function()
{
RelativeLayout rl = (RelativeLayout)findViewById(R.id.activity);
ImageView img = (ImageView)findViewById(R.id.image);
...
}
Second option:
RelativeLayout rl = (RelativeLayout)findViewById(R.id.activity);
ImageView img = (ImageView)findViewById(R.id.image);
public void function{
...
}
Which option use less resource? Global variables or local? (My program call this function every second)
In general, the second option is better. findViewById() can be fairly expensive if your view hierarchy is complex. It is better to call it once and store the results than to call it repeatedly.
Assuming the second option uses e.g. member variables, it won't even work. You need to call setContentView() e.g. in onCreate() before calling findViewById() and member variable initialization is performed before your onCreate() runs.
So the first one is better because it works while the other doesn't.
Other than that, at this level this smells of unnecessary micro-optimization. If you have performance issues, they are probably elsewhere.
Related: If you want to optimize findViewById() calls e.g. in an adapter where the same views are recycled over and over again, google for "android viewholder".
I have a fragment and I need to measure location/width/height of its views on screen and pass to some other class.
So what I have is a function which does it, something like this :
private void measureTest(){
v = ourView.findViewById(R.id.someTextField);
v.getLocationOnScreen(loc);
int w = v.getWidth();
...
SomeClass.passLocation(loc,w);
...
The problem is that the location/width/height of views is not ready within fragment lifecycle.
So if I run that function within these lifecycle methods :
onCreateView
onViewCreated
onStart
onResume
I either get wrong location and width/height measurments or 0 values.
The only solution I found is to add a GlobalLayoutListener like this to mainView
mainView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
public void onGlobalLayout() {
if(alreadyMeasured)
mainView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
else
measureTest();
}
});
this gets the job done.. but its just Yack! IMO.
Is there a better way of doing this? seems like such a basic thing to do
inside onActivityCreated of your fragment retrieve the currentView (with getView()) and post a runnable to its queue. Inside the runnable invoke measureTest()
There is no better way. That code isn't that bad! It's fired as soon as the view is layed out (my terminology might be a bit weird there) which happens right after measuring. That is how it is done in the BitmapFun sample (see ImageGridFragment, line 120) in Google's Android docs. There is a comment on that particular piece of code stating:
// This listener is used to get the final width of the GridView and then calculate the
// number of columns and the width of each column. The width of each column is variable
// as the GridView has stretchMode=columnWidth. The column width is used to set the height
// of each view so we get nice square thumbnails.
I have a bunch of code in a routine that looks a bit like this:
a.setContentView(R.layout.myLayout);
textview t1 = (TextView) a.findViewById(R.id.mylayout_t1);
t1.setText("Hello")
t1.setTypeface(font);
t1.setTextColor(colour);
t1.setTextSize(fontSize);
textview t2 = (TextView) a.findViewById(R.id.mylayout_t2);
t2.setText("Hello Again")
t2.setTypeface(font);
t2.setTextColor(colour);
t2.setTextSize(fontSize);
The problem I'm having is that before when the routine is called, the layout is done with all the fonts at the default font/size/colour and then they quickly change to the specified values, which is not very pleasant on the eye.
Is there some kind of command I can add to the beginning of the routine to suspend any layout, and then another command to resume at the end of the routine?
There are two ways:
1) Put your all code (you mentioned above) in onCreate() method and at last call t1.setVisible(true);
2) Put your code in the method in which you are creating your UI (like initUI() or something like that) and call this method before setting visibility to true.
Have you considered using XML to set the text style instead of doing it programmaticly. See this Android Dve Guide page for more on this topic.
Another (bad?) way might be to use XML to set the views visibility to false and when you have made your style changes, call t1.setVisibility(true). Haven't tried this one, so it might produce a similar, unwanted result.