Loading items only once inside Android Activity or Fragment - android-fragments

Would like to check with you something about activity and fragment lifecycle.
Assuming I have Activity A calling Activity B. Inside Activity B, I have Fragment C and Fragment D separated by Tabs.
Fragment C will load spinner values downloaded from web API. After user press back button to Activity A and then to Activity B again, i do not want to reload the spinner and call web API to populate its values.
Where should I put the code to initialize spinner only once at Fragment C in this scenario?
Here is how I create Activity B
public class Activity_B extends custom_activity{
final Context context = this;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.search);
TabLayout tabLayout = (TabLayout) findViewById(R.id.tab_layout);
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(new SectionPagerAdapter(getSupportFragmentManager()));
tabLayout.setupWithViewPager(viewPager);
}
public class SectionPagerAdapter extends FragmentPagerAdapter {
public SectionPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int position) {
switch (position) {
case 0:
return new Fragment_C();
case 1:
default:
return new Fragment_D();
}
}
#Override
public int getCount() {
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
switch (position) {
case 0:
return getResources().getString(R.string.fragment_c);
case 1:
default:
return getResources().getString(R.string.fragment_d);
}
}
}
}

You can achieve this behavior using android:launchMode="singleInstance"
<activity
android:name="com.aitrich.android.framework.ui.activity.HomeActivity"
android:label="#string/app_name"
android:launchMode="singleInstance"
android:screenOrientation="portrait" >
</activity>
using android:launchMode="singleInstance" flag in ActivityB will create only single instance of that activity..so you can retain the data when navigating from ActivityA again..
NOTE : Assuming that you are not finishing ActivityB

Related

Viewpager tabs recreated?

I have implemented a NavigationDrawer with a few items in it. Every item represents a different Fragment and one of them has tabs. My problem is that every time I open this Fragment the tabs reloaded! and added to the previous tabs. Why is this happening and how can I solve it?
This is the Fragment with the tabs:
public class fragment_profilo_tabs extends Fragment implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private ActionBar actionBar;
// Tab titles
String[] tabs = { "Profilo aziendale", "Credenziali" };
/* (non-Javadoc)
* #see android.support.v4.app.Fragment#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
*/
#Override
public View onCreateView(LayoutInflater inflater,
#Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
// TODO Auto-generated method stub
// Initilization
View view = View.inflate(getActivity(), R.layout.profilo_tabs, null);
viewPager = (ViewPager) view.findViewById(R.id.pager);
actionBar = getActivity().getActionBar();
mAdapter = new TabsPagerAdapter(getFragmentManager());
viewPager.setAdapter(mAdapter);
actionBar.setHomeButtonEnabled(false);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Adding Tabs
for (String tab_name : tabs) {
actionBar.addTab(actionBar.newTab().setText(tab_name)
.setTabListener(this));}
/**
* on swiping the viewpager make respective tab selected
* */
viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
#Override
public void onPageSelected(int position) {
// on changing the page
// make respected tab selected
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
return view;
}
#Override
public void onTabReselected(Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(Tab tab, FragmentTransaction ft) {
// on tab selected
// show respected fragment view
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(Tab tab, FragmentTransaction ft) {
}
}
And this is my ViewPagerAdapter:
public class TabsPagerAdapter extends FragmentPagerAdapter {
/* (non-Javadoc)
* #see android.support.v4.view.PagerAdapter#getItemPosition(java.lang.Object)
*/
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return POSITION_NONE;
}
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
// Top Rated fragment activity
return new fragment_profilo_modificaProfilo();
case 1:
// Games fragment activity
return new fragment_profilo_credenzialiAccesso();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 2;
}
}
Ok there are multiple issues. First and foremost lets talk about your ViewPagerAdapter:
The first thing that caught my eye is this:
#Override
public int getItemPosition(Object object) {
// TODO Auto-generated method stub
return POSITION_NONE;
}
This is very bad for performance. I know that some posts on Stack Overflow suggest using this to "fix" a few things, but that is not the way to go. Remove this, you don't need it.
But the main issue is in your Fragment. First and foremost this:
for (String tab_name : tabs) {
actionBar.addTab(actionBar.newTab()
.setText(tab_name)
.setTabListener(this));
}
You are adding tabs to the ActionBar. And the ActionBar is not part of you Fragment. Each time you display this Fragment you add the same tabs to the ActionBar but you never remove them again or anything.
My advice: Don't do this. Open a new Activity for the tabs Fragment. This should clearly not be in the same Activity. I cannot imagine any situation where suddenly adding tabs to the current Activity would satisfy the Android Platform Guidelines or provide good usability.
The general rule is that everything that has to do with the Activity itself - like things concerning the ActionBar or tabs in the ActionBar - should be handled in the Activity. Code which adds tabs to the ActionBar has no place inside a Fragment. So either add the tabs permanently in onCreate() of your Activity. Or create a new Activity with those tabs especially for the fragment_profilo_tabs.
As an aside: class names should never be Snake Case. Start class names with an uppercase letter and use Camel Case. Everything else will just confuse other programmers looking at your code

