How to give action to different fragments - android

I am very new to android so please excuse me if my question is an obvious one. I have to split the screen in 2 parts and give action to each of the fragments. One part will plot the data and the other part will display the data. I have used fragments to split the screen but I want to know how to give the action to each of the fragments so that it starts together.
I don't want to initiate any fragment with a click of button or else. I want that as soon as the app starts, then both the fragments starts to display their respective plots and data.
Your help in this regard will be highly appreciated.
EDIT
I have attached the codes
PlotFragment.java (the file which I want to run in one of the fragment as the app is launched)
package com.tempsensor;
import android.app.Fragment;
import android.os.Bundle;
import android.support.v4.app.FragmentActivity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
public class PlotFragment extends FragmentActivity {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstancesState){
Toast.makeText(this, "Plot Comes Here", Toast.LENGTH_LONG).show();
return null;
}
}
SecondActivity.java (the java file which contains the Activity within which I have created the two fragments)
package com.tempsensor;
import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.Button;
import android.support.v4.app.FragmentActivity;
public class SecondActivity extends Activity implements OnClickListener {
Button buttonexit;
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.second);
buttonexit = (Button) findViewById(R.id.button1);
buttonexit.setOnClickListener(this);
getFragmentManager().beginTransaction().add(R.id.plot_fragment, new PlotFragment()).commit();
getFragmentManager().beginTransaction().add(R.id.data_fragment, new DataFragment()).commit();
}
public void onClick(View arg0) {
if (arg0==buttonexit)
{
System.exit(0);
}
}
}
Where am I making a mistake, the desired result is that when the app launches these two fragments should start their task automatically, here I am just trying to print a text using Toast.

If I'm understanding your question correctly, you want the app to open and show both fragments with all their info already loaded, a plot on one and data in the other?
In the activity that is launched, you are creating a fragment and committing a transaction that adds it to a container, correct? All you should have to do do the same for the other fragment: create it and commit a transaction adding it to the other container.
Here's some simple code demonstrating what I mean:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
getFragmentManager().beginTransaction()
.add(R.id.plot_container, new PlotFragment()).commit();
getFragmentManager().beginTransaction()
.add(R.id.data_container, new DataFragment()).commit();
}

So, you can create method in second fragment. This method will return needed data.
For example: you must to get instanse of fragment in current activity and call this method like this:
for (Fragment f : getActivity().getFragmentManager().getFragments()) {
if (f instanceof SecondFragment) {
int data = ((SecondFragment) f).returnData();
}

First, i suggest you to change the way you add the fragments to the layout, something like this:
public class SecondActivity extends Activity implements OnClickListener {
#Override
protected void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
setContentView(R.layout.second);
buttonexit = (Button) findViewById(R.id.button1);
buttonexit.setOnClickListener(this);
PlotFragment plotFrag = new PlotFragment();
DataFragment dataFrag = new DataFragment();
addData(plotFrag);
addData(dataFrag);
getFragmentManager().beginTransaction().add(R.id.plot_fragment, plotFrag).commit();
getFragmentManager().beginTransaction().add(R.id.data_fragment, dataFrag).commit();
}
protected void addData(DataFragment dataFrag) {
Bundle args = new Bundle();
// Load data to args variable
dataFrag.setArguments(args);
}
protected void addData(PlotFragment plotFrag) {
Bundle args = new Bundle();
// Load plot data to args variable
plotFrag.setArguments(args);
}
}
In the Fragments classes:
public class DataFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstancesState){
Bundle args = getArguments() == null ? savedInstanceState : getArguments();
// Use args to setup Fragment content
Toast.makeText(this, "Data Comes Here", Toast.LENGTH_LONG).show();
return null;
}
public class PlotFragment extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstancesState){
Bundle args = getArguments() == null ? savedInstanceState : getArguments();
// Use args to setup Fragment content
Toast.makeText(this, "Plot Comes Here", Toast.LENGTH_LONG).show();
return null;
}

Related

How to create fragment interaction?

