Dynamic android form from XML - android

I want to generate a form into my activity_main.xml ScrollView. XML is loaded and parsed correctly but when I'm trying to addView(LinearLayout) then it throws exception e. My application gets url of a XML file via push notification and then parses it. According to XML it then should generate a form and display it to the user. I used this as an example: https://www.ibm.com/developerworks/xml/tutorials/x-andddyntut/#l1
Here is my main activity:
public class MainActivity extends Activity {
// label to display gcm messages
TextView lblMessage;
Controller aController;
public ScrollView sv;
Button execute;
// Asyntask
AsyncTask<Void, Void, Void> mRegisterTask;
public static String name;
public static String email;
final Context context = this;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ScrollView sv = (ScrollView) findViewById(R.id.sv);
...
}
// Create a broadcast receiver to get message and show on screen
private final BroadcastReceiver mHandleMessageReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
String newMessage = intent.getExtras().getString(Config.EXTRA_MESSAGE);
// Waking up mobile if it is sleeping
aController.acquireWakeLock(getApplicationContext());
ScrollView sv = (ScrollView) findViewById(R.id.sv);
new DoInBackground(getApplicationContext(), sv).execute(newMessage);
// Releasing wake lock
aController.releaseWakeLock();
}
};
and here is my async class:
public class DoInBackground extends AsyncTask<String, Void, Void> {
Context mContext;
ScrollView mSv;
String tag = "DynamicFormXML";
XmlGuiForm theForm;
ProgressDialog progressDialog;
Handler progressHandler;
public DoInBackground(Context context, ScrollView sv) {
this.mContext = context;
this.mSv = sv;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(String... params) {
if (GetFormData(params[0])) {
DisplayForm();
}
else
{
Log.e(tag,"Couldn't parse the Form.");
AlertDialog.Builder bd = new AlertDialog.Builder(mContext);
AlertDialog ad = bd.create();
ad.setTitle("Error");
ad.setMessage("Could not parse the Form data");
ad.show();
}
return null;
}
protected void onPostExecute() {
}
private boolean DisplayForm()
{
try
{
final LinearLayout ll = new LinearLayout(mContext);
mSv.addView(ll); //Here it fails
ll.setOrientation(android.widget.LinearLayout.VERTICAL);
...
} catch (Exception e) { // Goes to here
Log.e(tag,"Error Displaying Form");
return false;
}
}
I think the context of the main activity and also the empty Scrollview in main activity are forwarded correctly (they are not null) but i'm not 100% sure. Any help/hints are appreciated! :)

You can not touch the GUI from a background thread (e.g. the one running the doInBackground method).
In an AsynTask, you can put the UI code in onPostExecute, wich is invoked on the UI thread with the result of doInBackground.
If you have intermediate results you can call publishProgress from doInBackground, this will trigger the invocation of onProgressUpdate on the UI thread, where you can update the UI.
See AsyncTask API for an example and more details on what must be done on which thread.

Solution
Reordering code (so GUI stuff would be done onPostExecute) worked. Also i had a problem with not getting to onPostExecute() but i had to change it to onPostExecute(Void result).
Now my code looks like this and works like a charm:
public class DoInBackground extends AsyncTask<String, Void, Void> {
Context mContext;
LinearLayout mLl;
String tag = "DynamicFormXML";
XmlGuiForm theForm;
ProgressDialog progressDialog;
Handler progressHandler;
public DoInBackground(Context context, LinearLayout ll) {
this.mContext = context;
this.mLl = ll;
}
#Override
protected void onPreExecute() {
super.onPreExecute();
}
#Override
protected Void doInBackground(String... params) {
getFormData(params[0]);
return null;
}
protected void onPostExecute(Void result) {
DisplayForm();
}
I also added ScrollView and LinearLayout to my activity_main.xml so DisplayForm() looks like that (if you want to follow the example i mentioned before ):
private void DisplayForm() {
try
{
// walk thru our form elements and dynamically create them, leveraging our mini library of tools.
int i;
for (i=0;i<theForm.fields.size();i++) {
if (theForm.fields.elementAt(i).getType().equals("text")) {
theForm.fields.elementAt(i).obj = new XmlGuiEditBox(mContext,(theForm.fields.elementAt(i).isRequired() ? "*" : "") + theForm.fields.elementAt(i).getLabel(),"");
mLl.addView((View) theForm.fields.elementAt(i).obj);
}
...

Related

Android ProgressDialog takes time to appear on screen

I have attached on click listener to a text view, inside on click listener a function say f1 is called and inside f1 another function say f2 is called.
Inside f2 I have created a android ProgressDialog object using current activity context, and called show function on progressDialog object. ProgressDialog takes time to appear on screen around 5-6 sec.
I have analyzed my code, but not able to understand why it takes this much time ?
textView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ShowSyncDialog(); - f1
}
});
public void ShowSyncDialog()
{
fnSyncOfflineData(); - f2
}
public void fnSyncOfflineData()
{
ProgressDialog progressDialog = new ProgressDialog(context);
progressDialog.show();
//other code
}
You should call your functions in an AsyncTask. Also ProgressDialog must be shown at the beginning of this works. Use something like this:
public class YourTask extends AsyncTask<String, Void, String> {
private Context mContext;
private ProgressDialog progressDialog;
public YourTask(Context context) {
super();
mContext = context;
progressDialog = new ProgressDialog(context);
progressDialog.setMessage("Your Message");
}
#Override
protected void onPreExecute() {
super.onPreExecute();
progressDialog.show();
}
#Override
protected String doInBackground(String... values) {
// If you want to use 'values' string in here
String values = values[0];
String yourResult = yourFunction();
return yourResult;
}
#Override
protected void onPostExecute(String result) {
progressDialog.dismiss();
// Your task has done
...
}
}
Then call this task with:
new YourTask(YourActivity.this).execute();
You can change return type of task doInBackground method. This is just an example, you can search about AsyncTask.
Good luck.

Delaying a Task and showing progress bar

I seem to be going round in circles.
I have some code that even on a Galaxy S3 takes a few seconds to run. Drags data from database.
I want to add a progress bar popup (spinning circle) around this to give the user that the app is doing something.
I have tried Asyntasks elsewhere in app and work fine but for this type the main UI is not waiting for the Asyntask to finish before moving on and so the new activity that is called does not have all the data it needs and crashes.
Is AsyncTask the best way round this or is there an easier way to Puase the main Activity, show a progress bar and then move on once the long deley has been completed.
Thanks for time
UPDATE
public class UpdateDetailsAsyncTask extends AsyncTask<Void, Void, Boolean> {
private Context context;
private TaskCallback callback;
private ArrayList<Object> object;
private ProgressDialog progress;
public UpdateDetailsAsyncTask (
Context pContext,
ArrayList<Object> pObject,
TaskCallback pCallback) {
context = pContext;
callback = pCallback;
object = pObject;
}
#Override
protected void onPreExecute() {
Log.i("AsyncTask", "onPreExecuted");
progress = new ProgressDialog(context);
progress.setMessage(context.getResources().getString(R.string.loading));
progress.show();
}
#Override
protected Boolean doInBackground(Void... params) {
Log.i("Archery", "AsyncTask Excuted");
Log.i("Archery Scorepad", "Building Continue Round Details");
// Save Data to Database
return true;
}
protected void onPostExecute(Boolean result) {
Log.i("AsyncTask", "onPostExuted");
progress.dismiss();
callback.startNewActivity();
}
}
Task is called from main Activity
new UpdateDetailsAsyncTask(this, ArrayListOfObjects, this).exute();
UPDATE 2
..
UPDATE 3
The Code that does some work calls an a method within a Util Class which in calls a database class. I have log messages showing for all the rows of data I am saving to the database. It starts correctly and runs through it but the onPostExecute() appears to be called before the database method has completed.
Is my issue that I have nested classes within the activity and the task appears to have completed when the class below it has not?
Thanks
You must change to the next activity in onPostExecute from Asyntask
Yes!
Here is a simple code of AsuncTask
private class LoadImageAction extends AsyncTask<String, String, String>{
private Course course;
private ProgressBar pb;
public LoadImageAction(Course course, ProgressBar pb){
this.course = course;
this.pb = pb;
}
#Override
protected void onPreExecute(){
}
#Override
protected String doInBackground(String... arg0) {
// TODO Auto-generated method stub
return null;
}
#Override
protected void onProgressUpdate(String... string){
}
#Override
protected void onPostExecute(String result){
}
}
You can run the action by
new LoadImageAction().execute();

multithreading UI : exceptionininitializererror and runtime exception android

I'm trying to make multithreading but I've got this stacktrace on few smartphones (SGS2) :
java.lang.ExceptionInInitializerError
at com.android.bordeaux.code.model.AnnouncersContainer.setLoudArrayFromJSON_online(AnnouncersContainer.java:68)
at com.android.bordeaux.code.SplashscreenActivity_Second$1.run(SplashscreenActivity_Second.java:55)
Caused by: java.lang.RuntimeException: Can't create handler inside thread that has not called Looper.prepare()
at android.os.Handler.<init>(Handler.java:121)
at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
at android.os.AsyncTask$InternalHandler.<init>(AsyncTask.java:421)
at android.os.AsyncTask.<clinit>(AsyncTask.java:152)
... 2 more
Here is my main activity (splashscreen) wich is making wait during few seconds in order to wait my asynctask finish:
public class SplashscreenActivity_Second extends Activity
{
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.splashscreen_second);
// thread
Thread splashThread = new Thread()
{
#Override
public void run()
{
try
{
int waited = 0;
if (isInternetOn())
{
// Here I'm making my asynctask !!
AnnouncersContainer.setLoudArrayFromJSON_online(getApplicationContext());
while (waited < 5000)
{
sleep(50);
waited += 40;
}
}
else
{
AnnouncersContainer.setLoudArrayFromXML_local(getApplicationContext());
while(waited < 5000)
{
sleep(50);
waited += 60;
}
}
}
catch( InterruptedException e )
{
e.printStackTrace();
}
finally
{
Intent intent_to_tabhost = new Intent(SplashscreenActivity_Second.this, MyTabActivity.class);
startActivity(intent_to_tabhost);
finish();
}
}
};
splashThread.start();
}
}
Here is my asynctask :
public class DownloadAnnouncers extends AsyncTask<Void, Integer, Boolean>
{
public static Boolean loadFinished = false;
//JSON variables..
private static String url = null;
Context context;
public DownloadAnnouncers(Context context)
{
this.context = context;
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
protected Boolean doInBackground(Void... params)
{
// fine retrieving all my JSON data in a global array..
}
#Override
protected void onPostExecute(Boolean downloadedArray)
{
super.onPostExecute(downloadedArray);
}
}
I know the problem is that I'm making multithreading in the UI but someone can tell me how to delete my looping thread and replacing it just with onPostExecute() method of my Asynctask ?? (it could be better to wait for asynctask finishes rather than making wait..)
Or may be help me to correct this bug with multithreading..
EDIT :
my asynctask :
public class DownloadAnnouncers extends AsyncTask<Void, Integer, Boolean>
{
public static Boolean loadFinished = false;
//JSON variables..
private static String url = null;
Context context;
public DownloadAnnouncers(Context context)
{
this.context = context;
}
#Override
protected void onPreExecute()
{
super.onPreExecute();
}
protected Boolean doInBackground(Void... params)
{
// fine retrieving all my JSON data in a global array..
}
#Override
protected void onPostExecute(Boolean downloadedArray)
{
super.onPostExecute(downloadedArray);
Intent intent_to_tabhost = new Intent(context, MyTabActivity.class);
intent_to_tabhost.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent_to_tabhost);
}
}
But now if I press cancel button I'm returning to my splashscreen activity.. and I cannot do ctx.finish() in onPostExecute method..
You can't create a Handler inside a Thread that has not called Looper.prepare(), just as your Error message says:
Can't create handler inside thread that has not called Looper.prepare()
The Handler is created within the AsyncTask, so it's not something you might see at first.
If you want to keep your code as it is you would fix it by moving the creation of the AsyncTask to outside your custom Thread.
However, there is no need for you to create the thread, you can just create your AsyncTask in the ui thread (It will run on it's own thread so it won't lock your phone while working), and perform any action you want after the task is complete in the onPostExecute-method.
class ExampleAsync extends AsyncTask<Void, Integer, Boolean>
{
public static Boolean loadFinished = false;
//JSON variables..
private static String url = null;
Context context;
public DownloadAnnouncers(Context context){
this.context = context;
}
protected Boolean doInBackground(Void... params){
// fine retrieving all my JSON data in a global array..
}
#Override
protected void onPostExecute(Boolean downloadedArray){
Intent intent_to_tabhost = new Intent(context, MyTabActivity.class);
startActivity(intent_to_tabhost);
}
}
And in your Activity:
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.splashscreen_second);
AsyncTask myTask = new ExampleAsync(this);
myTask.execute();
}
Instead of waiting for the asyncTask , use the onPostExecute , which is called on the UI thread only after the task has finished.
You should also cancel the task if the activity was destroyed (for example if the user has pressed the back button while the splash screen is shown) .

