ImageView size valid only in onGlobalLayout() - java

I have a problem with getting the size of ImageView in an android app I'm making. I've read several solutions to this problem and none of them seem to solve my problem. I just want to get the size once at the beginning of the application like in onCreate() before loading anything to the ImageView (When it's empty.) and use it for the rest of my app.
The solution that almost works is the one with getViewTreeObserver().addOnGlobalLayoutListener.
I did that and it is getting me the right size and everything but only in the onGlobalLayout() method. All I want to do is to save that size in some class global variable but everything that happens in onGlobalLayout() stays there. It's like it has no effect on the rest of the class. I can't really write my whole application in that method it's ridiculous. Why is it happening and how can I fix that?
Also I tried the OnFocusChangeListener solution and it gives me the size only if I try to exit the app. As expected the focus is changed but this is not what I need.
Here is the code:
public class Fractaler extends Activity {
int finalWidth;
int finalHeight;
TextView txt;
ImageView fractal;
Fractal mandelbrot;
boolean viewIsMeasured = false;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
initialize();
}
private void initialize() {
txt = (TextView) findViewById(R.id.txt);
fractal = (ImageView) findViewById(R.id.mainImage);
fractal.getViewTreeObserver().addOnGlobalLayoutListener(new OnGlobalLayoutListener() {
#Override
public void onGlobalLayout() {
if (!viewIsMeasured) {
finalWidth=fractal.getWidth();
finalHeight=fractal.getHeight();
viewIsMeasured = true;
mandelbrot=new Fractal("Mandelbrot",finalWidth,finalHeight);
fractal.setImageBitmap(mandelbrot.create());
}
}
});
txt.setTextColor(Color.CYAN);
//txt.setText(String.valueOf(mandelbrot.getWidth())+" "+String.valueOf(mandelbrot.getHeight()));
}
#Override
protected void onStart() {
super.onStart();
}
}

Related

Can't get a game loop to work in Android Studio