These are the imports I have for the fragment class.
import android.os.Bundle;
import android.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
Class being initialized
public class FragmentDemo extends Fragment {
Public constructor is empty and must stay that way
public FragmentDemo() {
}
It must be attached to an activity, the interface depends on said activity
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Inflating layout for fragment
View result = inflater.inflate(R.layout.fragment_fragment_demo, container, false);
TextView textView = (TextView)result.findViewById(R.id.textView2);
Button button = (Button)result.findViewById(R.id.button2);
textView.setText("HEY, THIS IS A TEST!");
button.setOnClickListener(new View.OnClickListener() {
public void onClick(View view) {
Log.e("FRAGMENT", "BUTTON IS WORKING!");
// why no toast?
}
});
return result;
}
}
You must create two classes to make it more clear, one for the interaction of fragments, and a main class.
The first one should look like this:
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.TextView;
import org.w3c.dom.Text;
/**
* A simple {#link Fragment} subclass.
* Activities that contain this fragment must implement the
* {#link InteractionFragment.OnFragmentInteractionListener} interface
* to handle interaction events.
* Use the {#link InteractionFragment#newInstance} factory method to
* create an instance of this fragment.
*/
public class InteractionFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_NAME = "name";
private static final String ARG_LAST_NAME = "lastName";
// TODO: Rename and change types of parameters
private String name;
private String lastName;
private OnFragmentInteractionListener mListener;
public InteractionFragment() {
// Required empty public constructor
}
/**
* Use this factory method to create a new instance of
* this fragment using the provided parameters.
*
* #param param1 Parameter 1.
* #param param2 Parameter 2.
* #return A new instance of fragment InteractionFragment.
*/
// TODO: Rename and change types and number of parameters
// Factory method
public static InteractionFragment newInstance(String param1, String param2) {
InteractionFragment fragment = new InteractionFragment();
Bundle args = new Bundle();
args.putString(ARG_NAME, param1);
args.putString(ARG_LAST_NAME, param2);
fragment.setArguments(args);
return fragment;
}
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
name = getArguments().getString(ARG_NAME);
lastName = getArguments().getString(ARG_LAST_NAME);
}
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_interaction, container, false);
TextView nameText = (TextView)v.findViewById(R.id.name);
TextView lastNameText = (TextView)v.findViewById(R.id.last_name);
nameText.setText(name);
lastNameText.setText(lastName);
Button button = (Button)v.findViewById(R.id.button3);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mListener.somethingHappened("THE BUTTON WAS PRESSED, HOW EXCITING!");
}
});
return v;
}
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
public void onDetach() {
super.onDetach();
mListener = null;
}
public void printALog(){
Log.wtf("PRINTING", "ITS WORKING!");
}
/**
* This interface must be implemented by activities that contain this
* fragment to allow an interaction in this fragment to be communicated
* to the activity and potentially other fragments contained in that
* activity.
* <p>
* See the Android Training lesson <a href=
* "http://developer.android.com/training/basics/fragments/communicating.html"
* >Communicating with Other Fragments</a> for more information.
*/
public interface OnFragmentInteractionListener {
void somethingHappened(String message);
}
}
While the second one should look like this:
import android.app.Fragment;
import android.app.FragmentManager;
import android.app.FragmentTransaction;
import android.os.TransactionTooLargeException;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Toast;
public class MainActivity extends AppCompatActivity implements InteractionFragment.OnFragmentInteractionListener {
InteractionFragment fragment;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
private void addFragment(Fragment f){
// add a fragment through code
// fragment manager - the guy in charge of any fragment related logic
FragmentManager manager = getFragmentManager();
// transactions
FragmentTransaction transaction = manager.beginTransaction();
// actually do the thing
//fragment = InteractionFragment.newInstance("Juan","Perez");
// 3rd parameter - fragment
transaction.add(R.id.container, f, "dude");
transaction.commit();
}
#Override
public void somethingHappened(String message) {
Toast.makeText(this, message, Toast.LENGTH_SHORT).show();
}
public void doSomethingOnFragment(View v){
fragment.printALog();
}
public void swapFragments(View v){
FragmentManager manager = getFragmentManager();
Fragment f = manager.findFragmentByTag("dude");
if(f != null){
Toast.makeText(this, "FRAGMENT BEING REMOVED", Toast.LENGTH_SHORT).show();
FragmentTransaction transaction = manager.beginTransaction();
transaction.remove(f);
transaction.commit();
} else {
Toast.makeText(this, "NO FRAGMENT TO REMOVE", Toast.LENGTH_SHORT).show();
}
}
public void addFragment1(View v){
fragment = InteractionFragment.newInstance("Juan","Perez");
addFragment(fragment);
}
public void addFragment2(View v){
FragmentDemo fd = new FragmentDemo();
addFragment(fd);
}
}
I'm not sure to understand what is your question exactly, so I will assume first that your problem is related to your "Why no toast?" comment.
The thing is you are using the log function, this prints in logcat console, not through toasts.
If you want to display a toast, use
Toast.makeText(context, text, durationInMs).show();
More info here
I'm not sure to understand what is your question exactly, so I will assume first that your problem is related to your "Why no toast?" comment.
The thing is you are using the log function, this prints in logcat console, not through toasts.
If you want to display a toast, use
Toast.makeText(context, text, durationInMs).show();
And because you are in a Fragment the Context used is the activity which has this fragment so to get context we use getActivity(). Hence the Toast will be:
Toast.makeText(getActivity(), text, Toast.LENGTH_SHORT).show();

