I have an applications that I’ve built to target SDK 3.0.
In this application there is a relative layout that has a full screen VideoView with a button underneath it. The video view has an onTouchListener set on it that is listening for a gesture.
After the video completes the VideoView is set to visibility(View.GONE) On devices running Honeycomb(Toshiba Thrive, and HTC Flyer) this functions as I would expect it to. Once the VideoView is GONE the button is clickable just fine.
However on a device running ICS (Motorola Xoom) when I try to press the button the event goes to the VideoView touch listener rather than the buttons click listener.
Was the way that Views are intended to behave in this situation changed purposely on ICS or is this a bug?
Additionally the first thing that I tried to solve this problem also gave me some strange results.
I added these lines instead of the setVisibility(View.GONE);
Log.i(MainActivity.myTag, "vid is null ? " + (mVideoView == null));
Log.i(MainActivity.myTag, "lyt is null ? " + (vidLyt == null));
vidLyt.removeView(mVideoView); //This line throws null pointer
thinking that if the VideoView gets removed from my parent layout it would certainly no longer be accepting touch events.
but this causes a null pointer on the removeView() call. Even though the Log statments above it indicate that both of the Objects are in fact not null. Here is the exception:
As far as I can tell it doesn’t actually point to any line in my activity, but these 3 statements are the only 3 getting called at this time and both Log statments complete successfully.
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): java.lang.NullPointerException
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.ViewGroup.dispatchDraw(ViewGroup.java:2488)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.View.getDisplayList(View.java:10415)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:2597)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.View.getDisplayList(View.java:10380)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:2597)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.View.getDisplayList(View.java:10380)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.ViewGroup.dispatchGetDisplayList(ViewGroup.java:2597)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.View.getDisplayList(View.java:10380)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.HardwareRenderer$GlRenderer.draw(HardwareRenderer.java:840)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.ViewRootImpl.draw(ViewRootImpl.java:1910)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:1634)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.view.ViewRootImpl.handleMessage(ViewRootImpl.java:2442)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.os.Handler.dispatchMessage(Handler.java:99)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.os.Looper.loop(Looper.java:137)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at android.app.ActivityThread.main(ActivityThread.java:4424)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at java.lang.reflect.Method.invokeNative(Native Method)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at java.lang.reflect.Method.invoke(Method.java:511)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
01-20 16:32:42.480: ERROR/AndroidRuntime(9416): at dalvik.system.NativeStart.main(Native Method)
EDIT: here are the relevant sections.
mVideoView.setOnTouchListener(new OnTouchListener() {
public boolean onTouch(View v, MotionEvent me){
if(me.getAction() == MotionEvent.ACTION_DOWN){
touchY = me.getY();
//I am using the if statement below to fix the unexpected behavior currently.
//by returning false even though the VideoView receives the touch, this
//causes it to essentiall ignore it and pass it along to the button.
if(mVideoView.getVisibility() == View.GONE){
return false;
}
}else if(me.getAction() == MotionEvent.ACTION_UP){
Log.i(MainActivity.myTag, "Old = " + touchY + " New = " + me.getY());
//Moved north a little is the gesture I am interested in.
if(me.getY() < (touchY - 100)){
Thread t = new Thread() {
public void run(){
timeStamp = mVideoView.getCurrentPosition();
mVideoView.stopPlayback();
//Since we are in back thread use a handler to GONEify the VideoView
animVidHandler.sendEmptyMessage(0);
//There is some network stuff here that justifies the use of a
//different thread. But I am certain it isn't affecting the
//behavior of my views.
}
};
t.start();
}
}
return true;
}
});
inside the handler callback is only 1 statment:
mVideoView.startAnimation(flyOffAnim); //translate animation to "fly off" the screen.
I forgot to mention the animation before. Could that have something to do with this behavior?
The flyOffAnimation has a completionListener set on it that only contains 1 statement:
mVideoView.setVisibility(View.GONE);
VideoViewis really aSurfaceView, which really isn’t a normalView, though it plays one on TV.You are not getting a
NullPointerExceptionfrom those three statements. You are getting aNullPointerExceptionelsewhere as a side effect of those three statements, perhaps. I don’t know why you would, in this case.Perhaps try switching to having both be peers in a
ViewFlipperand switching between modes that way.