So I understand the basics of java programming but when I'm trying to use my little knowledge in android studio it make everything harder having classes and different files needing to be referenced. Coming from python, when making a simple game I would define different functions, then run them in a game loop like
while running:
or something similar. I know to define something in java you go like
public void Example() {}
but when I use this in java, when I try to run the program my game either instantly crashes or doesnt load anything.
The code at the moment is
public class MainActivity extends AppCompatActivity {
//Variables
Boolean running = true;
public int years = 0;
//Setup Year Counter
TextView textView = (TextView) findViewById(R.id.year_counter);
//Advance Button
public void advance() {
ImageButton button = (ImageButton) findViewById(R.id.advance);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
years += 1;
textView.setText("" + years + "");
}
});
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Game Loop
while (running) {
advance();
}
}
}
And this results in the app not opening.
Any help at all would mean a lot to me.
Thanks in advance :)
Although I don't actually see a crash, since you didn't really upload one, I can see why you might think your app wont work.
What you are doing constantly in the while loop is that you are only setting the button's click listener over and over again.
public class MainActivity extends AppCompatActivity {
//Variables
Boolean running = true;
public int years = 0;
//Setup Year Counter
TextView textView;
ImageButton button;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// you set up your views once, after the layout is inflated
setUpViews();
// you initialize your buttons functionality
initClickEvents();
//Game Loop
while (running) {
// do other stuff
}
}
private void setUpViews() {
textView = (TextView) findViewById(R.id.year_counter);
button = (ImageButton) findViewById(R.id.advance);
}
private void initClickEvents() {
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
years += 1;
textView.setText("" + years + "");
}
});
}
}
I'd recommend working with a game engine.
If you want to stick with java, libGDX is an option.
But if language (and IDE) doesn't matter, than a better option is Godot. The reason why I recommend Godot over some of the more popular game engines is because it's open source, 100% free and plus GDScript (godot's scripting language) is heavily influenced by python.
If you want to make you own game engine in java check out this: https://java-design-patterns.com/patterns/game-loop/#
Keep in mind that the while loop is calling the advance method. So every loop you are setting the view for button and then setting an OnClickListener for it. Not everything needs to be in the while loop.
You must implement (override) the render method (it is called in the game loop).
I suggest trying a complete example in the documentation.
``
#Override
public void render() {
ScreenUtils.clear(0, 0, 0.2f, 1);
advance();
...
``

Call void method from another method in the same class

At first my main problem was at how to call a method from the same class, even tough I think I found a way to do this, it's not working as I expected, and I would like to know what would be the best approach to my case.
This is the code I'm working on:
public class EscolhaAtendimento extends AppCompatActivity {
private ViewPager mSlideViewPager;
private LinearLayout mDotLayout;
String TAG = "TasksSample";
private TextView[] mDots;
private SliderAdapter sliderAdapter;
Dialog myDialog;
#Override
public void onCreate (Bundle SavedInstanceState){
super.onCreate(SavedInstanceState);
setContentView(R.layout.escolha_atendimento);
mSlideViewPager = findViewById(R.id.slideViewPager);
mDotLayout = findViewById(R.id.dotsLayout);
sliderAdapter = new SliderAdapter(this);
mSlideViewPager.setAdapter(sliderAdapter);
addDotsIndicator(0);
mSlideViewPager.addOnPageChangeListener(viewListener);
myDialog = new Dialog(this);
}
public void addDotsIndicator(int position){
mDots = new TextView[8];
mDotLayout.removeAllViews();
for (int i= 0; i < mDots.length; i++){
mDots[i] = new TextView(this);
mDots[i].setText(Html.fromHtml("•"));
mDots[i].setTextSize(35);
mDots[i].setTextColor(getResources().getColor(R.color.colorTransparentWhite));
mDotLayout.addView(mDots[i]);
}
if (mDots.length > 0){
mDots[position].setTextColor(getResources().getColor(R.color.colorWhite));
}
}
ViewPager.OnPageChangeListener viewListener = new ViewPager.OnPageChangeListener(){
#Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
#Override
public void onPageSelected (int i) {
switch (i) {
case 0: {
myDialog.show();
}
addDotsIndicator(i);
}
}
#Override
public void onPageScrollStateChanged(int state) {
}
};
public void ShowPopup(View v) {
TextView txtclose;
//Button btnFollow;
myDialog.setContentView(R.layout.pop_upfinal);
txtclose = myDialog.findViewById(R.id.txtclose);
txtclose.setText("X");
//btnFollow = (Button) myDialog.findViewById(R.id.btnfollow);
txtclose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myDialog.dismiss();
}
});
myDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
myDialog.show();
}
}
This class is an activity that on user swipe, the text and image from the buttons will change, even though their IDs will stay the same. (That's controlled by another class, it's working well).
Now, I wanted the image button on the activity do something different depending on which page is selected, and that's why there is a initial switch on the onPageSelected method, inside the Page change listener. The image button in the layout has the android:onClick="ShowPopup" tag, and I guess that also complicates things for me, if I wanted it to do something different in that same activity? Also, calling it that way on the switch, every time I change pages, and go back to the first one the popup window will open, since my call is explicit there. (As I said, even tough I found a way to somehow call my method, or at least it's result, it's not working as I expected).
Edit
I tried then changing it like this, so that the button wouldn't rely on the android:onClick="ShowPopup" Tag, and also wouldn't need to call a void method directly on the switch:
Added
public ImageButton popupchoice;
And also this to onCreate method:
popupchoice = this.findViewById(R.id.imgslide1);
Inside the switch I called it like this to get the button ID:
popupchoice.setOnClickListener(image1);
And set the View.OnClickListener like this:
View.OnClickListener image1 = new View.OnClickListener() {
public void onClick(View v) {
TextView txtclose;
//Button btnFollow;
myDialog.setContentView(R.layout.pop_upfinal);
txtclose = myDialog.findViewById(R.id.txtclose);
txtclose.setText("X");
//btnFollow = (Button) myDialog.findViewById(R.id.btnfollow);
txtclose.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
myDialog.dismiss();
}
});
myDialog.getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
myDialog.show();
}
};
But that returns me:
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.view.View.setOnClickListener(android.view.View$OnClickListener)' on a null object reference at .EscolhaAtendimento$1.onPageSelected(EscolhaAtendimento.java:81) Line 81 is the one inside the switch with the popupchoice.setOnClickListener(image1).
This error happens on page change, when coming back to the first Page, and also the button click won't work anymore.
I think you can use
EscolhaAtendimento.this.ShowPopup from inside your switch.

