Java Question b4a replaces ContentView when Activity subclass used

warwound

Expert
Licensed User
Longtime User
I'm trying to wrap the PanoramaGL library.
This requires that the PLView class is used instead of the 'standard' android Activity class.
PLView extends Activity.

So in my b4a activity module i use the attribute:

B4X:
#Extends: com.panoramagl.PLView

I'm unable to get any panorama to display.
Even reproducing the very simple java example in b4a displays nothing.
This is the very simple java example:

B4X:
@Override
public void onCreate(Bundle savedInstanceState){
     super.onCreate(savedInstanceState);
     PLSpherical2Panorama panorama = new PLSpherical2Panorama();
     panorama.setImage(new PLImage(PLUtils.getBitmap(this, R.raw.spherical_pano), false));
     this.setPanorama(panorama);
}

I created a new java android project using Android Studio, imported the android library files and tried the above example.
I see a panorama displayed.

I think the problem is that b4a uses the PLView but set's a BALayout as the activity's 'ContentView', my compiled b4a activity source code shows in onCreate():

B4X:
@Override
public void onCreate(Bundle savedInstanceState) {
	super.onCreate(savedInstanceState);
	if (isFirst) {
		processBA = new BA(this.getApplicationContext(), null, null, "uk.co.martinpearman.b4a.panoramaglexample", "uk.co.martinpearman.b4a.panoramaglexample.main");
		processBA.loadHtSubs(this.getClass());
		float deviceScale = getApplicationContext().getResources().getDisplayMetrics().density;
		BALayout.setDeviceScale(deviceScale);
		
	}
	else if (previousOne != null) {
		Activity p = previousOne.get();
		if (p != null && p != this) {
			BA.LogInfo("Killing previous instance (main).");
			p.finish();
		}
	}
	processBA.runHook("oncreate", this, null);
	if (!includeTitle) {
		this.getWindow().requestFeature(android.view.Window.FEATURE_NO_TITLE);
	}
	if (fullScreen) {
		getWindow().setFlags(android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN,   
				android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN);
	}
	mostCurrent = this;
	processBA.sharedProcessBA.activityBA = null;
	layout = new BALayout(this);
	setContentView(layout);
	afterFirstLayout = false;
	BA.handler.postDelayed(new WaitForLayout(), 5);
}

b4a is i think overwriting the GLSurfaceView created by PLView.
No exceptions are raised and my b4a activity silently fails to render it's panorama.
Instead i see just the empty BALayout.

Is there a solution for this?
Can i prevent b4a from calling setContentView?
 

thedesolatesoul

Expert
Licensed User
Longtime User
Can you try to do it using inline java?
In Activity_Create call your inline java sub for onCreate?
I remember there is a hook for oncreate, it seems it runs before contentview is set, so im guessing if you call your java/oncreate sub after this it will override whatever the autogen code did.
 

Erel

B4X founder
Staff member
Licensed User
Longtime User
I agree. You should call it from the Activity_Create event. You should probably do something as done in the IME library:
B4X:
ExtendedBALayout e = new ExtendedBALayout(ba.context, eventName, ba);
     ba.activity.setContentView(e);
     BALayout.LayoutParams lp = new BALayout.LayoutParams();
     lp.height = ba.vg.getLayoutParams().height;
     lp.width = ba.vg.getLayoutParams().width;
     ba.vg.setLayoutParams(lp);
     e.addView(ba.vg);
 

warwound

Expert
Licensed User
Longtime User
I couldn't understand where Erel's code was meant to be executed?
In the inline java _onCreate() callback?

Anyway i found a better solution...

PLView extends Activity, and the b4a activity must use PLView.
I sub-classed PLView overriding it's setContentView method:

B4X:
public class B4APLView extends PLView {
	
	@Override
	public void setContentView(View view) {
		if(view instanceof BALayout){
			BA.LogInfo("converting setContentView to addContentView");
			this.addContentView(view, new BALayout.LayoutParams(0, 0, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
		} else {
			//	this never gets executed
			BA.LogInfo("allowing call to setContentView");
			super.setContentView(view);
		}
	}
}

My b4a activity now uses B4APLView instead of PLView.
You can see that i intercept the call which sets the BALayout as the 'content view' and add the BALayout as the content view instead of setting it.
This leaves the PLView's GLSurfaceView intact and my b4a now correctly displays my panorama image.

I added a Panel in my b4a activity Activity_Create and sized it using 50%x, 50%y - it properly sizes itself to half of the screen's width and height.
I added a Button to the Panel and added a Log statement to the Button Click callback - the callback is executed when the Button is clicked.
So all b4a stuff seems to work normally and the panorama view is working too :).

Thanks all.

Martin.
 

thedesolatesoul

Expert
Licensed User
Longtime User
Interesting, informative and clever!
I feel its a bit convoluted and like the layers of an onion. Had the B4A activity also overrided setContentView you couldnt do this.
The B4A generated code is just creating a layout and setting it to the contentview, do you think it was necessary to do the 'addContentView'?
Thanks.
 

warwound

Expert
Licensed User
Longtime User
do you think it was necessary to do the 'addContentView'?

LOLS i didn't elaborate in my last post but will do so now...

If my setContentView() did nothing with the BALayout then the entire b4a activity 'stalls'.

When the BALayout 'has layout', sometime after it is added to the View hierachy, it's onLayout callback is called.
When that callback is called, b4a is listening and now continues to initialize the activity.
b4a waits for that onLayout to be called before it itself calls the Activity_Create callback.

If my setContentView() method silently ignored the BALayout passed to it then the BALayout's onLayout callback isn't called, the b4a code waiting for the onLayout callback never gets executed and the Activity_Create never gets called.

Even if Activity_Create was called you'd not be able to display any Views or Layouts as there'd be no BALayout for BA to add them to.

addContentView so far appears to work perfectly - i have the traditional b4a BALayout to use for any UI elements and my GLSurfaceView accelerated panorama works nice n smooth.
 

thedesolatesoul

Expert
Licensed User
Longtime User
wow what a spaghetti!
but its really insane how you managed to make it work...im guessing trial and error...but it truly boggles my mind
i went back to a generated b4a activity to see whats going on...you are right, by the time _activity_create is called we are in afterFirstLayout.

So what you have done is im guessing that PLView also calls setContentView method but within its own class, so that gets set there. And when B4AActivity calls setContentView the one in B4APLView gets called. Somehow by that time you are sure that the panoramaview has created and been added to the contentView, thats why you use addContentView for the BALayout.

What I would have thought to do, is to let onCreate run, and in _activity_create, i'd call a native injava method and created and called this.setpanorama there. (with the disadvantage that now this.setpanorama calls this.setContentView thus we lose the BALayout if we dont want to modify the PLView class). But also possibly add another BALayout to the activity as well (although i dont know if that requires some WaitForLayout again).
 

warwound

Expert
Licensed User
Longtime User
Look at the code in post #4, see the comment "this never gets called"?
Only b4a calls setContentView, the PanoramaGL libraries doesn't call setContentView.
I think PanoramaGL simply gets the default content view and makes it into a GLSurfaceView.

If there was a call to setContentView that i did not want to block then you'll see that super.setContentView is used to call the default super class setContentView method instead of my overridden setContentView method.

So PanoramaGL at no point overwrites the default android content view, it's only b4a that tries to overwrite the default content view.
 

thedesolatesoul

Expert
Licensed User
Longtime User

warwound

Expert
Licensed User
Longtime User
Ah yes you're right - PLView does call setContentView.

I was looking in the logs only until the b4a Activity_Create callback had been executed and at that point had not added a panorama to the PLView.
 
Top