Android losing touch events when changing tabs - java

I have a TabActivity with 2 tabs that contain a control that responds to Scale and Scroll events.
When I fire up my app to begin with it starts on Tab 1 and I can scale and scroll the view shown on the 1st Tab's activity.
When I switch to Tab 2 again I can scale and scroll the view shown on the 2nd tab's activity.
All looks good.
When I switch back to the 1st tab suddenly I can no longer scale or scroll. If i switch back to the 2nd again then that one works fine. Its as if the touch focus is being taken by the view on the 2nd tab and never being surrendered.
I've even tried re-initialising my GestureDetectors in case they somehow get destroyed when one Activity gets backgrounded.
I've tried the following code to no avail:
public void onPause()
{
super.onPause();
mView.clearFocus();
}
public void onResume()
{
super.onResume();
mView.requestFocus();
mView.InitGestureDetectors( this );
}
Where InitGestureDetectors simply does the following:
public void InitGestureDetectors( Context ctx )
{
mScaleGestureDetector = new ScaleGestureDetector( ctx, new ScaleGestureListener() );
mGestureDetector = new GestureDetector( ctx, new GestureListener() );
}
Can anyone tell me how I can get my touch gestures working again when i subsequently switch tabs?

I've eventually tracked down the source of my problems. This was being caused by a custom OnTabChangeListener used to create a transition animation when you change tabs. This caused, for some reason, the visibility state to not get set to GONE when a view moved into the background. So i added a custom AnimationListener to set the visibility flag appropriately in onAnimationEnd and onAnimationStart.
outAnim.setAnimationListener( new Animation.AnimationListener()
{
public void onAnimationEnd( Animation animation )
{
outView.setVisibility( View.GONE );
}
public void onAnimationRepeat( Animation animation )
{
}
public void onAnimationStart( Animation animation )
{
inView.setVisibility( View.VISIBLE );
}
} );
and now my touch events are being handled as I would expect!

Related

Android Non Touch Consuming Dialog

How would I create a dialog which does not consume touch events. Ie. I want to be able to interact with the underlying activity as normal.
public class LoadingDialog extends Dialog {
public LoadingDialog(Context context) {
super(context);
setContentView(R.layout.loading_dialog);
setCanceledOnTouchOutside(false);
getWindow().setBackgroundDrawable(new
ColorDrawable(android.graphics.Color.TRANSPARENT));
}
}
I tried the following with no success
#Override
public boolean dispatchTouchEvent(#NonNull MotionEvent ev) {
return false;
}
Thanks
Rather than use a dialogue, you can use a view that covers the whole screen with a tint and another view that looks like a dialogue on top of the tint and pass all touch events to the view below. Remove and show the dialogue by toggling the visibility of the view.

How to animate an Android View only once on startup?