Error in addSlide method in appintro in android studio

There is an error in this line:
addSlide(AppIntroSampleSlider.newInstance(R.layout.app_intro1));
addSlide (android.support.v4.app.Fragment)
In AppIntroBase, it cannot be applied
My code is here:
import android.content.Intent;
import android.os.Bundle;
import android.widget.Toast;
import com.github.paolorotolo.appintro.AppIntro;
/**
* Created by Arvind on 2/6/2017.
*/
public class MyIntro extends AppIntro {
#Override
public void init(Bundle savedInstanceState) {
//adding the three slides for introduction app you can ad as many you needed
addSlide(AppIntroSampleSlider.newInstance(R.layout.app_intro1));
addSlide(AppIntroSampleSlider.newInstance(R.layout.app_intro2));
addSlide(AppIntroSampleSlider.newInstance(R.layout.app_intro3));
// Show and Hide Skip and Done buttons
showStatusBar(false);
showSkipButton(false);
// Turn vibration on and set intensity
// You will need to add VIBRATE permission in Manifest file
setVibrate(true);
setVibrateIntensity(30);
//Add animation to the intro slider
setDepthAnimation();
}
#Override
public void onSkipPressed() {
// Do something here when users click or tap on Skip button.
Toast.makeText(getApplicationContext(),
getString(R.string.app_intro_skip), Toast.LENGTH_SHORT).show();
Intent i = new Intent(getApplicationContext(), MainActivity.class);
startActivity(i);
}
#Override
public void onNextPressed() {
// Do something here when users click or tap on Next button.
}
#Override
public void onDonePressed() {
// Do something here when users click or tap tap on Done button.
finish();
}
#Override
public void onSlideChanged() {
// Do something here when slide is changed
}
}
I created a class i.e. AppIntroSampleSlider.
My AppIntroSampleSlider class is:
package com.example.arvind.appintro1;
import android.app.Fragment;
import android.os.Bundle;
import android.support.annotation.Nullable;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
/**
* Created by Arvind on 13-Feb-17.
*/
public class AppIntroSampleSlider extends Fragment {
private static final String ARG_LAYOUT_RES_ID = "layoutResId";
public static AppIntroSampleSlider newInstance(int layoutResId) {
AppIntroSampleSlider sampleSlide = new AppIntroSampleSlider();
Bundle bundleArgs = new Bundle();
bundleArgs.putInt(ARG_LAYOUT_RES_ID, layoutResId);
sampleSlide.setArguments(bundleArgs);
return sampleSlide;
}
private int layoutResId;
public AppIntroSampleSlider() {}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if(getArguments() != null && getArguments().containsKey(ARG_LAYOUT_RES_ID))
layoutResId = getArguments().getInt(ARG_LAYOUT_RES_ID);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(layoutResId, container, false);
}
}
I want to why it is showing error in the code.So Please help me to solve this error.
I found a better example without error
http://www.androidhive.info/2016/05/android-build-intro-slider-app/
AppIntro library works best with the following import in your AppIntroSampleSlider.java file:
import android.support.v4.app.Fragment;
Instead of:
import android.app.Fragment;

How to send data to another fragment in one activity?

