I'm creating an app that need to nested navigation graphs and a want to close a parent fragment from child fragment.
I can close a fragment by calling
getActivity().getSupportFragmentManager().popBackStack();
with this i can only close the current top fragment in back stack.
in this picture i want to close Fragment2 from Framgent3.
EDIT:
I'm opening Fragment2 with:
Navigation.findNavController(getView()).navigate(R.id.action_fragment1_to_fragment2);
Fragment1:
public class Fragment1 extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment1, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getView().findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Navigation.findNavController(v).navigate(R.id.action_fragment1_to_fragment2);
}
});
}
}
Fragment1 layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Framgent2:
public class Fragment2 extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment2, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public void close () {
getActivity().getSupportFragmentManager().popBackStack();
}
}
in Fragment2 i have another graph, Fragment2 layout:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<fragment
android:id="#+id/child_graph_base"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:defaultNavHost="true"
app:navGraph="#navigation/child_graph" />
</android.support.constraint.ConstraintLayout>
Fragment3:
public class Fragment3 extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment3, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getView().findViewById(R.id.button).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
NavHostFragment navHostFragment = (NavHostFragment) getParentFragment();
Fragment2 fragment2 = (Fragment2) navHostFragment.getParentFragment();
fragment2.close();
}
});
}
}
Fragment3 layout:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#color/colorPrimaryDark">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Button"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Main Graph (MainActivity):
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/main_graph"
app:startDestination="#id/fragment1">
<fragment
android:id="#+id/fragment1"
android:name="alistar.navigation.fragments.Fragment1"
android:label="fragment1"
tools:layout="#layout/fragment1" >
<action
android:id="#+id/action_fragment1_to_fragment2"
app:destination="#id/fragment2" />
</fragment>
<fragment
android:id="#+id/fragment2"
android:name="alistar.navigation.fragments.Fragment2"
android:label="fragment2"
tools:layout="#layout/fragment2" />
</navigation>
Child Graph (in Framgent2)
<?xml version="1.0" encoding="utf-8"?>
<navigation xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/child_graph"
app:startDestination="#id/fragment3">
<fragment
android:id="#+id/fragment3"
android:name="alistar.navigation.fragments.Fragment3"
android:label="fragment3"
tools:layout="#layout/fragment3" />
</navigation>
i solved the problem by calling close() method in the parent fragment (Fragment2) from Fragment3. i changed close method to this:
public void close () {
Navigation.findNavController(getView()).popBackStack();
}
also i removed:
app:defaultNavHost="true"
from my child graph and worked OK!
include the graph of (frag2,frag3) in frag1's graph using include
<include app:graph="#navigation/frag1_graph"/>
and then:
add an action to your fragment3 with appropriate Destination and popUpTo arguments
<fragment
android:id="#+id/Fragment3"
android:name="whatever"
android:label="whatever"
tools:layout="#layout/whatever" >
<action
android:id="#+id/action_Fragment3_to_Fragmen1"
app:destination="#id/Fragment1"
app:launchSingleTop="true"
app:popUpTo="#+id/Fragment1"
app:popUpToInclusive="true" /> </fragment>
and then
findNavController(fragment).navigate(R.id.action_Fragment3_to_fragment1)
and then display fragment3 again. i think that's what you want right?
Related
I am trying to use a androidx.recyclerview.widget.RecyclerView inside a Fragment and having a tough time trying to add basic items to it via an adapter. Following is my Fragment:
public class TimelineFragment extends Fragment {
private DataBoundAdapter timelineAdapter;
private View rootView;
#Override
public View onCreateView(
LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState
) {
// Inflate the layout for this fragment
rootView = inflater.inflate(R.layout.fragment_timeline_home, container, false);
initStuff(container);
return rootView;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
}
public void onViewCreated(#NonNull View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
}
private void initStuff(ViewGroup container) {
ArrayList<FernPost> list = {...};
timelineAdapter = new DataBoundAdapter(list);
RecyclerView recyclerView = rootView.findViewById(R.id.scroll_timeline);
recyclerView.setLayoutManager(new GridLayoutManager(getContext(),2));
recyclerView.setAdapter(timelineAdapter);
timelineAdapter.notifyDataSetChanged();
}
}
This is the fragment xml:
<?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:layout_height="match_parent"
android:layout_width="match_parent"
android:orientation="vertical">
<TextView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="100dp"
android:text="hi there bullshit!!!"/>
<androidx.recyclerview.widget.RecyclerView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/scroll_timeline"
android:scrollbars="vertical"
android:visibility="visible">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
The Main activity layout xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout xmlns:tools="http://schemas.android.com/tools"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</com.google.android.material.appbar.AppBarLayout>
<com.google.android.material.floatingactionbutton.FloatingActionButton
android:id="#+id/fab"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="bottom|end"
android:layout_margin="#dimen/fab_margin"
app:srcCompat="#android:drawable/ic_input_add" />
<fragment
android:id="#+id/timeline_fragment"
android:name="com.blinkfast.fern.TimelineFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.coordinatorlayout.widget.CoordinatorLayout>
here is the adapter code for the recycler view:
public class DataBoundAdapter extends RecyclerView.Adapter<DataBoundViewHolder> {
private List<FernPost> fernPosts;
public DataBoundAdapter(List<FernPost> fernPosts) {
this.fernPosts = fernPosts;
}
#NonNull
#Override
public DataBoundViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View itemLayoutView = LayoutInflater.from(parent.getContext())
.inflate(R.layout.fernpost, null);
return new DataBoundViewHolder(itemLayoutView);
}
#Override
public void onBindViewHolder(#NonNull DataBoundViewHolder holder, int position) {
holder.bind(fernPosts.get(position));
}
#Override
public int getItemCount() {
return fernPosts.size()
}
}
And the view holder:
public class DataBoundViewHolder extends RecyclerView.ViewHolder {
private FernPost post;
private View fernPostView;
public DataBoundViewHolder(#NonNull View itemView) {
super(itemView);
this.fernPostView = itemView;
Log.i(DataBoundViewHolder.class.getName(), "");
}
public void bind(FernPost fernPost){
Log.i(DataBoundViewHolder.class.getName(), "binding");
this.post = fernPost;
}
}
I have spent entire day trying various thing making it work, but nothing has so far. The onCreateViewHolder / onBindViewHolder methods are just not getting called. Not sure what i am doing wrong.
Assuming your fragment (the one holding RecyclerView) is displayed and your list has items before passed to DataBoundAdapter, please use different inflation inside your adapter's onCreateViewHolder:
LayoutInflater.from(parent.getContext()).inflate(viewType, parent, false)
I am trying to call a fragment from an activity. But unable. This is what I tried.
#Override
public boolean onOptionsItemSelected(#NonNull MenuItem item) {
if (item.getItemId()==R.id.action_search){
SearchFragment searchFragment=new SearchFragment();
getSupportFragmentManager().beginTransaction().add(R.id.container,searchFragment,null).commit();
}
return true;
}
And My main_activity.xml
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/container"
tools:context=".Controllers.MainActivity">
After trying the app does crash but its also not opening the fragment which I want.
The fragment xml
<FrameLayout 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:id="#+id/searchFragment"
tools:context=".SearchFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewSearchFrag"
android:layout_width="match_parent"
android:layout_height="match_parent">
</androidx.recyclerview.widget.RecyclerView>
And Fragment java is
public class SearchFragment extends Fragment {
private RecyclerView mRecyclerViewSearch;
private RecyclerViewAdpater mAdpater;
public SearchFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,Bundle savedInstanceState) {
View view=inflater.inflate(R.layout.fragment_search, container, false);
mRecyclerViewSearch=view.findViewById(R.id.recyclerViewSearchFrag);
mRecyclerViewSearch.setLayoutManager(new LinearLayoutManager(getContext()));
mAdpater=new RecyclerViewAdpater(DataSevices.mChineseColors,getActivity());
return view;
}
}
Please correct me if I am doing anything wrong
You can use Framlayout instead of Linear-layout
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frame"
android:layout_width="match_parent"
android:layout_height="match_parent">
if you want some other components in your MainActivity , create a fragment for that.
and replace it.
in activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/frameLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity" />
in your fragment xml:
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".SearchFragment">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recyclerViewSearchFrag"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
create new fragment fragment_main.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
...
</LinearLayout>
and MainFragment.java
public class MainFragment extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup
container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
//...
return view;
}
}
I'm trying to put fragment in MainActivity's FrameLayout by clicking the button in MainActivity. It almost works, but the content of MainActivity's FrameLayout doesn't get replaced by the Fragment content, but added to it. So the question is: how to make it correct?
My MainActivity class
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Button btn = findViewById(R.id.pushMeBtn);
btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager f_m = getSupportFragmentManager();
FragmentTransaction f_t = f_m.beginTransaction();
f_t.replace(R.id.container_layout, new Fragment1()).addToBackStack(null).commit();
}
});
}
}
My activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/container_layout"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/pushMeBtn"
android:text="push me"
android:layout_gravity="center"/>
</FrameLayout>
</RelativeLayout>
My Fragment1 class
public class Fragment1 extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment1, container,false);
}
}
My fragment.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:background="#color/colorPrimary">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="339dp"
android:gravity="center"
android:text="fragment1"
android:textSize="25sp"/>
</RelativeLayout>
I'm trying to create a tabHost inside a Fragment, so I can create a tabs inside that fragment I've tried changing the extends to android.support.v4.app.Fragment but then the complete app won't build. This issue I have is the following:
wrong 2nd argument type. found'android.app.fragment' required 'android.support.v4.app.fragment'
This is the code I wrote:
public class TimeTableFragment extends Fragment {
FragmentNameFactory nameFactory = new FragmentNameFactory();
String body;
String major;
String status;
private FragmentTabHost mTabHost;
public TimeTableFragment(){
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceStatus){
((MainActivity) getActivity()).setFragmentName(nameFactory.getNameNL(7));
View view = inflater.inflate(R.layout.ftimetable, container, false);
mTabHost = (FragmentTabHost) view.findViewById(android.R.id.tabhost);
mTabHost.setup(getActivity(), getChildFragmentManager(), R.id.realtabcontent);
mTabHost.setup();
mTabHost.addTab(mTabHost.newTabSpec("fragmentb").setIndicator("Fragment B"));
mTabHost.addTab(mTabHost.newTabSpec("fragmentc").setIndicator("Fragment C"));
mTabHost.addTab(mTabHost.newTabSpec("fragmentd").setIndicator("Fragment D"));
return view;
}
#Override
public void onViewCreated(View view, Bundle savedInstance){
super.onViewCreated(view, savedInstance);
}
#Override
public void onActivityCreated(Bundle savedInstance){
super.onActivityCreated(savedInstance);
}}
The .xml (ftimetables):
<?xml version="1.0" encoding="utf-8"?>
<TabHost
android:id="#android:id/tabhost"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:android="http://schemas.android.com/apk/res/android">
<LinearLayout
android:layout_width="match_parent"
android:id="#+id/linearLayout1"
android:layout_height="match_parent"
android:orientation="vertical">
<TabWidget
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#android:id/tabs">
</TabWidget>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#android:id/tabcontent">
</FrameLayout>
</LinearLayout>
</TabHost>
I hope someone can help me fix this issue!
change your TimeTableFragment import code
import android.app.fragment(x)
import android.support.v4.app.fragment(o)
I'll go straight to the problem. What am I doing wrong when replacing fragments in Android? The expected result when I clicked on Sponsors:
The reality(what I actually got: basically, it just the Sponsors fragment, which just lays on Other fragment):
I'm calling a fragment by
fragment = new Other(this);
Sponsors.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:text="Sponsors" />
</RelativeLayout>
Sponsors.java
...
public class Sponsors extends android.support.v4.app.Fragment {
private Context context;
public Sponsors(Context context) {
this.context = context;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = inflater.inflate(R.layout.sponsors, null);
return rootView;
}
}
other.xml
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/other_frame_container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#android:color/white">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="COMMON"
android:textStyle="bold"
android:textColor="#color/header_red"
android:layout_marginTop="15dp"
android:layout_marginLeft="10dp" />
<View
android:layout_width="fill_parent"
android:layout_height="2dp"
android:background="#color/header_red"
android:layout_marginTop="10dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp" />
<TextView
android:id="#+id/sponsors"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:text="Sponsors"
android:textColor="#color/subtext"
android:textSize="16sp"
android:paddingTop="15dp"
android:paddingBottom="15dp"
android:paddingLeft="15dp" />
</LinearLayout>
</FrameLayout>
Other.java
public class Other extends android.support.v4.app.Fragment {
public Other(Context context) {
this.context = context;
}
public View onCreateView(LayoutInflater inflater, ViewGroup container,
final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
View rootView = inflater.inflate(R.layout.other, container, false);
sponsors = (TextView) rootView.findViewById(R.id.sponsors);
sponsors.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if (savedInstanceState == null) {
Sponsors duedateFrag = new Sponsors();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.add(R.id.other_frame_container, duedateFrag);
ft.addToBackStack(null);
ft.commit();
}
}
});
}
Change add to replace in FragmentTransaction
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.other_frame_container, duedateFrag);