I am currently working on a very simple UI for my Android App. My goal is to animate some (I don't know how many yet) buttons on startup and NEVER AGAIN.
So following the official docs, reading java doc and searching on stackoverflow aswell, I finally got it work. Here's what I do with a single test view.
Set the View and the Animation in the OnCreate() method.
private TextView test_text;
private Animation test_anim;
...
protected void onCreate(Bundle savedInstanceState) {
...
test_text = (TextView) findViewById(R.id.text);
test_anim = AnimationUtils.loadAnimation(getApplicationContext(), R.anim.test_animation);
}
Start the Animation in the OnWindowFocusChanged() method.
#Override
public void onWindowFocusChanged(boolean hasFocus) {
test_text.startAnimation(test_anim);
}
This procedure works, the animation is executed when the activity starts, the only problem is that the onWindowFocusChanged() method is called everytime the activity changes status. So the text animates when the app is resumed, when the layout rotates and stuff like that.
So, repeating: My goal is to animate the text only ONCE when the app boots up and then stop forever.
If it helps, I already tried to put the Animation start in other methods like onStart() or onResume(), but the issue remains the same.
You can use SharePreferences, to check a boolean value. If is true or not exists means is first launch or you can animate app in onWindowFOcusChange() method. Set it to false to never aniamte again.
////////////////////////////
/// CONSTANTS
////////////////////////////
private static final String PREF_NAME = "pref_name";
public static final String IS_STARTUP = "is_startup";
////////////////////////////
/// FIELDS
////////////////////////////
private SharedPreferences settings;
#Override
public void onWindowFocusChanged(boolean hasFocus) {
settings = getSharedPreferences(PREF_NAME, MODE_PRIVATE);
if (settings.getBoolean(IS_START_UP, true)) {
test_text.startAnimation(test_anim);
settings.edit().putBoolean(IS_START_UP, false).commit();
}
}
In case you want to aniamte again when app starts next time, you can set the pref IS_START_UP to true when exit the application.
if (!settings.getBoolean(IS_START_UP, false)) {
settings.edit().putBoolean(IS_START_UP, true).commit();
}
Use SharedPreference to store a boolean variable & make it to true immediately after first animation & check this each time before animation starts.
if(!isAnimatedAlready){
animate();
setIsAnimated(true);
}else{}
Simply you can add a boolean variable with initial value true and after first time you can change its value to false and inside onfocus you can add another condition
If(boolean){do the animation;
boolean=false;}
this will do want you want but if you want the animation to be once during the application life cycle you can use shared prefs or simply add a static Boolean variable in application class

How would I do this Fragment animation on API level 9 with nineoldandroids?

I am trying to execute an animation in which I have two fragments stacked on top of each other.
The top fragment is a details fragment.
the bottom fragment is a menu list view fragment.
I did this by creating two overlapping framelayouts in the activity layout. I want to be able to do an animation in which the background fragment would be revealed in a fashion similar to a door opening leaving only 20 percent of the edge of the top fragment in view.
I tried doing this animation with the standard view animation library available to API 9 but it seemed that only the pixels were moved and but the touch mapping still corresponded to the top fragment and the bottom menu fragment could not be accessed.
So I downloaded the nineoldandroids library and tried to user AnimatorSet with ObjectAnimators to do the animation... except this time when the fragment is animated away it reveals only a gray background rather than the fragment in the back like before.
This is a code snippet on how I tried to implement a simple translation to reveal the background fragment
private void animateFragmentOut() {
activeFragment = (Fragment)getSupportFragmentManager().findFragmentById(R.id.nav_item_fragment_container);
View myView = activeFragment.getView();
AnimatorSet set = new AnimatorSet();
set.playTogether(
ObjectAnimator.ofFloat(myView, "translationX", 0, 230)
);
set.setDuration(500).start();
}
Why is the background fragment not shown when I use this animation?
How do I use nineoldandroids to reveal the background fragment properly?
I ended up solving this problem by using a listener to inflate the background view right at the start of the animation using the following code
private void animateFragmentOut() {
activeFragment = (Fragment)getSupportFragmentManager().findFragmentById(R.id.nav_item_fragment_container);
View myView = activeFragment.getView();
Animator.AnimatorListener listener = new AnimatorListenerAdapter() {
#Override
public void onAnimationStart(Animator animation) {
launchNavigationFragment();
}
#Override
public void onAnimationEnd(Animator animation) {
}
};
ObjectAnimator rotateY = ObjectAnimator.ofFloat(myView,"rotationY",-15f);
ObjectAnimator scaleX = ObjectAnimator.ofFloat(myView,"scaleX",0.8f);
ObjectAnimator scaleY = ObjectAnimator.ofFloat(myView,"scaleY", 0.8f);
ObjectAnimator translateX = ObjectAnimator.ofFloat(myView,"translationX",400f);
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(rotateY,scaleX,scaleY,translateX);
animatorSet.addListener(listener);
animatorSet.setDuration(700);
animatorSet.start();
}
Saldy despite this working great on API levels 11> and up on my API 9 device I am still having the same problem I had with the standard animation library. The view pixels translate but the touch areas stay mapped in the same place. So I cannot interact with the background navigation menu fragment.

How to draw a view on top of everything?

I want to make an app that can create notification on the screen on top of anything that is currently being displayed. Something like the Go SMS message popup or something like the ChatHead in the following picture:
It would be even better if it is possible to draw it dynamically including touch events.What is the conventional or standard way to do this?
Example:
Like an Icon that can be clicked or dragged no matter whether you are on home screen or app drawer or other apps.Pay attention to the circular icons near the edges of the screen in the picture posted. You can drag them anywhere in any app.
What you are looking for is System Alert Window.
There's a library called StandOut! which will assist you in creating such apps.
Here is how things like Toast and dialog windows work:
In the case where just adding or bringing to front does not work, say when you are having a service add its own view to another client activity or application (FaceUnlock does this), or you cannot depend on hierarchies, you need to use the window manager and a window token to do the trick. You can then create layouts and take advantage of animations and hardware acceleration as before.
WindowManager windowManager = (WindowManager) getBaseContext().getSystemService(Context.WINDOW_SERVICE);
WindowManager.LayoutParams layoutParams = new WindowManager.LayoutParams(WindowManager.LayoutParams.FIRST_SUB_WINDOW);
layoutParams.width = 300;
layoutParams.height = 300;
layoutParams.format = PixelFormat.RGBA_8888;
layoutParams.flags =
WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
| WindowManager.LayoutParams.FLAG_HARDWARE_ACCELERATED;
layoutParams.token = getWindow().getDecorView().getRootView().getWindowToken();
//Feel free to inflate here
mTestView = new View(this);
mTestView.setBackgroundColor(Color.RED);
//Must wire up back button, otherwise it's not sent to our activity
mTestView.setOnKeyListener(new View.OnKeyListener() {
#Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
onBackPressed();
}
return true;
}
});
windowManager.addView(mTestView, layoutParams);
Then be sure to remove the view onDestroy (or onPause) or you will crash
if (mTestView != null) {
WindowManager windowManager = (WindowManager) getBaseContext().getSystemService(Context.WINDOW_SERVICE);
if (mTestView.isShown()) {
windowManager.removeViewImmediate(mTestView);
}
}
You don't need a new activity to do this. All you need to do is to add another view into your existing activity and bring it to the front, and draw/write the things that you want into that view.
If you want to do special things with this extra view, you could create your own view class
class DrawOnTop extends View {
public DrawOnTop(Context activity) {
super(activity);
}
#Override
protected void onDraw(Canvas canvas) {
// put your drawing commands here
}
}
and then you do something like
DrawOnTop mDraw = new DrawOnTop(this);
addContentView(mDraw, new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT));
mDraw.bringToFront();
Then to force it to draw, you need to use mDraw.invalidate();
You could have the parent of your whole layout as RelativeLayout. The first child being the "root" of your main layout. Anything after that can be considered an overlay which is placeable to your whims.
Example:
<RelativeLayout>
<LinearLayout>
... Main Layout here ...
</LinearLayout>
<TextView left="20dip" top="20dip" text="Overlay" alpha="0.7" />
</RelativeLayout>
The best way is to start a service with your application.
Create an ImageView.
Set the LayoutParams of the Image View.
Add the view along with the params to the window manager when the service is created.
ALL SET
Your Image sticks to your window (At any screen over all apps), till you application is closed.
You can even add onclicklisteners and ontouchlisteners to the imageview.
Eg. OnClick listeners to perform some actions and Ontouchlisteners move the image along the screen.

