Text View not getting updated. Seems fine while debugging - android

I have 2 virtually identical textviews in a layout that are updated by 2 similar bits of code. When the app runs (whether emulator or phone) one is updated but the other is not - but when run the same way in debug mode they are both updated. I've done lots of searching online, read lots of questions and answers and have tried various ways to update the textview, but the weird thing is this combination: it works in debug but not live, but another texview and a canvas are updated ok. Even a log message (placed in the same if clause as this textview update) works in debug but not when run on emulator (other log messages are being
displayed ok).
The layout also has a canvas used for a simple game and the relevant code is within the running of that game. The canvas is updated by a repeating runnable. I wanted to add a time limit to the game so thought I'd simply mark the start time and then check in the runnable the time since the start. The textview is intended to show the seconds passing - and it works in debug! Abbreviated version of code below.
Maybe it's something about timing, something not having time to run properly? It doesn't need another runnable, does it, seeing as it's managing to update the other textview ok? Should you do runnables within runnables?
Any help or guidance appreciated.
Layout
<LinearLayout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="#drawable/trees2"
android:orientation="vertical"
tools:context=".GameActivity" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="horizontal" >
<TextView
android:id="#+id/score"
android:layout_width="54dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical|center_horizontal"
android:text="0"
android:background="#drawable/border"
android:textAppearance="?android:attr/textAppearanceLarge" >
</TextView>
<TextView
android:id="#+id/countdown"
android:layout_width="54dp"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_vertical|center_horizontal"
android:text="0"
android:background="#drawable/border"
android:textAppearance="?android:attr/textAppearanceLarge" >
</TextView>
</LinearLayout>
<TextView
android:id="#+id/the_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:text="" />
<couk.jit.drawing.GameBoard
android:id="#+id/the_canvas"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_margin="20dip" />
</LinearLayout>
GameBoard.java
public class GameBoard extends View {
public GameBoard(Context context, AttributeSet aSet) {
super(context, aSet);
p = new Paint();
sprite1 = new Point(-1,-1);
m = new Matrix();
bm1 = BitmapFactory.decodeResource(getResources(), R.drawable.b1);
sprite1Bounds = new Rect(0,0, bm1.getWidth(), bm1.getHeight());
bm = BitmapFactory.decodeResource(getResources(),R.drawable.go_sign);
}
synchronized public boolean didItHitTarget() {
if .... return true;
else ... return false;
}
#Override
synchronized public void onDraw(Canvas canvas) {
canvas.drawRect(0, 0, getWidth(), getHeight(), p);
canvas.drawBitmap(bm3, sprite3.x, sprite3.y, null);
}
}
GameActivity.java
public class GameActivity extends Activity implements OnTouchListener {
private static final int FRAME_RATE = 20;
long startTime;
int timerCount = 0;
int score = 0;
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
h.postDelayed(new Runnable() {
#Override
public void run() {
initGfx();
}
}, 1000);
}
synchronized public void initGfx() {
Point p1, p2;
p1 = getStartPoint();
((GameBoard)findViewById(R.id.the_canvas)).setSprite1(p1.x, p1.y);
((View)findViewById(R.id.the_canvas)).setOnTouchListener(this);
frame.removeCallbacks(frameUpdate);
frame.postDelayed(frameUpdate, FRAME_RATE);
}
private Runnable frameUpdate = new Runnable() {
#Override
synchronized public void run() {
if (state == GameState.Ready)
updateReady();
if (state == GameState.Running)
updateRunning();
}
synchronized public void updateReady() {
frame.removeCallbacks(frameUpdate);
((GameBoard)findViewById(R.id.the_canvas)).invalidate();
startTime = System.nanoTime(); //set game start time
frame.postDelayed(frameUpdate, FRAME_RATE);
}
synchronized public void updateRunning() {
frame.removeCallbacks(frameUpdate);
if (((GameBoard)findViewById(R.id.the_canvas)).didItHitTarget()) {
score = score + 1;
//This works fine
((TextView)findViewById(R.id.score)).setText(Integer.toString(score));
Point p1;
p1 = getStartPoint();
((GameBoard)findViewById(R.id.the_canvas)).setSprite1(p1.x, p1.y);
}
float deltaTime = (System.nanoTime() - startTime) / 100000000000.0f;
int j = (int) deltaTime;
if ((int)deltaTime > timerCount) {
timerCount = timerCount + 1;
//This works in debug but does not update live
((TextView)findViewById(R.id.countdown)).setText(Integer.toString(timerCount));
Log.i(TAG, "Updating countdown");
}
((GameBoard)findViewById(R.id.the_canvas)).invalidate();
frame.postDelayed(frameUpdate, FRAME_RATE);
}
};

