Background Thread without Looper - android

My code contains two buttons and a TextView. When the program is running, by clicking on the start Thread button, a background Thread will be executed and at the same time a counter will be displayed in the TextView, and when you click on the stop Thread button, the background Thread will stop. My question is that I have not used the looper here and the counter value is displayed from the background thread in the TextView on the UI thread. How is this possible?
activity_main.xml
<?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:orientation="vertical"
android:gravity="center"
tools:context=".MainActivity">
<TextView
android:id="#+id/txtViewThreadCount"
android:hint="Thread Count"
android:textSize="30sp"
android:textColor="#color/black"
android:gravity="center"
android:padding="8dp"
android:layout_margin="10dp"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/btnStartThread"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="30sp"
android:layout_marginTop="30dp"
android:text="Start Thread" />
<Button
android:id="#+id/btnStopThread"
android:layout_width="300dp"
android:layout_height="wrap_content"
android:textSize="30sp"
android:layout_marginTop="30dp"
android:text="Stop Thread" />
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity implements View.OnClickListener{
//Fields
private static final String TAG = MainActivity.class.getSimpleName();
private TextView txtViewThreadCount;
private Button btnStartThread, btnStopThread;
private boolean stopLoop;
private int count = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Log.e(TAG, "Thread id : "+Thread.currentThread().getId());
btnStartThread = findViewById(R.id.btnStartThread);
btnStopThread = findViewById(R.id.btnStopThread);
txtViewThreadCount = findViewById(R.id.txtViewThreadCount);
btnStartThread.setOnClickListener(this);
btnStopThread.setOnClickListener(this);
}
public void onClick(View view){
switch(view.getId()){
case R.id.btnStartThread:
stopLoop = true;
new Thread(new Runnable() {
#Override
public void run() {
while(stopLoop){
try {
Thread.sleep(1000);
count++;
} catch (InterruptedException e) {
Log.e(TAG,e.getMessage());
}
Log.e(TAG,"Thread id in while loop: "+Thread.currentThread().getId()+", Count: "+count);
txtViewThreadCount.setText(count+"");
}
}
}).start();
break;
case R.id.btnStopThread:
stopLoop = false;
break;
}
}
}

See Processes and threads overview in the Android docs:
Do not access the Android UI toolkit from outside the UI thread
It doesn't state what will happen if you ignore this rule; one of the possible outcomes is "it may sometimes work and then crash on your users' devices".
You can use runOnUiThread or View.post in this situation to do move the setText call back onto the UI thread.
See also What is the Android UiThread?.

When an application is launched, the system creates a thread of execution for the application, called "main." This thread is very important because it is in charge of dispatching events to the appropriate user interface widgets, including drawing events. It is also almost always the thread in which your application interacts with components from the Android UI toolkit (components from the android.widget and android.view packages).
Normally you could not update the UI in the work thread. You could use
1.Activity.runOnUiThread(Runnable)
2.View.post(Runnable)
to fix this issue.
Please see for detail
https://developer.android.com/guide/components/processes-and-threads#WorkerThreads

Related

setVisibility not working even in uiThread

