Navigation Component set transition animation programmatically - android-fragments

yesterday i come across with a problem that i needed to set animation from nav_graph.xml in my baseFragment and programatically get action object from current node which includes enterAnim and exitAnim resource. Could not find solution here so here we go.
First we need to feed anim folder with our animations in res folder because its hungry.
slide_in_left.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate
android:duration="250"
android:fromXDelta="-100%"
android:fromYDelta="0%"
android:toXDelta="0%"
android:toYDelta="0%" />
</set>
other animations you can easily found on github or stackoverflow.
Here is my nav_graph.xml fragment from which we gonna make transition
<fragment
android:id="#+id/kebabsFragment"
android:name="com.kebabkrabby.kebabapp.KebabFragment"
android:label="so many kebabs"
tools:layout="#layout/fragment_kebab">
<action
android:id="#+id/action_kebabs_to_kebab_detail"
app:destination="#id/kebabDetailFragment"
app:enterAnim="#anim/slide_in_right"
app:exitAnim="#anim/slide_out_left" />
</fragment>
now in KebabFragment.tk u gonna call baseFragment method for transition to get into detail of our desired kebab and then popFromBackStack
//navigateAndClean(actionId, cleanFragmentId)
navigateAndClean(R.id.action_kebabs_to_kebab_detail, R.id.kebabsFragment)
in our baseFragment.kt
internal fun navigateAndClean(actionId: Int, currentFragmentIdToClear: Int) {
val navBuilder = NavOptions.Builder()
val navController = Navigation.findNavController(getRootView())
val currNavigationNode = navController.graph.findNode(currentFragmentIdToClear) // NavDestination of kebabsFragment object in nav_graph.xml
val action = currNavigationNode?.getAction(actionId) // finally we get this action_kebabs_to_kebab_detail action object
action?.navOptions?.enterAnim?.let { //do we have animation or not?
navBuilder.setEnterAnim(it)
}
action?.navOptions?.exitAnim?.let {
navBuilder.setExitAnim(it)
}
navBuilder.setPopUpTo(currentFragmentIdToClear, true) //remove from backstack
navController.navigate(actionId, null, navBuilder.build())
}
Some people will ask. Hey, Mr. Kebab but how do i get getRootView()
and Mr. Kebab will look at u and say "buddy, look at this world, together we can achieve big things"
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
rootView = inflater.inflate(mContentLayoutResourceId, container, false)
return rootView
}
fun getRootView(): View {
return rootView
}
Durum kebab. Enjoy.

Navigation components provides built in animation options for entry and exit transitions, a sample code block is attached below for reference
Code block to add animations within java/kotlin
navigate(
navController, resId, bundle,
NavOptions.Builder()
.setPopUpTo(R.id.splashFragment, true)
.setEnterAnim(R.anim.fade_in)
.setExitAnim(R.anim.fade_out)
.setPopEnterAnim(R.anim.fade_in)
.setPopExitAnim(R.anim.fade_out)
.build()
)
Code block for adding animation in xml file
<fragment
android:id="#+id/kebabsFragment"
android:name="com.kebabkrabby.kebabapp.KebabFragment"
android:label="so many kebabs"
tools:layout="#layout/fragment_kebab">
<action
android:id="#+id/confirmationAction"
app:destination="#id/confirmationFragment"
app:enterAnim="#anim/slide_in_right"
app:exitAnim="#anim/slide_out_left"
app:popEnterAnim="#anim/slide_in_left"
app:popExitAnim="#anim/slide_out_right" />
</fragment>

Related

I want my buttons and BG to stay in one position as the view controller elements transition