This was my solution in case anyone interested (or can still suggest better way!).
Going from blackbelt's point that certain parts of the code are simply not being entered, I thought that, although I couldn't find the reason, it must be something to do with the complexity of the timer clock combined with android's thread handling. So I created the timer using a separate thread, as in code here:
Android update TextView in Thread and Runnable

Related

Why does my Android app's view only update under a specific circumstance?

I am working on an Android app that is behaving oddly. One of the first things this app does upon start up, is to start a listening thread that listens to a "server" app, on the same device, to get data. Once this data is received by the listening thread, I use it to update the main view. This, however only works if my app is started after the server app.
First a few details. My app and the server app are on the same device. They communicate with each other via UDP (the server app is a port of a Windows application). My app uses fragments, but the view I want to update is not within a fragment. Now some code.
content_main.xml
<RelativeLayout>
<LinearLayout>
<ImageView
android:id="#+id/my_image_id"
android:src="#drawable/my_image" />
</LinearLayout>
<LinearLayout>
<!-- fragment code here -->
</LinearLayout>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState){
Listener myListener = new Listener(this);
Thread listenerThread = new Thread(myListener);
listenerThread.setName("My Listener Thread");
listenerThread.start();
}
}
Listener.java
public class Listener implements Runnable {
public Listener (Activity mainActivity) {this.mainActivity = mainActivity;}
private Activity mainActivity;
private ImageView myImageView;
private NewData newData;
#Override
public void run(){
while(true){
// Here is where my app gets the data from the server via UDP.
// This always works, I am always getting the correct data.
myImageView = (ImageView) mainActivity.findViewById(R.id.my_image_id);
mainActivity.runOnUiThread(new Runnable(){
#Override
public void run(){
if(newData == 1){
myImageView.setImageResource(R.drawable.new_image_01);
} else if(newData == 2){
myImageView.setImageResource(R.drawable.new_image_02);
} else {
myImageView.setImageResource(R.drawable.error_image);
}
}
});
}
}
}
Like I said, if I start my app after the server app, then this all works perfectly. The images always changes when the new data changes. However, if I start my app before the server app, or if I restart the server app while my app is still running, then the images never changes, even though I am still getting the correct data from the server app.
What can I do to make sure the view can update at any time?
EDIT: I moved where I get the view to outside of the while loop (as a test) and now the code doesn't work at all.
I gave it a try. apparently your code is working alright, if the thread starts after the activity is been initialized, i don't really know how your server app works so i tried to mimic something like this.
public class TestThreadActivity extends AppCompatActivity {
private Button restart_Button;
private ImageView imageView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test_thread);
Toolbar toolbar = (Toolbar) findViewById(R.id.my_custom_toolbar);
setSupportActionBar(toolbar);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
initializeUI();
}
private void initializeUI() {
restart_Button = (Button) findViewById(R.id.TestThreadActivity_restart_button);
imageView = (ImageView) findViewById(R.id.TestThreadActivity_imageView);
final Listener myListener = new Listener(this);
restart_Button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
imageView.setImageResource(R.drawable.piccolo);
final Thread listenerThread = new Thread(myListener);
listenerThread.setName("My Listener Thread");
listenerThread.start();
}
});
}
public class Listener implements Runnable {
public Listener (Activity mainActivity) {this.mainActivity = mainActivity;}
private Activity mainActivity;
private ImageView myImageView;
int newData = 0;
#Override
public void run(){
while(true){
// Here is where my app gets the data from the server via UDP.
// This always works, I am always getting the correct data.
newData = (new Random()).nextInt(3);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
myImageView = (ImageView) mainActivity.findViewById(R.id.TestThreadActivity_imageView);
mainActivity.runOnUiThread(new Runnable(){
#Override
public void run(){
System.out.println("newData: "+newData);
if(newData == 1){
myImageView.setImageResource(R.drawable.gohan);
} else if(newData == 2){
myImageView.setImageResource(R.drawable.goku);
} else {
myImageView.setImageResource(R.drawable.piccolo);
}
}
});
}
}
}
}
activity_test_thread.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context="activities.list.first.TestThreadActivity">
<include layout="#layout/my_custom_toolbar" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="4dp"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/TestThreadActivity_restart_button"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_weight="0.5"
android:text="Start"
android:contentDescription="this will restart the thread"
android:textAllCaps="false" />
</LinearLayout>
<ImageView
android:id="#+id/TestThreadActivity_imageView"
android:layout_width="match_parent"
android:layout_height="200dp"
android:src="#drawable/gohan" />
</LinearLayout>
</LinearLayout>
Output