How to send data to another fragment in one activity?
I have two fragment that have been created using Android Studio Design View Editor. I ccreated these two fragment on my MainActivity. fragment1 is the ID of first fragment, it contain just EditText and a button. fragment2 is the ID of second fragment, it just contain textView.
How to send data from EditText of fragment1 to textView of fragment2?
I have write some code below, please check it.
MainActivity.java
package com.example.radioswiba.belajar2buahfragment;
import android.net.Uri;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
public class MainActivity extends AppCompatActivity implements Fragment1.OnFragmentInteractionListener, Fragment2.OnFragmentInteractionListener{
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
}
#Override
public void onFragmentInteraction(Uri uri) {
}
}
Fragment1.java
//this code was generated by Android Studio
//i have deleted some unused code and comments
package com.example.radioswiba.belajar2buahfragment;
import android.content.Context;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.EditText;
public class Fragment1 extends Fragment {
//let's define some of variable
private EditText text_input;
private Button button_send;
private OnFragmentInteractionListener mListener;
public Fragment1() {
// Required empty public constructor
}
//this generated by Android Studio
public static Fragment1 newInstance(String param1, String param2) {
Fragment1 fragment = new Fragment1();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
//this generated by Android Studio
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
// my code here
View rootView = inflater.inflate(R.layout.fragment_fragment1, container, false);
text_input = (EditText) rootView.findViewById(R.id.status_text);
button_send = (Button) rootView.findViewById(R.id.post_btn);
button_send.setOnClickListener(postStatus);
return rootView;
}
View.OnClickListener postStatus = new View.OnClickListener(){
#Override
public void onClick(View v){
text_of_me = text_input.getText().toString();
//
//WHAT SHOULD I WRITE HERE?
//SHOULD I USED BUNDLE?
}
};
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof OnFragmentInteractionListener) {
mListener = (OnFragmentInteractionListener) context;
} else {
throw new RuntimeException(context.toString()
+ " must implement OnFragmentInteractionListener");
}
}
#Override
public void onDetach() {
super.onDetach();
mListener = null;
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
void onFragmentInteraction(Uri uri);
}
}
Fragment2.java
//the code almost same with Fragment1.java
I have search similar quenstion on stackoverflow, but i can not figure out. I have found many solution like below:
Fragment fragment = new Fragment();
Bundle bundle = new Bundle();
bundle.putInt(key, value);
fragment.setArguments(bundle);
There we create a new Fragment, meanwhile i have had two fragment on my activity, i have create it manually from file -> new -> new fragment from android Studio Menu. Should i create new Fragment by using above code?
A good way to communicate between fragments and/or activities is with Otto, an event bus library.
if you implement it correctly, I am quite confident it gonna solve your issue.
Here is few examples :
http://www.vogella.com/tutorials/JavaLibrary-EventBusOtto/article.html
https://github.com/CardinalNow/event-bus-example
http://www.recursiverobot.com/post/48752686831/playing-around-with-otto-on-android

AsyncTask *occasionally* getting stuck with connection timeout/error problems, potential memory leak or Android Studio bug?

