calling bringChildToFront() from a surfaceView within a frameLayout doesn't work? - android

Can you add a surfaceView child to a frameLayout, and then call bringChildToFront() to bring another child view in front of the surfaceView?
This is my first android app I've made, so please let me know if my setup makes any sense or if i'm doing the wrong things in the wrong places.
I have one activity, which has a FrameLayout.
I have 2 views (named pauseMenuView and mainMenuView) that are simply Views that have 4 buttons. These menu buttons are handled by overriding onClick() inside my activity.
I have a 3rd custom view (called gameView) that extends SurfaceView. This is where I am currently processing almost everything about my gameplay (handling touch events, drawing graphics, animation, collision detection). I have a seperate thread for controlling the state (paused, unpaused, etc.) and framerate of the game.
So basically, while playing the game, I have a pause icon in the top left corner of the screen. I'm trying to make it so that when they touch that icon, my gameView's onTouchEvent calls bringChildToFront(pauseMenuView) to display the pause menu. However, currently when I call bringChildToFront() or bringToFront(), nothing happens as if they have no effect. My gameView is still visible.
Here's the relevant code from my activity:
public static final int INDEX_MAIN_MENU_VIEW = 0;
public static final int INDEX_PAUSE_MENU_VIEW = 1;
public static final int INDEX_GAMEVIEW_VIEW = 2;
fl = new FrameLayout(this);
LayoutParams lp = new LayoutParams(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
fl.setLayoutParams(lp);
mainMenuView = View.inflate(this, R.layout.main_menu,null);
pauseMenuView = View.inflate(this, R.layout.pause_menu, null);
fl.addView(mainMenuView, INDEX_MAIN_MENU_VIEW);
fl.addView(pauseMenuView, INDEX_PAUSE_MENU_VIEW);
setContentView(fl);
View onePlayerButton = findViewById(R.id.onePlayer_button);
onePlayerButton.setOnClickListener(this);
/* then i set up a bunch of pauseMenu and mainMenu buttons the same way, so I won't put them here */
fl.bringChildToFront(mainMenuView);
So when the app starts, the mainMenuView is brought to the front, and when the player clicks the onePlayerButton, a gameView gets created and added to the frameLayout, brought to the front, and the user starts playing the game.
gv = new GameView(this, 1);
fl.addView(fv, INDEX_GAMEVIEW_VIEW);
fl.bringChildToFront(gv);
fl.invalidate();
Then, when in game, when the user clicks the pause icon in my gameView, the onTouchEvent() method catches it and I do this. (I just made my pauseMenuView public so my gameView could see it)...It seems like my program goes straight to the mThread.pause() line and doesn't do anything with the first 2 lines... I've tried setting the gameView to invisible here, and when I do that, the screen is completely black, but I can click where the pause buttons WOULD be if the pause menu was visible, and they actually do get detected by my activity's onClick()! What is going on here?
((TestGame)getContext()).fl.bringChildToFront(((TestGame)getContext()).pauseMenuView);
((TestGame)getContext()).pauseMenuView.bringToFront();
mThread.pause();

Related

Android: Z-Order with a screen-wide clickblocker-view

Edit: Solved. The problem disappeared when I made all my custom views override Button instead of View.
I'm trying to programmatically create a screen-sized invisible view which should capture all click events, thereby blocking all underlying elements. But I'm having some trouble.
The intended goal is that when the user clicks a certain button, a menu is created. While this menu is up, the other buttons that were on the page should no longer function. Clicking on the menu should make stuff happen, clicking anywhere else should make the menu disappear and make the other elements work again.
I've managed to create the screen wide view just fine, and it captures clicks and destroys the menu whenever the user clicks a point that is not already covered by a button. When the user does click on a spot that contains a button however, only the button's onclickevent is handled, and not that of my clickblocker. It should be the other way around.
This leads me to think it's a z-order problem (but not 100% sure).
Unfortunately I'm trying to target older versions, which don't have support for view.setZ(), and view.bringToFront() doesn't appear to do what I want it to do.
Here's the code for the viewblocker:
public class ClickBlockerView extends View{
public ClickBlockerView(Context context) {
super(context);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
setMeasuredDimension(C.GetWindowWidth(getContext()), C.GetWindowHeight(getContext()));
}
public static ClickBlockerView CreateClickBlocker(Context context){
ClickBlockerView c = new ClickBlockerView(context);
RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.WRAP_CONTENT, RelativeLayout.LayoutParams.WRAP_CONTENT);
c.setLayoutParams(params);
c.setId(R.id.clickBlocker);
c.setClickable(true);
return c;
}
}
And this is how I'm calling it (from inside another view):
private void createMenuAndClickblocker(){
ClickBlockerView clickblocker = ClickBlockerView.CreateClickBlocker(getContext());
clickblocker.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
destroyMenu();
}
});
((RelativeLayout) this.getParent()).addView(clickblocker);
clickblocker.bringToFront();
createMenu();
requestLayout();
}
Does anyone have some idea how to fix this? Other solutions to the problem are welcome too. I'd rather not have to use xml though (it would have to be added to every activity)
Update:
Apparently, the z-ordering goes fine with spinners and checkboxes, but not with buttons and my custom views. Odd...
Update 2:
When drawn, the shape is shown to correctly fill up the screen, but it is also drawn below the other custom views, even though it is created later and brought to the front.
Strangely enough, the click functionality has meanwhile completely stopped functioning, even though I didn't change anything in the code for this clickblocker.