I want to toggle visibility of some elements. I have read some Q/A that the main problem was doing this in main thread but I am running the code in runOnUiThread but not works yet:
TextView progress;
TextView registrationFailed;
Button tryAgainRegistration;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.registration);
sp = this.getSharedPreferences("settings", this.MODE_PRIVATE);
progress = (TextView) findViewById(R.id.progress);
registrationFailed = (TextView) findViewById(R.id.registrationFailed);
tryAgainRegistration=(Button) findViewById(R.id.tryAgainRegistration);
Setstat(0); // For test
}
public void Setstat(final Integer a){
runOnUiThread(new Runnable() {
#Override
public void run() {
Log.e("progress",a.toString()); // It shows 0 and 1 correctly
if (a == 1) {
progress.setVisibility(View.VISIBLE);
registrationFailed.setVisibility(View.GONE);
tryAgainRegistration.setVisibility(View.GONE);
} else {
progress.setVisibility(View.GONE);
registrationFailed.setVisibility(View.VISIBLE);
tryAgainRegistration.setVisibility(View.VISIBLE);
}
}
});
}
for more details this is the UI xml:
<TextView
android:id="#+id/progress"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:text="Registeration in progress"
android:visibility="visible" />
<TextView
android:id="#+id/registrationFailed"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal|center_vertical"
android:text="Registeration in progress"
android:visibility="gone" />
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:text="Try again"
android:id="#+id/tryAgainRegistration"
android:visibility="gone"
/>
Try running this in on create.
Just to see if it is because of the threads or not:
findViewById(R.id.yourView).setVisibility(View.GONE);
The runOnUiThread method needs a reference to the activity.
Otherwise it doesn't know which one the UI thread is.
You might get a warning about memory leaks. If you are worried about that, google for WeakReference.
((Activity)context).runOnUiThread(new Runnable() {
My specific problem was that I had created the XML view using constrainLayout but modified it manually without removing original settings of constrainLayout. So I guess the layout was not well recognized by system Ui.
In fact I made a mistake by manually renaming android.support.constraint.ConstraintLayout to LinearLayout as the root element.
<LinearLayout 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"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
tools:context=".Registration"
tools:showIn="#layout/activity_registration">

Thread in Fragment only called once

I am trying to implement a thread that changes something on the UI in a Fragment. Therefore I need to refer to the main thread.
Based on my research, I've found that the following code should do the trick:
new Handler(Looper.getMainLooper()).post(new Runnable() {
#Override
public void run() {
Toast.makeText(menuActivity, "HELLO", Toast.LENGTH_SHORT).show();
}
});
This will execute only once though, even if the Looper should normally keep the thread alive. Trying to invoke Looper.prepare() inside the Handler will cause a RuntimeException as only one Looper is allowed per thread.
Edit: My goal is to update a TextView permanently each second.
I have also tried the following:
Thread t = new Thread() {
#Override
public void run() {
menuActivity.runOnUiThread(new Runnable() {
#Override
public void run() {
System.out.println("-----------TEST");
}
});
}
};
t.start();
But this will execute only once too.
I've also read this article, but I guess my first snippet of code is just a shorter version of the code shown in the article.
Where may my mistake be in any of these snippets of code?
This question is not a duplicate, due to the fact that I presented a totally different snippet of code which is the base of the problem I had. Furthermore, the Looper is explained more in depth in this thread.
I understand that you want to update a text view repeatedly after 1 seconds. Here is a simple demo I just write.
Mockup screen
activity_main.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">
<TextView
android:id="#+id/text_view_money"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:gravity="center"
android:textColor="#android:color/black"
android:textSize="20sp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="30dp"
android:layout_marginTop="30dp"
android:orientation="horizontal">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="startUpdateTextViewMoney"
android:text="Start" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:onClick="stopUpdateTextViewMoney"
android:text="Stop" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:orientation="horizontal">
<EditText
android:id="#+id/edit_text_money"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="setMoney"
android:text="SET MONEY" />
</LinearLayout>
</LinearLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private static final int UPDATE_TEXT_MONEY_INTERVAL = 1000;
private Handler mMainHandler;
private TextView mTextViewMoney;
private TextView mEditTextMoney;
private String money;
private Runnable mUpdateTextViewMoneyTask = new Runnable() {
#Override
public void run() {
if (TextUtils.isEmpty(money)) {
mTextViewMoney.setText(String.valueOf(SystemClock.elapsedRealtime()));
} else {
mTextViewMoney.setText(money);
money = null;
}
mMainHandler.postDelayed(this, UPDATE_TEXT_MONEY_INTERVAL);
}
};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTextViewMoney = findViewById(R.id.text_view_money);
mEditTextMoney = findViewById(R.id.edit_text_money);
mMainHandler = new Handler(Looper.getMainLooper());
}
#Override
protected void onStop() {
super.onStop();
stopUpdateTextViewMoney(null);
}
public void startUpdateTextViewMoney(View view) {
mMainHandler.post(mUpdateTextViewMoneyTask);
}
public void stopUpdateTextViewMoney(View view) {
mMainHandler.removeCallbacks(mUpdateTextViewMoneyTask);
}
public void setMoney(View view) {
String money = mEditTextMoney.getText().toString();
this.money = !TextUtils.isEmpty(money) ? money : "";
}
}
When users press Start button, the app will start updating text view each second
When users press Stop button, the app will stop updating the text view.
If users want to set a new money to display in the next time, just enter in the edit text then press Set Money.
This will execute only once though, even if the Looper should normally
keep the thread alive.
You seem to be confused about the Looper's purpose/functionality. The Looper is keeping the thread alive. If the main thread was not kept alive, your application would exit. The Looper does not, however, provide repeated execution of the Runnables posted to the Handler/Thread which it is associated with. Each Runnable is executed exactly once. If you want to execute the same Runnable multiple times, you must post it multiple times. For example:
Handler mainThreadHandler = new Handler(Looper.getMainLooper());
Runnable doToast = new Runnable() {
#Override
public void run() {
Toast.makeText(menuActivity, "HELLO", Toast.LENGTH_SHORT).show();
}
});
mainThreadHandler.post(doToast); // Queue the first execution of the code in the Runnable's run() method.
mainThreadHandler.post(doToast); // Queue the second execution of the code in the Runnable's run() method.