Data passing between multiple fragment and activity: send different data to activity from multiple fragments

I have two activities, MainActivity, SecondActivity. Main Activity has a button. When I press the button Second Activity starts. Second Activity has 2 fragments, FragmentA and FragmentB. Fragment A has a button (B also), and I want to pass a string from Fragment A or B to Main Activity by clicking the button (Closing second activity).
I have used some references to do my job - When I click the button of Fragment A, second activity closes and a string passed to Main activity. It works But When I click the button of Fragment B (doing the same code as for Fragment A), I got an error,
java.lang.ClassCastException: (packagename).SecondActivity cannot be cast to (packagename).FragmentB$OnDataPass
dataflow:
Fragment A (button click) >> Main Activity (text view) (works)
Fragment B (button click) >> Main Activity (text view) (does not work)
Here is my Fragment A code:
String data ="String from fragment A";
public interface OnDataPass {
public void onDataPass(String data);
}
OnDataPass dataPasser;
#Override
public void onAttach(Activity a) {
super.onAttach(a);
dataPasser = (OnDataPass) a;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
view = inflater.inflate(R.layout.fragment_a, container, false);
btn = (Button) view.findViewById(R.id.btn1);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
dataPasser.onDataPass(data);
getActivity().finish();
}
});
return view;
}
public void passData(String data) {
dataPasser.onDataPass(data);
}
Here is my Fragment B code:
String data ="String from fragment B";
public interface OnDataPass {
public void onDataPass(String data);
}
OnDataPass dataPasser;
#Override
public void onAttach(Activity a) {
super.onAttach(a);
dataPasser = (OnDataPass) a; // Exception occurs here
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
view = inflater.inflate(R.layout.fragment_b, container, false);
btn = (Button) view.findViewById(R.id.btn1);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
// TODO Auto-generated method stub
dataPasser.onDataPass(data);
getActivity().finish();
}
});
return view;
}
public void passData(String data) {
dataPasser.onDataPass(data);
}
My second Activity code:
public class SecondActivity extends FragmentActivity implements OnDataPass {
Button btn1,btn2;
TabHost tabHost;
String dt="";
ViewPager viewPager = null;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
requestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_help);
viewPager = (ViewPager) findViewById(R.id.pager);
FragmentManager fragmentManager = getSupportFragmentManager();
viewPager.setAdapter(new MyAdapter(fragmentManager));
}
#Override
public void onDataPass(String data) {
// TODO Auto-generated method stub
Log.d("LOG::","hello " + data);
dt= data;
Intent intent = new Intent();
intent.putExtra("key", dt);
setResult(RESULT_OK, intent);
finish();
//startActivity(intent);
}
}
class MyAdapter extends FragmentPagerAdapter {
public MyAdapter(FragmentManager fm) {
super(fm);
// TODO Auto-generated constructor stub
}
#Override
public Fragment getItem(int i) {
// TODO Auto-generated method stub
Fragment fragment=null;
if(i==0) {
fragment = new FragmentA();
}
if(i==1) {
fragment = new FragmentB();
}
return fragment;
}
#Override
public int getCount() {
// TODO Auto-generated method stub
return 2;
}
#Override
public CharSequence getPageTitle(int position) {
// TODO Auto-generated method stub
if(position == 0) {
return "Tab 1";
}
if(position == 1) {
return "Tab 2";
}
return null;
}
}
What would I need to do to this code to get the string in FragmentB to MainActivity?
Please post code.
You have declared the OnDataPass interface in both Fragment A and Fragment B and I suspect you implemented SecondActivity with OnDataPass interface of Fragment A which You have tried to cast in Fragment B resulted in the exception.
Solution :
Move OnDataPass interface to SecondActivity
Remove OnDataPass interface from Fragment A and Fragment B.
Everything should be fine.