SavedIntanceState.getBoolean() is making my app crash (i think)

My Goal:
So I need help putting a boolean primitives into a bundle and retrieving it from the bundle when there is a screen orientation change in Android. I am using that boolean value in a conditional statement that helps decide if 2 Button views (mTrueButton, mFalseButton) should be enabled or not. What i have so far is causing the app to shut down (aka crash) when there is a screen rotation. I think I am not retrieving or writing my boolean from my bundle or into my bundle correctly, and it is causing the app to crash.
How The App Should Works:
When a user touches the mTrueButton or mFalseButton button to answer a question, both buttons become disabled so the user is not allowed to answer again. I want those buttons to keep being disabled when the user answers and then rotates the screen.**
I know that when a user rotates their Android device, onDestroy() is called because runtime configuration changes take place, causing the app to be relaunched without having knowledge of it's previous state, (unless store my necessary data onto a bundle and pass it onto my onCreate method).
These are SOME global variables in my activity class
private int index = 0;
priavate Button mTrueButton,mFalseButton;
private static final String KEY_INDEX = "index";
private static final String BTTN_ENABLED = "bttnEnabled";
private boolean trueFalseButtonsEnabled = true;
These are SOME statements in my onCreate() method of the same activity class
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
Log.d(TAG, "onCreate(Bundle) called");
setContentView(R.layout.activity_quiz);
if(savedInstanceState != null) {
index = savedInstanceState.getInt(KEY_INDEX, 0);
changeButtonEnableStatus(savedInstanceState.getBoolean(BTTN_ENABLED,true));
}
mTrueButton = (Button)findViewById(R.id.true_button);
mFalseButton = (Button)findViewById(R.id.false_button);
mTrueButton.setOnClickListener(new View.OnClickListener(){
#Override
public void onClick(View v){
checkAnswer(true);
changeButtonEnableStatus(false);
}
});
mFalseButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
checkAnswer(false);
changeButtonEnableStatus(false);
}
});
}
These are SOME methods in the same activity class but not in my onCreate()
private void changeButtonEnableStatus(boolean bool){
trueFalseButtonsEnabled = bool;
mTrueButton.setEnabled(bool);
mFalseButton.setEnabled(bool);
}
#Override
public void onSaveInstanceState(Bundle savedInstanceState){
super.onSaveInstanceState(savedInstanceState);
Log.d(TAG,"onSavedInstanceState() called");
savedInstanceState.putInt(KEY_INDEX,index);
savedInstanceState.putBoolean(BTTN_ENABLED, trueFalseButtonsEnabled);
}
Note that:
index = savedInstanceState.getInt(KEY_INDEX, 0);
works properly. It is setting global variable "index" to equal to the int primitive what was stored in into keywork "KEY_INDEX".
However I don't think: changeButtonEnableStatus(savedInstanceState.getBoolean(BTTN_ENABLED,true)); is working properly. My app seems to crash when I include that statement and run the app, and then rotate the device.

Why doesnt ImageView work for me? Android

