I'm basically trying to create this quiz system, where I have a template of questions, and I dynamically change the content of the questions:
For example:
Who directed movie X?
- Incorrect answer director
- Correct answer director
- Incorrect answer director
- Incorrect answer director
I will replace X with some movie that I select from my database.
Following the question will have 4 selectable options (also generated based on question), and the user can only click one of them. The question and answers are all inside a fragment .xml file. I'm trying to change the contents of the fragment before displaying it. I've read the android documents, but this section doesn't seem to have been written in enough detail.
public class QuizTemplate extends ActionBarActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_quiz_template);
if (savedInstanceState == null) {
PlaceholderFragment firstFragment = new PlaceholderFragment();
// Trying to set question before I commit transaction
// firstFragment.setQuestion("IT WORKS!");
FragmentManager manager = getSupportFragmentManager();
manager.beginTransaction()
.add(R.id.container2, firstFragment).commit();
// Tried to execute pending transactions, then find view
manager.executePendingTransactions();
PlaceholderFragment firstFragment = (PlaceholderFragment) manager.findFragmentById(R.id.container2);
RadioButton answer1 = (RadioButton) firstFragment.getView().findViewById(R.id.answer1);
RadioButton answer2 = (RadioButton) firstFragment.getView().findViewById(R.id.answer2);
RadioButton answer3 = (RadioButton) firstFragment.getView().findViewById(R.id.answer3);
RadioButton answer4 = (RadioButton) firstFragment.getView().findViewById(R.id.answer4);
TextView question = (TextView) firstFragment.getView().findViewById(R.id.question);
question.setText("test");
answer1.setText("test");
// ... and so on
}
}
Fragment Class
public static class PlaceholderFragment extends Fragment {
public PlaceholderFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_quiz_template,
container, false);
return rootView;
}
public void setQuestion(String text){
TextView textView = (TextView) getView().findViewById(R.id.question);
textView.setText(text);
}
}
(layout) fragment_quiz_template.xml
<RelativeLayout 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:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="com.example.moviequiz.QuizTemplate$PlaceholderFragment" >
<TextView
android:id="#+id/question"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="65dp"
android:text="#string/hello_world" />
<RadioButton
android:id="#+id/answer2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/answer3"
android:layout_below="#+id/answer3"
android:layout_marginTop="28dp"
android:onClick="changeQuestion"
android:text="#string/hello_world" />
<RadioButton
android:id="#+id/answer3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/answer4"
android:layout_below="#+id/answer4"
android:layout_marginTop="17dp"
android:onClick="changeQuestion"
android:text="#string/hello_world" />
<RadioButton
android:id="#+id/answer4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/answer1"
android:layout_centerHorizontal="true"
android:layout_marginTop="16dp"
android:onClick="changeQuestion"
android:text="#string/hello_world" />
<RadioButton
android:id="#+id/answer1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/answer4"
android:layout_below="#+id/question"
android:layout_marginTop="40dp"
android:onClick="changeQuestion"
android:text="#string/hello_world" />
</RelativeLayout>
None of my approaches have worked, as my app crashes upon reaching the quiz (Nothing in the log?). Any ideas what could be going wrong?
EDIT: Fixed my log, it now tells me:
Unable to start activity ComponentInfo: java.lang.NullPointerException
So my guess is when I call something like
RadioButton answer1 = (RadioButton) firstFragment.getView().findViewById(R.id.answer1);
answer1 is null. But why is this the case if I execute pending transactions?
Your code that generates the UI in the fragment takes time to execute. I believe that even though you are committing the transactions, the UI is still not ready when you are trying to access it. Don't forget that your fragment lifecycle is linked to your activity lifecycle yet you are expecting to access the fragment UI in the OnCreate method of your activity.
Consider instead using the callback pattern to communicate between your activities and fragments.
http://developer.android.com/training/basics/fragments/communicating.html
Using this pattern, you can call your activity code when you are sure the UI is complete and available.
Related
I am using fragments to update a text view I have so when the person clicks a button the text view moves on to the next question. I'm not sure if I am doing the correct work in one fragment instead of the other. My current screen looks like this:
I will probably have to add some more buttons/widgets to this but should I be adding it into the XML for the fragment or the fragment container?
Here is XML for fragment actions:
<?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:id="#+id/fragment_question_layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="16dp"
tools:context=".FragmentActions"
>
<!-- this is where fragments will be shown-->
<FrameLayout
android:id="#+id/question_container1"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="4"
android:scaleType="centerInside" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
android:id="#+id/questions_yes1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="#string/yes" />
<Button
android:id="#+id/questions_no1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:gravity="center"
android:text="#string/no" />
</LinearLayout>
</LinearLayout>
And here is the fragment details:
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/button_layout1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="10dp"
tools:context=".FragmentDetails">
<!--Blank Fragment Layout-->
<TextView
android:id="#+id/questions_text_view1"
android:layout_width="match_parent"
android:layout_height="91dp"
android:gravity="center"
android:textAlignment="center"
/>
</FrameLayout>
Updated FragmentDetails
public class FragmentDetails extends Fragment {
private final String TAG = getClass().getSimpleName();
private List<Integer> mQuestionIds;
private int mListIndex;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
//Inflate the fragment layout
View rootView = inflater.inflate(R.layout.fragment_details, container, false);
//Get a reference to the textView in the fragment layout
final TextView textView = (TextView) rootView.findViewById(R.id.questions_text_view1);
if (mQuestionIds != null) {
textView.setText(mQuestionIds.get(mListIndex));
//Increment the position in the question lisy as long as index is less than list length
if (mListIndex < mQuestionIds.size() - 1) {
mListIndex++;
setmQuestionIds(QuestionList.getQuestions());
setmListIndex(mListIndex);
} else {
//end of questions reached
textView.setText("End of questions");
}
//Set the text resource to display the list item at that stored index
textView.setText(mQuestionIds.get(mListIndex));
}
else {
//Log message that list is null
Log.d(TAG, "No questions left");
}
//return root view
return rootView;
}
public void setmQuestionIds (List < Integer > mQuestionIds) {
this.mQuestionIds = mQuestionIds;
}
public void setmListIndex ( int mListIndex){
this.mListIndex = mListIndex;
}
}
Fragment Actions activity
public class FragmentActions extends AppCompatActivity {
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_actions);
Button yes = findViewById(questions_yes1);
// Only create new fragments when there is no previously saved state
if (savedInstanceState == null) {
//Create Question Fragment
final FragmentDetails fragmentDetails = new FragmentDetails();
fragmentDetails.setmQuestionIds(QuestionList.getQuestions());
yes.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//set the list of question Ids for the head fragent and set the position to the second question
//Fragment manager and transaction to add this fragment
FragmentManager fragmentManager = getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.question_container1, fragmentDetails)
.commit();
}
});
}
}
}
If your Buttons remain the same while the TextView changes, you may add your Buttons to the fragment container.
Remember that, your fragments will be presented inside the FrameLayout of the fragment container. You gotta keep your Buttons, outside the FrameLayout.
Or if you want to have different Buttons for different fragments (Questions, in your case), you can also add the Buttons to the fragments. But in that case, you gotta add them separately to each of the fragments.
I guess there's no right answer to your question. You could try different approaches.
Maybe you could implement the buttons in the fragment container, as #smmehrab pointed out. I see this as a more difficult solution, because when you click on an item from the container you can manage the views of the container, not the fragment's views. You would get NullPointer if I recall correctly. This happens because the context when the button is clicked in the fragment container is different than the context when clicking from within the fragment. So you should implement an interface on the fragment container that listens to clicks, and the fragment catches the click. You could do this, and I actually am doing it in my current app, but I have no choice.
You could instead use Motion Layout (which extends from Constraint Layout) as the root view of your fragment, instead of CardView. This way you could set all the fragment's views with a flat hierarchy (flat hierarchies improves rendering time, so that's an improvement, and you can use CardView as one child) and set the buttons right there, in the Motion Layout (remember, the motion layout would be the fragment's root view). You could set the click listener right there and implement animations between different textViews.
I'm sure there are plenty of other solutions, take this only as a contribution.
If you're unfamiliar with Motion Layout you can just google it, android official documentation about it is great.
I have a view pager consisting of 4 tabs. Each tab holds its own fragment.
How would I be able to use fragment transaction to replace the fragment in tab 3 with a new fragment?
I've tried a lot of things, but I have not been able to come up with a solution at all. I've tried looking around on google and stackoverflow, but with no success.
I assume that your fragment has a button that is put in the center. By clicking on it, you can change the layout stays under of this button. The content/layout of the first fragment you mentioned should be replaced with wrapperA and the content/layout of the second one should be replaced with wrapperB. I put a simple red background for wrapperA to distinguish it with wrapperB, wrapperB is also green due to the same reason. I hope this is what you want:
public class SwitchingFragment extends Fragment {
Button switchFragsButton;
RelativeLayout wrapperA, wrapperB;
View rootView;
#Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.layout_switch, container, false);
wrapperA = (RelativeLayout) rootView.findViewById(R.id.wrapperA);
wrapperB = (RelativeLayout) rootView.findViewById(R.id.wrapperB);
switchFragsButton = (Button) rootView.findViewById(R.id.switchFragsButton);
switchFragsButton.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
if (wrapperB.getVisibility() == View.GONE) {
wrapperB.setVisibility(View.VISIBLE);
// There is no need to change visibility of wrapperA since it stays behind when wrapperB is visible.
// wrapperA.setVisibility(View.GONE);
}
else {
wrapperB.setVisibility(View.GONE);
// Again, there is no need.
// wrapperA.setVisibility(View.VISIBLE);
}
}
});
return rootView;
}
}
The layout:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:orientation="vertical">
<!-- The content of first fragment should be in "wrapperA". -->
<RelativeLayout
android:id="#+id/wrapperA"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/red"
android:visibility="visible">
</RelativeLayout>
<!-- The content of second fragment should be in "wrapperB". -->
<RelativeLayout
android:id="#+id/wrapperB"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/green"
android:visibility="gone">
</RelativeLayout>
<!-- As I said, I assume that the layout switcher button is stable
and so it should be in front of the switching layouts. -->
<Button
android:id="#+id/switchFragsButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:layout_centerVertical="true"
android:background="#color/black"
android:text="Switch"
android:textSize="20sp"
android:textStyle="bold"
android:textColor="#color/white"/>
</RelativeLayout>
Alternatively, you can try to change the fragment directly by notifying your FragmentPagerAdapter as described in this link:
Replace fragment with another fragment inside ViewPager
I've got a fragment which dynamially adds LinearLayouts with two imagebuttons and a edittext.
The Value from the edittext is stored in a .txt File and retrieved via the function retrievecounter.
This is the Layout which gets added:
<?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="wrap_content"
android:orientation="horizontal"
android:weightSum="100" >
<ImageButton
android:id="#+id/imageView1"
android:layout_width="56dp"
android:layout_height="50dp"
android:layout_weight="5"
android:src="#drawable/ic_action_photo" />
<EditText
android:id="#+id/singelfirstet1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="85"
android:ems="10" >
<requestFocus />
</EditText>
<ImageButton
android:id="#+id/buttonDelete"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_weight="10"
android:background="#android:color/transparent"
android:contentDescription="#string/button_delete_row_description"
android:src="#drawable/testi" />
</LinearLayout>
This is my fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
ScrollView mLinearLayout = (ScrollView) inflater.inflate(
R.layout.editlistsfirst, container, false);
operations = new ArrayList<String>();
operations = retrievecounter(0);
temp = (LinearLayout) mLinearLayout.findViewById(R.id.lineartest);
LayoutInflater inflater2 = (LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
for (int i = 0; i < operations.size(); i++) {
View tempr = (inflater2.inflate(R.layout.list_singlefirst,
container, false));
ImageButton bt = (ImageButton) tempr
.findViewById(R.id.buttonDelete);
EditText et = (EditText) tempr.findViewById(R.id.singelfirstet1);
et.setText(operations.get(i));
String show = et.getText().toString();
Log.w("Class",show);
bt.setOnClickListener(this);
temp.addView(tempr);
}
return mLinearLayout;
}
I navigate between the fragments via an actionbar with tabs.
This is my onTabselected function:
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// TODO Auto-generated method stub
ft.replace(R.id.fragment_container, fragment);
}
On the first click the values from the .txt file are displayed correctly, but after i switch to a different tab/fragment and back to this fragment only the last value of my ArrayList gets displayed.
Strange however is that the Log.w displays the correct value for the edittext both times.
Any idea what causes this?
Thanks in advance
If i call my function of the button inside the linearlayout:
temp.removeView((View) v.getParent());
for all buttons till all layouts are gone and go to another tab and back it displays the correct values, but i've got no idea why. Maybe this is helpful in finding the problem.
edit2: I Found a solution, don't know if it is practical or why it works but it does:
public void onPause(){
super.onPause();
temp.removeAllViews();
}
After i added this it works. If someone could explain why that would be much appreciated.
One of the things to consider when dealing with Views like this is that they are only 'updated' when they are first created. When you switch between your tabs, the data will not be refreshed because it was A)Not recreated [refreshed] B)Not updated by an adapter.
When you removed all views on Pause(), you emptied your view, then it is recreated from your replace() function which is similar as creating a new view. Refreshing a layout by removing and re-adding everything every time you change something can become very computationally expensive which is why people will use an adapter to control their layouts.
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.
I want to create a gui application for android where the user will be able to add or remove fields of certain type (4 different type of fields) to the application. Is there a way to do so in xml?
The only way I could figure to do so is by edditing the xml file from within the app which sounds as a bad idea for me.
Hope my question is clear.
Yotam.
Edit:
I have added a simple code for direct java implantation:
import android.app.Activity;
import android.graphics.Color;
import android.os.Bundle;
import android.view.ViewGroup;
import android.widget.TextView;
public class Leonidas extends Activity {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//setContentView(R.layout.counter);
TextView TV = new TextView (this);
TextView UV = new TextView (this);
TV.setText("hello");
UV.setText("goof");
//setContentView(TV);
//setContentView(UV);
ViewGroup.LayoutParams lpars = new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.FILL_PARENT);
this.addContentView(UV,lpars);
this.addContentView(TV, lpars);
this.setVisible(true);
}
}
Edit2:
I have searched for example and got the following working:
LayoutInflater inflater;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
inflater = LayoutInflater.from(this);
Button b = (Button) this.findViewById(R.id.alert);
b.setOnClickListener(this);
}
#Override
public void onClick(View v) {
final LinearLayout canvas = (LinearLayout)Leonidas.this.findViewById(R.id.counter_field);
final View cv = this.inflater.inflate(R.layout.counter,canvas,false);
canvas.addView(cv);
}
You can do it from within your handler too (in the implementation class).
After inflating your xml layout, you respond to some kind of user interactions.
In the handler you
either create a new View from
scratch, and specify its
layoutparams,
or inflate one using xml
After having the new view, you add it to the current (this) view, and due to its layoutparams, it will be the size, shape, color, etc. that you want.
Update:
If you'd like to add more complex views to your activity, it's better to write them in xml, and inflate them:
sample_component.xml: //inside res/layout
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="fill_parent"
android:layout_height="wrap_content" android:padding="0px">
<TextView android:id="#+id/servicename_status" android:paddingLeft="15px"
android:paddingRight="5px"
android:textStyle="bold" android:focusable="false" android:textSize="14px"
android:layout_marginLeft="10px" android:layout_marginRight="3px"
android:layout_width="fill_parent" android:layout_height="wrap_content" />
<TextView android:id="#+id/lastcheck" android:focusable="false"
android:textSize="14px" android:layout_width="fill_parent"
android:layout_marginLeft="10px" android:layout_marginRight="3px"
android:layout_height="wrap_content" android:layout_below="#id/servicename_status" />
<TextView android:id="#+id/duration" android:focusable="false"
android:textSize="14px" android:layout_width="fill_parent"
android:layout_marginLeft="10px" android:layout_marginRight="3px"
android:layout_height="wrap_content" android:layout_below="#id/lastcheck" />
<TextView android:id="#+id/attempt" android:focusable="false"
android:textSize="14px" android:layout_width="fill_parent"
android:layout_marginLeft="10px" android:layout_marginRight="3px"
android:layout_height="wrap_content" android:layout_below="#id/duration" />
<TextView android:id="#+id/statusinfo" android:focusable="false"
android:textSize="14px" android:layout_width="fill_parent"
android:layout_marginLeft="10px" android:layout_marginRight="3px"
android:layout_height="wrap_content" android:layout_below="#id/attempt" />
<CheckBox android:id="#+id/alert" android:focusable="false"
android:layout_alignParentRight="true" android:freezesText="false"
android:layout_width="wrap_content" android:layout_height="wrap_content"
android:layout_marginTop="5px" />
</RelativeLayout>
Inside your Leonidas activity class you have the handlers that have to respond to different user actions by adding/removing items to/from the view.
Below is a sample handler of a click event, which uses LayoutInflater, to add the sample_component.xml view to your activity:
public final class MyClickListener implements View.OnClickListener
{
private LayoutInflater inflater;
public MyClickListener()
{
inflater = LayoutInflater.from(Leonidas .this);
}
#Override
public void onClick(View v)
{
// TODO: change RelativeLayout here to whatever layout
// you'd like to add the new components to
final RelativeLayout canvas = (RelativeLayout)Leonidas.this.findViewById(R.id.my_canvas);
final View childView = inflater.inflate(R.layout.sample_component, canvas, false);
// TODO: Look up the 5 different signatures of the addView method,
// and pick that best fits your needs
canvas.addView(childView);
// check which button was pressed
switch (view.getId())
{
case R.id.btn_prev:
//handler for the prev button
break;
case R.id.btn_next:
//handler for the next button
break;
default:
break;
}
}
}
Note, that MyClickListener is implemented as an inline class within your Leonidas activity, thay's why for the context parameter it is used: this.Leonidas.
Update
The R.id.my_canvas would be the id of the view that you want to add components to. it is in your main.xml (or whatever xml you use for your Leonidas view).
If you put the MyClickListener class inside your Leonidas.java class (declare as inline class), it will recognize it.
Instead of specifying elements in the XML, you can create them dynamically and add it to the UI. This is demonstrated in the Android Hello World Tutorial here.