I have an Android Tab Layout with swipeable views.
The file structure is as follows:
There is an activity class: TabMainActivity.java
Under this activity, there is a fragment class: bookLockerFragment.java
This fragment class is linked to an XML file which contains various buttons.
public class bookLockerFragment extends Fragment {
Button btnSis;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.fragment_booklocker, container, false);
btnSis = (Button) rootView.findViewById(R.id.btnSis);
}
.......
}
I have read up on vogella activity testing tutorials, but there is minimal information on unit testing for fragment.
I attempted to write some code but got stuck at this line:
transaction.add(R.layout.fragment_booklocker, fragment, "tag");
I get the following error:
The method add(int, Fragment, String) in the type FragmentTransaction is not applicable for the arguments (int, booklockertest, String)
Code:
public class booklockertest extends
ActivityInstrumentationTestCase2 < TabMainActivity > {
private TabMainActivity mActivity;
Fragment fragment;
FragmentManager mFragmentManager;
public booklockertest() {
super(TabMainActivity.class);
// TODO Auto-generated constructor stub
}
protected void setUp() throws Exception {
super.setUp();
mActivity = getActivity();
}
private Fragment startFragment(booklockertest fragment) {
FragmentTransaction transaction =
mActivity.getSupportFragmentManager().beginTransaction();
transaction.add(R.layout.fragment_booklocker, fragment, "tag");
transaction.commit();
getInstrumentation().waitForIdleSync();
Fragment frag =
mActivity.getSupportFragmentManager().findFragmentByTag("tag");
return frag;
}
public void testFragment() {
booklockertest fragment = new booklockertest() {
//Override methods and add assertations here.
};
Fragment frag = startFragment(fragment);
}
}
stumble upon this, and you probably have figured it out already, but
private Fragment startFragment(booklockertest fragment) { ... }
...
public void testFragment() {
booklockertest fragment = new booklockertest() {
//Override methods and add assertations here.
};
Fragment frag = startFragment(fragment);
}
should be
private Fragment startFragment(bookLockerFragment fragment) { ... }
...
public void testFragment() {
bookLockerFragment fragment = new bookLockerFragment() {
//Override methods and add assertations here.
};
Fragment frag = startFragment(fragment);
}
Related
I was able to save outState as follows but was unable to restore when I land on this AttendanceFragment.cs second time.
public override void OnSaveInstanceState(Bundle outState)
{
base.OnSaveInstanceState(outState);
dataGotFromServer = JsonConvert.SerializeObject(dataList);
outState.PutString(KEY_OUTSTATE, dataGotFromServer);
}
I tried here to restore but could not get it
public override void OnViewStateRestored(Bundle savedInstanceState)
{
base.OnViewStateRestored(savedInstanceState);
if(savedInstanceState!=null)
{
var result = savedInstanceState.GetString(KEY_OUTSTATE, dataGotFromServer);
}
}
and also I tried on CreateView(), OnActivityCreated() and On Create() but unsuccessfull to restore.
And my code for fragment replacement is as
public void ReplaceFragment(Context context, Fragment newFragment, string TAG)
{
Android.Support.V4.App.FragmentManager fragmentManager = ((FragmentActivity)context).SupportFragmentManager;
Android.Support.V4.App.FragmentTransaction ft = fragmentManager.BeginTransaction();
ft.Replace(Resource.Id.HomeFrameLayout, newFragment);
ft.AddToBackStack(TAG);
ft.Commit();
}
Edited:
This is how I call this fragment
case (Resource.Id.nav_attendance):
var role = session.GetUserDetails().Get(SessionManagement.KEY_ROLE).ToString();
if (role=="Student")
{
Fragment attendanceTabFragment = new AttendanceTabFragment();
customFragment.ReplaceFragment(this, attendanceTabFragment,typeof(AttendanceTabFragment).Name);
}else
{
Fragment attendanceFragment = new AttendanceFragment();
customFragment.ReplaceFragment(this, attendanceFragment, typeof(AttendanceFragment).Name);
}
Any idea or sample code much appreciated.
Thank you.
Unless the Activity that contains the Fragment get disposed, Fragment's OnSaveInstanceState is not going to be called.
In a situation were you are swapping Fragments in and out, using Fragment.Arguments is an option instead of a singleton/static var...
re: getArguments / setArguments
In using arguments:
Create a new Bundle in the Fragment constructor and assign it to
Arguments
In the OnPause override update the Arguments/Bundle
with the items you need to save.
In the OnResume override read the
Arguments/Bundle items that you need to restore.
Example Fragment:
public class Fragment1 : Fragment
{
public Fragment1(System.IntPtr javaReference, Android.Runtime.JniHandleOwnership transfer) : base(javaReference, transfer)
{
CreateArgumentBundle();
}
public Fragment1()
{
CreateArgumentBundle();
}
void CreateArgumentBundle()
{
Arguments = new Bundle();
}
public override View OnCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
return inflater.Inflate(Resource.Layout.Frag1, container, false);
}
public override void OnPause()
{
base.OnPause();
Arguments.PutString("someKey", "StackOverflow");
}
public override void OnResume()
{
base.OnResume();
var someKeyString = Arguments.GetString("someKey", "someDefaultString(new bundle)");
Log.Debug("SO", someKeyString);
}
}
In your ReplaceFragment make sure that you are assigning the TAG in the Replace call:
public void ReplaceFragment(Context context, Fragment newFragment, string TAG)
{
SupportFragmentManager
.BeginTransaction()
.Replace(Resource.Id.fragmentContainer, newFragment, TAG)
.AddToBackStack(TAG)
.Commit();
}
Before calling your ReplaceFragment, check to see if the Fragment exists (FindFragmentByTag) before creating a new one, this example just swaps two fragments in and out and only creates new ones if the manager does not contain one:
button.Click += delegate
{
toggle = !toggle;
var frag = SupportFragmentManager.FindFragmentByTag(toggle ? "frag1" : "frag2");
frag = frag ?? ( toggle ? (Fragment)new Fragment1() : (Fragment)new Fragment2() );
ReplaceFragment(this, frag, toggle ? "frag1" : "frag2");
};
Note: You will still need to handle the other Fragment lifecycle events in the case that the hosting Activity is recycled:
Fragment LifeCycles
I have an activity with bottom navigation tabs that are changing the fragments in it. When I click back and forth on those tabs, at some point it stops working. Code executes just fine as I put some logs in it. But the fragments aren't being switched.
Code is in kotlin but it's rather straight forward
fun showTabFragment(tag: String) {
val currentFragment: Fragment? = supportFragmentManager.fragments?.lastOrNull()
var fragment = supportFragmentManager.findFragmentByTag(tag)
val fragmentExists = fragment != null
if (fragment == null) {
when (tag) {
TAG_LOGBOOK -> fragment = LogbookFragment()
TAG_RECIPES -> fragment = RecipesFragment()
TAG_PROFILE -> fragment = ProfileFragment()
else -> fragment = MeetingPlacesFragment()
}
}
val transaction = supportFragmentManager.beginTransaction()
if (currentFragment != null) {
Log.i("jacek", "hiding " + currentFragment.javaClass.simpleName)
transaction.hide(currentFragment)
}
if (fragmentExists) {
Log.i("jacek", "showing " + fragment.javaClass.simpleName)
transaction.show(fragment)
} else {
Log.i("jacek", "adding " + fragment.javaClass.simpleName)
transaction.add(R.id.container, fragment, tag)
}
transaction.commit()
}
The fragments are quite heavy. I will try with some lightweight ones, but still that shouldn't be a problem in my opinion. Is there anything else I could try?
I'm using the latest support library - 25.2.0
Also I'm not interested in replacing the fragments as the point is to add crossfade animation without recreating them
You need to reuse the same instance of a fragment that you wanted to hide or show.
private fun replaceFragment(fragment: Fragment) {
supportFragmentManager.beginTransaction().apply {
if (fragment.isAdded) {
show(fragment)
} else {
add(R.id.fmFragmentContainer, fragment)
}
supportFragmentManager.fragments.forEach {
if (it != fragment && it.isAdded) {
hide(it)
}
}
}.commit()
}
#Ali's answer is good, yet imagine if you have 5 fragments. This is another way to show/hide your fragments:
// in BaseFragment
public abstract String getTAG();
//in FragmentA, FragmentB and FragmentC
public String getTAG(){
return TAG;
}
//Activity containing the fragments
//android.support.v4.app.Fragment;
private FragmentA fragmentA; //inherited BaseFragment
private FragmentB fragmentB; //inherited BaseFragment
private FragmentC fragmentC; //inherited BaseFragment
private ConcurrentHashMap<String,BaseFragment> mapOfAddedFragments = new ConcurrentHashMap<>();
/**
* Displays fragment A
*/
private void displayFragmentA() {
displayFragment(fragmentA)
}
/**
* Displays fragment B
*/
private void displayFragmentB() {
displayFragment(fragmentB)
}
/**
* Displays fragment C
*/
private void displayFragmentC() {
displayFragment(fragmentC)
}
/**
* Loads a fragment using show a fragment
* #param fragment
*/
private void displayFragment(BaseFragment fragment){
if(!mapOfAddedFragments.containsKey(fragment.getTAG()))
mapOfAddedFragments.put(fragment.getTAG(), fragment);
showFragment(fragment.getTAG(), R.id.containerBody);
}
/**
* Displays a fragment and hides all the other ones
* #param fragmentTag is the tag of the fragment we want to display
*/
private void showFragment(String fragmentTag, #IdRes int containerViewId){
FragmentTransaction ft = this.getSupportFragmentManager().beginTransaction();
BaseFragment fragment = null;
fragment = mapOfAddedFragments.get(fragmentTag);
if(fragment != null) {
if (fragment.isAdded())
ft.show(fragment);
else { //fragment needs to be added to the frame container
ft.add(containerViewId, fragment, fragment.getTAG());
}
}
else //the chosen fragment doesn't exist
return;
//we hide the other fragments
for (ConcurrentHashMap.Entry<String, BaseFragment> entry : mapOfAddedFragments.entrySet()){
if(!entry.getKey().equals(fragmentTag)){
BaseFragment fragmentTemp = entry.getValue();
// Hide the other fragments
if(fragmentTemp != null)
if(fragmentTemp.isAdded())
ft.hide(fragmentTemp);
}
}
//commit changes
ft.commit();
}
And to instantiate them you can do this in the onCreate() method of your activity:
//don't forget to get the .TAG elsewhere before using them here
//never call them directly
private void instantiateFragments(Bundle inState) {
if (inState != null) {
fragmentA = inState.containsKey(FragmentA.TAG) ?
(FragmentA) getSupportFragmentManager().getFragment(inState, FragmentA.TAG):
FragmentA.newInstance(FragmentA.TAG,"0");
fragmentB = inState.containsKey(FragmentB.TAG) ?
(FragmentB) getSupportFragmentManager().getFragment(inState, FragmentB.TAG):
FragmentB.newInstance(FragmentB.TAG,"1");
fragmentc = inState.containsKey(FragmentC.TAG) ?
(FragmentC) getSupportFragmentManager().getFragment(inState, FragmentC.TAG):
FragmentC.newInstance(FragmentC.TAG,"2");
}
else{
fragmentA = FragmentA.newInstance(FragmentA.TAG,"0");
fragmentB = FragmentB.newInstance(FragmentB.TAG,"1");
fragmentc = FragmentC.newInstance(FragmentC.TAG,"2");
}
}
Edit according to Shujaat Ali Khan's question:
The BaseFragment extends support4 fragment:
public abstract class BaseFragment extends Fragment {
public abstract String getTAG();
//whatever we can add to be inherited
}
FragmentA for example:
public class FragmentA extends BaseFragment {
// Store instance variables
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
public static final String TAG = "FragmentA";
// newInstance constructor for creating fragment with arguments
public static FragmentA newInstance(String param1, String param2) {
FragmentA fragment = new FragmentA();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
// Store instance variables based on arguments passed
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getString(ARG_PARAM1);
mParam2 = getArguments().getString(ARG_PARAM2);
}
}
// Inflate the view for the fragment based on layout XML
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragmentA, container, false);
return view;
}
//other lifecycle methods
#Override
public String getTAG() {
return TAG;
}
}
Finally the R.id.containerBody is the id of a FrameLayout containing the fragments in the activity containing these fragments.
The problem here is even though you're hiding "current" fragment, there are other fragments loaded in the memory and that gives inconsistent behaviour.
You should be able to fix this by hiding all the fragment except the fragment you want to show.
Thanks to this answer. Show hide fragment in android
eg:
private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentA = FragmentA.newInstance();
fragmentB = FragmentB.newInstance();
fragmentC = FragmentC.newInstance();
}
protected void displayFragmentA() {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
if (fragmentA.isAdded()) {
ft.show(fragmentA);
} else {
ft.add(R.id.fragement_container, fragmentA);
}
if (fragmentB.isAdded()) { ft.hide(fragmentB); }
if (fragmentC.isAdded()) { ft.hide(fragmentC); }
ft.commit();
}
Similarly you will have to write functions for displayFragmentB() and displayFragmentC()
May you assist by telling me the best way to moves from an activity to a fragment. This is what I have so far but it doesnt seem to work.
This is how I am calling the function(getCategory)
private void selectItem( int group, int usage)
{
if (!shown) return;
classificationGroup = group;
((DashboardActivity)getActivity()).getCallCategory(classificationGroup);
}
And in the activity I am trying to move to a fragment
public Fragment getCallCategory(int position) {
return new CallLogsFragment();
}
The standard pattern for creating fragments looks like this:
Inside your fragment class (make sure to import the android.support.v4.app.Fragment):
public class MyFragment extends Fragment {
public static final String TAG = "MyFragment";
private int position;
// You can add other parameters here
public static MyFragment newInstance(int position) {
Bundle args = new Bundle();
// Pass all the parameters to your bundle
args.putInt("pos", position);
MyFragment fragment = new MyFragment();
fragment.setArguments(args);
return fragment;
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
this.position = getArguments().getInt("pos");
}
}
Inside your activity:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Add your parameters
MyFragment fragment = MyFragment.newInstance(10);
// R.id.container - the id of a view that will hold your fragment; usually a FrameLayout
getSupportFragmentManager().beginTransaction()
.add(R.id.container, fragment, MyFragment.TAG)
.commit();
}
}
When you want to access public methods of your fragment instance, use FragmentManager#findFragmentByTag(String tag) to find your instance of fragment:
MyFragment fragment = (MyFragment) getSupportFragmentManager().findFragmentByTag(MyFragment.TAG);
if(fragment != null){
// Do something with fragment
}
For a more detailed explanation, I suggest you read the official docs on fragments: https://developer.android.com/guide/components/fragments.html
I want to achieve below concept in android:-
Tab A -> Fragment A -> Fragment 1 -> Fragment 2
Tab B -> Fragment B
Tab C -> Fragment C
Tab D -> Fragment D
I am adding tabs on view pager and able to achieve till
Tab A -> Fragment A
Tab B -> Fragment B
Tab C -> Fragment C
Tab D -> Fragment D
I tried with
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(android.R.id.content, fragment1);
ft.addToBackStack(null);
ft.commit();
but not able to add another fragment on Fragment A.
it is adding fragment 1 upon Fragment A but Fragment A is still visible in background. So please provide some suggestion to achieve above task.
Thanks in Advance.
It's mine. Hope it's helpful.
First, you need to create the ContentFragment for 4 tabs A, B, C, D like below:
public class ContentFragmentA extends BaseFragment {
private static Context context;
#SuppressWarnings("static-access")
public ContentFragmentA(Context context) {
super();
this.context = context;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
View view = inflater.inflate(R.layout.content_frame_a, container, false);
this.setFragmentManager(getChildFragmentManager());
this.setContainerId(R.id.content_frame_a);
this.replaceFragmentIntoStack(new FragmentOne(context), "FragmentOne");
return view;
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
}}
Then, create the content_fragment_a.xml file
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/content_frame_a"
android:layout_width="match_parent"
android:layout_height="match_parent" >
Then, for each new fragment you should extends BaseFragment. And when if you want to put new fragment into tab, just use addFragmentIntoStack(), or replaceFragmentIntoStack().
public class BaseFragment extends Fragment{
private FragmentManager fragmentManager;
private int containerId;
private boolean doubleBackToExit = false;
/**
* #param containerId the containerId to set
*/
public void setContainerId(int containerId) {
this.containerId = containerId;
}
/**
* #param fragmentManager the fragmentManager to set
*/
public void setFragmentManager(FragmentManager fragmentManager) {
this.fragmentManager = fragmentManager;
}
/**
*
*/
public BaseFragment() {
super();
// TODO Auto-generated constructor stub
BarddyApplication.getInstance().baseFragment = BaseFragment.this;
}
public void addFragmentIntoStack(BaseFragment newContent, String tagName) {
newContent.setFragmentManager(fragmentManager);
newContent.setContainerId(containerId);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(containerId, newContent, tagName);
fragmentTransaction.addToBackStack(tagName);
fragmentTransaction.commitAllowingStateLoss();
}
public void popFragment() {
fragmentManager.popBackStack();
}
public void replaceFragmentIntoStack(BaseFragment newContent, String tagName) {
newContent.setFragmentManager(fragmentManager);
newContent.setContainerId(containerId);
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.replace(containerId,newContent,tagName);
fragmentTransaction.addToBackStack(null);
fragmentTransaction.commitAllowingStateLoss();
}}
Very simple!
Fist you need to fragment A blank and then you need create new three fragment:
Fragment A1, fragment 1, fragmen2
And now when you chose tab A, you show fragment A1 in fragment A
FragmentTransaction transaction = getFragmentManager()
.beginTransaction();
transaction.replace(R.id.category_fragment, new CategoryFragment());
transaction.commit();
Now you can use method replace() fragment 1 or 2 to id of Fragment A (not id of A1) to replace A1 with fragment 1 or 2.
Hope it help you!
I have a Fragment with a Button embedded inside of a FragmentActivity. When I click the button I want the Fragment to be replaced with another Fragment. The problem is: the Fragment is a inner static class of my Activity and the method is a non-static one. I solved this problem by making an instance of my Activity class, but when I click on the button inside the fragment the application crashes.
Here is the code:
public class Stdp extends SherlockFragmentActivity implements ActionBar.OnNavigationListener {
public static class Bottomfrag extends SherlockFragment {
static Bottomfrag newInstance() {
Bottomfrag f = new Bottomfrag();
return f;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.bottom_choose, container, false);
View li = v.findViewById(R.id.layoutbottom);
li.setBackgroundColor(Color.parseColor("#FFBB33"));
View button = v.findViewById(R.id.button1);
button.setOnClickListener(new OnClickListener(){
#Override
public void onClick(View arg0) {
Stdp t = new Stdp();
t.addFragmentToStack();
}
});
return v;
}
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_stdp);
Context context = getSupportActionBar().getThemedContext();
ArrayAdapter<CharSequence> list = ArrayAdapter.createFromResource(context, R.array.test_array, R.layout.sherlock_spinner_item);
list.setDropDownViewResource(R.layout.sherlock_spinner_dropdown_item);
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_LIST);
getSupportActionBar().setListNavigationCallbacks(list, this);
if (savedInstanceState == null) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment bottom = new Bottomfrag();
ft.add(R.id.su1, bottom);
ft.commit();
}
}
#Override
public boolean onNavigationItemSelected(int itemPosition, long itemId) {
// TODO Auto-generated method stub
return false;
}
public void addFragmentToStack() {
Fragment newFragment = Bottomfrag.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.su1, newFragment);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
ft.addToBackStack(null);
ft.commit();
}
}
I found a solution for my problem. The only thing I had to do is to move the addFragmentToStack to the Bottomfrag class.
I solved this problem by making an instance of my Activity class.
Instantiating an Activity is almost never a solution when it comes to Android development... in fact, I can't imagine a scenario in which you would ever want to create a new Activity using the default constructor.
You can reference the static instance of your Activity with ActivityName.this.
You should also move addFragmentToStack to the Bottomfrag class.