I'm new at Android and Java development I've put together a simple demo app to start learning. It is made of:
a main activity extending ActionBarActivity, in which
a ViewPager is instantiated which has
a FragmentPagerAdapter responsible for showing up...
...one Fragment out of three at a given time and
one of those fragments, when it is created, just to try things out, executes an AsyncTask (defined by another class) which triggers an HTTP request that when done (onPostExecute)...
populates a TableLayout in the Fragment that fired it.
I'm also trying to keep compatibility with older Android platforms, so I'm using support library where necessary.
The problem that I am seeing, or I should say that I was seeing (keep reading..), is that from time to time I get
org.apache.http.conn.HttpHostConnectException: Connection to http://123.123.123.123:80/notes.json refused
together with a timeout error message after a long wait of aproximately 40 seconds. Of course Internet permission has already been added.
This happens randomly and it usually does after first run of Garbage Collector.
After spending days trying to debug it, I finally did a system reboot and this behaviour totally disappeared.
But, given I spent so much time on it (thinking that I was badly leaking something), I would still like to:
Understand what was going on
Understand if I am correctly profiling memory leaks
1: Understand what was going on:
The screen that shows up when starting the app, is the fragment that calls the AsyncTask responsible of updating the fragment UI itself (it is assigned to the first Action Bar tab).
As soon as the app started I began to continuosly rotate the screen to see what would happen to the memory. The screenshot below is from Android Studio memory profiler.
The first connection-timeout error usually occurred right after first run of Garbage Collector, after I had filled up all available memory by rotating the device many times.
At this point it happened that the fragment UI failed at being updated by AsyncTask (which was stuck processing the apparently unresponsive connection). The web server would not receive the HTTP request at all, and even if I rotated the screen again - in order for the Activity and Fragment to restart - subsequent AsyncTask's did not work too and no new HTTP requests would be made.
Of course I've been catching all exceptions and, at the beginning of onPostExecute() I had to do if (arrayOfJSONobjs == null) { return; } in order to avoid feeding in a null object to the subsequent fragment UI's building methods.
So, what do you think could have happened to make the connection work like that? How is that that after reboot I am not seeing that again? I've tried disabling antivirus, firewall, and checked if router or web server were applying some kind of protection for too many consecutive requests. (My device connects to the web server from the internet, using my public IP). Nothing worked, except reboot. The only thing I'm left with thinking is.. possibly some bug in Android Studio which got in the middle of the requests at some time?
2: Am I correctly understanding memory allocation and GC?
Looking at the code, do you think there is some place where I could possibly be leaking the context?
Is what I see in the memory profiler screenshot the expected good behaviour for a non leaking app? I mean, am I supposed to see memory being filled up even when there is no leaking (provided that then it gets garbage collected) ?
I don't know how to better put this, but am I expected to see this kind of graph when everything is going ok? As you can see, the first time, the GC is only invoked when memory is completely filled up, but afterwards GC triggers in sooner, when there is still some memory available (I was still rotating the device though). Is this normal?
Despite the errors above (but still possibly related to them, in case memory leaking is actually happening): I am unsure about having to pass both the view and the context to the AsyncTask object. Could I just pass only one of those and infer the other one from it, in order to minimize as much as possible the references I am passing?
Cleaning up: the subquestion about whether or not TableLayout is a good fit for the layout I'm trying to build has been moved to another question.
Code:
MainActivity.java
package com.mydom.demoapp;
import android.os.Bundle;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.NavUtils;
import android.support.v4.view.ViewPager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.ActionBarActivity;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import com.mydom.demoapp.adapter.TabsPagerAdapter;
public class MainActivity extends ActionBarActivity implements ActionBar.TabListener {
private ViewPager viewPager;
private TabsPagerAdapter mAdapter;
private ActionBar actionBar;
private String[] tabs = { "Music", "Movies", "News"};
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager = (ViewPager) findViewById(R.id.pager);
mAdapter = new TabsPagerAdapter(getSupportFragmentManager());
viewPager.setAdapter(mAdapter);
actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setHomeButtonEnabled(true);
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) {
actionBar.setSelectedNavigationItem(position);
}
#Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
#Override
public void onPageScrollStateChanged(int arg0) {
}
});
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
if (item.getItemId() == android.R.id.home) {
NavUtils.navigateUpFromSameTask(this);
return true;
}
return super.onOptionsItemSelected(item);
}
#Override
public void onTabReselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
#Override
public void onTabSelected(ActionBar.Tab tab, FragmentTransaction ft) {
// on tab selected show appropriate fragment
viewPager.setCurrentItem(tab.getPosition());
}
#Override
public void onTabUnselected(ActionBar.Tab tab, FragmentTransaction ft) {
}
#Override
public void onStop(){
super.onStop();
Log.d("mytag", "MainActivity: onStop entered");
}
}
TabsPageAdapter.java
package com.mydom.demoapp.adapter;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentPagerAdapter;
import com.mydom.demoapp.MusicFragment;
import com.mydom.demoapp.MoviesFragment;
import com.mydom.demoapp.NewsFragment;
// This adapter provides fragment views to tabs.
public class TabsPagerAdapter extends FragmentPagerAdapter{
public TabsPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
switch (index) {
case 0:
return new MusicFragment();
case 1:
return new MoviesFragment();
case 2:
return new NewsFragment();
}
return null;
}
#Override
public int getCount() {
// get item count - equal to number of tabs
return 3;
}
}
MusicFragment.java (responsible for instantiating and launching an AsyncTask)
package com.mydom.demoapp;
import android.net.Uri;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.mydom.demoapp.async_task.AsyncTaskRunner;
public class MusicFragment extends Fragment {
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
private OnFragmentInteractionListener mListener;
public static MusicFragment newInstance(String param1, String param2) {
MusicFragment fragment = new MusicFragment();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
public MusicFragment() {
// Required empty public constructor
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
Log.d("mytag", "MusicFragment: onCreate entered");
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
// Inflate the layout for this fragment
Log.d("janfry", "MusicFragment: onCreateView entered");
return inflater.inflate(R.layout.fragment_music, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
Log.d("mytag", "MusicFragment: onViewCreated entered");
runner = new AsyncTaskRunner();
runner.execute(this.getActivity(), view);
// I am passing it the context (by getting the activity) and the view so that it will know where to update the UI.
}
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
// TODO: Rename method, update argument and hook method into UI event
public void onButtonPressed(Uri uri) {
if (mListener != null) {
mListener.onFragmentInteraction(uri);
}
}
public interface OnFragmentInteractionListener {
// TODO: Update argument type and name
public void onFragmentInteraction(Uri uri);
}
#Override
public void onStop(){
super.onStop();
Log.d("mytag", "MusicFragment: onStop entered");
}
}
AsyncTaskRunner.java
package com.mydom.demoapp.async_task;
import android.content.Context;
import android.graphics.Color;
import android.os.AsyncTask;
import android.util.Log;
import android.view.View;
import android.widget.TableLayout;
import android.widget.TableRow;
import android.widget.TextView;
import com.mydom.demoapp.R;
import com.mydom.demoapp.Utils;
import org.apache.http.HttpResponse;
// import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.HttpClient;
import org.apache.http.client.methods.HttpGet;
// import org.apache.http.conn.ConnectTimeoutException;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.util.EntityUtils;
import org.json.JSONArray;
// import org.json.JSONException;
import org.json.JSONObject;
// import java.io.IOException;
// import java.net.SocketTimeoutException;
// import java.util.concurrent.TimeoutException;
public class AsyncTaskRunner extends AsyncTask<Object, String, JSONArray> {
// These will be set by doInBackground() according to what the fragment passed to it
// I am declaring them as instance variables because I'll need them in the onPostExecute method too, so to have a ref to the frag to update.
// By the way, can I infer one from the other someway?
Context contextRef;
View viewRef;
#Override
protected void onPreExecute(){
Log.d("janfry", "AsyncTaskRunner: onPreExecute entered");
}
#Override
protected JSONArray doInBackground(Object... params){
Log.d("mytag", "AsyncTaskRunner: doInBackground entered");
contextRef = (Context) params[0];
viewRef = (View) params[1];
HttpResponse response;
String str = "";
JSONArray arrayOfJSONObjects = null;
final HttpParams httpParams = new BasicHttpParams();
HttpConnectionParams.setConnectionTimeout(httpParams, 5000);
HttpClient myClient = new DefaultHttpClient(httpParams);
HttpGet myConnection = new HttpGet("http://123.123.123.123:80/notes.json");
try {
response = myClient.execute(myConnection);
str = EntityUtils.toString(response.getEntity(), "UTF-8");
} catch (Throwable t) {
t.printStackTrace();
}
try{
arrayOfJSONObjects = new JSONArray(str);
} catch ( Throwable t) { t.printStackTrace(); }
try {
Log.d("mytag", arrayOfJSONObjects.getString(0));
} catch (Throwable t) {
t.printStackTrace();
}
return arrayOfJSONObjects;
}
#Override
protected void onProgressUpdate(String... notused){
}
#Override
protected void onPostExecute(JSONArray arrayOfJSONobjs) {
Log.d("mytag", "AsyncTaskRunner: onPostExecute entered");
TableLayout tab_lay = (TableLayout) viewRef.findViewById(R.id.musicTableLayout);
tab_lay.removeAllViews();
TextView[] arrayOfTextViews;
arrayOfTextViews = new TextView[arrayOfJSONobjs.length()];
for(int pos = 0; pos < arrayOfJSONobjs.length(); pos++) {
// and let's populate it with textviews...
TextView textViewForObjName = new TextView(contextRef);
try {
JSONObject oneJsonObj; // will hold the parsed JSON for one obj
oneJsonObj = arrayOfJSONobjs.getJSONObject(pos);
textViewForObjName.setText(oneJsonObj.getString("name"));
} catch (Throwable t) {
t.printStackTrace();
}
textViewForObjName.setHeight(Utils.dip(contextRef, 30));
textViewForObjName.setBackgroundColor(Color.parseColor("#ABCABC"));
// let's add the text_view we built to the Array
arrayOfTextViews[pos] = textViewForObjName;
} // we now have an array of textviews, that has not been added to the UI yet.
// I want to populate the array with 3 textviews per row.
// Is it a good idea to use this layout for this way of laying out content?
// Would you have done that differently?
TableRow table_row = new TableRow(contextRef);
int col_counter = 0;
for (TextView aTextView : arrayOfTextViews) {
table_row.addView(aTextView);
col_counter++;
if (col_counter == 3) {
tab_lay.addView(table_row);
table_row = new TableRow(contextRef);
col_counter = 0;
}
}
}
}
#corsair992 is suggesting (it's just a guess) that "it may be that you were exceeding some maximum limit of concurrent connections allowed in Apache's DefaultHttpClient or elsewhere."
I advised her/him to put the above comment in a proper answer (together with other observations s/he did), but s/he said that it's just a guess and not a complete answer.
Still to me it's the most reasonable hypothesis and I think it deserves full visibility and attention so I am saving it as an answer myself so that maybe it can be further commented and expanded (please upvote the original #corsair992 comment under my question).

View.onRestoreInstanceState not being called when fragment setRetainInstance(true) (double screen orientation change when fragment is in a back stack)

I have two fragments: one on top of another. Fragments has setRetainInstance(true) in their constructors.
When I do a double screen flip and press back key, top fragment gets popped and bottom fragment becomes visible. But bottom fragment's view do not receive onRestoreInstanceState.
When I remove setRetainInstance(true) everything works just fine. But I need that setRetainInstance(true) to work with multiple threads, so I can't solve my problem that simple. :)
How to guarantee onRestoreInstanceState to be called in this case?
Here is some example code:
package com.example.saverestoreviewstate;
import android.app.Activity;
import android.app.Fragment;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.LinearLayout;
import android.widget.TextView;
public class MainActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
getFragmentManager().beginTransaction().replace(android.R.id.content, new Fragment1()).commit();
}
}
public static class SaveRestoreView extends TextView {
public SaveRestoreView(Context context) {
super(context);
}
String value;
public void setValue(String value) {
this.value = value;
setText(value);
}
#Override
public Parcelable onSaveInstanceState() {
Bundle bundle = new Bundle();
bundle.putParcelable("super", super.onSaveInstanceState());
bundle.putString("value", value);
return bundle;
}
#Override
public void onRestoreInstanceState(Parcelable state) {
Bundle bundle = (Bundle)state;
super.onRestoreInstanceState(bundle.getParcelable("super"));
value = bundle.getString("value");
setText(value);
}
}
public static class Fragment1 extends Fragment {
private static boolean firstRun = true;
public Fragment1() {
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Context context = inflater.getContext();
LinearLayout layout = new LinearLayout(context);
layout.setId(12);
layout.setOrientation(LinearLayout.VERTICAL);
SaveRestoreView saveRestore = new SaveRestoreView(context);
saveRestore.setId(128);
if (firstRun) {
saveRestore.setValue("*******save this string*******");
firstRun = false;
}
layout.addView(saveRestore);
Button button = new Button(inflater.getContext());
button.setText("Create 2nd fragment");
button.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View v) {
getFragmentManager().beginTransaction().replace(android.R.id.content, new Fragment2()).addToBackStack(null).commit();
}
});
layout.addView(button);
return layout;
}
}
public static class Fragment2 extends Fragment {
public Fragment2() {
setRetainInstance(true);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return new View(inflater.getContext());
}
}
}
So, I've finished implementing save/restore logic manually.
The minus is that I can't turn off save\restore that already exist, so onSaveInstance/onRestoreInstance is called twice on a View. Except of that one time when it is not called by itself - then it IS called once B-).
To make it work just add to Fragment1 class:
SparseArray<Parcelable> savedState;
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (savedState != null)
view.restoreHierarchyState(savedState);
}
#Override
public void onDestroyView() {
super.onDestroyView();
savedState = new SparseArray<Parcelable>();
getView().saveHierarchyState(savedState);
}

Categories

Resources