listview doesn't refresh in android fragment with notifyDataSetChanged()

In my android application, I am unable to solve this issue since two months even after trying so many similar solutions found at Stack Overflow. I am populating the listview with an ArrayAdapter. When an item of listview is pressed for long, a dialog box appears and asks for confirmation. After confirmation, the selected item gets deleted from the array-list but in listview last item seems to be removed. When you close the fragment and come back to it, you see the correct items but not immediately after closing the alert dialog.
Can anyone please help? Thanks!
Adapter Class
public class DuasListAdapter extends ArrayAdapter<String>{
// class variables
private final Context context;// to save context
private final List<String> duas;// to save list of stores
LayoutInflater inflater;// so save layout inflater for the view
public DuasListAdapter(Context ctx, List<String> duasList) {
super(ctx, R.layout.adapter_list_duas, duasList);
context = ctx;// save context
duas = duasList;// save list of stores
inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);// save inflater layout
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
convertView=inflater.inflate(R.layout.adapter_list_duas, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
holder.duaIndex = position;
if(SingletonClass.duasAra.get(holder.duaIndex)!=""){
holder.tv_dua_arabic.setText(SingletonClass.duasAra.get(holder.duaIndex));
}
else{
if(keyUrdSel && !keyEngSel)
holder.tv_dua_arabic.setText(SingletonClass.duasTitUrd.get(holder.duaIndex));
else
holder.tv_dua_arabic.setText(SingletonClass.duasTitEng.get(holder.duaIndex));
}
holder.tv_dua_no.setText(" ("+ (holder.duaIndex+1));
return convertView;
}
static class ViewHolder {
int duaIndex;
LinearLayout background;// background to display color for read and unread messages
ImageView iv_duatype;
TextView tv_dua_arabic;// title of message
TextView tv_dua_ref;// message by and message created on
TextView tv_dua_no;
/**
* #description constructor to save context and list of stores views
* #param v to refer the view
* #return
*/
public ViewHolder(View v) {
// initialize their respective views
background = (LinearLayout) v.findViewById(R.id.ll_duas_list);
iv_duatype = (ImageView) v.findViewById(R.id.iv_duatype);
tv_dua_arabic = (TextView) v.findViewById(R.id.tv_dua_arabic);
tv_dua_no = (TextView) v.findViewById(R.id.dua_no);
}
}
Fragment Class
public class DuasListFragment extends Fragment {
Context context;
LinearLayout ll_back_duas_list_header;
TextView ll_back_duas_list_header_title;
ListView lvDuaas;
public DuasListAdapter duasAdapter;
int intListViewItemPosition = -1;
ArrayList<String> duas;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
context = inflater.getContext();
View view = inflater.inflate(R.layout.fragment_list_duas, container, false);
// TODO Auto-generated method stub
return view;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
ll_back_duas_list_header = (LinearLayout) getActivity().findViewById(R.id.ll_back_duas_list_header);
ll_back_duas_list_header_title = (TextView) getActivity().findViewById(R.id.ll_back_duas_list_header_title);
lvDuaas = (ListView) getActivity().findViewById(R.id.lv_duas);
String verses = new SharedPreferencesSupplication().read(SingletonClass.keyListOfVerses, "a1");
String[] versesList = verses.split(",");
duas = new ArrayList<String>();
for (int i = 0; i < versesList.length; i++) {
if (versesList[i].length() > 0)
duas.add(versesList[i]);
}
duasAdapter = new DuasListAdapter(context, duas);
lvDuaas.setAdapter(duasAdapter);
lvDuaas.setOnItemLongClickListener(new OnItemLongClickListener() {
#Override
public boolean onItemLongClick(final AdapterView<?> parent,
View view, final int position, long id) {
// TODO Auto-generated method stub
DialogInterface.OnClickListener dialogClickListener = new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
switch (which) {
case DialogInterface.BUTTON_POSITIVE:
duasAdapter = (DuasListAdapter) parent.getAdapter();
duas.remove(position);
duasAdapter.notifyDataSetChanged();
((MainActivity) context).removeItemFromList(position);
break;
case DialogInterface.BUTTON_NEGATIVE:
// No button clicked
break;
}
}
};
AlertDialog.Builder builder = new AlertDialog.Builder(context);
builder.setMessage("Delete Dua from Favorites?").setPositiveButton("Yes", dialogClickListener).setNegativeButton("No", dialogClickListener).show();
return true;
}
});
super.onActivityCreated(savedInstanceState);
}
#Override
public void onResume() {
// TODO Auto-generated method stub
super.onResume();
// duasAdapter.clear();
//duasAdapter.addAll(duas);
duasAdapter.notifyDataSetChanged();
}
}
I also faced same problem with notifydatasetchanged method.
My problem was List that I had attached to adapter was getting updated but notifydatasetchanged method was not working.
If your ListView is designed to show moderate number of items in it,instead of calling notifydatasetchanged you can recreate your adapter.
It worked for me.I wasusing BaseAdapter.

