I am displaying a FrameLayout with some components inside a Fragment. In onCreateView(..) I am animating the content of the FrameLayout and everything works fine. Now I want to animate the content before closing the Fragment.
In my current solution I am overriding onBackPressed() in the parent Activity and then I'm calling the method onBackPressed() inside my Fragment and animating the content there. The problem with this solution is, that I want to inflate the Fragment from various activities and then this is not really a nice solution... Does anybody know a better approach?
Thanks for your help!
Note:
I also tried to override onCreateView() and onPause() but the animation is not shown if I start it in those methods
and the following method does not fulfill my requirements either as it animates the whole fragment and I want to animate the content
getActivity().getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up, R.anim.slide_out_down, R.anim.slide_in_down)
Maybe you can try to handle onBackPressed in your fragment like below:
yourRootLayout.setFocusableInTouchMode(true);
yourRootLayout.requestFocus();
yourRootLayout.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){
animateMyViews();
return true;
}
return false;
}
});
I wanted to perform sliding exit animation before dismissing the view/fragment.
These are the steps I performed:
I created a runnable task which can dismiss the current screen.
Passed that runnable to animating view.
Used view.postOnAnimationDelayed(runnable, 400) so that it can animate and execute the runnable after 400 milliseconds.
Also I made sure that my animation duration is >= 400 so that the transition is smooth.
Below is a little bit altered version of code for idea.
I used view.postOnAnimationDelayed(runnable, 400) to
Dialog dialog = new Dialog(getActivity(), getTheme()) {
#Override
public void onBackPressed() {
ParentFragment.this.onBackPressed();
Runnable runnable = new Runnable() {
#Override
public void run() {
//Referencing this class in runnable
getInstance().dismiss();
}
};
//webView is the child view loaded on this fragment
if(webView != null && webView.webViewClient != null) {
webView.webViewClient.animateClose(webView, runnable);
} else {
super.dismiss();
}
};
function animateOnClose in webViewClient looked like this:
public void animateClose(final WebView view, Runnable runnable) {
Animation anim = AnimationUtils.loadAnimation(getMainActivityContext(),
animationResource);
view.startAnimation(anim);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
view.postOnAnimationDelayed(runnable, 400);
// you can also try view.postOnAnimation(runnable);
} else {
runnable.run();
}
}
I think what you need is to notify the Fragment that you are about to destroy it.
A crude pseudocode might look like
myFragment.aboutToClose();
While in your Fragment's aboutToClose() method.
public void aboutToClose()
{
// Perform all the animations you want.
// Don't forget to add onAnimationEnd() call back.
onAnimationEnd()
{
// Notify Activity that Animations have completed.
callback.animationsCompleted();
}
}
Finally in your calling Activity.
public void animationsCompleted()
{
// Destroy fragment.
getActivity().getSupportFragmentManager()
.beginTransaction()
.setCustomAnimations(R.anim.slide_in_up, R.anim.slide_out_up, R.anim.slide_out_down, R.anim.slide_in_down);
}
Related
I was using MVP in a previous application with my View component essentially an RelativeLayout.
Anytime I would want to block any touch interaction on the RelativeLayout (for example while a network access) I would return true from touchIntercept like this.
public abstract class RootView<T> extends RelativeLayout implements BaseView<T> {
#Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
if (mIsScreenLocked)
return true;
else
return super.onInterceptTouchEvent(ev);
}
#Override
public void showProgress(boolean show, boolean lockScreen) {
ProgressBar progressBar = ((ProgressBar) findViewById(R.id.progress_bar));
if (progressBar != null) {
if (show) {
progressBar.setVisibility(View.VISIBLE);
} else {
progressBar.setVisibility(View.GONE);
}
}
mIsScreenLocked = lockScreen;
freezeBottomBar(show);
}
}
In my new application my views are Fragments , the base of which extends a fragment
public abstract class BaseFragment
{
}
I would like to achieve something similar , to block all touch interactions on the fragment when a user initiates any network access.
public abstract class BaseFragment
{
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mIsScreenLocked = true;
initializeControls();
attachListeners();
if (savedInstanceState == null)
onScreenInitializationComplete(getArguments());
else
onScreenInitializationComplete(getArguments(), savedInstanceState);
***createTouchInterceptor***(view);
//should we run ignition here?
}
private void createTouchInterceptor(View fragmentView) {
fragmentView.setOnTouchListener((view, motionEvent) -> {
if (mIsScreenLocked)
return true;
else
return false;
});
}
}
This ofcourse wont work , since when a button is pressed on the fragment , the button would receive the touch.
Any ideas?
I am not sure that this is the best idea from a UX point of view, since the user doesn't really know why his/her actions are doing nothing, but for the sake of answering the question you can either
Use the same solution as you did for the RelativeLayout with the top container ViewGroup in your fragment's view.
disable setEnabled(false) only the "clickable" views, like buttons...etc, other views like TextViews and such, don't need to be disabled
Use an overlay transparent view, a view that is fully transparent over your root view that consumes the events in case of your fragment being disbaled.
However, you might want to consider a different approach such as
Showing a blocking progress dialog
Only disabling the button in question and let the user touch other views
I want my fragment to not receive any clicks on the views while the fragment transition animation is not yet finished. It is just a simple fade. But things get wonky when I immediately press any view while the next fragment is fading in.
Any thoughts how to achieve this?
This is actually used in my own app. The idea is very simple, it just works, but needs quite a lot of additional coding.
The idea is very simple, use a boolean variable to maintain whether the screen should be locked, let's call it screenLocked. I do not actually block the click, but let the click do nothing.
For those actions which takes time, set screenLocked to true before start working, and set it back to false when the task is finished. Also you have to add checking on screenLocked before any action is done.
Another difficulty of the this method is that you need to have clear end point of your tasks. Using Fragment transition as an example, if the backstack is poped, there has no actual callback notifying you, for this case. To handle this, I would set another flag releaseOnResume before starting Fragment transition, and in onResume, I would use this flag to check if I should set screenLocked back to false.
Other solutions I have tried but not used:
Before I settled with the method I just mentioned, I have tried setEnabled, setClickable, or any UI based blocking, e.g. add a FrameLayout on top and capture all touch events.
These methods are not bad, especially given that they are easy to implement.
The only problem is that, onClick events can be queued due to double tapping, when you are handling the first onClick event, actually there could be another one queued up, even if you do any UI changes immediately to block any further clicks, you can't stop the next onClick event to come because it is queued already.
Hope this helps.
I use a countdown timer.
I manage this through the ontouch listener.
I create a method that manages the creation of the timer. I call it in the ontouch event. I use two methods (this is optional, but good for extensibility) to handle button enabling and disabling. I then use these methods with the timer to enable and disable the button.
See my code snippet.
In oncreate:
#Override protected void onCreate(Bundle savedInstanceState) {
/.../
button.setOnTouchListener(new View.OnTouchListener() {
#Override public boolean onTouch(View v, MotionEvent event) {
disableButton(button);
countDwn1();
/... time to do whatever you need..
// custom methods...
fragment = new MyFragAddFragment();
replaceFragment(fragment);
return false;
}
});
Methods:
public void countDwn1() {
CountDownTimer countDownTimer = new CountDownTimer(2000, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
enableButton(button);
}
}.start();
}
public void disableButton(Button button) {
button.setEnabled(false);
}
public void enableButton(Button button) {
button.setEnabled(true);
}
You can extend this method to include passing the button as a parameter into the timer, for extensibility.
In the end I used something like this. I created a parent class for all my fragments and overriden the OnCreateAnimation method which is called on every animation.
#Override
public Animation onCreateAnimation(int transit, boolean enter, int nextAnim) {
//Check if the superclass already created the animation
Animation anim = super.onCreateAnimation(transit, enter, nextAnim);
//If not, and an animation is defined, load it now
if (anim == null && nextAnim != 0) {
anim = AnimationUtils.loadAnimation(getActivity(), nextAnim);
}
//If there is an animation for this fragment, add a listener.
if (anim != null) {
anim.setAnimationListener(new Animation.AnimationListener() {
#Override
public void onAnimationStart(Animation animation) {
isAnimationFinished = false;
}
#Override
public void onAnimationEnd(Animation animation) {
isAnimationFinished = true;
}
#Override
public void onAnimationRepeat(Animation animation) {
}
});
}
return anim;
}
The isAnimationFinished variable is a public variable that can be used by the calling activity and the child classes
In our application we have one parent linear layout which contains another linear layout as child.Child linear layout is nothing but treating as dialog box with fixed width and height. As expected if i click on back key of device entire activity is getting removed from stack. I just want if dialog box is open then only dialog box should get removed i.e. only child linear layout should be gone.How to dismiss only view not the activity on back key press?
Please do needful help.
Thanks,
AA.
You need to override Activity.onBackPressed() and do what you need to do:
#Override
public void onBackPressed() {
if (mDialogView.getVisibility() == View.VISIBLE) {
mDialogView.setVisibility(View.GONE);
} else {
super.onBackPressed();
}
}
Well, you could override onBackPressed to something like:
#Override
public void onBackPressed() {
if(!closeDialogsAndStuff()) {
super.onBackPressed();
}
}
private boolean closeDialogsAndStuff() {
/**
* check if dialogs should be closed and if so, close them and return true
* otherwise return false
* */
return true;
}
Implement onBackPressed method(), and inside that method dismiss your dialog
#Override
public void onBackPressed()
{
// code here to show dialog
super.onBackPressed(); // optional depending on your needs
dialog.dismiss();
}
You can ask if you have any further queries :)
put this code in your activity
public boolean onKeyDown(int keyCode, KeyEvent event) { //if user click back from device
if (keyCode == KeyEvent.KEYCODE_BACK) {
if(dialog.isShowing()) dialog.dismiss(); //dismiss dialog if shown
else finish(); //finish current activity
}
return true;
}
I have a LinearLayout View in my activity.
When I press back button I want LinearLayout's children to slide out.
I have the following code which doesn't do anything:
private void SlideOut()
{
LayoutAnimationController controller = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_animation_row_slide_out);
Animation animation=controller.getAnimation();
animation.setFillAfter(true);
LinearLayout menuLayout =((LinearLayout)findViewById(R.id.menuLayout));
menuLayout.setLayoutAnimation(controller);
menuLayout.startLayoutAnimation();
}
#Override
public void onBackPressed(){
//super.onBackPressed();
SlideOut();
}
I have commented out super.OnBackPressed() to see if the animation starts, and it don't start.
Somebody with similar problems ?
check if SlideOut() is called in onBackPressed - i'm guessing your back press is being handled elsewhere - possibly by the virtual keyboard
try this.it may help you:
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
if(keyCode==KeyEvent.KEYCODE_BACK){
SlideOut();
return true;
}else{
return false;
}
//return super.onKeyDown(keyCode, event);
}
can u just change the code here here :
private void SlideOut()
{
Animation controller = AnimationUtils.loadLayoutAnimation(this, R.anim.layout_animation_row_slide_out);
// Animation animation=controller.getAnimation();
animation.setFillAfter(true);
LinearLayout menuLayout =((LinearLayout)findViewById(R.id.menuLayout));
menuLayout.startAnimation(controller);
}
I think you might be exiting the activity before animation is finished. Try implementing Animation Listener
I followed this tutorial http://www.andengine.org/forums/tutorials/multiple-screen-andengine-game-v2-t4755.html to create a simple application with multiple scene and only one activity.
I'd like to know how can i can return to the previous scene when i use the back button and finish the activity when i'm in the first scene.
I tried so in the MultiScreen Class:
#Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if ((keyCode == KeyEvent.KEYCODE_BACK)) {
this.mEngine.getScene().back();
}
return super.onKeyDown(keyCode, event);
}
replacing the core.getEngine().setScene(scene); in the SceneManager with this.mEngine.getScene().setChildScene(scene);
scene work differently from how I understood, I resolve with:
#Override
public void onBackPressed()
{
Scene scene = this.mEngine.getScene();
if(scene.hasChildScene()){
scene.back();
}
else{
this.finish();
}
}
You can override the back key in one of two ways, either by overriding the onBackPressed() method, or the dispatchKeyEvent() method
Overriding onBackPressed:
#Override
public void onBackPressed()
{
// your code here
}
Overriding dispatchKeyEvent:
#Override
public boolean dispatchKeyEvent(KeyEvent event)
{
if (keyCode == KeyEvent.KEYCODE_BACK)
{
// your code here
}
return (yourbooleanhere);
}
I had a dilemma like you in that sense. I explain. I have an activity like works with a ViewFlipper. I need to go to the different "layout" that I wrote on ViewFlipper tag and, when a I was in a particular "layout" that is not the main "layout" of the activity, I need to come back to the main pressing the back button, but, it doesn't work what I want.
To resolve this dilema, I override OnBackPressed function like:
#Override
public void onBackPressed() {
if (condition) {
// go to the main
} else {
super.onBackPressed();
}
}
where condition, as name says, how do you know that you are in the main "layout"? For example, in my ViewFlipper, the main "layout" are ordered by numbers, that begin on 0, so my main "layout" is 0 and how I know that I am in the 0 "layout", viewflipper.getDisplayedChild(). If it returns 0, I stay in the main "layout" or not, otherwise.
So simple but, I know, not so elegant. It's an idea, I think that can help you.