Sometimes updating progress bar on main thread from worker thread doesn't crash the android application

I have an example of android app to update progressbar on main thread from worker thread using handler, it's working fine but when i try to update progressbar inside worker thread without handler, it still works, though it's supposed to crash. Because according to Android documentation UI thread is not safe and must not be accessed from worker thread directly.
***Update
Sorry i put the wrong activity_main.xml.
This is the correct one i am using.
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
tools:context="com.example.riaz.handlerexample.MainActivity">
<ProgressBar
android:id="#+id/progressBar"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="8dp"
android:layout_marginRight="8dp"
android:layout_marginTop="8dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:indeterminate="false"
android:max="100"
android:progress="0"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp" />
</android.support.constraint.ConstraintLayout>
***Update
Here is the activity_main.xml. ** Update. Wrong layout file, Please ignore
<?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" >
<ProgressBar
android:id="#+id/progressBar1"
style="?android:attr/progressBarStyleHorizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:indeterminate="true"
android:max="10"
android:padding="4dip" >
</ProgressBar>
<TextView
android:id="#+id/tv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Progress percentage"
android:textSize="16sp"/>
<Button
android:id="#+id/button1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="startProgress"
android:text="Start"
android:textSize="16sp"/>
</LinearLayout>
Java code
import android.os.Handler;
import android.os.Message;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.widget.ProgressBar;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity {
Thread thread; // work as a background thread
Handler handler; // handle main thread messages from worker thread
ProgressBar progressBar; // on UI/main thread
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
progressBar = (ProgressBar) findViewById(R.id.progressBar);
Toast.makeText(MainActivity.this, "OnCreate Thread Name = "
+Thread.currentThread().getName(),
Toast.LENGTH_LONG).show(); // Thread Name = main
System.out.println("OnCreate Method,Thread Name= " +
Thread.currentThread().getName()); // Thread Name = main
thread = new Thread( new MyThread());// creating a background thread
thread.start(); // starting the background thread. it will execute run() method in
// MyThread class
handler = new Handler(){ //handles the incoming messages from background thread
#Override // receives message from worker thread sendmessage(message)
public void handleMessage(Message msg) {
System.out.println("Inside Handler,Thread Name= " +
Thread.currentThread().getName()); //thread Name = main
// progressBar.setProgress(msg.arg1);
}
};
}
// this is background / worker thread
class MyThread implements Runnable
{
#Override
public void run() {
Toast.makeText(MainActivity.this, "Worker Thread Name = "
+Thread.currentThread().getName(),
Toast.LENGTH_LONG).show(); // Thread Name = main
for (int i=0; i<100;i++)
{
System.out.println("Worker Thread,Thread Name= " +
Thread.currentThread().getName());
// Message message = Message.obtain(); // blank empty message
// message.arg1 = i;
// handler.sendMessage(message); //calling main thread
progressBar.setProgress(i);
//create delay to see the progress on progressbar
try {
Thread.sleep(300);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
As a CommonsWare answer on Question
Nowadays, ProgressBar has logic that allows setProgress() to be called on a background thread. It checks to see what thread you are on and does its own post() of a Runnable if needed.
Answer reference link

TextView.setText() crashing my app

I'm just learning Android and I'm already having trouble with complete basics:
I'd like to have a timer that increments a TextView but it crashes the first time the timer ticks.
Here's my code:
public class Game extends Activity {
Timer timer = new Timer();
int i = 0;
TextView text;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_game);
text = (TextView) findViewById(R.id.timerText);
text.setText("0");
timer.schedule(new TimerTask() {
public void run() {
i++;
System.out.println(i);
text.setText(i + "");
if(i>100) timer.cancel();
}
}, 1000, 1000);
}
}
Here's my XML:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
...
<TextView
android:id="#+id/timerText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="50dp"
android:text="Large Text"
android:textAppearance="?android:attr/textAppearanceLarge" />
</RelativeLayout>
I found a ton of solutions for setText crashing but none of them seemed to fix this.
You need to call text.setText(i + ""); from runOnUiThread()
Look at this : Android: Accessing UI Element from timer thread
Explantation : you've got error because you try accessing to UI element(in your case - textView) from separate background thread.
Also look this answer with simplifiest example: https://stackoverflow.com/a/6702767/2956344
if you are accesing to UI element in the timer run you should use a Handler or runOnUITheard.
Please check:
http://developer.android.com/reference/android/app/Activity.html#runOnUiThread(java.lang.Runnable)
and
Android: Accessing UI Element from timer thread

Coupling Android layout to its logic outside of the owner Activity

I'm facing some complexities when developing a medium-complex Android application. I'm searching for information about the possibility of using code-behind-like techniques for easier maintanability of Android software.
Currently (please highlight anything wrong), I have found that in order to make a multi-step wizard with extra dialogs (eg. dialogs that are not part of the main sequence) I need to code a single XML layout file with a single ViewFlipper containing each subview as child node. Today I discovered how to navigate across views more than forward/backward (viewFlipper.setDisplayedChild(i)), giving access to extra views.
Now all the Java code is contained within the main Activity class, which is beginning to look bad. As an experienced .NET developer I have learned how to use custom controls to wrap both layout and business logic inside modules.
I know that in Android I can define a view programmatically as an independent class and add it to the main layout programmatically, however I want to know if it's possible in Android to define a layout by XML (for easier WYSIWYG creation/editing) and define all the code within a dedicated class, with initialization logic, button callbacks, async tasks, etc.
I'm not sure if it's feasible or there is a good compromise that can be achieved.
I have read this question without clearing my doubts.
Thank you.
Code examples:
An extract of the layout file (I expect 4 wizard steps, a help view and an EULA view)
<?xml version="1.0" encoding="utf-8"?>
<ViewFlipper xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/view_phone"
style="#android:style/Theme.Light.NoTitleBar"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<!-- First screen/welcome -->
<LinearLayout
android:id="#+id/view_phone_screen1"
style="#android:style/Theme.Light.NoTitleBar"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:weightSum="100" >
<TextView
android:id="#+id/view_phone_screen1_lblChooseProvider"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="#string/view_phone_lblChooseProvider_1ststep"
android:textAppearance="?android:attr/textAppearanceLarge" />
<ImageButton
android:id="#+id/view_phone_btnFrecciarossa"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#string/provider_FRECCIAROSSA"
android:gravity="center_vertical|clip_vertical"
android:padding="10dp"
android:src="#drawable/logo_frecciarossa"
android:tag="#+id/provider_FRECCIAROSSA" />
<ImageButton
android:id="#+id/view_phone_btnItalo"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:contentDescription="#string/provider_ITALO"
android:gravity="center_vertical|clip_vertical"
android:padding="10dp"
android:src="#drawable/logo_italo"
android:tag="#+id/provider_ITALO" />
</LinearLayout>
<!-- Second screen - will need to do some asynchronous task -->
<RelativeLayout
android:id="#+id/view_phone_screen2"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="#+id/view_phone_screen2_lblConnectingToWifi"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:text="#string/view_phone_lblConnectToWifi_2ndstep"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/view_phone_step2_lblConnectedToWifi"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/imageView1"
android:layout_centerHorizontal="true"
android:layout_marginTop="58dp"
android:text="#string/view_phone_step2_connectingToWifi"
android:textAppearance="?android:attr/textAppearanceLarge" />
<TextView
android:id="#+id/view_phone_step2_lblPhoneNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/editText1"
android:layout_below="#+id/view_phone_step2_lblConnectedToWifi"
android:layout_marginTop="51dp"
android:text="#string/view_phone_step2_msgInputPhoneNumber"
android:textAppearance="?android:attr/textAppearanceMedium" />
<TextView
android:id="#+id/view_phone_step2_lblUnableDetectPhoneNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="#string/view_phone_step2_msgUnableDetectPhoneNumber"
android:textAppearance="?android:attr/textAppearanceSmall"
android:visibility="invisible" />
<Button
android:id="#+id/view_phone_screen2_backward"
style="#style/buttonBackward" />
<Button
android:id="#+id/view_phone_screen2_forward"
style="#style/buttonForward_disabled"
android:enabled="false" />
<EditText
android:id="#+id/view_phone_step2_txtPhoneNumber"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignRight="#+id/view_phone_step2_lblPhoneNumber"
android:layout_below="#+id/view_phone_step2_lblPhoneNumber"
android:inputType="phone"
android:singleLine="true" >
<requestFocus />
</EditText>
</RelativeLayout>
</ViewFlipper>
Code example from Activity (expect to implement ALL the logic of 4+2 step wizard)
public class MyActivity extends Activity {
/** Called when the activity is first created. */
private final static String LOG_TAG = "LOG_TAG";
private int stepNumber;
#Override
public void onCreate(Bundle savedInstanceState) {
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
this.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
super.onCreate(savedInstanceState);
this.stepNumber=1;
setContentView(R.layout.view_phone);
//This class wraps the click for the two buttons
ProviderSelectionListener providerSelectionListener = new ProviderSelectionListener(this);
this.findViewById(R.id.view_phone_btnFrecciarossa).setOnClickListener(providerSelectionListener);
this.findViewById(R.id.view_phone_btnItalo).setOnClickListener(providerSelectionListener);
}
#Override
protected void onPause() {
super.onPause();
try {
if (MyApplication.getPlatformManager() != null)
MyApplication.getPlatformManager().onApplicationPause();
} catch (MyCustomException e) {
// WTF (Worse Than Failure!)
Log.e(LOG_TAG, super.getString(R.string.zf_error_unknown_error_pauseactivity), e);
e.printStackTrace();
}
}
#Override
protected void onResume() {
super.onResume();
try {
if (MyApplication.getPlatformManager() != null)
MyApplication.getPlatformManager().onApplicationResume();
} catch (MyCustomException e) {
// WTF (Worse Than Failure!)
Log.e(LOG_TAG, super.getString(R.string.zf_error_unknown_error_pauseactivity), e);
e.printStackTrace();
}
}
/*
* SLIDE INIZIO
*/
protected void slideNext() {
ViewFlipper vf = (ViewFlipper) findViewById(R.id.view_phone);
vf.setOutAnimation(getApplicationContext(), R.anim.slide_out_left);
vf.setInAnimation(getApplicationContext(), R.anim.slide_in_right);
vf.showNext();
}
protected void slidePrevious() {
ViewFlipper vf = (ViewFlipper) findViewById(R.id.view_phone);
vf.setOutAnimation(getApplicationContext(), R.anim.slide_out_right);
vf.setInAnimation(getApplicationContext(), R.anim.slide_in_left);
vf.showPrevious();
}
/*
* SLIDE FINE
*/
/*
* STEP 1 INIZIO
*/
public void completeStep1(ISmsWifiProvider provider) {
if (provider == null) {
Log.e(LOG_TAG, "Provider nullo");
return;
}
MyApplication.setAuthenticationProvider(provider);
slideNext();
initializeStep2();
}
public void returnToStep1() {
MyApplication.setAuthenticationProvider(null);
slidePrevious();
}
/*
* STEP 1 FINE
*/
/*
* STEP 2 INIZIO
*/
private void initializeStep2() {
// Event handler
Button backButton = (Button) findViewById(R.id.view_phone_screen2_backward), fwButton = (Button) findViewById(R.id.view_phone_screen2_forward);
fwButton.setEnabled(false);
backButton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
returnToStep1();
}
});
}
/*
* STEP 2 FINE
*/
#Override
public void onBackPressed() {
// This will be called either automatically for you on 2.0
// or later, or by the code above on earlier versions of the
// platform.
return;
}
}
I want to know if it's possible in Android to define a layout by XML (for easier WYSIWYG creation/editing) and define all the code within a dedicated class, with initialization logic, button callbacks, async tasks, etc.
Yes. It is one of the techniques for creating a custom View. For example, I have a custom ColorMixer widget in this project, which can be used directly in an activity, or in a dialog, or in a custom preference.
I agree with your tactical decision to implement a wizard via a ViewFlipper -- see this answer to another StackOverflow question for "Murphy's Theory of the Activity".
I suspect that the right answer, longer term, is for somebody (who might be me) to come up with a Fragment-based wizard pattern, as that gives you the decoupling you desire.

Categories

Resources