OpenGL ES 2.0 - How can multiple views use the same renderer?

Right now I have my ApplicationActivity, this activity is responsible for managing multiple views (GLSurfaceViews). Can / Should I have all the views set the renderer to a "global" renderer?
Code:
public class ApplicationActivity extends Activity
{
private static final String TAG = ApplicationActivity.class.getSimpleName();
private final Stack<Screen> screens = new Stack<Screen>();
private GlRenderer glRenderer;
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
Log.d(TAG, "Main Activity Created");
setupGraphics();
ChangeScreen(new MainMenu(this, glRenderer)); //Creating a new Screen sets the renderer
}
private void setupGraphics()
{
requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN);
glRenderer = new GlRenderer(this);
}
public void Draw() //Is called by the glRenderer onDrawFrame() { mainActivity.Draw() }
{
}
}
Its the same activity switching between GLSurfaceViews and by my knowledge I believe that the method setRenderer sets the view renderer and then starts the rendering thread (creating a new thread) but I don't want to recreate the thread every time I switch between views - may create potential problems.
So in the end I want a Renderer class just to keep graphics sepreate from business logic and such but, I don't know if using one Renderer is even possible, without setting the thread again?
You can only use Multiple Views with the same Renderer only if you properly switch out between them with GLSurfaceView.onPause() / .onResume();
My specific case:
#Override
protected void onPause() //Overrides onPause from Activity
{
surfaceViews.peek().onPause();
super.onPause();
}
So everytime the activity pauses I would have to pause the current View. And if the Activity resumes then resume the View also.
I also have a method called SetView which will either (pause and remove then change to another View) or (pause and then change to another View) this is accomplished using a Stack
public void SetView(View screen)
{
if (!screens.empty())
{
screens.peek().onPause();
screens.pop();
}
screens.push(screen);
setContentView(screens.peek());
}
Of course though because we are using Views instead of Activities now we must Override the onBackPressed() to go back to previous Views.
#Override
public void onBackPressed()
{
if (screens.size() == 1)
super.onBackPressed();
else
{
screens.pop();
setContentView(screens.peek());
screens.peek().onResume();
}
}
By doing new GLRenderer() you create new instance of your class. So there is no problem to have the same renderer used in different activities.
EDIT: I seem to misunderstand your question - if you want many GL surfaces visible at once, then no, it is not possible. But it got nothing to do with reusing renderer code.

Categories