Get refrence to surface object assigned to window - android

Is it possible to get a reference to Surrface assigned to window of activity?
I know that you can do this by:
getWindow().takeSurface(new SurfaceHolder.Callback2() {
#Override
public void surfaceRedrawNeeded(SurfaceHolder surfaceHolder) {
}
#Override
public void surfaceCreated(SurfaceHolder surfaceHolder) {
// Draw view styff
Canvas c = surfaceHolder.lockCanvas();
c.drawRGB(0, 255, 255);
surfaceHolder.unlockCanvasAndPost(c);
#Override
public void surfaceChanged(SurfaceHolder surfaceHolder, int i, int i1, int i2) {
}
#Override
public void surfaceDestroyed(SurfaceHolder surfaceHolder) {
}
});
But as stated in the documentation this basically disables the view hierarchy. Is there a way to get to this surface after view hierarchy has been drawn so I can draw some overlay over it?

Related

Android SurfaceView IllegalArgumentException lockCanvas

I'm working on a video into a SurfaceView.
My goal is to get recurrent bitmaps of the running video.
Here is my Custom implementation:
private static final String TAG = "XXX";
private Activity activity;
private SurfaceHolder mSurface;
private MediaPlayer mMediaPlayer;
private SurfaceHolder mActiveSurface;
public ImageView imageView;
boolean locked, locked1;
private boolean isCreated;
public AlphaSurfaceView(Context context,Activity activity) {
super(context);
getHolder().addCallback(this);
setWillNotDraw(false);
this.activity = activity;
}
public AlphaSurfaceView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AlphaSurfaceView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
#Override
public void draw(Canvas canvas) {
super.draw(canvas);
}
#Override
public void onDraw(Canvas canvas) {
//super.onDraw(canvas);
if(isCreated){
if(getHolder() != null && getHolder().getSurface().isValid()){
Canvas c = null;
try {
if(!locked){
try {
c = getHolder().lockCanvas();
locked = true;
}catch (IllegalArgumentException e){
e.printStackTrace();
locked = false;
}
BitmapDrawable bdrawable = new BitmapDrawable();
bdrawable.draw(c);
}
}catch (Exception e){
e.printStackTrace();
locked = false;
}finally {
if(c != null && locked){
getHolder().unlockCanvasAndPost(c);
locked = false;
}
}
}
}else{
super.onDraw(canvas);
}
invalidate();
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
mSurface = holder;
mMediaPlayer = MediaPlayer.create(getContext(), Uri.parse("XX"), mSurface);
mActiveSurface = mSurface;
try {
mMediaPlayer.setOnPreparedListener(new MediaPlayer.OnPreparedListener() {
#Override
public void onPrepared(MediaPlayer mp) {
mMediaPlayer.start();
mMediaPlayer.setLooping(true);
}
});
} catch (Exception e) {
e.printStackTrace();
}
this.isCreated = true;
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.d(TAG, "surfacechanged");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
mMediaPlayer.stop();
}
#Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
mMediaPlayer.stop();
}
The problem is an IllegalArgumentException occured by the lockCanvas() method.
I tried many possibilities as:
Adding some boolean (locked & creation of surfaceview)
Test if surface is valid
even add exported="true" in targeted activity in Manifest's xml
By the way the first idea was to use getDrawingCache() but even i added setCacheEnabled(true), the return was null.
So how to resolve this Exception or using another way to get each frame?
Thanks!
You can't draw on a Surface and send a video to it. A Surface is the producer end of a producer-consumer pair, and you can only have one producer at a time.
The easiest way to have a Canvas overlap a SurfaceView Surface is to draw on the View part of the SurfaceView. Send the video to the Surface, and use onDraw() (like a custom view) to draw on the View.
Bear in mind that the Surface is a separate layer that sits behind the View UI layer, so you will need to draw on the View with transparency to see the Surface contents.
Another approach is to use multiple overlapping SurfaceViews, but this is less efficient and more limited. An example with three overlapping Surfaces can be found in Grafika's multi-surface test Activity.
surfaceHolder.addCallback(new SurfaceHolder.Callback() {
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
Log.e("video","surfaceChanged");
}
#Override
public void surfaceCreated(SurfaceHolder arg0) {
Log.e("video","surfaceCreated");
mediaPlayer.setDisplay(surfaceHolder);
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
Log.e("video","surfaceDestroyed");
if (mediaPlayer != null) mediaPlayer.release();
}
});
That should help.
Let the MediaPlayer do the drawing thing.

How to add SurfaceView displaying camera preview with opacity

I want to add SurfaceView which displaying camera preview to my layout. It should be semi-transparent (alpha 0.5). I add View by addView() method.
Ho to do that. I tried animation but it seems to not works with camera.
My Surfave view looks like that:
public class ShowCamera extends SurfaceView implements SurfaceHolder.Callback {
private SurfaceHolder holdMe;
private Camera theCamera;
public ShowCamera(Context context,Camera camera) {
super(context);
theCamera = camera;
holdMe = getHolder();
holdMe.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
try {
theCamera.setPreviewDisplay(holder);
theCamera.startPreview();
} catch (IOException e) {
}
}
#Override
public void surfaceDestroyed(SurfaceHolder arg0) {
}
}
My minAPI = 8.