How to transfer variables to a fragment was created using the ActionBarActivity?

The activity is called from another activity delivering a string array, which needs to be displayed in a fragment of the target activity.
MainActivity (string array) -> Result2Activity: Tab1 should display string index0, Tab2 should display string index1 etc.
I followed a similar problem here:
Access Fragment View from Activity's onCreate
My problem is right now that the onActivityCreated method is never called in the Result2Activity.
I am also not sure if this method is the right way to address the fragment:
getSupportFragmentManager().findFragmentByTag("Out1");
What is the best way to achieve that ?
Result2Activity:
public class Result2Activity extends ActionBarActivity implements
ActionBar.TabListener {
/**
* The {#link android.support.v4.view.PagerAdapter} that will provide
* fragments for each of the sections. We use a {#link FragmentPagerAdapter}
* derivative, which will keep every loaded fragment in memory. If this
* becomes too memory intensive, it may be best to switch to a
* {#link android.support.v4.app.FragmentStatePagerAdapter}.
*/
SectionsPagerAdapter mSectionsPagerAdapter;
/**
* The {#link ViewPager} that will host the section contents.
*/
ViewPager mViewPager;
public List<String> fragments = new Vector<String>();
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.result2_activity);
// Set up the action bar.
final ActionBar actionBar = getSupportActionBar();
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
// Create the adapter that will return a fragment for each of the three
// primary sections of the activity.
mSectionsPagerAdapter = new SectionsPagerAdapter(
getSupportFragmentManager());
// Set up the ViewPager with the sections adapter.
mViewPager = (ViewPager) findViewById(R.id.pager);
mViewPager.setAdapter(mSectionsPagerAdapter);
// When swiping between different sections, select the corresponding
// tab. We can also use ActionBar.Tab#select() to do this if we have
// a reference to the Tab.
mViewPager
.setOnPageChangeListener(new ViewPager.SimpleOnPageChangeListener() {
#Override
public void onPageSelected(int position) {
actionBar.setSelectedNavigationItem(position);
}
});
// For each of the sections in the app, add a tab to the action bar.
for (int i = 0; i < mSectionsPagerAdapter.getCount(); i++) {
// Create a tab with text corresponding to the page title defined by
// the adapter. Also specify this Activity object, which implements
// the TabListener interface, as the callback (listener) for when
// this tab is selected.
actionBar.addTab(actionBar.newTab()
.setText(mSectionsPagerAdapter.getPageTitle(i))
.setTabListener(this));
}
Log.d("DEBUG","1: onCreate finished");
}
public void onActivityCreated (Bundle savedInstanceState) {
Log.d("DEBUG","2: onActivityCreated finished");
//super.onActivityCreated(savedInstanceState);
// retrieve data from MainActivity
Intent intent = getIntent();
String message[] = intent.getStringArrayExtra(MainActivity.OUTPUT);
//send data to fragment Out1Fragment
Out1Fragment set_out1 =
(Out1Fragment)
getSupportFragmentManager().findFragmentByTag("Out1");
set_out1.settext(message[0]);
}
#Override
public void onTabSelected(ActionBar.Tab tab,
FragmentTransaction fragmentTransaction) {
// When the given tab is selected, switch to the corresponding page in
// the ViewPager.
mViewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab,
FragmentTransaction fragmentTransaction) {
}
#Override
public void onTabReselected(ActionBar.Tab tab,
FragmentTransaction fragmentTransaction) {
}
public class SectionsPagerAdapter extends FragmentPagerAdapter {
public SectionsPagerAdapter(FragmentManager fm) {
super(fm);
fragments.add(Out1Fragment.class.getName());
fragments.add(Out2Fragment.class.getName());
fragments.add(Out3Fragment.class.getName());
}
#Override
public Fragment getItem(int position) {
Fragment fragment = null;
switch (position) {
case 0:
fragment = new Out1Fragment();
break;
case 1:
fragment = new Out2Fragment();
break;
case 2:
fragment = new Out3Fragment();
break;
}
return fragment;
}
#Override
public int getCount() {
// Show 3 total pages.
return 3;
}
#Override
public CharSequence getPageTitle(int position) {
Locale l = Locale.getDefault();
switch (position) {
case 0:
return getString(R.string.out_section1).toUpperCase(l);
case 1:
return getString(R.string.out_section2).toUpperCase(l);
case 2:
return getString(R.string.out_section3).toUpperCase(l);
}
return null;
}
}
}
Out1Fragment:
public class Out1Fragment extends Fragment {
private static TextView textview;
public Out1Fragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View dataView = inflater.inflate(R.layout.out1_fragment, container,
false);
textview = (TextView) dataView.findViewById(R.id.outPut1);
return dataView;
}
public void settext(String text)
{
textview.setText(text);
}
}
XML Resource:
result2_activity.xml
<android.support.v4.view.ViewPager xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="dd.testing.finance.Result2Activity" />
out1_fragment.xml
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/ScrollView2"
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"
android:tag="Out1"
tools:context="dd.testing.finance.Result2Activity$Out1Fragment" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical" >
<TextView
android:id="#+id/outPut1"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginLeft="10sp"
android:layout_marginRight="10sp"
android:textSize="16sp" />
</LinearLayout>
</ScrollView>
Not really sure if this was the right way, but at least it does exaclty what I needed.
In the Out1Fragment.class added the onActivityCreated method:
public void onActivityCreated (Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
settext(((Result2Activity)getActivity()).getOutput1());
}
From there calling the added method getOutput1 in the Result2Activity:
public String getOutput1 () {
// retrieve data from MainActivity
Intent intent = getIntent();
String message[] = intent.getStringArrayExtra(MainActivity.OUTPUT);
return message[0];
}

