I’m developing an Android drawing app, using a SurfaceView (to be more specific it’s a class that extends SurfaceView). I already saw a lot of topics on this forum, but I didn’t get my answer.
My activity is basically a FrameLayout, which contains all of my views. I would like to set my SurfaceView on the lowest level of my FrameLayout, in order to see the upper elements (buttons, fragments, etc.). I also would like to set the background of my SurfaceView with a drawable but I’m facing problems.
I tried first to set the background of my SurfaceView itself and it works. Unfortunately, my painting content (canvas and bitmap) was overload by this background so I tried a second solution. I apply the drawable background to my FrameLayout, and I set my SurfaceView background as transparent, using setZOrderOnTop. My SurfaceView was indeed transparent, but my drawing stuff was above my buttons and fragments.
So my first question is: why do we need to setZOrderOnTop to get a transparent background? Then, how can I set a simple drawable background, keeping my structure hierarchy?
Thank’s for your answers!
XML View:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/mainFrame"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/repeat_background"
tools:context=".MyActivity">
<!-- Option menu -->
<fragment ... />
<Button ... />
<Button ... />
<Button ... />
...
</FrameLayout>
#drawable/repeat_background
<?xml version="1.0" encoding="utf-8"?>
<bitmap xmlns:android="http://schemas.android.com/apk/res/android"
android:src="#drawable/drawing_font_texture"
android:tileMode="repeat" />
MyActivity
#Override
protected void onCreate(Bundle p_savedInstanceState) {
super.onCreate(p_savedInstanceState);
setContentView(R.layout.main_controller_activity);
// Getting my FrameLayout
FrameLayout mainFrame = (FrameLayout) findViewById(R.id.mainFrame);
// Instantiating my SurfaceView
this.drawableView = new MCustomDrawableView(getApplicationContext());
// Don't works
//setZOrderMediaOverlay(true);
this.drawableView.setZOrderOnTop(true);
this.drawableView.getHolder().setFormat(PixelFormat.TRANSLUCENT);
FrameLayout.LayoutParams style = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.MATCH_PARENT);
// Add the SurfaceView to the FrameLayout, on lowest position
mainFrame.addView(this.drawableView, 0, style);
}
I think your solution is on the right way. Set the background to the framelayout or add another layout under the surfaceview for your drawable. Then increase the z indices of the surfaceview AND the z indices of the buttons/elements that should be over the surfaceview,otherwise they are under it.
If you use material design, you can also set the elevation in dp for nice shadows.
Related
I am trying to figure out how to fill background color of a card from bottom to top based on the processing of a particular task. I want the background color of card to be filled slowly from bottom to top approach based on some timer or processing. How to achieve this scenario. Please help me here.
You can easily achieve this with Clipping. This is a modified code from Android Drawable Resources.
First specify the clipping view in XML file saved at res/drawable/clip.xml
<?xml version="1.0" encoding="utf-8"?>
<clip xmlns:android="http://schemas.android.com/apk/res/android"
android:drawable="#drawable/android"
android:clipOrientation="vertical"
android:gravity="bottom" />
It basically means that whatever drawable you assign to this xml, it will be clipped vertically starting from the bottom. Then in your cardview XML set this drawable as a background.
<CardView
android:id="#+id/cardview"
android:background="#drawable/clip"
android:layout_height="wrap_content"
android:layout_width="wrap_content" />
Then in your activity do something like this
CardView cardview = (CardView) findViewById(R.id.cardview);
ClipDrawable drawable = (ClipDrawable) cardview.getBackground();
drawable.setLevel(/*your level*/);
Regarding the level:
Increasing the level reduces the amount of clipping and slowly reveals
the image.The default level is 0, which is fully clipped so the image
is not visible. When the level is 10,000, the image is not clipped and
completely visible.
Hope this helps.
I have created a custom SurfaceView which I use to draw shapes, and the user can zoom and pan the view to interact with the shapes. This all works great, but the problem now is that the shapes will always be drawn on top of everything on my screen, also on top of other views such as buttons and textviews. I have tried ordering the custom view and my buttons in different ways in the XML layout, tried parenting the views to FrameLayouts and RelativeLayouts (as suggested here).
I would like to have buttons and other layout elements floating on top of my custom view. As seen in the image below, when I pan my view the rectangle will obscure the buttons.
How can I prevent my custom SurfaceView from drawing on top of everything?
My drawing code runs in a separate thread in the SurfaceView class:
public class CustomShapeView extends SurfaceView implements Runnable, SurfaceHolder.Callback {
/* Other implementation stuff */
#Override
public void run() {
while (isDrawing) {
if (!holder.getSurface().isValid())
continue; //wait till it becomes valid
Canvas canvas = null;
try {
canvas = holder.lockCanvas(null);
synchronized (holder) {
try {
lock.lock();
canvas.concat(sampleMatrix);
canvas.drawColor(Color.TRANSPARENT, PorterDuff.Mode.CLEAR);
drawShapes(canvas);
} finally {
lock.unlock();
}
}
} catch (Exception e) {
e.printStackTrace();
} finally {
if (canvas != null) {
holder.unlockCanvasAndPost(canvas);
}
}
}
}
}
My test XML layout, which I would expect to create a button (Left) which is behind the custom view and a button (Right) which is floating above the custom view, is:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Left"
android:id="#+id/button"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"/>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.example.CustomShapeView
android:id="#+id/floor_plan_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</FrameLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Right"
android:id="#+id/button2"
android:layout_alignParentBottom="true"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"/>
</RelativeLayout>
</RelativeLayout>
In my code I was calling the function setZOrderOnTop(true); which made sure that the surface view would always be drawn on top of everything. This piece of code was left in by the previous developer in order to have a custom background for the canvas instead of only a solid color. With SurfaceView it is a tradeoff between having a custom background for the canvas or being able to draw views on top of the canvas.
The options are
With setZOrderOnTop(true); there can be custom backgrounds but no overlaying views.
With setZOrderOnTop(false); there can be overlaying views but only solid color backgrounds for the canvas (by calling e.g. canvas.drawColor(Color.WHITE); to clear between draw calls).
I chose the second option as I value overlaying views over custom backgrounds.
I'm facing a similar issue. I want to put transparent SurfaceView between another views which are being animated in RelativeLayout. When I do so, during animation views sometimes dissapear, sometimes only part of them is visible, it is weird. Then I discovered if I put SurfaceView to the layout first (so at the bottom) and then I call SurfaceView.setZOrderMediaOverlay(true), the content of SurfaceView is drawn at the bottom of the window, view are drawn on the top. If I put SurfaceView anywhere in the layout and then call SurfaceView.setZOrderOnTop(true), the content is always drawn on the top of the window (and also on the top of the views). If I put SurfaceView between views, its behavior is crazy, maybe it's because content of SurfaceView is drawn from separated thread and views are drawn from GUI (main) thread so they are "fighting" and that's why this behavior. Only way I found is drawing at the bottom or at the top. Try to create a new Fragment with transparent background where the SurfaceView is at the bottom as I described in first case and put that buttons you want to have above on top of it (so add them later in some RelativeLayout or FrameLayout) then show this fragment on the top of activity which holds content you want to have below it. Hopefully I helped a bit.
I would like to have the app draw several lines for showing user directions by using a background image as a base view and the app "draws" onto it in another layer on top of the background ImageView. Several lines would be made for the objectives to be met, and also in a specific location, so it has to have several layers too right??
I found a trick for drawing a single line using XML
<View
android:id="#+id/line"
android:layout_width="2dip"
android:layout_height="300dip"
android:background="#000000"
/>
But that code doesn't show positions and not in a layered form.
Update :
I would like the lines to appear through drawing, not through another picture
You can make a custom SurfaceView class, set the image as background and paint your lines on its Canvas. If I correctly understood what you want to achieve, that's probably the easiest way.
SurfaceView is a widget from the Android framework. Extend it to make your own: MySurfaceView extends SurfaceView. Then override public void onDraw(Canvas canvas), as android_user77 noted. Use this canvas object to paint on. Finally, add your class in the layout xml:
<com.your.app.MySurfaceView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#drawable/my_image" />
For details as why to use SurfaceView instead of View:
Difference between SurfaceView and View?
You can extend view and do the drawing by overriding the onDraw method
Use FrameLayout to create many "layers". I do not think you want to draw anything on the view. So do not use onDraw(), SurfaceView...
The code is like:
<FrameLayout ...>
<ImageView id="#+id/layer1"...>
<ImageView id="#+id/layer2"...>
</FrameLayout>
I would like to achieve a popup/overlay screen like Places (i`m more interested how can i place that screen for example in the right/left side of the screen ) in the android maps application (image below). I can create an activity and use the Dialog theme, this mostly resolve my problem, but it placed center in the screen. Somebody have any better idea how i can create a popup/overlay screen like the places in a non-map application and place to top/right of the screen ?. My guess they did it with map overlays.
well, here's what I did... I used a FrameLayout to overlay a LinearLayout where I can fill it with a View. Here's an excerpt of my code:
XML:
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1">
<fragment class="com.some.location.to.fragment"
android:id="#+id/fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:id="#+id/overlay_pane"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:visibility="gone"
android:background="#color/transparent">
</LinearLayout>
</FrameLayout>
Then in my code on some button, I would just add a View (in your case it can just be the dialog's contents) to the overlay_pane LinearLayout.
Java example:
ViewGroup container = findViewById(R.id.overlay_pane);
container.setVisibility(View.VISIBLE);
container.addView(some_view_you_inflated);
But this inflated view would have the following background: #drawable/dialog_full_holo_light for a nice border effect like the Honeycomb style.
You can find the background drawable in your SDK in the following folder
your_SDK_dir/platforms/android-12/data/res/drawable-hdpi/dialog_full_holo_light.9.png
So just copy that into your drawables.
Note: that you can also use the dialog_full_holo_dark or any custom background for the same effect.
I hope that helps :) Let me know if I was unclear at any point
Note: Instead of using a fragment, you could merely use a LinearLayout with match_parent for both layout_width and layout_height. And then you would fill that LinearLayout with the "background" UI (in the question's example.. that would be the map)
Okay, so I've done some looking around and I see how you are SUPPOSED to do it, but for me, it is just not working.
I need to be able to set the alpha of a RelativeLayout both in XML and in code. For my XML, I have the following
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/player_controls"
android:layout_height="match_parent"
android:layout_width="match_parent"
android:alpha="0.0">
<RelativeLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/player_controls_touch_me"
>
</RelativeLayout>
</RelativeLayout>
I get the error: no resource identifier found for attribute 'alpha' in package 'android'
Also, based on the Android documentation, I should be able to call setAlpha(double) on any View object, but when I try to make that call on a RelativeLayout it tells me that this method is not defined for this object.
Why am I not able to control the alpha transparency for a RelativeLayout object in Android? Am i missing something? Thanks!
Update
Although using the visibility property works, it prevents me from be able to click on the ViewGroup. This is important for me because I am utilizing the OnTouchListener of the ViewGroup.
What I am trying to do is to have a layer with media controls, initially hidden. when the user taps anywere on the screen, I want the controls to fade in and when they tap the screen again I want the controls to fade out. I have this part already working. I am using a viewgroup that sits over-top my entire application with an OnTouchListener attached that can determine if it has or hasn't been touched. My problem is that after the animation runs to fade out the controls, they re-appear. If I use #Hydrangea suggestion, I can have it fade out and immediately made invisible. This gives me the desired effect, but then the ViewGroup is unclickable and the user cannot get the controls to come back (or go away, depending on what we decide to do first).
I hope this makes sense.
You'll want to use a alpha animation to fade things in and out. This will maintain your touch events for your layouts. Here's an example
public class Main extends Activity {
/** Called when the activity is first created. */
private boolean mShowing = false;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
findViewById(R.id.textview).setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
if(mShowing){
Animation animation = new AlphaAnimation(1.0f, 0.0f);
animation.setFillAfter(true);
arg0.startAnimation(animation);
} else {
Animation animation = new AlphaAnimation(0.0f, 1.0f);
animation.setFillAfter(true);
arg0.startAnimation(animation);
}
mShowing = !mShowing;
}
});
}
}
Here's the accompanying xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
>
<TextView
android:id="#+id/textview"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="#string/hello"
android:clickable="true"
/>
</LinearLayout>
Unless you need levels of alpha between 0 and 1, I'd suggest, if you truly want to make this item invisible, to use setVisibility();
android:visibility="invisible"
I checked out the android:alpha line, and my ide doesn't find it either. I can't guess why, though... the documentation seems pretty clear.
The alpha property is new in Android 3.0, and it's not the most efficient way to hide a view. Use View.setVisibility() or android:visibility to achieve what you want.
You can set alpha by setting the (background) color i guess. Color values can be in the format of #aarrggbb (alpha, red, green, blue).
You can add to the right answer the following option:
animation.setDuration(xxx);
To each animation instance. In this way your animation will look better.
Based on your discription, you should be able to create a view that contains only the relative layout and have the onClickListener set to it. This way you can set the visibility of the relative layout to invisible, but still register a click.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/clickable_layout"
android:layout_height="match_parent"
android:layout_width="match_parent" >
<RelativeLayout
android:id="#+id/player_controls"
android:layout_height="match_parent"
android:layout_width="match_parent" >
<RelativeLayout
android:layout_height="match_parent"
android:layout_width="match_parent"
android:id="#+id/player_controls_touch_me"
>
</RelativeLayout>
</RelativeLayout>
</FrameLayout>
Use onTouchEvent in Activity, and then you could get touch event to control to your RelativeLayout even if it is "invisible".