My code is the following:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//landscape
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.activity_game);
//initialize arks
new Timer().schedule(new TimerTask() {
#Override
public void run() {
initializeArks();
}
}, 2000);
}
Nothing special there, here comes the initializeArks():
public void initializeArks(){
iv= (ImageView) findViewById(R.id.imageView);
tw= (TypeWriter) findViewById(R.id.textView);
CountDownTimer timer = new CountDownTimer(10, 1000) {
public void onTick(long millisUntilFinished) {
}
public void onFinish() {
iv.setImageResource(R.drawable.loading);
}
}.start();
}
Keep in mind that this code is only for debugging, it doesnt do anything specific, but as I found out the problem is with the ImageView
Also i have my variables declared STATIC for the purpose of using them in my other classes:
public static ImageView iv;
public static TypeWriter tw;
My XML is a basic one:
<ImageView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/imageView"
android:layout_centerVertical="true"
android:layout_centerHorizontal="true" />
I would be grateful if somebody explained me why this doesnt work or would send me a working code snippet for me to use for only one purpose: displaying images.
Once i install the app on my phone and open it I wait exactly 4 seconds (thats how much im delaying it by yhe code above) and it simply crashes
I think that you should be execute the line
iv.setImageResource(R.drawable.loading);
inside UI thead. Look at this answer
Initialise your imageview in onCreate() method not in initializeArks() method and dont declare them as static like as follows:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//landscape
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE);
setContentView(R.layout.activity_game);
iv= (ImageView) findViewById(R.id.imageView); //<==== initialise in onCreate
tw= (TypeWriter) findViewById(R.id.textView);
//initialize arks
new Timer().schedule(new TimerTask() {
#Override
public void run() {
initializeArks();
}
}, 2000);
}
And declared variable as
public ImageView iv;
public TypeWriter tw;
I couldnt find a way to achive what I wanted so I looked in the big picture, and my scenario allowed me to not have an ImageView. Fortunately changing the layout background does the trick for me and it doesnt have any negative effects for my project. Thank you for your wasted time here
It seems like you do not run on the UI Thread.
Try to run inside your thread like this:
runOnUiThread(new Runnable() {
#Override
public void run() {
initializeArks();
}
});

How to stop textureview listener and display an image before closing app

I have a simple xml layout as follow with just a TextureView inside a FrameLayout.
And the following main activity:
public class MainActivity extends Activity
implements TextureView.SurfaceTextureListener {
private static final String TAG = "MainActivity";
// Local references to layout components
private TextureView mTxvCameraPreview;
// Camera manager
private CameraManager mCameraManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Initialize layout and components
setContentView(R.layout.activity_main);
mTxvCameraPreview = (TextureView)findViewById(R.id.txv_camera_preview);
// Camera managing
mCameraManager = new CameraManager(this);
mTxvCameraPreview.setSurfaceTextureListener(this);
}
#Override
protected void onResume() {
super.onResume();
if (mTxvCameraPreview.isAvailable())
mCameraManager.startPreview(mTxvCameraPreview.getSurfaceTexture());
}
#Override
protected void onPause() {
super.onPause();
mCameraManager.stopPreview();
}
#Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) {
mCameraManager.startPreview(surface);
}
#Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) {
mCameraManager.stopPreview();
return true;
}
#Override
public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) {} // Unused
#Override
public void onSurfaceTextureUpdated(SurfaceTexture surface) {} // Unused
}
For every frame I do some elaboration and it is OK.
What I want is to add a button which stops the camera and display in the TextureView a file load from sdcard.
A pseudo-code of this can be something like this:
public void onButtonClicked(View view) {
// stop the surface listener (it is needed?)
File imgFile = new File(“/sdcard/Images/elaboration_result.jpg”);
Bitmap myBitmap = BitmapFactory.decodeFile(imgFile.getAbsolutePath());
// show myBitmap on TextureView (if possible)
}
It is possible to do this without modifying the xml layout?
Thanks in advance!
You cannot set the content of a TextureView manually, I'm afraid. Your best bet is to make a new ImageView on top of your TextureView and set its image when you need to.
public void onButtonClicked(View view) {
// if we don't need to keep previewing new frames, stop the preview
mCameraManager.stopPreview();
// now, show our ImageView (which should be in front of the TextureView)
Bitmap myBitmap = BitmapFactory.decodeFile(file_path_here);
myImageView.setImageBitmap(myBitmap);
myImageView.setVisibility(View.VISIBLE);
}
No need to create another Image. Just use
textureView.getBitmap(file.getAbsolutePath);

Categories

Resources