Don't show transition if view is not visible

I have a list of products, if I click on one, the image of the product is transitioned into the detail screen.
And if I go back, the image is transitioned back to the list.
This works fine.
The problem is that when I scroll down in my detail screen, the image is no longer visible.
But when I go back to the list screen the image is still transitioned, resulting is a buggy transition.
Video example here
I want to achieve something like the Play Store
Where there is no return animation if the image is no longer visible.
Code
Starting detail activity:
Intent intent = new Intent(getActivity(), ProductDetailActivity.class);
intent.putExtra(ProductDetailActivity.EXTRA_PRODUCT, product);
Bundle options = ActivityOptionsCompat.makeSceneTransitionAnimation(getActivity(),
productViewHolder.getProductCover(), productViewHolder.getProductCover().getTransitionName()).toBundle();
getActivity().startActivity(intent, options);
In DetailActivity I set the transition name:
coverImageView.setTransitionName(getString(R.string.transition_key_product_cover_with_id, product.getId()));
styles.xml:
<item name="android:windowContentTransitions">true</item>
Any idea how to implement the behaviour I want to achieve?
With the following link you can know if a view inside an scroll is visible or not: https://stackoverflow.com/a/12428154/4097924
Then you can make a simple method to know if your imageView is visible inside the scrollview, similar to this easy example:
public boolean isVisibleInsideScroll(){
Rect scrollBounds = new Rect();
scrollView.getHitRect(scrollBounds);
if (imageView.getLocalVisibleRect(scrollBounds)) {
// Any portion of the imageView, even a single pixel, is within the visible window
return true;
} else {
// NONE of the imageView is within the visible window
return false;
}
}
Then I see two possible options that I have not proved:
option 1: overwrite the method onBack (and every way to go back if you have another one). Inside the method, you can assign the transition when the element it is visible before leaving the screen:
#Override
public void onBackPressed(){
if(isVisibleInsideScroll()){
coverImageView.setTransitionName(getString(R.string.transition_key_product_cover_with_id, product.getId()));
}
super.onBackPressed();
}
option 2: overwrite the method onScroll (and every time the scrollview is scrolled) you can register or unregister the animation if the view is visible.
The code in this option is similar to the previous one.
Good luck! I like a lot your animation, I saw it in youtube. :)

When the application is restarted, dynamically generated Buttons are not present?

I'm using a predefined Button to add new Buttons dynamically.
But when I restart the application, the dynamically generated Buttons are not present in layout in which they are created.
Code to generate the Buttons.
if(v == btnaddnew) //Button to new buttons in layout Dynamically
{
final Button btn1 = new Button(this);
btn1.setText("New");
btn1.setId(btncount);
LinearLayout ll = (LinearLayout)findViewById(R.id.layout1);
LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
ll.addView(btn1, lp);
btncount++;
}
In main activity
int btncount = 15;
Everytime you close your application and restart it the onCreate method is invoked for your Activity! I suspect you are loading your base layout.xml file, your dynamically added buttons are not part of this layout and are only defined in your code. I am assuming the code to add these dynamic buttons are not in the onCreate, they are probably some button click callback?
My point is this, the dynamically added artifacts will not be present if you close the application for the reason mentioned above!
If you want to resume to the last state after closing it, you need to figure a way to store your current layout before the onDestroy method is invoked for the activity! This stored layout can be reloaded in your onCreate. Let me clear that, in your onCreate you can check if there is a stored layout if yes load from it! Else load from your layout file.
EDIT:
Look at this : http://developer.android.com/guide/components/activities.html#SavingActivityState

Can I make android app that will cover half of the screen only