Android GLES blocks UI update

DI have a GLES20 glsurfaceview that sends an fps status string to my main activity via a messageHandler which I want to display on the screen while the GL is running.
public static Handler messageHandler = new Handler() {
public void handleMessage(android.os.Message msg) {
String message = (String)msg.obj;
Globals.stats.setText(message);
Globals.stats.setVisibility(View.VISIBLE);
Globals.stats.invalidate();
}
};
Global.stats points to an onscreen textview.
During the time the GLES code is running and updating the screen the textview does not display. Once the GL rendering stops the textview does update with the last sent message and appears.
Is there anyway I can tell OpenGL or the surfaceview to yield and allow all the UI elements to update before continuing?
The layout is a relative layout with the textview on it, ie
<RelativeLayout
android:id="#+id/mainContent"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:focusableInTouchMode="true"
android:background="#ff000000"
android:gravity="fill">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="Medium Text"
android:id="#+id/statsTextView"
android:textColor="#ffffffff"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:background="#80000000" />
</RelativeLayout>
The GLES code is taken from many examples on the web. A glsurf class
public class GLSurf extends GLSurfaceView {
private final GLRenderer mRenderer;
public GLSurf(Context context) {
super(context);
setEGLContextClientVersion(2);
mRenderer = new GLRenderer(context);
setRenderer(mRenderer);
setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
}
#Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
mRenderer.onPause();
}
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
mRenderer.onResume();
}
}
And a public class
GLRenderer implements Renderer {
class that uses the
public void onDrawFrame(GL10 unused) {
to draw the GLES output and then uses
MainActivity.messageHandler.sendMessage(msg);
to send the fps status.
Is there any better way to get the textview to update while the GLES is running? The message does get sent and received because once the GL stops the textview is updated with the latest frame number and fps info.

Getting rid of the black screen

This question has probably been asked a lot many times.. Yes! I am facing the so called "black screen problem"..
My first activity is which the main activity, in which login is done, and this activity is followed by the next one which is a simple summary report of the user's account.
Now, for this report, i fetch the dataset, parse it and use it to build the table dynamically.
The villan black screen appears here!! The report, as in the table is rendered properly, but upon that screen, this black tranluscent layer keeps appearing.
I tried everything, using handlers, asynctask, translucent theme but nothing helps!! The villan still appears when my summary report loads. It goes away if i press the "Back" button, and the screen appears normal, as it is expected to be when it loads the first time.. I cant figure out what exactly is going wrong, whether, its my coding approach(dynamically generating the table) or it is an emulator problem or what.
My emulator details are as follows:
CPU:ARM
Target: Android 2.3.3
skin: WVGA800
sd card:1024M
Someone please rescue me!!
EDIT:
In my Second activity i do the following:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_summary_report);
ad = new AlertDialog.Builder(this).create();
boolean is = IsConnectedToNetwork();
if (is == true){
Handler execWS = new Handler();
execWS.post(callWS);//Fetch Data via web service and parse it
if(result != null){
Handler genUI = new Handler();
genUI.post(createUI);// generate the table based on "result". ie. in a for loop i create textviews and append it to the table layout
}
}
else{
Error = "Connection Failed. Try Again!";
}
if(Error.length() != 0){
ad.setMessage(Error);
ad.setTitle("Error..");
ad.show();
}
}
My Xml layout for the second activity..
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:baselineAligned="false"
android:gravity="center"
android:orientation="vertical">
<TableLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="5dip">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:contentDescription="#string/Logo"
android:src="#drawable/complogo"
android:gravity="top"/>
<TextView
android:id="#+id/lblLoginInfo"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center_horizontal"/>
</TableLayout>
<TableLayout
android:id="#+id/tblSummRep"
android:layout_width="wrap_content"
android:layout_height="0dp"
android:layout_weight="4"
android:padding="5dip">
</TableLayout>
<TableLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:padding="5dip">
<TextView
android:id="#+id/AppName"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/AppName"
android:textColor="#ff6600" />
<TextView
android:id="#+id/AppVersion"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:text="#string/AppVersion"
android:textColor="#ff6600" />
<TextView
android:id="#+id/Disclaimer"
android:gravity="center"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/Disclaimer"
android:textColor="#ff6600" />
</TableLayout>
</LinearLayout>
I append the textview to "tblSummRep"..
UPDATE
my asynctask class..
private class ShareWork extends AsyncTask<Void, Void, Boolean>{
ProgressDialog pDlg;
//String[][] result = null;
protected void onPreExecute(){
pDlg.setMessage("Please wait while the Report Loads...");
pDlg.show();
}
#Override
protected Boolean doInBackground(Void... params) {
boolean RetVal = false;
//my fetch code
return RetVal;
}
protected void onPostExecute(Boolean value){
if(value == true){
pDlg.setMessage("Please try again later!!");
pDlg.show();
}
else{
createUI.run();
}
}
}
I do not know what do you refer to by "black screen problem", but you state that you are fetching data (I guess from a remote server) and that happens. That sounds to me about blocking the UI thread. In which method of the activity are you fetching the data (maybe in the onCreate)? You should not perform time-consuming actions on the graphical thread, such as querying a remote server (which could take some seconds). In these cases always an AsyncTask should be used. The user should receive some feedback about what is going on in the background, so using a ProgressBar is normally recommendable (or at least a spinner).
You can take a look about how to solve a similar problem with an AsyncTask here.
Update
You have just posted your code. The problem lies (as I presumed) in this line of code:
execWS.post(callWS);//Fetch Data via web service and parse it
I guess this is a WS call. You are blocking here the UI thread. Create an AsyncTask the same way as the one of the link I provided, and upon completion of the task execute the rest of the code (display error or the dynamic table with the data).
Your AsyncTask could look like this (I have not tried it):
private class LoadTableTask extends AsyncTask<URL, Integer, Object> {
protected Object doInBackground(URL... urls) {
Handler execWS = new Handler();
execWS.post(urls[0]);//Fetch Data via web service and parse it
return result;
}
protected void onProgressUpdate(Integer... progress) {
//Not used in your case. It would be a good idea to create an undefined progress dialog in your case
}
protected void onPostExecute(Object result) {
if(result != null){
Handler genUI = new Handler();
genUI.post(createUI);// generate the table based on "result". ie. in a for loop i create textviews and append it to the table layout
}
}
}
Your onCreate() method would be replaced by:
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_summary_report);
ad = new AlertDialog.Builder(this).create();
boolean is = IsConnectedToNetwork();
if (is == true){
new LoadTableTask().execute(callWS);
}
else{
Error = "Connection Failed. Try Again!";
}
if(Error.length() != 0){
ad.setMessage(Error);
ad.setTitle("Error..");
ad.show();
}
}

