I need to pass data to a fragment, declared in activity layout before it is rendered:
<fragment android:name="com.app.fragments.MyFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/myFragment" />
I tried to do it in Activity's onCreate but fragment is already rendered at that time.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Here fragment is already rendered
mPosts = (MyFragment)getSupportFragmentManager().findFragmentById(R.id.myFragment);
mPosts.setData(someData);
}
I've seen the way when fragment is created programmatically in activity onCreate and than added to a container. It's not a very bad solution. But... Is there anything simpler?
For XML layout there is one question over here: https://stackoverflow.com/a/23226281/842607
Even they has to say you cannot pass arguments, but you can use callbacks or custom attributes.
Else
Normally, we use Bundle in arguments in this way:
Fragment class
public class ShopProductListingFragment extends Fragment {
private static final String TAG = ShopProductListingFragment.class.getSimpleName();
public static final String KEY_CATEGORY = "category";
public static final String KEY_URL_PARAMS = "url_params";
public static final String KEY_URL = "url";
public static ShopProductListingFragment getInstance(NewCategory category, #NonNull HashMap<String, String> urlParams) {
Bundle bundle = new Bundle();
if (null != category)
bundle.putLong(KEY_CATEGORY, category.getCategory_id());
bundle.putSerializable(KEY_URL_PARAMS, urlParams);
ShopProductListingFragment fragment = new ShopProductListingFragment();
fragment.setArguments(bundle);
return fragment;
}
public static ShopProductListingFragment getInstance(#NonNull String url) {
Bundle bundle = new Bundle();
bundle.putSerializable(KEY_URL, url);
ShopProductListingFragment fragment = new ShopProductListingFragment();
fragment.setArguments(bundle);
return fragment;
}
public ShopProductListingFragment() {
// Required empty public constructor
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initVals();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_shop_product_listing, container, false);
ButterKnife.bind(this, rootView);
initViews();
return rootView;
}
private void initVals() {
Bundle bundle = getArguments();
if (null == bundle || Bundle.EMPTY == bundle)
throw new NullPointerException("Invalid or null arguments passed to fragment ShopProductListingFragment");
if (bundle.containsKey(KEY_CATEGORY))
categoryId = bundle.getLong(KEY_CATEGORY);
mCategory = StyFi.getInstance().getCategory(categoryId);
if (bundle.containsKey(KEY_URL_PARAMS))
urlParams = (HashMap<String, String>) bundle.getSerializable(KEY_URL_PARAMS);
if (bundle.containsKey(KEY_URL))
urlFilter = bundle.getString(KEY_URL);
}
}
Layout file of Activity
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
This is how it is instantiated in Activity
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity);
if (null == savedInstanceState) {
ShopProductListingFragment fragment = ShopProductListingFragment.getInstance(category, getUrlParams(data));
FragmentManager fragmentManager = getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction().replace(R.id.content_frame, fragment, "SHOP");
fragmentTransaction.commit();
}
}
Related
i have a problem that i cant find anywhere to solve, i am learning android studio from udemy, and the way the professor do fragment exercises dont work on me. He makes 2 fragments, one for list and one for details, the problem is when i am trying to componets to be read from mainactivity to run my tasks, there is a NullPointerException. Here is my Code:
DetailFragment
public class DetailFrag extends Fragment
{
ImageView imageView;
TextView tvName, tvTel;
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private String mParam1;
private String mParam2;
public DetailFrag() {
// Required empty public constructor
}
public static DetailFrag newInstance(String param1, String param2) {
DetailFrag fragment = new DetailFrag();
Bundle args = new Bundle();
args.putString(ARG_PARAM1, param1);
args.putString(ARG_PARAM2, param2);
fragment.setArguments(args);
return fragment;
}
#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
return inflater.inflate(R.layout.fragment_detail, container, false);
}
}
ListFragment
public class ListFrag extends Fragment {
RecyclerView recyclerView;
RecyclerView.Adapter myAdapter;
RecyclerView.LayoutManager layoutManager;
View view;
public ListFrag() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
view = inflater.inflate(R.layout.fragment_list, container, false);
return view;
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
recyclerView = view.findViewById(R.id.list);
recyclerView.setHasFixedSize(true);
layoutManager = new LinearLayoutManager(this.getActivity());
recyclerView.setLayoutManager(layoutManager);
myAdapter = new PersonAdapter(this.getActivity(), ApplicationClass.people);
recyclerView.setAdapter(myAdapter);
}
}
MainActivity
public class MainActivity extends AppCompatActivity implements PersonAdapter.ItemClicked {
TextView tvName,tvTel;
EditText etName, etTel;
Button btnAdd;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tvName = findViewById(R.id.tvName)
tvTel = findViewById(R.id.tvTel);
etName = findViewById(R.id.etName);
etTel = findViewById(R.id.etTel);
btnAdd = findViewById(R.id.btnAdd);
btnAdd.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
}
});
}
#Override
public void onItemClicked(int index) {
tvName.setText(ApplicationClass.people.get(index).getName());
tvTel.setText(ApplicationClass.people.get(index).getTelNr());
}
}
Exception:
E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.recyclerfragments, PID: 31089
java.lang.NullPointerException: Attempt to invoke virtual method 'void android.widget.TextView.setText(java.lang.CharSequence)' on a null object reference
at com.example.recyclerfragments.MainActivity.onItemClicked(MainActivity.java:43)
at com.example.recyclerfragments.PersonAdapter$ViewHolder$1.onClick(PersonAdapter.java:46)
at android.view.View.performClick(View.java:7575)
at android.view.View.performClickInternal(View.java:7548)
at android.view.View.access$3600(View.java:837)
at android.view.View$PerformClick.run(View.java:28933)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:236)
Activity Main XML:
<?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"
tools:context=".MainActivity">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView"
android:name="com.example.recyclerfragments.ListFrag"
android:layout_width="200dp"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
tools:layout="#layout/fragment_list" />
<LinearLayout
android:id="#+id/linearLayout"
android:layout_width="0dp"
android:layout_height="match_parent"
android:orientation="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="#+id/fragmentContainerView"
app:layout_constraintTop_toTopOf="parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView2"
android:name="com.example.recyclerfragments.DetailFrag"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="2"
tools:layout="#layout/fragment_detail" />
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainerView3"
android:name="com.example.recyclerfragments.AddPersonFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="3"
tools:layout="#layout/fragment_add_person" />
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
According to the stacktrace given your issue is that tvTel or tvName is null i.e. the view you are trying to reference is not available on MainActivity. Reading your comments on the question it seems these views are present on Fragment and not on MainActivity.
Views of a fragment can be accessed on an Activity if and only if
fragment is attached to that Activity.
What it means in your code is that when you tried to use findViewById in onCreate of your MainActivity your fragment was not attached to your activity causing findViewById to return null.
Now to make your onItemClicked work you need shift findViewById to onItemClicked
#Override
public void onItemClicked(int index) {
if(tvName == null)
tvName = findViewById(R.id.tvName);
tvName.setText(ApplicationClass.people.get(index).getName());
if(tvTel == null)
tvTel = findViewById(R.id.tvTel);
tvTel.setText(ApplicationClass.people.get(index).getTelNr());
}
Above will fixed your issue of null view reference if fragment containing these view ids is attached to the activity.
In my fragment_main.xml, I have the following code :
<?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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/nav_host_fragment"
android:name="androidx.navigation.fragment.NavHostFragment"
android:layout_width="match_parent"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/bottomBar"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:navGraph='#navigation/bottom_bar_nav_graph' />
<!--custom bottom navigation ui-->
</androidx.constraintlayout.widget.ConstraintLayout>
I would like to have access to navController(***bottom_bar_nav_graph***) from fragment.
Is it possible?
If I get what you are trying to do correctly, you what to do something like
findViewbyId(R.id.bottom_bar_nav_graph)
which is not possible.
What this line of code is doing
app:navGraph='#navigation/bottom_bar_nav_graph'
is to let FragmentContainerView know about your graph bottom_bar_nav_graph which is an XML file that describes how your fragments relate, pass data and navigate between each other.
But can have access to nav_host_fragment which is the id for the host fragment.
If you want to access the navController with the id in Activity, you can use
val navController = findNavController(R.id.nav_host_fragment)
extends Fragment {
private static final String ARG_SECTION_NUMBER = "section_number";
private PageViewModel pageViewModel;
private FragmentMainBinding binding;
public static PlaceholderFragment newInstance(int index) {
PlaceholderFragment fragment = new PlaceholderFragment();
Bundle bundle = new Bundle();
bundle.putInt(ARG_SECTION_NUMBER, index);
fragment.setArguments(bundle);
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
pageViewModel = new ViewModelProvider(this).get(PageViewModel.class);
int index = 1;
if (getArguments() != null) {
index = getArguments().getInt(ARG_SECTION_NUMBER);
}
pageViewModel.setIndex(index);
}
#Override
public View onCreateView(
#NonNull LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentMainBinding.inflate(inflater, container, false);
View root = binding.getRoot();
final TextView textView = binding.sectionLabel;
pageViewModel.getText().observe(getViewLifecycleOwner(), new Observer<String>() {
#Override
public void onChanged(#Nullable String s) {
textView.setText(s);
}
});
return root;
}
There is an activity containing a Button and a LinerLayout which is the container of Fragment. The layout of Fragment only contains a TextView. I want to send a string to fragment from activity, but what fragment received is null. Why?
xml of Activity:
<Button
android:id="#+id/load"
android:layout_width="match_parent"
android:layout_height="80dp"
android:text="load" />
<!--the container of fragment-->
<LinearLayout
android:id="#+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal" />
xml of Fragment:
<TextView
android:id="#+id/fragment_text"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
Java code of Activity:
public class DynamicFragmentActivity extends Activity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_dynamic_fragment);
findViewById(R.id.load).setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
//create fragment and set data
MyFragment myFragment = new MyFragment();
Bundle bundle = new Bundle();
bundle.putString("text", "demo");
myFragment.setArguments(bundle);
//commit
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.container, myFragment);
fragmentTransaction.commit();
}
});
}
}
Java code of Fragment:
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.myfragment, container, false);
TextView textView = (TextView) view.findViewById(R.id.fragment_text);
//receive data
String text = getArguments().getBundle("text")+"";
textView.setText(text);
return view;
}
}
Try this , Hope it helps ...........
public class MyFragment extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.myfragment, container, false);
TextView textView = (TextView) view.findViewById(R.id.fragment_text);
//change getBundle() to getString()
String text = getArguments().getString("text")+"";
textView.setText(text);
return view;
}
}
What you are doing now is get a Bundle called text and what you really want is get a string in the bundle:
Bundle arg = getArguments();
String txt = arg.getString("text");
The issue of many fragments in one activity has been covered here before, but I cannot seem to find a solution for my positioning problem.
I want a horizontal scroll bar on top of the screen and under it a custom ListFragment. The code I am posting below puts up the scroll bar and then the ListFragment overwrites it. When I try to modify the hosting FrameLayout to have two FrameLayouts I get runtime errors.
Simply, How can I position the listFragment under the scrollbar?
public class GoogleNewsMainActivity extends FragmentActivity{
public static Context c;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
c=this;
setContentView(R.layout.activity_fragment);
/* FragmentManager fm = getSupportFragmentManager();
Fragment fragment = fm.findFragmentById(R.id.fragmentContainer);
if (fragment == null) {
Scroll_Menu_Fragment scrollFragment = new Scroll_Menu_Fragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, scrollFragment)
.commit();
GoogleNewsMainFragment f = new GoogleNewsMainFragment();
fm.beginTransaction()
.add(R.id.fragmentContainer, f)
.commit();
} */
}
}
Here is the scroll bar fragment:
public class Scroll_Menu_Fragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// TODO Auto-generated method stub
return inflater.inflate(R.layout.scrollbar_fragment, container, false);
}
#Override
public void onPause() {
// TODO Auto-generated method stub
super.onPause();
}
}
This is the ListFragment
public class GoogleNewsMainFragment extends ListFragment implements LoaderCallbacks<Cursor> {
private static final String COLUMN_ID = "id";
public final static String NEWSFROMSERVICE = "NEWSFROMSERVICE";
static ImageView thumbnail;
static String mSectionSelected;
private NewsCursor mCursor;
private ListView lv;
private NewsCursorAdapter adapter;
public static final String ID = "id";
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setRetainInstance(true);
setHasOptionsMenu(true);
mSectionSelected = ""; // until they select one
getLoaderManager().initLoader(0, null, this);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_google_news_main, container, false);
getActivity().setTitle(R.string.news_list_title);
return v;
}
And finally the hosting layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragmentContainer"
android:baselineAligned="false"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment
android:id="#+id/scrollFragment"
android:layout_width="fill_parent"
android:layout_weight="10"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
class="com.gilchrist.android.googlenews.Scroll_Menu_Fragment" ></fragment>
<fragment
android:id="#+id/listFragment"
android:layout_width="fill_parent"
android:layout_weight="90"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
class="com.gilchrist.android.googlenews.GoogleNewsMainFragment" ></fragment>
</LinearLayout>
I just updated the activity layout. The problem was caused by the weight values I was using. By going to 90 and 10 I got exactly what I wanted. All of the code above now works.
My app has a list and detail view, where I allow the user to swipe between the different detail views, using a ViewPager in portrait. In landscape, I show both fragments on the screen.
I'm having an issue where, when the device is rotated, the icons are duplicating themselves over and over in the Actionbar. I've learned that I'm probably creating new fragments whenever the device is rotated and I should be checking if the fragment is in the FragmentManager and then create a new instance if it isn't.
I am able to do that easily using the landscape layout, however, I'm not sure how to check if the fragment exists in portrait mode, using the ViewPager. Do I do that in getItem() of the FragmentStatePagerAdapter?
This is the code I'm using:
public class Main extends SherlockFragmentActivity
{
private static List<Integer> mIds;
#Override
public void onCreate(final Bundle icicle)
{
super.onCreate(icicle);
setContentView(R.layout.main);
mViewPager = (ViewPager)findViewById(R.id.viewpager); //view pager exists, so we are using the portait layout
if (mViewPager != null)
{
mIds = new ArrayList<Integer>();
mIds.add(0);
mIds.add(1);
mIds.add(2);
}
else //in landscape
{
ListFragment lf = (ListFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentList);
if (lf == null)
lf = new ListFragment();
DetailFragment df = (DetailFragment)getSupportFragmentManager().findFragmentById(R.id.fragmentDetail);
if (df == null)
{
df = new DetailFragment();
df.setArguments(getIntent().getExtras());
}
getSupportFragmentManager().beginTransaction().add(R.id.fragmentList, lf).commit();
getSupportFragmentManager().beginTransaction().add(R.id.fragmentDetail, df).commit();
}
}
private static class MyFragmentPagerAdapter extends FragmentStatePagerAdapter {
public MyFragmentPagerAdapter(FragmentManager fm) {
super(fm);
}
#Override
public Fragment getItem(int index) {
//can't use getSupportFragmentManager().findFragmentById() here because I get a "Cannot make a static reference to the non-static method" error
if (index == 0)
return ListFragment.newInstance();
else
return DetailFragment.newInstance(mIds.get(index-1));
}
#Override
public int getCount() {
return 4;
}
}
}
Main Layout (portrait)
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_height="fill_parent"
android:layout_width="fill_parent">
<android.support.v4.view.ViewPager
android:id="#+id/viewpager"
android:layout_height="match_parent"
android:layout_width="match_parent"
/>
</RelativeLayout>
Main Layout (landscape)
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_height="fill_parent"
android:layout_width="fill_parent"
android:weightSum="3"
>
<FrameLayout
android:id="#+id/fragmentList"
android:layout_weight="1"
android:layout_width="0dp"
android:layout_height="fill_parent"
/>
<FrameLayout
android:id="#+id/fragmentDetail"
android:layout_width="0dp"
android:layout_weight="2"
android:layout_height="fill_parent"
/>
</LinearLayout>
List Fragment
public class ListFragment extends SherlockListFragment implements DialogKeywordAddListener
{
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public static ListFragment newInstance() {
return new ListFragment();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
return inflater.inflate(R.layout.listing, container, false);
}
}
Detail Fragment
public class DetailFragment extends SherlockListFragment
{
private int mId;
#Override
public void onCreate(Bundle savedInstanceState){
super.onCreate(savedInstanceState);
setHasOptionsMenu(true);
}
public static DetailFragment newInstance() {
DetailFragment df = new DetailFragment();
Bundle bundle = new Bundle();
bundle.putInt("id", id);
df.setArguments(bundle);
return df;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
super.onCreateView(inflater, container, savedInstanceState);
if (getArguments() != null)
mId = getArguments().getInt("id");
return inflater.inflate(R.layout.detail, container, false);
}
}
Well, this question's solution seemed to work for me. Just setting:
super.onCreate(null);
in onCreate of the Activity solved my fragment issue.