I have been working on this for hours(today)/months. I just want my BG to stay permanent in all view controllers as well as the same buttons with the same commands for all of them.
It is only the foreground element that is transitioning around in the center of the viewfinder, from side to side.
I tried using a subclass, it did not effect my view controller at all. When it came to trying to get my buttons to stay, i tried to cheat and use a tab bar, but the tab bar controller is locked at the bottom and I can't move it up the y axis.
Is there an easier way? Is there a way to make a view controller have the main components and every other view controller has sub components that transitions, one from another using the main components controller.
When attempting to make a subclass, I made a touch class file and put..
import UIKit
class WallpaperWindow: UIWindow {
var wallpaper: UIImage? = UIImage(named: "BG.png") {
didSet {
// refresh if the image changed
setNeedsDisplay()
}
}
init() {
super.init(frame: UIScreen.main.bounds)
//clear the background color of all table views, so we can see the background
UITableView.appearance().backgroundColor = UIColor.clear
}
required init?(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func draw(_ rect: CGRect) {
// draw the wallper if set, otherwise default behaviour
if let wallpaper = wallpaper {
wallpaper.draw(in: self.bounds);
} else {
super.draw(rect)
}
}
and then put
var window: UIWindow? = WallpaperWindow()
into my AppDelegate...
the code was working find, just my background did not change at all...
Related in making the tab bar move up the y axis I had no luck, it was locked....counting even tough the UIcoding..

Find the width of a view inside of a fragment

I want to find the width of a view inside of a fragment and I've written the following code
class ExampleFragment : Fragment() {
var team1_value = 0
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val v = inflater!!.inflate(R.layout.fragment_queens, container, false)
val width = v.layout_team1_picker.width.toFloat()
Log.d("width1","width is $width")
return v
}
The answer I get is 0.0. Howerver if I do the same thing inside of a button
class ExampleFragment : Fragment() {
var team1_value = 0
override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?,
savedInstanceState: Bundle?): View? {
// Inflate the layout for this fragment
val v = inflater!!.inflate(R.layout.fragment_queens, container, false)
v.image_team1_plus.setOnClickListener{
val width = v.layout_team1_picker.width.toFloat()
Log.d("width2","width is $width")
}
return v
}
I get a positive 320.0 whenever I press the button. Why is this the case?
Because in the onClick case, you're running the code when the button is clicked, which is after the view has been packed / rendered. In the onCreateView case, you're running the code when it is created, which is before it is packed / rendered, so it doesn't yet have a size.
If you moved the width-displaying code from onCreateView to onStart, which is called when the fragment is visible, then you should see the desired behaviour.
Override the onViewCreated method and get the width inside.
As stated in the other answers, during onCreateView, your views still have no size yet. Another approach would be to add a GlobalLayoutListener, to be aware of when your view has been rendered.
There are different ways of achieving this, here's a nice breakdown of it :
https://antonioleiva.com/kotlin-ongloballayoutlistener/
Basically :
Create an extension function to
1. wait for the view to be rendered (by attaching a globalLayoutListener to its viewTreeObserver)
2. Call whatever function you want once width/height are known
inline fun <T: View> T.afterMeasured(crossinline f: T.() -> Unit) {
viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
if (measuredWidth > 0 && measuredHeight > 0) {
viewTreeObserver.removeOnGlobalLayoutListener(this)
f()
}
}
})
}
To call it, simply do :
v.layout_team1_picker.afterMeasured {
Log.d("width1","width is $v.layout_team1_picker.width")
}

flex mobile validation: how to remove red error border?

I use validators in my flex mobile application.
I want to remove the red border when validator has triggered an error.
<mx:EmailValidator id="emailV" source="{login_txt}" property="text" triggerEvent="click" trigger="{connexion_btn}" />
<mx:StringValidator id="passwordV" source="{password_txt}" property="text" trigger="{connexion_btn}" triggerEvent="click" />
I tried:
target.errorString = null; // not good
Any clue ?
Usually I would set the errorString to an empty string; and I do that on the instance of the component with the red string on it. I believe in that case, it would be your trigger component:
login_txt.errorString = '';
password_txt.errorString = '';
I'm unclear based on the limited code provided if the target you are setting the errorString on will be the same as the actual component specified as the validator source. It could be, we just aren't provided enough information to know for sure.
The red glow is defined in spark.skins.spark.ErrorSkin, which is the default value of a UIComponent's errorSkin property. You can't set this property to null, but you can extend the ErrorSkin class and override the methods that generate the glow (namely, updateDisplayList and processBitmap).
I created a NullFocusSkin that I use to remove the red error glow and the blue focus glow. I set the component's errorSkin and focusSkin properties to that and hey presto - no more nasty glow, and no need to manually clear the errorString!
import spark.skins.spark.HighlightBitmapCaptureSkin;
public class NullFocusSkin extends HighlightBitmapCaptureSkin
{
override protected function updateDisplayList(unscaledWidth:Number, unscaledHeight:Number):void
{
// Do nothing.
}
override protected function processBitmap():void
{
// Do nothing.
}
}
Besides setting the error string to empty, I had to call showFocus(), or the red border wouldn't go away.
login.errorString = '';
login.focusManager.showFocus();

