Mvvmcross Android Fragment (ShowViewModel) Custom Animation effect (swipe) - android-fragments

I have a problem with override Custom Animation on my Fragment. I have on my Home view three buttons (First, Second and Third) and when I'm inside this views I want swipe between those view and I need swipe animation effect from left to right and from right to left etc..
For Example my SecondFragments looks like this:
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame, true)]
public class SecondFragment : BaseFragment<SecondViewModel>, View.IOnTouchListener
{
private Easter _easter;
protected override int FragmentId => Resource.Layout.fragment_second;
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
var view = base.OnCreateView(inflater, container, savedInstanceState);
_easter = new Easter(new KonamiCode());
var easyEgg = new CustomEgg("Easy")
.WatchForSequence(Command.SwipeLeft(), Command.SwipeRight());
_easter = new Easter(easyEgg);=
_easter.CommandDetected += cmd => DoSwipe(cmd.Value);
var coreLayout = view.FindViewById<LinearLayout>(Resource.Id.coreLayout);
coreLayout?.SetOnTouchListener(this);
return view;
}
private void DoSwipe(string swipeText)
{
if (swipeText.Equals("LEFT"))
{
Activity.OverridePendingTransition(Resource.Animator.slide_to_right, Resource.Animator.slide_from_left);
}
if (swipeText.Equals("RIGHT"))
{
Activity.OverridePendingTransition(Resource.Animator.slide_to_left, Resource.Animator.slide_from_right);
}
ViewModel.SwipeView(swipeText);
}
public bool OnTouch(View v, MotionEvent e)
{
_easter.OnTouchEvent(e);
return true;
}
}
Method ViewModel.SwipeView looks like:
public override void SwipeView(string swipeText)
{
if (swipeText.Equals("RIGHT"))
{
Close(this);
UserDialogs.Instance.Toast("RIGHT SWIPE!");
ShowViewModel<FirstViewModel>();
}
if (swipeText.Equals("LEFT"))
{
Close(this);
UserDialogs.Instance.Toast("LEFT SWIPE!");
ShowViewModel<ThirdViewModel>();
}
}
I tried Activity.OverridePendingTransition for this but it doesnt work. I tried something with TransactionManager but still doesnt work. I need just override animations only for these three view no for whole app.
For example my test project is HERE on github.

I tried Activity.OverridePendingTransition for this but it doesnt work.
OverridePendingTransition allows you to specify a custom animation when starting an activity from outside the Context of the current top Activity. That means OverridePendingTransition method only be called immediately after one of the flavors of startActivity(Intent) or finish() to specify an explicit transition animation to perform next. So when you swipe between those Fragment, it has no effect.
Difference between Animator folder and Anim fodler.
An animation resource can define one of two types of animations: Property Animation and View Animation.
Property Animation
File Location:
Resource/animator/filename.xml
SYNTAX:
The file must have a single root element: either <set>, <objectAnimator>, or <valueAnimator>. You can group animation elements together inside the <set> element, including other <set> elements.
View Animation
File Location:
Resource/anim/filename.xml
SYNTAX:
The file must have a single root element: either an <alpha>, <scale>, <translate>, <rotate>, or <set> element that holds a group (or groups) of other animation elements (even nested <set> elements).
Since your animation use translate, I suggest you put these xml file in Resource/anim folder.
I need just override animations only for these three view no for whole app.
Since your base Activity is MvxCachingFragmentCompatActivityas, you can override OnBeforeFragmentChanging method to set a custom transition animation.
public override void OnBeforeFragmentChanging(MvvmCross.Droid.Shared.Caching.IMvxCachedFragmentInfo fragmentInfo, Android.Support.V4.App.FragmentTransaction transaction)
{
//Replace this animation with your own animation.
transaction.SetCustomAnimations(
// Your entrance animation xml reference
Resource.Animation.abc_fade_in,
// Your exit animation xml reference
Resource.Animation.abc_fade_out,
Resource.Animation.abc_fade_in,
Resource.Animation.abc_fade_out);
base.OnBeforeFragmentChanging(fragmentInfo, transaction);
}
SetCustomAnimations set specific animation resources to run for the fragments that are entering and exiting in this transaction. The popEnter and popExit animations will be played for enter/exit operations specifically when popping the back stack.
FragmentTransaction setCustomAnimations (int enter,
int exit,
int popEnter,
int popExit)
EDIT :
Custom animation for every swipe between Fragment, for every Fragment, you could custom animation like this :
[MvxFragment(typeof(MainViewModel), Resource.Id.content_frame, true)]
public class SecondFragment : BaseFragment<SecondViewModel>, View.IOnTouchListener
{
private void DoSwipe(string swipeText)
{
if (swipeText.Equals("LEFT"))
{
FragmentManager.BeginTransaction()
.SetCustomAnimations(Resource.Animation.slide_from_left, Resource.Animation.slide_from_right)
.Replace(Resource.Id.content_frame,ThirdFragment.NewInstance(null))
.Commit();
}
if (swipeText.Equals("RIGHT"))
{
...
}
ViewModel.SwipeView(swipeText);
}
}