How to perform commands after "setContentView" is being called

I'm using "include" on my main layout. Each one of them is a RelativeLayout which needs an OnClick listener to be attached, and update some information related.
So I've tried to do it simply by:
setContentView(R.layout.allobjects);
ObjectListeners objectListeners = new ObjectListeners(objects);
for(int i=0;i<1;i++)
{
RelativeLayout objectBoxRelativeLayout = (RelativeLayout)findViewById(R.id.object1 + i);
objectBoxRelativeLayout.setOnClickListener(objectListeners.GetObjectListener(i));
SomeObject currentObject = this.objects.get(i);
Object viewObject = findViewById(R.id.object1 + i);
this.setObjectView(viewObject, currentObject);
}
The issue is that it takes too long after the "setContentView(R.layout.allobjects);" command, and the application shows black screen until it finish loading.
In addition, I use "setContentView(R.layout.allobjects);" after I perform the above commands. All of these commands have to be written after "setContentView(R.layout.allobjects);".
How can I handle that kind of situation ? Do I have to use onPreExecute and implement AsyncTask ?
Yes, AsyncTask is good solution to show loading dialog while these commands being executed.
UPDATE:
Add this class under your onCreate() function:
private class MyTask extends AsyncTask<Void, Void, Void> {
private ProgressDialog dialog;
private Context context;
public MyTask(Activity activity) {
context = activity;
dialog = new ProgressDialog(context);
}
protected void onPreExecute() {
dialog.setTitle("Loading...");
dialog.setMessage("Loading...");
dialog.show();
}
#Override
protected Void doInBackground(Void... params) {
//do your code here in background
protected void onPostExecute(Void res) {
dialog.dismiss();
}
}
then use the task inside onCreate() like this:
MyTask mt = new MyTask(this);
mt.execute();

Can't set ListView Adapter from AsyncThread

I'm using a ListView on my Activity and it takes a while to load from a SQLite DB, so I wanted to show a ProgressDialog to the user to let them know something is loading. I tried to run the task on a separate thread but I'm getting a CalledFromWrongThreadException. Here's my main Activity code:
#Override
public void onCreate(Bundle savedInstanceState)
{
try
{
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_CUSTOM_TITLE);
setContentView(R.layout.open_issues);
getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.custom_title);
//Set Window title.
final TextView title = (TextView) findViewById(R.id.customTitle);
if (title != null)
title.setText("Open Issues");
//Call Async Task to run in the background.
new LoadIssuesTask().execute();
}
catch (Exception e)
{
Errors.LogError(e);
}
}
And the LoadIssuesTask code:
private class LoadIssuesTask extends AsyncTask<Void, Void, Cursor> {
ProgressDialog pdDialog = null;
protected void onPreExecute()
{
try
{
pdDialog = new ProgressDialog(OpenIssues.this);
pdDialog.setMessage("Loading Issues and Activities, please wait...");
pdDialog.show();
}
catch (Exception e)
{
Errors.LogError(e);
}
}
#Override
protected Cursor doInBackground(Void... params) {
LoadIssues();
return null;
}
#Override
protected void onPostExecute(Cursor c) {
pdDialog.dismiss();
pdDialog = null;
}
}
And the LoadIssues code:
private void LoadIssues(){
//Set listview of Issues.
ListView lvIssues = (ListView)findViewById(R.id.lvIssues);
lvIssues.setOnItemClickListener(viewIssuesListener);
IssueCreator = new IssueInfoCreator(this, Integer.parseInt(AppPreferences.mDBVersion));
IssueCreator.open();
lvIssues.setAdapter(new IssueInfoAdapter(this, IssueCreator.queryAll()));
IssueCreator.close();
}
Constructor for IssueInfoAdapter:
public IssueInfoAdapter(Context c, List<IssueInfo> list){
mListIssueInfo = list;
//create layout inflater.
mInflater = LayoutInflater.from(c);
}
It's throwing the error on the .setAdapter method inside LoadIssues().
ERROR:
03-12 10:41:23.174: E/AndroidRuntime(11379): Caused by: android.view.ViewRootImpl$CalledFromWrongThreadException:
Only the original thread that created a view hierarchy can touch its views.
You're trying to access the views in the doInBackground method that doesn't run on the main UI thread. You'll have to set your adapter in the method onPostExecute that runs on the UI thread:
#Override
protected void onPostExecute(List<IsueInfo> items) {
pdDialog.dismiss();
ListView lvIssues = (ListView)findViewById(R.id.lvIssues);
lvIssues.setOnItemClickListener(viewIssuesListener);
lvIssues.setAdapter(new IssueInfoAdapter(this, items));
}
and in your doInBackground method:
#Override
protected List<IssueInfo> doInBackground(Void... params) {
IssueCreator = new IssueInfoCreator(this, Integer.parseInt(AppPreferences.mDBVersion));
IssueCreator.open();
IssueCreator.close();
return IssueCreator.queryAll();
}
Also your AsyncTask should be:
private class LoadIssuesTask extends AsyncTask<Void, Void, List<IssueInfo>>
In private void LoadIssues method call handler.setMessage(0) and create a private Handler instance to call setAdapter method
Use Handler instead of Asynctask.

Categories

Resources