Antialiasing in TextureView

I tried to play the same video with a SurfaceView and a TextureView and noticed that the image rendered with the TextureView is more aliased (less 'smooth') than with the SurfaceView.
What is the reason for this ? Is there any way to configure rendering of TextureView to look better ?
The TextureView is used like this:
TextureView textureView = new TextureView(this);
textureView.setSurfaceTextureListener(new SurfaceTextureListener() {
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {
Log.i("test", "onSurfaceTextureAvailable()");
MediaPlayer player = MediaPlayer.create(TestActivity.this, Uri.parse(VIDEO_URL));
Surface surface = new Surface(surfaceTexture);
player.setSurface(surface);
player.start();
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {
Log.i("test", "onSurfaceTextureUpdated()");
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {
Log.i("test", "onSurfaceTextureSizeChanged()");
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
Log.i("test", "onSurfaceTextureDestroyed()");
return false;
}
});
setContentView(textureView);
And for the SurfaceView:
SurfaceView surfaceView = new SurfaceView(this);
surfaceView.getHolder().addCallback(new Callback() {
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i("test", "surfaceCreated()");
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("test", "surfaceDestroyed()");
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i("test", "surfaceChanged()");
MediaPlayer player = MediaPlayer.create(TestActivity.this, Uri.parse(VIDEO_URL));
player.setSurface(holder.getSurface());
player.start();
}
});
setContentView(surfaceView);
Well, it seems that applying a scaling (other than 1) on the TextureView does the 'smoothing' effect that I'm looking for.
textureView.setScaleX(1.00001f);
That sounds like a strange hack...but it works. Would be interesting to dig out what's done in the scaling that changes the rendering aspect...

Problem in displaying Bitmaps on canvas

I am have some bitmaps which i want to display serially one after another but my code displays only last bitmap.Can anybody tell me why is it happening?
here is the code
class Panel extends SurfaceView implements SurfaceHolder.Callback {
private boolean _run = false;
public Panel(Context context) {
super(context);
getHolder().addCallback(this);
_run = true;
}
#Override
public void onDraw(Canvas canvas) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
Log.i("Read","surfaceChanged is called");
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i("Read","surfaceCreated is called");
while (_run ) {
display();
}
}
public void display() {
Canvas c;
c = null;
try {
c = getHolder().lockCanvas(null);
synchronized (getHolder()) {
onPreviewFrame();
invalidate();
c.drawColor(Color.BLACK);
c.drawBitmap(bmp, 10, 10, null);
//panel.surfaceDestroyed(panel.getHolder());
}
} finally {
if (c != null) {
getHolder().unlockCanvasAndPost(c);
}
}
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("Read","surfaceDestroyed is called");
_run = false;
}
}
I am not sure what your requirement is but if your are trying to create a continous horizontal image scroll. Take a look at this
https://github.com/blessenm/SlideshowDemo

Initializing SurfaceView with a Drawable

I'd like to open up a SurfaceView with a icon placed in the center of the screen when an application is first started. I'm evoking icon creation using an implementation of SurfaceHolder.Callback to track when the 'Canvas' object is ready. My question is is there a better way? Are there less cumbersome methods of starting a SurfaceView with some Drawables loaded on creation without having to resort to placing draw logic within a callback object?
Here's my code for reference. First the object which does drawing:
public class CanvasDraw{
protected final SurfaceHolder mHolder;
protected final Drawable mDrawable;
public interface DrawLogic{
void draw(Rect _surface);
}
public CanvasDraw(SurfaceView _view, Drawable _drawable){
mHolder = _view.getHolder();
mDrawable = _drawable;
}
public void draw(DrawLogic _logic){
Canvas canvas = null;
try{
canvas = mHolder.lockCanvas();
if( canvas != null ){
Log.i("DRAWABLE", "Drawing " + mDrawable.toString());
_logic.draw( mHolder.getSurfaceFrame() );
mDrawable.draw(canvas);
}
else{
Log.i("DRAWABLE", "Canvas null valued");
}
}
finally{
if( canvas != null ){
mHolder.unlockCanvasAndPost(canvas);
}
}
}
}
and then my private callback object:
private class DrawOnceCallback implements SurfaceHolder.Callback {
private final SurfaceHolder mHolder;
public DrawOnceCallback(SurfaceHolder _holder ){
mHolder = _holder;
mHolder.addCallback(this);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i("SURFACEHOLDER","Surface created. Canvas available.");
mDrawTiles.draw( new CanvasDraw.DrawLogic(){
#Override
public void draw(Rect _surface) {
mTiles.setBounds(
_surface.centerX() - mDrawWidth/2,
_surface.centerY() - mDrawHeight/2,
_surface.centerX() + mDrawHeight/2,
_surface.centerY() + mDrawHeight/2);
}
});
mHolder.removeCallback(this);
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {Log.i("SURFACEHOLDER","Surface destroyed.");}
}
I did something like this simply by assigning the image I wanted to a view in the layout XML file. If you've got to wait until your canvas and all that is ready anyways why not just use a default property? My opening splash screen was just a view with
android:background="#drawable/splash"
added to its layout.

Categories

Resources