Refresh screen while function is being executed

I have a simple function that counts from 0 to 5000 and does funky math with those numbers, and I would like to display progress bar showing where it is currently counting at (each iteration roughly takes 1 second).
My ProgressBar is the following
<TableRow
android:visibility="gone"
android:id="#+id/progress_bar_row" >
<ProgressBar
android:id="#+id/progress_bar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_span="2"
android:layout_height="wrap_content" />
</TableRow>
When user presses button my function starts, it makes the row visible, and I would like to refresh the ProgressBar with each iteration. My function is the following
private void find_number(View v)
{
String response = "";
TextView answer = (TextView)findViewById(R.id.answer);
TableRow progress_bar_row = (TableRow)findViewById(R.id.progress_bar_row);
ProgressBar progress_bar = (ProgressBar)findViewById(R.id.progress_bar);
progress_bar.setProgress(0);
progress_bar.setMax(5000);
progress_bar_row.setVisibility(View.VISIBLE);
answer.setText("");
for(int i = 0; i <= 5000; i++)
{
progress_bar.setProgress(i);
// a lot of heavy math is being performed here and answer is stored inside response string
}
//progress_bar.dismiss();
progress_bar_row.setVisibility(View.GONE);
answer.append(response);
}
I can't seem to figure out a way of going about refreshing the screen. I poked around and got a suggestion to use threads and handlers, I never used threads before, nor would I know how to change my function to adapt to thread use. Or if there is a simpler way without using any threads and simply calling some kind of refresh function I would appreciate it too. Any help or guidance would be very welcome. Thanks in advance
you should use AsyncTask for this problem.
private class YourTask extends AsyncTask<String, Integer, Long> {
protected Long doInBackground(String... strings) {
for(int i = 0; i <= 5000; i++)
{
// your code here
publishProgress((int) ((i / (float) 5000) * 100));
}
return i;
}
protected void onProgressUpdate(Integer... progress) {
progress_bar.setProgress(progress);
}
protected void onPostExecute(Long result) {
//Do something after this task finish
}
}