How to save fragment's state across fragment transactions

I am having some difficulties in wrapping my head around android fragments. I have an ActionBar with a Spinner and I create a new Fragment on every click on the spinner item. I have something like the following code:
public class MyActivity extends Activity
{
...
...
private void initActionBarSpinner()
{
getActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
OnNavigationListener callback = new OnNavigationListener()
{
#Override
public boolean onNavigationItemSelected(int itemPosition, long itemId)
{
if (itemPosition >= 0 && itemPosition < dataSet.size())
{
openTagFragment(itemPosition);
return true;
}
return false;
}
};
spinnerAdapter = new ArrayAdapter<String>(this, R.layout.action_bar_spinner, R.id.spinnertAdapterItem, tags);
getActionBar().setListNavigationCallbacks(spinnerAdapter, callback);
}
#Override
public void onCreate(Bundle savedInstanceState)
{
Log.d(TAG, TAG + " oncreate");
super.onCreate(savedInstanceState);
initActionBarSpinnerAdapter();
}
protected void openTagFragment(int itemPosition)
{
Log.d(TAG, "Replacing fragment");
String fragmentTag = dataSet.get(itemPosition);
MyFragment myFragment = new MyFragment();
myFragment.setData(dataSet.get(itemPosition));
FragmentTransaction fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.replace(R.id.fragmentContainer, myFragment, fragmentTag);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commit();
}
}
Problem I am facing: on orientation change, a new fragment gets created, which means it fetches the same data again from the server. I can see onSaveInstanceState() getting called on existing fragment when rotated but if I am going to do a new on Fragment again, when and how will I get a valid savedInstance bundle on fragment's onCreate()?

Resources