I am working on a 2d game and I'm trying to make the view where I draw and update everything tell the activity that contains it that the game ended and display a pop-up on the screen. The problem is that I don't know how to access the method in the activity from the view because they work on different threads.
The code looks more or less like this and I want to access the method displayFrame() from the view class. The method in the real code does more operations with other textViews etc. that only the activity has access to.
public class MainActivity extends Activity{
private MyView myView;
RelativeLayout finalFrame;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
myView = new MyView(this.getApplicationContext());
this.setContentView(myView);
finalFrame = (RelativeLayout) findViewById(R.id.framefinal);
}
private void displayFrame(){
this.finalFrame.setVisibility(View.VISIBLE);
}
}
public class MyView extends SurfaceView implements SurfaceHolder.Callback{
private MyThread myThread;
private boolean finished = false;
public MyView(Context context) {
super(context);
getHolder().addCallback(this);
this.myThread = new MyThread(getHolder(), this);
this.myThread.setRunning(true);
}
public void update() {
this.finished = someMethod();
if(this.finished){
MainActivity.displayFrame();
}
}
public void draw(Canvas canvas){
//draw something
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
public class MyThread extends Thread{
private MyView myView;
private SurfaceHolder surfaceHolder;
private boolean running = false;
private static Canvas canvas;
public MyThread(SurfaceHolder surfaceHolder, MyView myView){
super();
this.surfaceHolder = surfaceHolder;
this.myView = myView;
}
#Override
public void run(){
while(running){
canvas = null;
try {
canvas = this.surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
this.myView.update();
this.myView.draw(canvas);
}
} catch (Exception e) {e.printStackTrace();}
finally{
if(canvas!=null)
{
try {
surfaceHolder.unlockCanvasAndPost(canvas);
}
catch(Exception e){e.printStackTrace();}
}
}
}
}
public void setRunning(boolean running){
this.running = running;
}
}
The easiest way to do that is to define a listener interface in your view and make the activity implement it. Then you can notify the activity that your game has ended and execute the appropriate logic.
public class MyView extends SurfaceView implements SurfaceHolder.Callback {
// your other fields
private GameListener mGameListener;
public void setGameListener(GameListener gameListener) {
mGameListener = gameListener;
}
public void update() {
// your logic
if (mGameListener != null) {
// calling onGameEnd on the main thread
post(new Runnable() {
public void run() {
mGameListener.onGameEnd();
}
});
}
}
// rest of your class as it is
public interface GameListener {
void onGameEnded();
}
}
public class MainActivity extends Activity implements GameListener {
// the rest of your class
public void onGameEnded() {
// call the method you need
}
}
public class MyView extends SurfaceView implements SurfaceHolder.Callback{
private MyThread myThread;
private boolean finished = false;
private Context context;
public MyView(Context context) {
super(context);
this.context=context;
getHolder().addCallback(this);
this.myThread = new MyThread(getHolder(), this);
this.myThread.setRunning(true);
}
runOnUiThread(new Runnable(){
#Override
public void run(){
update();
}
});
public void update() {
this.finished = someMethod();
if(this.finished){
MainActivity mainActivity = (MainActivity) context;
mainActivity.displayFrame();
}
}
public void draw(Canvas canvas){
//draw something
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width, int height) {
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
}
}
Can you try this
Related
I am using Glide for resizing an image and saving it to the file system in an Android app, and I've created a custom FileTarget class that extends Glide's native Target to do it.
It works. Yay! But I'm sort of appalled by all the empty methods I had to 'define' to get it to satisfy the Target implementation. All I really care about is onResourceReady...
Is there any way to implement this without declaring all these empty methods? Should I be worried that they're all like this?
I'm a little new to Android so I appreciate your patience.
public static class FileTarget<T extends Bitmap> implements Target<Bitmap> {
private final int width;
private final int height;
String fileName;
Bitmap.CompressFormat format;
int quality;
public FileTarget(int width, int height, String fileName, Bitmap.CompressFormat format, int quality) {
this.width = width;
this.height = height;
this.fileName = fileName;
this.format = format;
this.quality = quality;
}
#Override
public void onLoadStarted(Drawable drawable) {}
#Override
public void onLoadFailed(Drawable drawable) {}
#Override
public void onLoadCleared(Drawable drawable) {}
#Override
public void getSize(SizeReadyCallback cb) {
cb.onSizeReady(width, height);
}
#Override
public void removeCallback(#NonNull SizeReadyCallback cb) {}
#Override
public void setRequest(#Nullable Request request) {}
#Nullable
#Override
public Request getRequest() {
return null;
}
#Override
public void onStart() {}
#Override
public void onStop() {}
#Override
public void onDestroy() {}
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
try {
FileOutputStream out = new FileOutputStream(fileName);
resource.compress(format, quality, out);
out.flush();
out.close();
onFileSaved();
} catch (IOException e) {
e.printStackTrace();
onSaveException(e);
}
}
public void onFileSaved() {
// do nothing, should be overriden
}
public void onSaveException(Exception e) {
// do nothing, should be overriden
}
}
Within the Android framework, there is a common pattern of creating an abstract FooAdapter class that implements all of the methods in a Foo interface with no-ops.
For example, consider this class:
public abstract class AnimatorListenerAdapter implements Animator.AnimatorListener,
Animator.AnimatorPauseListener {
#Override
public void onAnimationCancel(Animator animation) { }
#Override
public void onAnimationEnd(Animator animation) { }
#Override
public void onAnimationRepeat(Animator animation) { }
#Override
public void onAnimationStart(Animator animation) { }
#Override
public void onAnimationPause(Animator animation) { }
#Override
public void onAnimationResume(Animator animation) { }
}
You could create the same thing for Glide:
public abstract class TargetAdapter<T> implements Target<T> {
#Override
public void onLoadStarted(Drawable drawable) {}
#Override
public void onLoadFailed(Drawable drawable) {}
#Override
public void onLoadCleared(Drawable drawable) {}
#Override
public void getSize(SizeReadyCallback cb) {}
#Override
public void removeCallback(#NonNull SizeReadyCallback cb) {}
#Override
public void setRequest(#Nullable Request request) {}
#Override
public Request getRequest() {}
#Override
public void onResourceReady(#NonNull T resource, #Nullable Transition<? super T> transition) {}
}
Now, when you create your FileTarget class, you can declare that it extends TargetAdapter rather than implements Target, and only override the methods that you care about:
public static class FileTarget<T extends Bitmap> extends TargetAdapter<Bitmap> {
// ...
#Override
public void getSize(SizeReadyCallback cb) {
cb.onSizeReady(width, height);
}
#Nullable
#Override
public Request getRequest() {
return null;
}
#Override
public void onResourceReady(#NonNull Bitmap resource, #Nullable Transition<? super Bitmap> transition) {
try {
FileOutputStream out = new FileOutputStream(fileName);
resource.compress(format, quality, out);
out.flush();
out.close();
onFileSaved();
} catch (IOException e) {
e.printStackTrace();
onSaveException(e);
}
}
// ...
}
My application requires to post data to a custom view and redraw the view according to sent data from MainActivity.
Here are sample code.
Custom view->
public class custom_view extends View {
private simple_line line;
public custom_view(Context context) {
super(context);
line=new simple_line(0);
}
#Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
line.draw_line(canvas);
}
#Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
setMeasuredDimension(getMeasuredWidth(),getMeasuredHeight());
}
private class simple_line{
private float x_val;
private Paint mpaint;
public simple_line(float val){
x_val=val;
mpaint=new Paint();
mpaint.setColor(Color.RED);
mpaint.setStyle(Paint.Style.STROKE);
mpaint.setStrokeWidth(10);
}
public float getX_val() {
return x_val;
}
public void setX_val(float x_val) {
this.x_val = x_val;
invalidate();
}
public void draw_line(Canvas canvas){
Path mpath=new Path();
mpath.moveTo(getWidth()/2+x_val,0);
mpath.lineTo(getWidth()/2+x_val,getHeight());
mpath.close();
canvas.drawPath(mpath,mpaint);
}
}
public void update_xval(float val){
line.setX_val(val);
}
}
Here is Main activity->
public class MainActivity extends AppCompatActivity{
private Button button;
private custom_view myview;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
button=(Button)findViewById(R.id.mybutton);
myview=(custom_view)findViewById(R.id.myview);
myview=new custom_view(this);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myview.update_xval(100);
}
});
}
}
I am unable to update x_val when button is pressed.Where am I going wrong.What needs to be done to update x_val?
there is an issue in draw_line(Canvas canvas) function. you are not calling this function in custom_view. you need to invalidate() after draw_line(Canvas canvas)called.
I am trying to fade in a TextureView but for some reason its not animating. It just simply pops in the video, no fade at all and i dont really know why that is because after some research i have found that TextureView can be animated normally.
Here is my code, i hope you guys can give me a pointer in the right direction.
PS, i have left out all irrelevant code that does not concern itself with the textureview and the animation.
public class MainActivity extends AppCompatActivity implements MediaPlayer.OnPreparedListener, MediaPlayer.OnCompletionListener
{
private static final String TAG = MainActivity.class.getSimpleName();
private MediaPlayer mediaPlayer1;
private ArrayList<Uri> videoUris = new ArrayList<>();
private int current_video_index = 0;
private TextureView textureView;
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
textureView = (TextureView) findViewById(R.id.textureView);
mediaPlayer1 = new MediaPlayer();
mediaPlayer1.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer1.setOnPreparedListener(this);
mediaPlayer1.setOnCompletionListener(this);
initVideoUris();
initNewVideo();
}
private void startVideo(final Uri uri)
{
textureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener()
{
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height)
{
try {
mediaPlayer1.setDataSource(MainActivity.this, uri);
mediaPlayer1.setSurface(new Surface(surface));
mediaPlayer1.setOnCompletionListener(MainActivity.this);
mediaPlayer1.setOnPreparedListener(MainActivity.this);
mediaPlayer1.setAudioStreamType(AudioManager.STREAM_MUSIC);
mediaPlayer1.prepareAsync();
} catch (IOException e) {
e.printStackTrace();
}
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height)
{
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface)
{
return false;
}
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface)
{
}
});
}
#Override
public void onPrepared(MediaPlayer mp)
{
mp.start();
fadeInView(textureView);
}
#Override
public void onCompletion(MediaPlayer mp)
{
if (!moreVideosAvailable())
{
finish();
}
}
private boolean moreVideosAvailable()
{
return current_video_index < videoUris.size();
}
private void fadeInView(View view)
{
view.setAlpha(0f);
view.setVisibility(View.VISIBLE);
view.animate().alpha(1f).setDuration(2000).setListener(null).start();
}
}
I solved it by making a videoPlayerFragment that uses TextureView as the surface for displaying the video. Then simply animate the whole fragment instead of the textureView.
good afternoon I have two classes, one that implement one progressBar this class I intend to create a public method that will run startActivity(). the main class would have a button that would call the method. However this giving error null reference.
ProgressBarUtil:
public class ProgressBarUtil extends BaseActionBarActivity {
private ProgressBar progressBar;
private Handler handler;
private int progress;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.progress_bar_util);
inicializaComponentes();
}
public void inicializaComponentes() {
progressBar = (ProgressBar) findViewById(R.id.progressBar);
progressBar.setIndeterminate(true);
handler = new Handler();
executaProgressbar();
}
public void startProgressBar(){
startActivity(new Intent(getBaseContext(),ProgressBarUtil.class));
}
private void executaProgressbar() {
new Thread(new Runnable() {
#Override
public void run() {
try {
Thread.sleep(1000);
finish();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
class that will call the method starProgressBar
The other class
#Override
public void onClick(View view) {
int id = view.getId();
if (btnDownload.getId() == id){
new ProgressBarUtil().startProgressBar();
}
}
I have 3 classes, class GameThread that extend thread, class Main that extend Activity, class GameView that extend surfaceview implement surfaceHolder.onCallback:
public class Main extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.i("oncreate","ONcreate");
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.gameloopxml);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items to the action bar if it is present.
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
}
Class GameThread:
public class GameThread extends Thread {
GameView gameview;
SurfaceHolder surfaceHolder;
public boolean running;
public GameThread(SurfaceHolder surfaceHolder,GameView gameview){
this.gameview=gameview;
running=false;
this.surfaceHolder=surfaceHolder;
}
#Override
public void run() {
Canvas c;
//super.run();
while(running){
c=null;
try{
c=surfaceHolder.lockCanvas();
synchronized (surfaceHolder) {
if(c!=null){
c.drawColor(Color.BLACK);
gameview.draw(c);
gameview.update();
}
}
}finally{
if(c!=null){
surfaceHolder.unlockCanvasAndPost(c);
}
}
}
}
}
Class GameView:
public class GameView extends SurfaceView implements SurfaceHolder.Callback{
GameThread gamethread;
Paint p=new Paint();
int x,y;
Bitmap bitmap;
public GameView(Context context, AttributeSet attrs) {
super(context, attrs);
Log.i("surface", "constructor");
bitmap=BitmapFactory.decodeResource(getResources(), R.drawable.ic_launcher);
//super(context);
p.setColor(Color.RED);
//add the callback this in order to intercept events
getHolder().addCallback(this);
//create the gameloop
//make it foccusable so it can handle events
setFocusable(true);
}
#Override
public void surfaceChanged(SurfaceHolder holder, int format, int width,
int height) {
Log.i("surface", "changed");
}
#Override
public void surfaceCreated(SurfaceHolder holder) {
Log.i("surface", "created");
gamethread=new GameThread(getHolder(),this);
gamethread.running=true;
gamethread.start();
}
#Override
public void surfaceDestroyed(SurfaceHolder holder) {
Log.i("surface", "destroyed");
boolean tryagain=false;
while(tryagain){
try {
gamethread.join();//destroy the thread
tryagain=false;
gamethread.running=false;
} catch (InterruptedException e) {
//try again shutting down the thread
}
}
}
public void draw(Canvas c) {
c.drawRect(x, y, x+80, y+80, p);
}
public void update(){
x++;
y++;
}
}
Everything works fine in my code except that whenever I press the back or home key I am getting a Nullpointer exception in the logcat.