Problem with a Handler(ImageView)

I want to show 2 images (one when connected and the second when disconnected) and i'm using a handler to handle that, however, i'm not having any of the 2 images showen, don't know why :.
private Runnable handleUpdateStatus = new Runnable()
{
Boolean mRegistered;
public void run()
{
ImageView statusImageDisplay = (ImageView)findViewById(R.id.connected);
if (mRegistered)
{
statusImageDisplay.setImageDrawable(getResources().getDrawable(R.drawable.connected));
Log.i("CONNECTED","IMAGE SET");
}
else
{
statusImageDisplay.setImageDrawable(getResources().getDrawable(R.drawable.disconnected));
Log.i("DISCONNECTED","IMAGE SET");
}
}
};
Changing image code excerpt:
public void onRegistrationDone(String localProfileUri, long expiryTime) {
updateStatus("Enregistré au serveur.");
Log.d("SUCCEED","Registration DONE");
mRegistered = true;
mRegistrationUpdateHandler.removeCallbacks(handleUpdateStatus);
mRegistrationUpdateHandler.postDelayed(handleUpdateStatus, 4000);
}
What to do when the registration failed? this code:
public void onRegistrationFailed(String localProfileUri, int errorCode,String errorMessage) {
updateStatus("Enregistrement échoué. Veuillez vérifier vos paramètres.");
Log.d("ERROR REGISTRATION",errorMessage);
mRegistered = false;
mRegistrationUpdateHandler.removeCallbacks(handleUpdateStatus);
mRegistrationUpdateHandler.postDelayed(handleUpdateStatus, 2000);
}
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/sipLabel"
android:textSize="20sp"
android:layout_width="fill_parent"
android:layout_height="wrap_content" />
<ImageView android:id="#+id/connected" android:src="#drawable/connected"
android:layout_below="#id/sipLabel"
android:layout_width="fill_parent" android:scaleType="center"
android:layout_height="fill_parent" android:layout_weight="0.35"
android:gravity="center" android:visibility="invisible" />
</LinearLayout>
Any idea please of how solving this problem ?
Thank you very much.
You (must) have your mRegistered variable declared twice, and while you're setting the value for the global one (the one declared inside your Activity), you examine the value of the other one, declared inside your handleUpdateStatus Runnable.
You should clean up your code a bit:
remove the mRegistration declaration
from your Runnable implementation,
// Boolean mRegistered;
set the image drawable via resource
id,
and it will work:
private boolean mRegistered;
private Runnable handleUpdateStatus = new Runnable()
{
public void run()
{
ImageView statusImageDisplay = (ImageView) findViewById(R.id.connected);
if (mRegistered)
{
statusImageDisplay.setImageResource(R.drawable.connected);
Log.i("CONNECTED", "IMAGE SET");
}
else
{
statusImageDisplay.setImageResource(R.drawable.disconnected);
Log.i("DISCONNECTED", "IMAGE SET");
}
}
};
This is all you need to change (assuming, that your layout really contains an image with id connected).
Update
As about mRegistrationUpdateHandler, you should declare it as a global variable of your Activity (either final or initialized inside the onCreate method):
private final Handler mRegistrationUpdateHandler = new Handler();
or
private Handler mRegistrationUpdateHandler;

Categories

Resources