I'm looking to create a small flash card type element for my app. I've looked at several solutions namely the FragmentStatePagerAdapter and it's parent the FragmentPagerAdapter. Neither of these solve the problem I want to solve. They deal with listviews of fragments. I want to have a list of fragments that when I hit a button I move to the next one fragment in the list until I'm all done.
I've got all the saving the data part of it solved. I just cannot figure out how to chain my fragments together.
To be explicit, what I'm looking for is:
a->b->c->d->done and go back to activity or a finished fragment.
The user would obviously use a button to progress from fragment to fragment.
I chose fragments because I figured that would be the easiest. I'm not opposed to activities, but my problem is still largely the same.
I've tried implementing the FragmentPager stuff, but as I said it didn't suite my needs.
How dynamic are the fragments you are making? If there's a set amount of interchangeable elements, you can try creating a delegate function in your main activity that opens fragments depending on a set of parameters. Better still, you make your fragments modular so that you only have a few fragments with different states based on what you give them.
public void onCardWithIdSelected(int id, String param1, String param2, ...) {
Fragment fragment = NULL;
if(id == 0) {
fragment = cardFragment.newInstanceFromParams(param1, param2, ...); //this will pass the parameters onto the desired fragment
}
else if(id == 1) {
fragment = cardFragment.newInstanceFromParams(param1, param2, ...); //this will pass the parameters onto the desired fragment
}
else if(id == 2) {
fragment = cardFragment.newInstanceFromParams(param1, param2, ...); //this will pass the parameters onto the desired fragment
}
//and so on...
FragmentTransaction transaction = getSupportFragmentManager()
.beginTransaction();
transaction.replace(R.id.content_frame, fragment);
transaction.addToBackStack(null); //only do this if you don't want users to be able to go back
// Commit the transaction
transaction.commit();
}
Then whenever you want to move to a different fragment from one, you just call this function on the main activity with your desired parameters.
I figured out how to do this with a rather simple solution:
I have my activity which has some variables so that it knows what fragment it is on, along with that it inflates a layout with a framelayout and uses fragmentmanager transactions to replace given the fragment number we are on. Then I have a parcelable class that defines the flashcard that is passed to each fragment upon instantiation. On the activity's layout I have 3 buttons, "check", "correct", "incorrect" which, using View.GONE/View.VISIBLE am able to give the UI experience I want. On the click of the "correct"/"incorrect" we start a transaction and move down the list to the next card.
The code:
/**
* The activity
*/
public class VocabTestActivity extends Activity {
private int mWordsCorrect = 0;
private int mWordsIncorrect = 0;
private int mCurrentPosition = 0;
private ArrayList<Fragment> mCards = new ArrayList<Fragment>();
private final int FLAG_TOTAL_CARDS = 5;
private GrammarDataSource mDataSource;
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_practice);
final Button buttonCheck = (Button) findViewById(R.id.buttonCheckWordPractice);
final Button buttonCorrect = (Button) findViewById(R.id.buttonCorrectWordPractice);
final Button buttonIncorrect = (Button) findViewById(R.id.buttonIncorrectWordPractice);
final TextView textViewProgressBar = (TextView) findViewById(R.id.textViewProgressBarPractice);
this.mDataSource = new GrammarDataSource(this);
this.initializeCards();
buttonCheck.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
buttonCheck.setVisibility(View.GONE);
buttonCorrect.setVisibility(View.VISIBLE);
buttonIncorrect.setVisibility(View.VISIBLE);
}
});
buttonCorrect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mWordsCorrect++;
getFragmentManager().beginTransaction().replace(
R.id.practice_frame,
mCards.get(mCurrentPosition++)
).commit();
buttonCheck.setVisibility(View.VISIBLE);
buttonCorrect.setVisibility(View.GONE);
buttonIncorrect.setVisibility(View.GONE);
textViewProgressBar.setText(getString(R.string.practice_progress_bar, mCurrentPosition, FLAG_TOTAL_CARDS));
}
});
buttonIncorrect.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mWordsIncorrect++;
getFragmentManager().beginTransaction().replace(
R.id.practice_frame,
mCards.get(mCurrentPosition++)
).commit();
buttonCheck.setVisibility(View.VISIBLE);
buttonCorrect.setVisibility(View.GONE);
buttonIncorrect.setVisibility(View.GONE);
textViewProgressBar.setText(getString(R.string.practice_progress_bar, mCurrentPosition, FLAG_TOTAL_CARDS));
}
});
}
private void initializeCards() {
for(VocabWord v : this.mDataSource.selectFlashCards(FLAG_TOTAL_CARDS)) {
VocabTestFragment frag = VocabTestFragment.newInstance(new ParcelableWord(v));
mCards.add(frag);
}
}
}
/**
* The fragment
*/
public class VocabTestFragment extends Fragment {
private ViewGroup mRoot;
public final String TAG = getClass().getSimpleName();
private VocabWord mWord;
public static VocabTestFragment newInstance(ParcelableWord w) {
VocabTestFragment frag = new VocabTestFragment();
Bundle args = new Bundle();
args.putParcelable("word", w);
frag.setArguments(args);
return frag;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
this.mRoot = (ViewGroup) inflater.inflate(R.layout.fragment_practice_vocab, container, false);
ItalianWord word = null;
ParcelableWord pw = getArguments().getParcelable("word");
pw.printIt();
word = (ItalianWord) pw.getWord();
TextView tv = (TextView) this.mRoot.findViewById(R.id.word);
if(word != null) {
tv.setText(word.getmId() + " is the id\t" + word.getmWord());
} else {
tv.setText("Word not provided");
}
return this.mRoot;
}
}
/**
* fragment_practice_vocab
*/
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:background="#android:drawable/gallery_thumb"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/word"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_vertical|center_horizontal"
android:textAppearance="?android:attr/textAppearanceMedium"
android:text="placeholder"
android:layout_weight="4" />
</LinearLayout>
/**
* practice_activity
*/
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:padding="4dip"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/textViewProgressBarPractice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/practice_progress_bar"
/>
<FrameLayout
android:id="#+id/practice_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/textViewProgressBarPractice"
>
</FrameLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="end"
android:layout_alignParentBottom="true"
>
<Button
android:id="#+id/buttonCheckWordPractice"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Check"
/>
<Button
android:id="#+id/buttonCorrectWordPractice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:visibility="gone"
android:text="Correct"
/>
<Button
android:id="#+id/buttonIncorrectWordPractice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="2"
android:visibility="gone"
android:text="Incorrect"
/>
</LinearLayout>
</RelativeLayout>
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.
So I am still fairly new to working with Android Studio and everything in it. I have been stuck on trying to get fragments to communicate directly with each other. Here I'm simply just trying to set the TextView text element within one of my fragments. I have looked for hours and tried a lot, but I'm not sure what to do. Also, I am implementing my fragments through code in a FrameLayout.
Here is my fragment whose text value I'm trying to edit:
public class ReceivingFrag extends Fragment {
TextView sender;
public void updateText(String text) {
sender.setText(text);
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag_sender, container, false);
sender = (TextView) v.findViewById(R.id.sender);
return v;
}
}
I believe my root problem is that getView() and sender both return Null. I also understand that fragments are not technically views, but rather aid in the layout of views and ViewGroups. Any help is appreciated.
Not sure if it helps, but this is the method that calls the updateText() method within the ReceivingFrag class.
public void sendText(String text){
ReceivingFrag frag = new ReceivingFrag();
getSupportFragmentManager().beginTransaction().add(R.id.receiving_container, frag).commit();
getSupportFragmentManager().executePendingTransactions()
frag.updateText(text);
}
**Edit:
This is my MainActivity class that is calling and creating the Fragment:
public class MainActivity extends AppCompatActivity implements SendingFragment.TextClicked {
private static final String TAG = MainActivity.class.getSimpleName();
public final static String EXTRA_MESSAGE = "com.example.myfirstapp.MESSAGE";
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
String[] myStringArray = {"Hello", "Nice To See You", "Bye"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_1, myStringArray);
ListView listView = (ListView) findViewById(R.id.mobile_list);
listView.setAdapter(adapter);
sendText("Hello");
}
#Override
public void sendText(String text){
ReceivingFrag frag = new ReceivingFrag();
getSupportFragmentManager().beginTransaction().add(R.id.receiving_container, frag).commit();
getSupportFragmentManager().executePendingTransactions();
frag.updateText(text);
}}
**Edit 2:
This is the MainActivity layout file:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/apk/tools"
xmlns:tools2="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:weightSum="1">
<EditText android:id="#+id/edit_message"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="#string/edit_message"
android:layout_weight="1" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:onClick="sendMessage"
android:text="#string/button_send"/>
</LinearLayout>
<ListView android:id="#+id/mobile_list"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="#+id/receiving_container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></FrameLayout></LinearLayout>
And this is the layout for the Fragment:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/sender"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/frag_sender"
android:background="#color/gray"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"/></LinearLayout>
Solution:
So as mentioned below, the runtime error was fixed by adding
#Override
protected void onResume() {
super.onResume();
sendText("hello");
}
to the MainActivity class. After reading from https://developer.android.com/guide/components/fragments.html#Lifecycle
I think the statement
"Once the activity reaches the resumed state, you can freely add and remove fragments to the activity. Thus, only while the activity is in the resumed state can the lifecycle of a fragment change independently."
best explains the situation and why the error initially occurred.
If you instead put the sendText() in your onResume() like this,
#Override
protected void onResume() {
super.onResume();
sendText("Hello");
}
It will not give you the Null Pointer Exception. The fragment is still null when you call on it from onCreate().
Change your Fragment to this:
public class ReceivingFrag extends Fragment {
private TextView sender;
public void updateText(String text) {
sender.setText(text);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.frag_sender, container, false);
sender = (TextView) v.findViewById(R.id.sender);
return v;
}
}
and in your activity, before calling the updateText method, make sure the fragment transaction has executed by doing:
public void sendText(String text){
ReceivingFrag frag = new ReceivingFrag();
getSupportFragmentManager().beginTransaction().add(R.id.receiving_container, frag).commit();
getSupportFragmentManager().executePendingTransactions();
frag.updateText(text);
}
When browsing between fragment tabs in my application, the tab that I am going back to is automatically refreshed from an obsolete, to a current view. I would like to also have my application refresh the current tab after making a selection in the menubar.
What is the best way to do this? TIA
Make a update method in your fragment.
public class MyFragment extends Fragment {
public void update(){
//TODO: Do Update
}
}
Make a method like this in your MainActivity. Call it when you need to refresh the fragment.
public void refreshData(){
try {
MyFragment fragment = (MyFragment) getFragmentManager().findFragmentById(R.id.container);
fragment.update();
} catch (ClassCastException e) {
//MyFragment not active, do nothing
}
}
Got it by using FragmentManager to replace the obsolete fragment with a new one.
Now able to make a selection of an ActiveView menu item that triggers an event, and have the active fragment replaced with a current one showing this per screenshot below.
Problems getting this together were mostly related to identifying the correct R.id.layout reference to use in the FragmentManager syntax. I've posted the relevant XML and java code in hopes that others may find it useful.
MainActivity.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity">
<LinearLayout
**android:id="#+id/fragment_placeholder"**
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
</LinearLayout>
dataCapture.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:tag="data_capture"
android:id="#+id/data_capture"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
tools:context=".dataCapture" >
...
<TextView
style="#style/colorSizeStylexml"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="10dip"
android:text="Tracklog: " />
<TextView
android:id="#+id/textViewOff"
android:layout_width="40dp"
android:layout_height="40dp"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginRight="10dip"
android:text="OFF"
android:background="#ff000000"
android:textStyle="bold"
android:focusableInTouchMode="true"
android:textColor="#ffffffff" />
<TextView
android:id="#+id/textViewOn"
android:layout_width="40dp"
android:layout_height="40dp"
android:gravity="center"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginRight="10dip"
android:text="ON"
android:background="#ff58ff2d"
android:textStyle="bold" />
</LinearLayout>
...
</LinearLayout>
Activity.java
public class MainActivity extends Activity{
static int iTrackLogFlag = 0; //if value = (0) Tracklog is off, if (1) Tracklog is on
public dataCapture dataCapture;
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
...
//put Actionbar in tab mode
ActionBar actionBar = getActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
//set titles for tabs
ActionBar.Tab dataCaptureTab = actionBar.newTab().setText("DataCapture");
ActionBar.Tab dataEditTab = actionBar.newTab().setText("DataEdit");
...
/*******************************************************************************************
* Create instances of each of the fragments. dataCapture is refreshed several times from
* fragmentManager (ActiveBar menutab for tracklog ON-OFF, and at closure of lookup table
* edit PopupWindows) hence the different format.
*******************************************************************************************/
//Fragment dataCaptureFragment = new dataCapture();
android.app.FragmentManager fragmentManager = getFragmentManager();
android.app.FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
dataCapture dataCapture = new dataCapture();
fragmentTransaction.add(dataCapture,"data_capture");
fragmentTransaction.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
Fragment dataEditFragment = new dataEdit();
...
//attach those fragment instances to their respective tabs
dataCaptureTab.setTabListener(new MyTabsListener(dataCapture));
dataEditTab.setTabListener(new MyTabsListener(dataEditFragment));
...
//add each tab to the ActionBar
actionBar.addTab(dataCaptureTab);
actionBar.addTab(dataEditTab);
...
if (savedInstanceState == null){//...do nothing
}else if (savedInstanceState != null){
actionBar.setSelectedNavigationItem(savedInstanceState.getInt(TAB_KEY_INDEX,0));
}
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.corax, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch(item.getItemId()) {
case R.id.menuitem_tracklogOnOff:
openTracklogDialog();//opens a dialog box...trying to minimize clutter in the toolbar.
//
return true;
}
return false;
}
private void openTracklogDialog(){
AlertDialog.Builder TracklogDialog = new AlertDialog.Builder(this);
TracklogDialog.setTitle("Tracklog control");
TracklogDialog.setMessage("Press a button below to start or stop the tracklog.");
TracklogDialog.setPositiveButton("STOP",new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
iTrackLogFlag = 0;//"0" means OFF, sets button on frontend to black w/white letters OFF
}
});
TracklogDialog.setNegativeButton("START",new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
//Toast.makeText(getApplicationContext(), "Tracklog started.", Toast.LENGTH_LONG).show();
iTrackLogFlag = 1;//"1" means ON, sets button to green w/black letters
Fragment currentFragment = (dataCapture)getFragmentManager().findFragmentByTag("data_capture");
if(currentFragment == null) {
Toast.makeText(appContext, "This == NULL.", Toast.LENGTH_SHORT).show();
currentFragment = new dataCapture();
}else if(currentFragment != null){
getFragmentManager().popBackStack(null, FragmentManager.POP_BACK_STACK_INCLUSIVE);
getFragmentManager().beginTransaction().replace(R.id.fragment_placeholder, new dataCapture(),"data_capture").addToBackStack(null).commit();
Toast.makeText(appContext, "This != NULL. currentFragment = "+currentFragment+", dataCapture = "+dataCapture+".", Toast.LENGTH_LONG).show();
}
dialog.dismiss();
}
});
AlertDialog alert = TracklogDialog.create();
alert.show();
}
}
The problem is that when I click one element from the list is does not go to detail fragment view..but it work fine when it goes to landscape mode.Please tell me how can I go from list view to detail view by staying in portrait mode.
This is my list class
public class HadithList extends ListFragment
{
#Override
public void onActivityCreated(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onActivityCreated(savedInstanceState);
Log.d("ERROR", "In hadith list");
String[] strHadith = new String[] {"Hadith one","Hadith two","Hadith three","Hadith four"};
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity()
,android.R.layout.simple_list_item_1,strHadith);
setListAdapter(adapter);
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
// TODO Auto-generated method stub
String item = (String) getListAdapter().getItem(position);
HadithDetail hadithDetail = (HadithDetail) getFragmentManager().findFragmentById(R.id.hadith_detail);
//HadithDetail hadithDetail1 = (HadithDetail) getFragmentManager().findFragmentByTag("Details");
FragmentTransaction ft = getFragmentManager().beginTransaction();
Toast.makeText(getActivity(), "Selected "+position, Toast.LENGTH_SHORT).show();
//if(hadithDetail != null && hadithDetail.isInLayout())
hadithDetail.setText(getDetails(item));
ft.replace(R.id.hadith_list, hadithDetail);
ft.commit();
}
private String getDetails(String topic)
{
if(topic.toLowerCase().contains("one"))
{
return "Here is hadith 1 detail";
}
if(topic.toLowerCase().contains("two"))
{
return "Here is hadith 2 detail";
}
if(topic.toLowerCase().contains("three"))
{
return "Here is hadith 3 detail";
}
if(topic.toLowerCase().contains("four"))
{
return "Here is hadith 4 detail";
}
return "Cannot find detail";
}
}
This is my Detail class
![public class HadithDetail extends Fragment
{
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
Log.d("ERROR", "In hadith detail");
View view = inflater.inflate(R.layout.hadith_detail,container,false);
return view;
}
public void setText(String txt)
{
TextView view = (TextView) getView().findViewById(R.id.txtDetail);
view.setText(txt);
}
}][1]
Activity Main
<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" >
<fragment
android:id="#+id/hadith_list"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
class="com.example.hadith_app.HadithList"
/>
</LinearLayout>
Detail Layout
<TextView
android:id="#+id/txtDetail"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center_horizontal|center_vertical"
android:layout_marginTop="20dip"
android:text="Hadith 1 Detail"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textSize="30sp"
/>
</LinearLayout>
Activity_Main.xml (Land*strong text*)
<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" >
<fragment
android:id="#+id/hadith_list"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="match_parent"
class="com.example.hadith_app.HadithList"
/>
<fragment
android:id="#+id/hadith_detail"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="match_parent"
class="com.example.hadith_app.HadithDetail"
/>
</LinearLayout>
First: FragmentTransaction.replace() takes the ID of a ViewGroup, not of the fragment. You need to have a ViewGroup (such as FrameLayout) in your layout XML that acts as a container for your fragments.
Second: A fragment declared statically in an XML layout cannot be removed. You need to add it programmatically when the activity is created. You can do this like so:
public class MyActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
// savedInstancState is null on the first time onCreate() runs
Fragment f = new HadithList();
getFragmentManager().beginTransaction().add(R.id.fragment_container, f).commit();
}
}
}
I believe you should read those two links first ! to see an example of how to implement the List and Details
http://www.vogella.com/articles/AndroidFragments/article.html
http://developer.android.com/guide/components/fragments.html
Some notes:
1) First of all ,you should commit your transaction in the activity.
2) Here ft.replace(R.id.hadith_list, hadithDetail); you are trying to replace your static fragment with another one. It doesn't work like this.
(I think you should get an error when you are doing that but I am not sure).
3) Dynamic Fragments should be added in a FrameLayout. And not in the sameLayout as your List.
Anyway, just check the above Links which explain very well how you should implement the List and Details Fragments and I am sure you will find what is wrong.
I can't provide a complete example because for sure it will not be as good as the examples you will find in the above tutorials.
I have this app, that I created a custom dialog for. I must of goofed something up cause while the .show call on the dialog does indeed bring it up, it looks like a whole new fragment and it is not floating but instead replacing the ui with its contents. I did see in their help for DialogFragment:
http://hi-android.info/docs/reference/android/app/DialogFragment.html#Lifecycle
that one can embed a dialog as a regular fragment or not. Though I am not doing anything to do this so I cannot figure out why its acting like an embedded fragment and not floating. After thinking on it, is it the way I defined my XML definition? The dialogfragment example above didn't really give a definition for the xml layout, so maybe that is where my issue is? (Even added the gravity to the xml file, still no dice)
My xml definition for this Dialog is here:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:gravity="center_horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="20sp"
android:text = "Location:"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="left"/>
<Spinner
android:id="#+id/location_spinner"
android:layout_width = "450sp"
android:layout_height="wrap_content"/>
<!-- fill out the data on the package total cost etc -->
</LinearLayout>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button android:id="#+id/location_dlg_ok"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Okay"/>
<Button android:id="#+id/location_dlg_cancel"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Cancel"/>
<Button android:id="#+id/location_dlg_new"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Create new..."/>
</LinearLayout>
</LinearLayout>
Like I said displays just fine, the code for the fragment:
package com.viciousbytes.studiotab.subactivities.dialogfragments;
import ... ...
public class LocationPicker extends DialogFragment {
ArrayList<Location> mLocations;
public static LocationPicker newInstance()
{
LocationPicker loc = new LocationPicker();
return loc;
}
private void setLocations(ArrayList<Location> loc)
{
mLocations=loc;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
// Pick a style based on the num.
int style = DialogFragment.STYLE_NORMAL, theme = android.R.style.Theme;
setStyle(style, theme);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.location_dialog, container, false);
Spinner spinner = (Spinner)v.findViewById(R.id.location_spinner);
ArrayAdapter<Location> adapter = new ArrayAdapter<Location>(v.getContext(), android.R.layout.simple_spinner_item, mLocations);
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
if(mLocations==null)
spinner.setPrompt("No Locations");
else
spinner.setAdapter(adapter);
spinner.setOnItemSelectedListener(new LocationSelectedListener());
// Watch for button clicks.
Button newBtn = (Button)v.findViewById(R.id.location_dlg_new);
newBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// When button is clicked, call up to owning activity.
//create new start that activity...
}
});
// Cancel do nothing dismissthis
Button cancelBtn = (Button)v.findViewById(R.id.location_dlg_cancel);
cancelBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// When button is clicked, call up to owning activity.
//create new start that activity...
}
});
// okay button means set listener with the selected location.
Button okBtn = (Button)v.findViewById(R.id.location_dlg_ok);
okBtn.setOnClickListener(new OnClickListener() {
public void onClick(View v) {
// When button is clicked, call up to owning activity.
//create new start that activity...
}
});
return v;
}
}
It is called from a fragment itself? though does that matter? because I am calling a TimePIckerDialog and a DatePickerDialog and those work fine, but my calling code from my other fragment is:
void showLocationDialog() {
FragmentTransaction ft = getFragmentManager().beginTransaction();
Fragment prev = getFragmentManager().findFragmentByTag("locpicker");
if (prev != null) {
ft.remove(prev);
}
ft.addToBackStack(null);
// Create and show the dialog.
DialogFragment newFragment = LocationPicker.newInstance();
newFragment.show(ft, "locpicker");
}
Your constructors are wrong. Try to have just one static method newInstance to instantiate the fragment for all cases and use a Bundle to store the arguments that you want to use in the fragment. Refer to Basic Dialog section here and extend it to your case.