I am trying to make a remote control for a Google TV.
I want to change the text I have in a layout (TextView statusText) with connected when the device has successfully connected. But I get an exception when I try to do this:
"07-07 22:42:20.870: E/AndroidRuntime(5750):android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views.
"Appreciate any help/pointers
Here is my MainActivity.java and main.xml:
MainActivity.java:
package uk.co.mypack.gtvremote;
//imports removed for paste
public class MainActivity extends Activity implements ClientListener{
private AnymoteSender anymoteSender;
private TextView statusText;
protected AnymoteClientService mAnymoteClientService;
private static String statusPrefix = "Status: ";
private Context mContext;
private ProgressBar progressBar;
private Handler handler;
private TouchHandler touchPadHandler;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
progressBar = (ProgressBar) findViewById(R.id.a_progressbar);
progressBar.setVisibility(View.VISIBLE);
mContext = this;
ImageButton upArrowButton = (ImageButton) findViewById(R.id.upArrow);
ImageButton leftArrowButton = (ImageButton) findViewById(R.id.leftArrow);
ImageButton centreButton = (ImageButton) findViewById(R.id.centreButton);
ImageButton rightArrowButton = (ImageButton) findViewById(R.id.rightArrow);
ImageButton downArrowButton = (ImageButton) findViewById(R.id.downArrow);
upArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_UP);
}
});
leftArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_LEFT);
}
});
centreButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_CENTER);
}
});
rightArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_RIGHT);
}
});
downArrowButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
sendKeyEvent(KeyEvent.KEYCODE_DPAD_DOWN);
}
});
handler = new Handler();
// Bind to the AnymoteClientService
Intent intent = new Intent(mContext, AnymoteClientService.class);
bindService(intent, mConnection, Context.BIND_AUTO_CREATE);
statusText = (TextView) findViewById(R.id.statusText);
}
/** Defines callbacks for service binding, passed to bindService() */
private ServiceConnection mConnection = new ServiceConnection() {
/*
* ServiceConnection listener methods.
*/
#Override
public void onServiceConnected(ComponentName name, IBinder service) {
mAnymoteClientService = ((AnymoteClientService.AnymoteClientServiceBinder) service)
.getService();
mAnymoteClientService.attachClientListener(MainActivity.this);
}
#Override
public void onServiceDisconnected(ComponentName name) {
mAnymoteClientService.detachClientListener(MainActivity.this);
mAnymoteClientService = null;
}
};
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.main, menu);
return true;
}
#Override
public void onConnected(AnymoteSender anymoteSender) {
if (anymoteSender != null) {
// Send events to Google TV using anymoteSender.
// save handle to the anymoteSender instance.
this.anymoteSender = anymoteSender;
//THIS IS WHERE I AM TRYING TO SET THE TEXTVIEW
TextView localStatusText = (TextView) findViewById(R.id.statusText);
localStatusText.setText(statusPrefix + "Connected to GoogleTV");
//ABOVE IS WHERE I AM TRYING TO SET THE TEXTVIEW
// Attach touch handler to the touchpad view
touchPadHandler = new TouchHandler(
findViewById(R.id.touchPad), Mode.POINTER_MULTITOUCH, anymoteSender);
} else {
statusText.setText(statusPrefix + "Connection attempt failed, cant find send handler");
//attempt to connect again?
//attemptToConnect();
}
// Hide the progressBar once connection to Google TV is established.
handler.post(new Runnable() {
public void run() {
progressBar.setVisibility(View.INVISIBLE);
}
});
}
#Override
public void onDisconnected() {
// show message to tell the user about disconnection.
statusText.setText(statusPrefix + "Disconnected");
// Try to connect again if needed. This may be need to be done via button
attemptToConnect();
this.anymoteSender = null;
}
#Override
public void onConnectionError() {
// show message to tell the user about disconnection.
statusText.setText(statusPrefix + "Connection error encountered");
// Try to connect again if needed.
attemptToConnect();
this.anymoteSender = null;
}
#Override
protected void onDestroy() {
if (mAnymoteClientService != null) {
mAnymoteClientService.detachClientListener(this);
}
unbindService(mConnection);
super.onDestroy();
}
public void attemptToConnect()
{
//stub to invoke connection attempt
}
private void sendKeyEvent(final int keyEvent) {
// create new Thread to avoid network operations on UI Thread
if (anymoteSender == null) {
Toast.makeText(MainActivity.this, "Waiting for connection",
Toast.LENGTH_LONG).show();
return;
}
anymoteSender.sendKeyPress(keyEvent);
}
}
Main.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/LinearLayout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<TextView
android:id="#+id/control_message"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="#dimen/padding_medium"
android:text="#string/control_msg"
android:textSize="90dp"
tools:context=".MainActivity" />
<LinearLayout
android:id="#+id/middlePanel"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
<TextView
android:id="#+id/statusText"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="Status: Disconnected - startup"
android:textSize="20dp" />
<ImageView
android:id="#+id/touchPad"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="#drawable/greysquare"
/>
<LinearLayout
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" >
<ImageButton
android:id="#+id/upArrow"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#drawable/blackuparrow" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal" >
<ImageButton
android:id="#+id/leftArrow"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#drawable/blackleftarrow" />
<ImageButton
android:id="#+id/centreButton"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#drawable/emptycircle"
android:paddingBottom="10dp"
android:paddingLeft="10dp"
android:paddingRight="10dp"
android:paddingTop="10dp" />
<ImageButton
android:id="#+id/rightArrow"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#drawable/blackrightarrow" />
</LinearLayout>
<ImageButton
android:id="#+id/downArrow"
android:layout_width="150dp"
android:layout_height="150dp"
android:background="#drawable/blackdownarrow" />
</LinearLayout>
</LinearLayout>
<ProgressBar
android:id="#+id/a_progressbar"
style="#android:style/Widget.ProgressBar.Large"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center" />
</LinearLayout>
The onConnected() callback is not called on the Main UI thread, but on a separate thread that is used by the Service. So it is not able to access the TextView created in Main UI thread. What you should do is create a Handler in the main UI thread and then use that handler to post a runnable that makes changes to the TextView. You can read more about Handlers on the Android developer site.
Related
I've to just show a count down fragment at certain trigger which includes a progress bar that reduces count on tick of a countdown timer.
I'm using following simple steps:
Call startTimer() in onCreateView of fragment.
In startTimer() define a CountDownTimer class where onTick method reduces progress of a ProgressBar and onFinish shows a Toast message.
Start this timer on UI thread using getActivity().runOnUIThread()
Problem is the timer executes but ProgressBar keeps showing indeterminate progress i.e. it remains unchanged. Toast is also seen at the finish but nothing changes for progress bar.
What's going wrong here?
Below is the current code:
public class IncomingRequestFragment extends Fragment {
private OnFragmentInteractionListener mListener;
private FrameLayout root;
private CountDownTimer mCountDownTimer;
private ProgressBar mProgressBar;
public IncomingRequestFragment() {
// Required empty public constructor
}
public static IncomingRequestFragment newInstance(String param1, String param2) {
IncomingRequestFragment fragment = new IncomingRequestFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
int progress=25;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
root = (FrameLayout) inflater.inflate(R.layout.fragment_incoming_request, container, false);
mProgressBar = (ProgressBar) root.findViewById(R.id.countDown);
progress = mProgressBar.getMax();
mProgressBar.setIndeterminate(false);
mProgressBar.setProgress(progress);
startTimer();
return root;
}
private void startTimer() {
mCountDownTimer = new CountDownTimer(25000,1000) {
#Override
public void onTick(long millisUntilFinished) {
mProgressBar.setProgress(progress--);
Log.d("count",String.valueOf(progress));
}
#Override
public void onFinish() {
Toast.makeText(getContext(),"You just missed a trip!",Toast.LENGTH_SHORT).show();
}
};
getActivity().runOnUiThread(new Runnable() {
#Override
public void run() {
mCountDownTimer.start();
}
});
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
Layout XML:
<FrameLayout 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"
tools:context="com.taxiwaxidriver.ui.fragments.IncomingRequestFragment">
<ProgressBar
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/countDown"
android:layout_margin="60dp"
android:max="25"
android:progressTint="#android:color/holo_blue_dark"
android:progressBackgroundTint="#android:color/holo_blue_dark"
android:progress="25"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:padding="12dp"
android:weightSum="2"
android:orientation="horizontal">
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:textColor="#color/white"
android:text="REJECT"
android:id="#+id/rideLater"
android:background="#drawable/rounded_button_left"/>
<Button
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="ACCEPT"
android:textColor="#color/white"
android:background="#drawable/rounded_button_right"
android:id="#+id/rideNow"/>
</LinearLayout>
</FrameLayout>
Output of above code is:
05-07 10:49:05.623 21455-21455 D/count: 24
05-07 10:49:06.626 21455-21455 D/count: 23
05-07 10:49:07.636 21455-21455 D/count: 22
05-07 10:49:08.653 21455-21455 D/count: 21
.
.
.
05-07 10:49:28.917 21455-21455 D/count: 1<br>
mistake was missing the style attribute for ProgressBar defined in XML
style=""?android:attr/progressBarStyleHorizontal"
Thanks to pskink's comment
I am trying to implement "Swipe to load more" method in my application but I got that error when I swipe down in the first time. This is what it showed in the console:
E/SwipeRefreshLayout: Got ACTION_MOVE event but don't have an active
pointer id. E/SwipeRefreshLayout: Got ACTION_MOVE event but don't have
an active pointer id. E/SwipeRefreshLayout: Got ACTION_MOVE event but
don't have an active pointer id. E/SwipeRefreshLayout: Got ACTION_MOVE
event but don't have an active pointer id.
This is my layout code:
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
tools:context="training.com.chatgcmapplication.ChatActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<android.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<android.support.v4.widget.SwipeRefreshLayout
android:id="#+id/swipeLayout"
android:layout_width="match_parent"
android:layout_height="380dp">
<ListView
android:id="#+id/listMessage"
android:layout_width="match_parent"
android:layout_height="380dp"
android:layout_alignParentLeft="false"
android:layout_alignParentTop="false"
android:divider="#null"
android:listSelector="#android:color/transparent"
android:stackFromBottom="true"
android:transcriptMode="alwaysScroll" />
</android.support.v4.widget.SwipeRefreshLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:weightSum="1">
<EditText
android:id="#+id/txt_chat"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="0.92"
android:inputType="text" />
<Button
android:id="#+id/btn_send"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginLeft="30dp"
android:text="#string/btn_send" />
</LinearLayout>
</LinearLayout>
</android.support.design.widget.CoordinatorLayout>
And when I swipe in the second time: it load double data.
This is ChatActivity, what implement that method:
public class ChatActivity extends AppCompatActivity implements View.OnClickListener, SwipeRefreshLayout.OnRefreshListener {
private static EditText txt_chat;
private String registId;
private String chatTitle;
private MessageSender mgsSender;
private int userId;
private DatabaseHelper databaseHelper;
private TimeUtil timeUtil;
private MessageAdapter messageAdapter;
private int offsetNumber = 5;
private SwipeRefreshLayout swipeRefreshLayout;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_chat);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
if (getSupportActionBar() != null) {
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
Button btn_send = (Button) findViewById(R.id.btn_send);
txt_chat = (EditText) findViewById(R.id.txt_chat);
swipeRefreshLayout = (SwipeRefreshLayout) findViewById(R.id.swipeLayout);
swipeRefreshLayout.setOnRefreshListener(this);
ListView lv_message = (ListView) findViewById(R.id.listMessage);
timeUtil = new TimeUtil();
databaseHelper = DatabaseHelper.getInstance(getApplicationContext());
btn_send.setOnClickListener(this);
Bundle bundle = getIntent().getExtras();
chatTitle = bundle.getString("titleName");
if (getIntent().getBundleExtra("INFO") != null) {
chatTitle = getIntent().getBundleExtra("INFO").getString("name");
this.setTitle(chatTitle);
} else {
this.setTitle(chatTitle);
}
registId = bundle.getString("regId");
userId = databaseHelper.getUser(chatTitle).getUserId();
List<Message> messages = databaseHelper.getLastTenMessages(AppConfig.USER_ID, databaseHelper.getUser(chatTitle).getUserId(), 0);
messageAdapter = new MessageAdapter(getApplicationContext(), R.layout.chat_item, (ArrayList<Message>) messages);
LocalBroadcastManager.getInstance(this).registerReceiver(onNotice, new IntentFilter("Msg"));
if (messages.size() > 0) lv_message.setAdapter(messageAdapter);
}
private BroadcastReceiver onNotice = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String message = intent.getStringExtra("message");
try {
Message messageObj = new Message();
messageObj.setMessage(message);
messageObj.setUserId(userId);
messageObj.setSender_id(AppConfig.USER_ID);
messageObj.setExpiresTime(timeUtil.formatDateTime(timeUtil.getCurrentTime()));
messageAdapter.add(messageObj);
} catch (ParseException e) {
e.printStackTrace();
}
messageAdapter.notifyDataSetChanged();
}
};
#Override
public void onBackPressed() {
super.onBackPressed();
finish();
}
#Override
protected void onDestroy() {
LocalBroadcastManager.getInstance(this).unregisterReceiver(onNotice);
super.onDestroy();
}
private static MessageSenderContent createMegContent(String regId, String title) {
String message = txt_chat.getText().toString();
MessageSenderContent mgsContent = new MessageSenderContent();
mgsContent.addRegId(regId);
mgsContent.createData(title, message);
return mgsContent;
}
#Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.btn_send:
String message = txt_chat.getText().toString();
databaseHelper = DatabaseHelper.getInstance(getApplicationContext());
mgsSender = new MessageSender();
new AsyncTask<Void, Void, Void>() {
#Override
protected Void doInBackground(Void... params) {
MessageSenderContent mgsContent = createMegContent(registId, AppConfig.USER_NAME);
mgsSender.sendPost(mgsContent);
return null;
}
}.execute();
databaseHelper.addMessage(message, timeUtil.getCurrentTime(), userId, AppConfig.USER_ID);
txt_chat.setText("");
try {
Message messageObj = new Message();
messageObj.setMessage(message);
messageObj.setUserId(AppConfig.USER_ID);
messageObj.setSender_id(userId);
messageObj.setExpiresTime(timeUtil.formatDateTime(timeUtil.getCurrentTime()));
messageAdapter.add(messageObj);
} catch (ParseException e) {
e.printStackTrace();
}
messageAdapter.notifyDataSetChanged();
break;
}
}
#Override
public void onRefresh() {
swipeRefreshLayout.setRefreshing(true);
List<Message> messages = databaseHelper.getLastTenMessages(AppConfig.USER_ID, databaseHelper.getUser(chatTitle).getUserId(), offsetNumber);
messageAdapter.insertToTheFirst(messages);
messageAdapter.notifyDataSetChanged();
offsetNumber += 5;
Log.i("Offset number", offsetNumber + "");
swipeRefreshLayout.setRefreshing(false);
}
}
UPDATE ISSUE's REASON
I found the reason of that issue. It due to I force the listview scroll to the bottom when it init with this code :
android:stackFromBottom="true"
I replace that code with this but still have same issue:
lv_message.post(new Runnable() {
#Override
public void run() {
lv_message.setSelection(lv_message.getCount() -1);
}
});
I think the problem is , You are trying to show , dismiss and refresh swipeRefreshLayout inside the same method i.e onRefresh().
Make seperate methods for showing and dismissing dialog and invoke them from the place where they are required as I have done below:
#Override
public void onRefresh() {
// refresh code here.
}
#Override
public void showDialog() {
swipeRef.post(new Runnable() {
#Override
public void run() {
if(swipeRef != null)
swipeRef.setRefreshing(true);
}
});
}
#Override
public void dismissDialog() {
if(swipeRef!=null && swipeRef.isShown() )
swipeRef.setRefreshing(false);
}
I put a progress bar in my android studio project , and want it to move , but i don't know how to do that . I tried to look for tutorials on YouTube and follow along , but that didn't work out for me. Then i looked on google for tutorials but that didn't work for me . When i search them up i only see tutorials with the circle loading bars and I want mine to be horizontal . I only know where to start the loading bar but i really want to know how to make it move and then go into the game . I don't have any code because when i seen that it wasn't working i just deleted it . If anybody has a good tutorial or code that could help me i would appreciate very much . Thanks.
have very easy way to use progress bar within whole code
custom_progressbar.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:orientation="vertical">
<ProgressBar
android:id="#+id/progressbar_view"
android:layout_width="#dimen/_70sdp"
android:layout_height="#dimen/_70sdp"
android:indeterminate="true"
android:padding="#dimen/_10sdp" />
</RelativeLayout>
CustomProgressBar.java
public class CustomProgressBar extends Dialog {
Activity activity;
public CustomProgressBar(Activity act) {
super(act);
this.activity = act;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.custom_progressbar);
setCancelable(false);
// set backgroung transparent
getWindow().setBackgroundDrawable(new ColorDrawable(android.graphics.Color.TRANSPARENT));
}
}
and apply whenever you want activity or fragment
public CustomProgressBar customProgressBar;
customProgressBar = new CustomProgressBar(context);
customProgressBar.show();
public class MainActivity extends AppCompatActivity {
private ProgressBar progressBar;
private TextView loading;
private int progressStatus = 0;
private Handler pHandler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
loading = (TextView) findViewById(R.id.loading);
new Thread(new Runnable() {
#Override
public void run() {
while (progressStatus < 100) {
progressStatus ++;
android.os.SystemClock.sleep(50);
pHandler.post(new Runnable() {
#Override
public void run() {
progressBar.setProgress(progressStatus);
}
});
}
pHandler.post(new Runnable() {
#Override
public void run() {
loading.setText("COMPLETE");
}
});
}
}).start();
}
This code works. You should be able to use the default XML code for the ProgressBar. You will need to add the elements, for example, the actual progress bar in the visual design. You will need to add an extra '}' at the end I think.
Check this: Infact you have to code for the horizontal progress bar movement and set its progress, it's specially used in downloading to show percentage of operation completion.
XML code:
<ProgressBar
android:id="#+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="23dp"
android:layout_marginTop="20dp"
android:indeterminate="false"
android:max="100"
android:minHeight="50dp"
android:minWidth="200dp"
android:progress="1" />
java code:
public class MainActivity extends Activity {
private ProgressBar progressBar;
private int progressStatus = 0;
private TextView textView;
private Handler handler = new Handler();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar1);
textView = (TextView) findViewById(R.id.textView1);
//Long operation by thread
new Thread(new Runnable() {
public void run() {
while (progressStatus < 100) {
progressStatus += 1;
//Update progress bar with completion of operation
handler.post(new Runnable() {
public void run() {
progressBar.setProgress(progressStatus);
textView.setText(progressStatus+"/"+progressBar.getMax());
}
});
try {
// Sleep for 200 milliseconds.
//Just to display the progress slowly
Thread.sleep(200);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
// Inflate the menu; this adds items
//to the action bar if it is present.
getMenuInflater().inflate(R.menu.activity_main, menu);
return true;
}
}
.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:weightSum="50">
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="false"
android:id="#+id/Bar1"
android:max="10"
android:padding="15dp"
android:paddingTop="5dp"
android:paddingRight="20dp"
android:paddingLeft="20dp">
</ProgressBar>
<Button
android:id="#+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="startProgress"
android:text="Start" />
<ProgressBar
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/progressBar2"
android:layout_gravity="center_horizontal" />
</LinearLayout>
MainActivity.java
package com.example.dell.vlakna;
import android.app.Activity;
import android.os.Bundle;
import android.view.View;
import android.widget.ProgressBar;
import static com.example.dell.vlakna.R.layout.activity_main;
public class MainActivity extends Activity {
private ProgressBar bar;
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(activity_main);
bar = (ProgressBar) findViewById(R.id.Bar1);
}
public void startProgress(View view) {
bar.setProgress(0);
new Thread(new Task()).start();
}
class Task implements Runnable {
#Override
public void run() {
for (int i = 0; i <= 10; i++) {
final int value = i;
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
bar.setProgress(value);
}
}
}
}
this code run good.
Ok, so, i want to make something like this:
http://postimg.org/image/qs3okxitf/
Now, im using zbarscannerview like this:
public class BarKodScreen extends AppCompatActivity implements ZBarScannerView.ResultHandler {
private ZBarScannerView mView;
private BarcodeFormat barcodeFormatEAN13, barcodeFormatEAN8;
private List<BarcodeFormat> listaZaFormat = new ArrayList<BarcodeFormat>();
private ImageView img;
private LinearLayout lejout;
private View kamera;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_barkod);
mView = new ZBarScannerView(this);
lejout = (LinearLayout) findViewById(R.id.cameraPreview);
img = (ImageView) findViewById(R.id.cameraImageView);
kamera = (View) findViewById(R.id.zaKameru);
kamera = mView;
lejout.addView(kamera);
lejout.addView(kamera);
lejout.removeView(img);
lejout.addView(img);
barcodeFormatEAN13 = BarcodeFormat.EAN13;
barcodeFormatEAN8 = BarcodeFormat.EAN8;
listaZaFormat.add(barcodeFormatEAN13);
listaZaFormat.add(barcodeFormatEAN8);
mView.setFormats(listaZaFormat);
}
#Override
public void onResume() {
super.onResume();
mView.setResultHandler(this); // Register ourselves as a handler for scan results.
mView.startCamera(); // Start camera on resume
}
#Override
public void onPause() {
super.onPause();
mView.stopCamera(); // Stop camera on pause
}
#Override
public void handleResult(Result rawResult) {
// Do something with the result here
Log.v("GetCOntent", rawResult.getContents()); // Prints scan results
barKodZahtev(rawResult.getContents());
}
}
and my xml is:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/cameraPreview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<View
android:id="#+id/zaKameru"
android:layout_width="match_parent"
android:layout_height="150dp"/>
<ImageView
android:id="#+id/cameraImageView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:alpha="0.6"
android:src="#drawable/share" />
</LinearLayout>
What I dont understand is how to add the image(text from the screenshot) as a child view for my zbarscannerView.
Check out their rapository on github, they have something there.
I have managed to use an Asynctask with an indeterminate progress bar during screen rotation. Asynctask starts only once, progress bar is restored on rotation just as I wanted.
I have different layouts for portrait and layout orientations. Layouts include a button and a textview. The size and text color of textview in layout-land is different. And the orientation is landscape.
The problem is when I rotate the screen while asynctask is running, it cant update the textview in onPostExecute method. When I rotate, it recreates the activity with layout-land file. But why I cant update my Textview?
layout\activity_main.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">
<Button
android:text="Start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startClicked"
/>
<TextView
android:id="#+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
tools:context=".MainActivity" />
</LinearLayout>
layout-land\activity_main.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="horizontal">
<Button
android:text="Start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startClicked"
/>
<TextView
android:textSize="36dp"
android:textColor="#ff0000"
android:id="#+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
tools:context=".MainActivity" />
</LinearLayout>
MainActivity.java:
package com.example.asynctaskconfig;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
static String data;
static ProgressDialog pd;
MyAsyncTask task;
TextView tv;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.hello);
if (getLastNonConfigurationInstance() != null) {
task = (MyAsyncTask) getLastNonConfigurationInstance();
if (task != null) {
if (!(task.getStatus().equals(AsyncTask.Status.FINISHED))) {
showProgressDialog();
}
}
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (pd != null)
pd.dismiss();
if (task != null)
return (task);
return super.onRetainNonConfigurationInstance();
}
private void showProgressDialog() {
if (pd == null || !pd.isShowing()) {
pd = new ProgressDialog(MainActivity.this);
pd.setIndeterminate(true);
pd.setTitle("DOING..");
pd.show();
}
}
private void dismissProgressDialog() {
if (pd != null && pd.isShowing())
pd.dismiss();
}
public class MyAsyncTask extends AsyncTask<String, Void, Boolean> {
#Override
protected void onPreExecute() {
showProgressDialog();
}
#Override
protected Boolean doInBackground(String... args) {
try {
Thread.sleep(5000);
data = "result from ws";
} catch (Exception e) {
return true;
}
return true;
}
protected void onPostExecute(Boolean result) {
if (result) {
dismissProgressDialog();
updateUI();
}
}
}
private void updateUI() {
tv.setText(data == null ? "null" : data);
}
public void startClicked(View target) {
task = new MyAsyncTask();
task.execute("start");
}
}
What I have done is as follows:
1- Add android:freezesText="true" to all my TextViews. This enables TextViews to save their states on configuration changes.
2- Make your AsyncTask a static inner class.
3- Modify AsyncTask to keep a reference to the Activity it lives in. So AsyncTask can access UI widgets of Activity via this reference.
4- Here, it is important to keep a valid activity reference during screen rotations. So, override onDestroy method and unbind the Activity from AsyncTask. Thus, task wont keep the old(died) activity.
5- In onRetainNonConfigurationInstance, if task is still running, update its activity reference with the current activity, so it is successfully tied to new activity.
6- Finally, in onPostExecuteMethod, access the UI elements of the Activity via activity reference.
Complete Working Solution:
layout\activity_main.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">
<Button
android:text="Start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startClicked"
/>
<TextView
android:freezesText="true"
android:id="#+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
tools:context=".MainActivity" />
</LinearLayout>
layout-land\activity_main.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="horizontal">
<Button
android:text="Start"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="startClicked"
/>
<TextView
android:freezesText="true"
android:textSize="36dp"
android:textColor="#ff0000"
android:id="#+id/hello"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/hello_world"
tools:context=".MainActivity" />
</LinearLayout>
MainActivity.java:
package com.example.asynctaskconfig;
import android.app.Activity;
import android.app.ProgressDialog;
import android.os.AsyncTask;
import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
public class MainActivity extends Activity {
static ProgressDialog pd;
MyAsyncTask task;
TextView tv;
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.hello);
if (getLastNonConfigurationInstance() != null) {
task = (MyAsyncTask) getLastNonConfigurationInstance();
if (task != null) {
task.activity = this;
if (!(task.getStatus().equals(AsyncTask.Status.FINISHED))) {
showProgressDialog();
}
}
}
}
#Override
protected void onDestroy() {
super.onDestroy();
if (task != null) {
task.activity = null;
}
}
#Override
public Object onRetainNonConfigurationInstance() {
if (pd != null)
pd.dismiss();
if (task != null)
return (task);
return super.onRetainNonConfigurationInstance();
}
private void showProgressDialog() {
if (pd == null || !pd.isShowing()) {
pd = new ProgressDialog(MainActivity.this);
pd.setIndeterminate(true);
pd.setTitle("DOING..");
pd.show();
}
}
private void dismissProgressDialog() {
if (pd != null && pd.isShowing())
pd.dismiss();
}
static class MyAsyncTask extends AsyncTask<String, Void, String> {
MainActivity activity;
public MyAsyncTask(MainActivity activity) {
this.activity = activity;
}
#Override
protected void onPreExecute() {
activity.showProgressDialog();
}
#Override
protected String doInBackground(String... args) {
try {
Thread.sleep(8000);
return "data from ws";
} catch (Exception e) {
return "exception";
}
}
protected void onPostExecute(String result) {
activity.dismissProgressDialog();
activity.tv.setText(result == null ? "null" : result);
}
}
public void startClicked(View target) {
task = new MyAsyncTask(this);
task.execute("start");
}
}
The problem in your case is essentially that the TextView you are trying to change is no longer the TextView visible on the screen. The rotation caused Android to discard the old Activity and building a new one - complete with all views in the xml file. Thus, your TextView 'tv' is still part of the old activity, changes will just do nothing.
The easiest way now to get the behavior you want, is to just look again for the textview, i.e., use "findViewById" again in your updateUI method and you should be fine!
In this case you probably don't want the activity to be recreated on orientation changes. You may handle the orientation changes within your activity by adding this to your activity in your manifest:
android:configChanges="keyboardHidden|orientation"
Then override onConfigurationChanged:
#Override
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.hello);
}
Try this instead:
private void updateUI() {
final TextView tv = (TextView) findViewById(R.id.hello);
tv.setText(data == null ? "null" : data);
}
If that fails, is it possibly a timing-issue? That is, could it be the task completed during the orientation change? Just to be on the safe side, you could modify your onCreate method by adding an updateUI() call if task has finished:
#Override
public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.activity_main);
tv = (TextView) findViewById(R.id.hello);
if (getLastNonConfigurationInstance() != null) {
task = (MyAsyncTask) getLastNonConfigurationInstance();
if (task != null) {
if (!(task.getStatus().equals(AsyncTask.Status.FINISHED))) {
showProgressDialog();
} else
updateUI();
}
}
}