I made broadcast receiver when receive calls I want to add a button( besides answer and decline)that will do a certain function, so I see 2 solutions one is that I make ( if it's possible half screen transparent activity that will have the button but not block the answer and decline buttons, or the second solution that I see is to make something like BIG CALLER PICTURE apps that there is on the play.google can anyone refer me to some good examples. THX
public class CallReceiveD extends BroadcastReceiver {
private ITelephony telephonyService;
String phoneNumber;
#Override
public void onReceive(Context context, Intent intent) {
// i will call the intent here
}}
You definitely can create an activity as a dialog that would take up part of the screen. Although I am not sure this will accomplish what you want because by opening the new activity the phone activity should get onPause called and will probably mess up your flow. Maybe try opening an activity and define in the activity in the manifest
<activity android:theme="#android:style/Theme.Dialog" />
And see if that works at all for what you want to do. Then you can tweek the dim around the activity it's location,size, etc.
I am not sure if it will help but there is a framework called StandOut for "floating" apps. Maybe it could help you ...
http://forum.xda-developers.com/showthread.php?t=1688531
It provides ways to create app windows floating over the screen instead of taking up the whole. You can still interact with the app below. The thread provides a lot of examples, maybe there's something for you in it.
I can be wrong but from what I know you will not be able to start a half screen intent (Activity). You can do a transparent activity but it will block system buttons underneath. I would say you have to try overriding the system calling receiver tool too achieve this.
You can certainly create a partial screen activity. Nothing underneath the new activity, if started with a dialog theme as MikeIsrael suggests. Simple write an Activity as you normally would, but give it layout_width and layout_height of your choosing - says 300x400dp.
Then can then use onAttachWindow to set the size and location. This example sets a window to be on the top right of the display, but below the actionBAr
public void onAttachedToWindow() {
super.onAttachedToWindow();
View view = getWindow().getDecorView();
WindowManager.LayoutParams lp = (WindowManager.LayoutParams) view.getLayoutParams();
lp.gravity = Gravity.LEFT | Gravity.TOP;
lp.width = (int)(400 * screenInfo.density);
lp.x = screenInfo.widthPixels - lp.width;
lp.y = actionBarHeight;
getWindowManager().updateViewLayout(view, lp);
}
Note that although the Gravity says top left, the X,Y determines where I place the window

onClick launches Activity incorrectly (in 2.3.6)

I'm working on an interface that provides a set of multiple Button objects, each of which has attached the same OnClickListener. When said Buttons are clicked, they should launch an Activity, as specified in onClick.
Here is my code for reference:
public class Calcs extends SherlockFragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// ...
CalcLoader buttonListener = new CalcLoader(getActivity());
LinearLayout buttons = (LinearLayout) v.findViewById(R.id.calculatorlist); // v is the inflated View
for (int i = 0; i < buttons.getChildCount(); i++) {
View b = buttons.getChildAt(i);
if (b instanceof Button) {
((Button) b).setOnClickListener(buttonListener);
}
}
// Test Code: Location 1
Intent i = new Intent(getActivity(), MyCalcActivity.class);
getActivity().startActivity(i);
// ...
}
private class CalcLoader implements OnClickListener {
private Activity mOwner;
public CalcLoader(Activity owner) {
mOwner = owner;
// Test Code: Location 2
Intent i = new Intent(mOwner, MyCalcActivity.class);
mOwner.startActivity(i);
}
public void onClick(View v) {
if (v instanceof Button) {
// Actual Code: Location 3
Intent i = new Intent(mOwner, MyCalcActivity.class);
mOwner.startActivity(i);
}
}
}
}
Despite this, however, I'm getting some odd behavior. In the above code, I've placed some startActivity tests, labelled locations 1 and 2. In both cases, the Activity launches correctly, and all is well!
However, at location 3, where the working code should execute, I get some strange behavior from the launched Activity:
At first, the Activity is launched just fine. It displays a single text field and it is focused, with the soft keyboard coming up. This is correct.
Now, when I click the back button, the keyboard closes. This is correct.
Click back again, and the field loses focus. This should NOT happen. Instead, the Activity SHOULD close and return to the previous one.
Click back again, and the entire app closes (instead of returning to the previous Activity). Obviously, this should NOT happen.
To reiterate, when the Activity is started from location 1 or 2, everything functions correctly; the back stack is correct and returns to the initial Activity properly.
What is going wrong here? Why, when I start my Activity from onClick, does it fail, while it works from any other location?
Update: Saving the Intent in the constructor and reusing it in the onClick method produces the same glitched result, as does starting the Activity from the UI thread.
Second update: Making the text field unfocusable had no effect on the glitch; the back button still closed the app. Additionally, running in the 2.3.3 emulator had the same result. Oddly, though, after the second back button press (the text field losing focus), if you wait ~3 seconds, the Activity closes and returns to the main one.
Third update: No key events (onKeyDown or onBackPressed) are fired for the back button that takes focus from the text field. Additionally, if you interact with the Activity after the text field loses focus, it shows the animation of loading a new Activity of the same type, but the glitch is present here as well.
This appears to be an OS-level issue, found in Android 2.2 (API 8), 2.3.1 (API 9), and 2.3.3 (API 10). Eclair (API 7), and APIs 11+ do not have this issue. At this point, I believe I'm looking for some kind of workaround...
Turns out my issue was not caused by something I detailed in my original post, so I apologize for that.
The issue was caused by a SurfaceView item on a tab unrelated to the tab I was testing on. After rebuilding the tabs and layouts from the ground up (and building each time), I discovered the lag-back-glitch was only caused when a SurfaceView was present in a non-focused tab.
I finally found that I was not the only one with this issue.
To solve:
Created an onPause method in the main Activity. In here, I destroy the SurfaceView using container.removeView(..);.
Created an onResume method in the main Activity. In here, I inflate the SurfaceView from a new XML file containing ONLY the SurfaceView item, and add it to the original container.
Lastly, implemented a android.view.SurfaceHolder.Callback in the SurfaceView to erase the contents of the surface before it is removed.
It stumps me that this happens only on APIs 8-10, but I'm glad it's solved now. Kudos to everyone that offered their assistance!

Categories

Resources