They best way you could achieve this is by using a ViewPager that contains your fragments. I implemented the exact thing yesterday, you can look at this example on how to use this with MvvmCross.

Related

Navigation Drawer: how make fragments persistent (keep alive) while switching (not rotating)

With Fragment:setRetainInstance(true); the fragment is not re-instantiated on a phones orientation change.
And of course i want my fragments to be kept alive while switching from one fragment to another.
But the Android Studio 4 provides a wizard-template with only
DrawerLayout drawer = findViewById(R.id.drawer_layout);
NavigationView navigationView = findViewById(R.id.nav_view);
// Passing each menu ID as a set of Ids because each
// menu should be considered as top level destinations.
mAppBarConfiguration = new AppBarConfiguration.Builder(
R.id.nav_home, R.id.nav_gallery, R.id.nav_slideshow)
.setDrawerLayout(drawer)
.build();
NavController navController = Navigation.findNavController(this, R.id.nav_host_fragment);
NavigationUI.setupActionBarWithNavController(this, navController, mAppBarConfiguration);
NavigationUI.setupWithNavController(navigationView, navController);
From hours of debugging and searching the net if think it would need to inherent from the class FragmentNavigator so i can overwrite FragmentNavigator:naviagte where a new fragment gets created via final Fragment frag = instantiateFragment(.. and then is added with ft.replace(mContainerId, frag);
So i could find my old fragment and use ftNew.show and ftOld.hide instead.
Of course this is a stupid idea, because this navigate method is full of other internal stuff.
And i have no idea where that FrameNavigator is created.
I can retrieve it in the MainActivity:OnCreate with
NavigatorProvider navProvider = navController.getNavigatorProvider ();
Navigator<NavDestination> navigator = navProvider.getNavigator("fragment");
But at that time i could only replace it with my derived version. And there is no replaceNavigtor method but only a addNavigator method, which is called where ?
And anyways this all will be far to complicated and therefore error prone.
Why is there no simple option to keep my fragments alive :-(
In older Wizard-Templates there was the possibility of
#Override
public void onNavigationDrawerItemSelected(int position) {
Fragment fragment;
switch (position) {
case 1:
fragment = fragment1;
break;
case 2:
fragment = fragment2;
break;
case 3:
fragment = fragment3;
break;
}
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if(mCurrentFragment == null) {
ft.add(R.id.container, fragment).commit();
mCurrentFragment = fragment;
} else if(fragment.isAdded()) {
ft.hide(mCurrentFragment).show(fragment).commit();
} else {
ft.hide(mCurrentFragment).add(R.id.container, fragment).commit();
}
mCurrentFragment = fragment;
}
but i have no idea how to do this with the Android 4.0 template where my MainActivity is only derived as:
public class MainActivity extends AppCompatActivity {
private AppBarConfiguration mAppBarConfiguration;
Ideas welcome :'(
Hi there & sorry for my late answer! I had a similar problem with navigation drawers and navigation component. I tried around a little and found a working solution, which might be helpful for others too.
The key is the usage of a custom FragmentFactory in the FragmentManager of the MainActivity. See the code for this below:
public class StaticFragmentFactory extends FragmentFactory {
private myNavHostFragment1 tripNavHostFragment;
private myNavHostFragment2 settingsNavHostFragment;
#NonNull
#Override
public Fragment instantiate(#NonNull ClassLoader classLoader, #NonNull String className) {
if (MyNavHostFragment1.class.getName().equals(className)) {
if (this.myNavHostFragment1 == null) {
this.myNavHostFragment1 = new MyNavHostFragment1();
}
return this.myNavHostFragment1 ;
} else if (MyNavHostFragment2.class.getName().equals(className)) {
if (this.myNavHostFragment2 == null) {
this.myNavHostFragment2 = new MyNavHostFragment2();
}
return this.myNavHostFragment2;
}
return super.instantiate(classLoader, className);
}
}
The FragmentFactory survives the navigation between different fragments using the NavigationComponent of AndroidX. To keep the fragments alive, the FragmentFactory stores an instance of the fragments which should survive and returns this instance if this is not null. You can find a similar pattern when using a singleton pattern in classes.
You have to register the FragmentFactory in the corresponding activity by calling
this.getSupportFragmentManager().setFragmentFactory(new StaticFragmentFactory())
Please note also that I'm using nesten fragments here, so one toplevel fragment (called NavHostFragmen here) contains multiple child fragments. All fragments are using the same FragmentFactory of their parent fragments. The custom FragmentFactory above returns the result of the super class method, when the fragment to be instantiated is not known to keep alive.

Confused on ActionListeners

Hello fellow coders of the night,
I am stuck with a moral dilemma (well not moral, but mostly i don't know what to do).
Suppose I have one button that can do several actions, depending on the menu item which is chosen.
Basically, I've imagined this
private void menuButtonActionPerformed(ActionEvent b)
ActionEvent a
if(a.getSource()==menuItem)
if(b.getSource()==button)
do this and that
Is this the correct way to do this? because if it is I'd have to add ActionListeners on the menuItem but I get stuck with some stupid error code somewhere!
Thanks in advance for helping me!
Post Scriptum : #David, I've tried this, however the initial condition isn't verified.
private void buttonValidateActionPerformed(java.awt.event.ActionEvent evt)
ActionListener l = (ActionEvent e) -> {
if(e.getSource()==menuItemAdd)
{
System.out.println("eureka!");
buttonSearch.setEnabled(false);
if (evt.getSource()==buttonValidate)
{
DataTransac dt = new DataTransac();
dt.addCoders("...");
}
}
if(e.getSource()==itemDelete)
{
DataTransac dt = new DataTransac();
dt.deleteCoders("...");
}
};
menuItemAdd.addActionListener(l);
itemDelete.addActionListener(l);
That won't work; your listener will get a different invocation for each time the listener is used -- so the event source will be either a button or a menu item for a single invocation.
You'll need to respond to the menu item with one ActionListener that stores state, and then separately handle the button action. You could do this with one listener, but I wouldn't; I'd do this:
private MenuItem selected;
private class MenuItemListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
// if you really want to have one listener for multiple menu items,
// continue with the .getSource() strategy above, but store some
// state outside the listener
selected = (MenuItem)event.getSource();
// you could alternatively have a different listener for each item
// that manipulates some state
}
}
private class ButtonListener implements ActionListener {
public void actionPerformed(ActionEvent event) {
// take conditional action based on selected menu item, as you describe
// in the question
}
}
void setup() {
JMenuItem first = /* ... */;
JMenuItem second = /* ... */;
MenuItemListener listener = new MenuItemListener();
first.addActionListener(listener);
second.addActionListener(listener);
JButton button = /* ... */;
button.addActionListener(buttonListener);
}
Generally speaking this is the preferred approach -- use a different listener for each semantic action, rather than one that introspects the source. Your code will be cleaner, simpler, and easier to understand.
For the same reasons, some people prefer to use anonymous classes for Java event listeners. Here's a Gist that shows several syntaxes: https://gist.github.com/sfcgeorge/83027af0338c7c34adf8. I personally prefer, if you are on Java 8 or higher:
button.addActionListener( event -> {
// handle the button event
} );

How do I keep my Editor from scrolling below keyboard when moving the cursor?

I've got an Editor which is tall enough to accommodate multiple lines of input. The editor is at the bottom of the screen if it matters. Once multiple lines are entered, if you move the cursor up a line, the entire view shifts downward, so the line you just left is now obscured by the keyboard. I'd like it to not do this unless the line the cursor is on is either off screen or close to being off screen. Sort of like how the built in Android message app works. Here's what I mean in pictures.
Default state, everything looks good. We have 3 lines of input
I move the cursor up one line: notice that the entire view has shifted down one line and so "line 3" is now obscured. I don't want this behavior (it among other things hides some UI elements).
This is the Android messaging app. This is the behavior I want: when you move the cursor to that next line, the view doesn't just shift downward. If you have enough lines to scroll past the visible area, Android just shifts the text and not the entire view to accommodate it.
In essence, I just want the contents of the editor to shift (when appropriate) and not the entire window.
I managed to figure it out. Firstly, in MainActivity.cs, after the LoadApplication call, I added this:
Xamarin.Forms.Application.Current.On<Xamarin.Forms.PlatformConfiguration.Android>().UseWindowSoftInputModeAdjust(WindowSoftInputModeAdjust.Resize);
Using the default or .Pan didn't work at all. Note that just doing this didn't fix the issue. I also had to take the class that's described here and use it. Without that class, things didn't work correctly. Also the "alternative method" described on that page didn't work (which was basically just changing the soft input mode adjust).
In the event that the link I mentioned dies, here's the class. I did slightly modify it so that by default it is not enabled. You just set Disabled = false on it to enable it. Also you call the static method Init to initialize the class.
public class AndroidBug5497WorkaroundForXamarinAndroid
{
private readonly View _childOfContent;
private readonly FrameLayout.LayoutParams _frameLayoutParams;
private int _usableHeightPrevious;
public static AndroidBug5497WorkaroundForXamarinAndroid Instance { get; private set; }
private bool _disabled = true;
public bool Disabled
{
get => _disabled;
set
{
if (_disabled != value)
{
_disabled = value;
if (_disabled)
_childOfContent.ViewTreeObserver.GlobalLayout -= PossiblyResizeChildOfContent;
else
_childOfContent.ViewTreeObserver.GlobalLayout += PossiblyResizeChildOfContent;
}
}
}
public static void Init(Activity activity)
{
if (Instance != null)
return;
Instance = new AndroidBug5497WorkaroundForXamarinAndroid(activity);
}
private AndroidBug5497WorkaroundForXamarinAndroid(Activity activity)
{
FrameLayout content = (FrameLayout)activity.FindViewById(Android.Resource.Id.Content);
_childOfContent = content.GetChildAt(0);
_frameLayoutParams = (FrameLayout.LayoutParams)_childOfContent.LayoutParameters;
}
private void PossiblyResizeChildOfContent(object sender, EventArgs e)
{
int usableHeightNow = ComputeUsableHeight();
if (usableHeightNow != _usableHeightPrevious)
{
var usableHeightSansKeyboard = _childOfContent.RootView.Height;
var heightDifference = usableHeightSansKeyboard - usableHeightNow;
_frameLayoutParams.Height = usableHeightSansKeyboard - heightDifference;
_childOfContent.RequestLayout();
_usableHeightPrevious = usableHeightNow;
}
}
private int ComputeUsableHeight()
{
Rect r = new Rect();
_childOfContent.GetWindowVisibleDisplayFrame(r);
return (r.Bottom - r.Top);
}

Defining a custom UINavigationBar through subclassing removes navigation items

I'm trying to follow the standard approach to creating a custom UINavigationBar in order to change its background image, but have found an issue in the subclassing process. If I subclass UINavigationController, with the intent of overriding the virtual NavigationBar property to provide my own implementation, all navigation items (any left or right buttons, and the title view) disappear. At first I thought it was due to the background being rendered over top of the navigation items, but I can reproduce the problem with a no-op subclass.
It's reproducible with the following code:
[Register("NavigationBar")]
public class NavigationBar : UINavigationBar
{
public NavigationBar () : base()
{
}
public NavigationBar (NSCoder coder) : base(coder)
{
}
public NavigationBar (IntPtr ptr) : base(ptr)
{
}
public NavigationBar (NSObjectFlag t) : base(t)
{
}
public NavigationBar (RectangleF frame) : base(frame)
{
}
}
[Register("NavigationController")]
public class NavigationController : UINavigationController
{
private UINavigationBar _navBar;
public NavigationController () : base()
{
}
public NavigationController (NSCoder coder) : base(coder)
{
}
public NavigationController (IntPtr ptr) : base(ptr)
{
}
public NavigationController (NSObjectFlag t) : base(t)
{
}
public override UINavigationBar NavigationBar
{
get
{
if(_navBar == null)
{
return base.NavigationBar;
}
return _navBar;
}
}
public void SetNavigationBar(UINavigationBar navigationBar)
{
_navBar = (UINavigationBar)navigationBar;
}
}
Now, all you need to do to lose your navigation items is to use the custom classes instead of the default ones:
var navigationBar = new NavigationBar();
navigationBar.BarStyle = UIBarStyle.Black;
navigationBar.TintColor = HeaderColor;
var navigationController = new NavigationController();
navigationController.SetNavigationBar(navigationBar);
// ...
Well, your SetNavigationBar() method doesn't pass that down to the native base class and since you don't do any explicit drawing yourself, how is the native drawing code ever supposed to be invoked for your custom NavigationBar class?
In your example code, that NavigationBar is just floating around in space and never gets told to draw.
In order to subclass UINavigationBar, you must define the IntPtr constructor in your derived class and instantiate the UINavigationController using the public UINavigationController(Type navigationBarType, Type toolbarType) constructor. Example:
public class MyNavigationBar: UINavigationBar
{
public MyNavigationBar(IntPtr h) : base(h)
{
}
// Do something.
}
....
var navController = new UINavigationController(typeof(MyNavigationBar), typeof(UIToolbar));
Took me a while to figure it out. More information on this page: http://developer.xamarin.com/guides/ios/platform_features/introduction_to_ios_6/ in section Subclassing UINavigationBar.
Can you create a sample project where you're adding NavigationItems directly to a UINavigationController and then using the sub-classed UINavigationController/UINavigationBar causes these buttons to disappear?
Thanks,
ChrisNTR
After a lot of research and back and forth with Xamarin, the answer to this problem is that you must use an IB stub file that is essentially no-op, but exists to shuttle the desired base type for your navigation elements. There is a working example on my OSS project: http://github.com/danielcrenna/artapp

Remove + (Plus sign) next to GWT Tree's Root Item

I wondered if anyone knew whether it was possible to remove the '+' Sign that appears next to the root item in a GWT Tree? I didn't see a CSS rule to handle it. Can one replace the + sign with say, a GIF?
(Adapted code from GWT manual below, for illustrative purposes)
TreeItem root = new TreeItem("root"); // Wish to remove + sign from next to this item
root.addItem("item0");
root.addItem("item1");
root.addItem("item2");
root.setState(true, true);
// Add a CheckBox to the tree
TreeItem item = new TreeItem(new CheckBox("item3"));
root.addItem(item);
Tree t = new Tree();
t.addItem(root);
// Add it to the root panel.
RootPanel.get().add(t);
CSS styling does not apply to this at all. Rather, it requires using a different constructor for tree.. If you set spacer.png to a 1x1 transparent PNG it will remove the + signs. Here is the full code I used.
The correct way to do it is as follows:
public interface MyResources extends ClientBundle {
public static final MyResources INSTANCE = GWT.create(MyResources.class);
#Source("spacer.png")
public ImageResource leaf();
}
public class TreeImagesExample implements Tree.Resources
{
#Override
public ImageResource treeClosed() {
// TODO Auto-generated method stub
return MyResources.INSTANCE.leaf();
}
#Override
public ImageResource treeLeaf() {
// TODO Auto-generated method stub
return MyResources.INSTANCE.leaf();
}
#Override
public ImageResource treeOpen() {
// TODO Auto-generated method stub
return MyResources.INSTANCE.leaf();
}
}
Main Function:
TreeImagesExample tx= new TreeImagesExample();
Tree t = new Tree(tx);
t.addItem(root);
For this to work properly, we will also need a SelectionHandler, of course, so that we can register tree clicks on the tree items rather than the (now non-existent) + signs.
You'll have to use CSS to override the plus icon image, which is set as the background image of the item with a left-margin to move the text off of it.
The root item doesn't have its own specific style so you'll have to add a style name (i.e., CSS class) on the tree item yourself and remove the background image yourself, in CSS.

Resources