fragments, android:zAdjustment (z order) and animations

I'm using the support library. Now, I want to have a fragment shifting in from the bottom, moving OVER the previous one.
For this I use this to keep the previous fragment (the one that is being slided over) visible until the new fragment is in its place:
<alpha xmlns:android="http://schemas.android.com/apk/res/android"
android:fromAlpha="1.0" android:toAlpha="1.0"
android:duration="2500"
android:zAdjustment="bottom" />
this is animation used for the new fragment to slide in from bottom:
<set xmlns:android="http://schemas.android.com/apk/res/android">
<translate android:fromYDelta="100%p" android:toYDelta="0"
android:duration="#android:integer/config_mediumAnimTime"
android:zAdjustment="top"/>
I've put the z adjustment to bottom and top for both, but still the 'bottom' animation is still on top of the new fragment! I have put the duration to 2500 for testing and it stays on top for the whole time.
Does zAdjustment not work for fragment animations?
According to this google group thread Z adjustment only works for window animations.
"The Z adjustment only works for window animations. I thought this was documented, but apparently not."
-- Dianne Hackborn (Android framework engineer)
You can override the onCreateAnimation method and for any animations you can check what animation is currently running and if you need it to be on top, set the Z-axis from there.
override fun onCreateAnimation(transit: Int, enter: Boolean, nextAnim: Int): Animation? {
if (nextAnim == R.anim.enter_from_right || nextAnim == R.anim.exit_to_right) {
ViewCompat.setTranslationZ(view, 1f)
} else {
ViewCompat.setTranslationZ(view, 0f)
}
return super.onCreateAnimation(transit, enter, nextAnim)
}
Recommend implementing this as a base Fragment class for all your fragments.
I've also got stuck with that problem. So instead of using transaction.replace(containerId, newFragment) I've created two containers for fragments and now my code looks like this one
Add first fragment:
transaction.add(containerId1, firstFragment).commit();
Add second fragment with animation over the first one:
findViewById(containerId2).bringToFront();
transaction.setCustomAnimations(R.anim.slide_in_up,
R.anim.stay).remove(oldFragment).add(containerId2, newFragment).commit()
You can use androidx.fragment.app.FragmentContainerView as a fragments container. It automatically handles z order for animations specified in setCustomAnimations()

How can I change the size of icons in the Tree control in Flex?

I embed SVG graphics in my Flex application using
package MyUI
{
public class Assets
{
[Embed(source="/assets/pic.svg"]
[Bindable]
public static var svgPic:Class;
}
}
and then extending the Tree class with some of my own code, setting the icon upon adding a node to the data provider:
public class MyTree extends Tree
{
public function MyTree()
{
// ...
this.iconField = "svgIcon";
// ...
this.dataProvider = new ArrayCollection;
this.dataProvider.addItem({ /* ... */ svgIcon: MyUI.Assets.svgPic /* ... */ });
// ...
}
}
Now I have two things I want to do:
use the SVG graphics in multiple places in the app, scaling them to the appropriate size for each appearance, i. e. scale them to a proper icon size when using them in the tree
change the size of the icon at runtime, e. g. display a slightly larger icon for selected items or let an icon "pulse" as a response to some event
I read the Flex documentation on the 9-slice scaling properties in the Embed tag, but I think that's not what I want.
Edit:
I unsuccessfully checked the "similar questions" suggested by SO, among others this one:
Flex: Modify an embedded icon and use it in a button?
Subclass mx.controls.treeClasses.TreeItemRenderer and make it resize the icon to your desired dimensions, or create your own item renderer implementation by using the same interfaces as TreeItemRenderer. Set a custom item renderer with the itemRenderer property:
exampleTree.itemRenderer = new ClassFactory( ExampleCustomItemRendererClass );
The answer to this question might point you in the right direction, without knowing more about the trouble you're having:
Flex: Modify an embedded icon and use it in a button?
Hope it helps!

Resources