I want to set the text of textview in activity from a fragment. This is how I do it.
MainActivity.java
public class MainActivity extends AppCompatActivity implements NavigationView.OnNavigationItemSelectedListener {
public TextView textViewNotification;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
NavigationView navigationView = findViewById(R.id.nav_view);
navigationView.setNavigationItemSelectedListener(this);
textViewNotification = (TextView) MenuItemCompat.getActionView(navigationView.getMenu().findItem(R.id.nav_notification));
}
}
HomeFragment.java
public class HomeFragment extends Fragment {
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
((MainActivity)getActivity()).getSupportActionBar().setTitle("PIT IAI & FIP Regional");
ViewGroup root = (ViewGroup) inflater.inflate(R.layout.fragment_home, container, false);
MainActivity activity = (MainActivity) getActivity();
activity.notification.setText("This is a test"); // => got error here.
return root;
}
}
But it didin't work. This is the error that I got:
android.content.res.Resources$NotFoundException: String resource ID #0x1
at android.content.res.Resources.getText(Resources.java:348)
at android.widget.TextView.setText(TextView.java:5846)
How is it exactly to get and set public attributes of activity from fragments? is it not possible? Please help.
Supposed you want to send the text to the activity based on some action.
You can use an interface, first create a public interface in your fragment and add one method inside it which takes one string parameter
public interface CommunicateWithActivity{
void onCommunicate(String s)
}
, declare a global variable mListener of type CommunicateWithActivity,
private CommunicateWithActivity mListener;
then override onAttach and inside try/catch block
#Override
public void onAttach(#NonNull Context context) {
super.onAttach(context);
try {
mListener = (CommunicateWithActivity) context;
} catch (ClassCastException e) {
throw new ClassCastException(context.toString() + "CommunicateWithActivity implementation in Activity is required");
}
}
then in the activity implement the interface an override "onComunicate(String s)" method in the activity and you will get your string inside the methd.
#Override
public void onCommunicate(String s) {
//do whatever you want
}
Henry Gunawan your MainActivity does not have any public field called notification you actually named it textViewNotification.
Moreover i am seeing some bad practices in your code
Avoid declaring public fields instead use getters and setters to access them
Your fragment assuming that it's host is always a MainActivity instance and it has a field called notification , which is not a good practice , fragments are meant to be a standalone unit, fragments should not depends on specifics of their host activity as they may be host by any activity hence this is a misuse of fragments, use should instead use callbacks if you want your host activity to do something for you as explained by Amr Sakr.
Related
I am working on an application where I am using bottom navigation on Home where i have three fragments on the second fragment called Post Ad I have a button called enter fragment zone through that i enter into another fragment now when I enter inside another fragment now I don't want there to show the bottom navigation so to hide it I am using a method inside my main activity called "setBottomNavigationVisibility" where I am writing code to set the visibility of bottom nav. but the problem is that it is throwing the null pointer exception in the mainactivty's method saying that
"void com.google.android.material.bottomnavigation.BottomNavigationView.setVisibility(int)' on a null object reference" on the method's line where i am setting the visibility
code of MainActivity
public class MainActivity extends AppCompatActivity {
NavController navController;
BottomNavigationView bottomNavigationView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
navController = Navigation.findNavController(this, R.id.fragmentContainerView);
bottomNavigationView = findViewById(R.id.activity_main_bottom_navigation_view);
NavigationUI.setupWithNavController(bottomNavigationView, navController);
}
public void setBottomNavigationVisibility(int visibility) {
bottomNavigationView.setVisibility(visibility);
}}
On the above method when i am trying to setting the visibility on the line bottomNavigationView.setVisibility(visibility); that's where it is thrwoing the exception
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
binding = FragmentSecondBinding.inflate(inflater, container, false);
// Inflate the layout for this fragment
View view = binding.getRoot();
viewModel = new ViewModelProvider(requireActivity()).get(PageViewModel.class);
((MainActivity) requireActivity()).setBottomNavigationVisibility(View.GONE);
binding.toolbar.setNavigationOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.action_secondFragment2_to_postad);
}
});
No Please guide me how can i solve this error.
do something like this, define interface
interface DashboardActivityDelegate {
void hideBottomBar();
void showBottomBar();
}
your activity must implement this interface
and in your fragment in which you want to hide bottom nav view do this:
declare global variable
private DashboardActivityDelegate dashboardActivityDelegate;
#Override
public void onAttach(Context context) {
super.onAttach(context);
if (context instanceof DashboardActivityDelegate) {
dashboardActivityDelegate = (DashboardActivityDelegate)context;
}
}
#Override
public void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (dashboardActivityDelegate != null) {
dashboardActivityDelegate.hideBottomBar();
}
}
#Override
public void onDestroy() {
super.onDestroy();
if (dashboardActivityDelegate != null) {
dashboardActivityDelegate.showBottomBar();
}
}
in fragment you want hide bottom bar, just call this line in onViewCreated
getActivity().findViewById(R.id.activity_main_bottom_navigation_view).setVisibility(View.GONE);
and when you leave fragment, call this line in onDestroy or onStop to visible bottom nav for another fragment:
getActivity().findViewById(R.id.activity_main_bottom_navigation_view).setVisibility(View.VISIBLE)
I am trying to invoke a method named "change_color()" in my one fragment "A" from another fragment "B" using an interface, implemented by the parent activity. When I try to cast my parent activity to the instance of my interface, I get this ClassCastException.
Here's the snippet of fragment "B",
Public class B extends Fragment implements View.onClickListener{
public attendance_to_history var;
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_attendance_take,
container, false);
return rootView;
}
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
var = (attendance_to_history) getActivity();
}
Here's the code of interface
interface attendance_to_history{
public void invoke();}
Here's code from my parent activity:
public class tabbed_activity extends AppCompatActivity implements attendance_to_history{
#Override
public void invoke() {
fragment_A frag = new fragment_A();
frag.change_color();
}
}
attendance_to_history connector;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
connector=(attendance_to_history) activity;
}
replace instead for onActivityCreated
here is the solution:
1- in the activity that holds the fragment B, make it implements attendance_to_history and override the method invoke inside the activity.
then your code will work fine.
I'm new in Android App developing via Java. I'm using Eclipse. If I create an Activity, Eclipse automatically generates a Placeholderfragment Class and Fragment.xml. Can I disable this function? Or is it not advisable to do that? I delete those files because I find it more complicated to use than just write in one xml file at the moment.
Second question is how do I implement a "starting Page" for my App? For example some sort of a logopage which automatically disables after a few seconds and switches to a new activity. Create a separate Activity for it or do I use something else?
Actually you need two activities, one startup Activity which is used to show your logo or some guide,the other is a MainActivity which should be started by the startUp Activity.
In short You can do something like this:
public class MainActivity extends FragmentActivity {
Fragment fragment;
String className;
#Override
protected void onCreate(Bundle savedInstanceState) {
Log.d("MainActivity", "onCreate");
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
//Store the name of the class
className=MainActivity.class.getSimpleName();
//First fragment should be mounted on oncreate of main activity
if (savedInstanceState == null) {
/*fragment=FragmentOne.newInstance();
getSupportFragmentManager().beginTransaction().replace(R.id.container, fragment).addToBackStack(className).commit();
*/
Fragment newFragment = FragmentOne.newInstance();
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
ft.replace(R.id.container, newFragment).addToBackStack(null).commit();
Log.d("FRAGMENT-A", "fragment added to backstack");
}
}
}
FragmentOne.java
public class FragmentOne extends Fragment{
String className;
public static FragmentOne newInstance(){
Log.d("FragmentOne", "newInstance");
FragmentOne fragment = new FragmentOne();
return fragment;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d("FragmentOne", "onCreateView");
View view=inflater.inflate(R.layout.fragment_one, container, false);
//Store the name of the class
className=FragmentOne.class.getSimpleName();
return view;
}
}
Let me know if you need any more info
Well, in a Single Activity setup, the way I did this was the following:
public class SplashFragment extends Fragment implements View.OnClickListener
{
private volatile boolean showSplash = true;
private ReplaceWith activity_replaceWith;
private Button splashButton;
public SplashFragment()
{
super();
}
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
activity_replaceWith = (ReplaceWith) activity;
}
catch (ClassCastException e)
{
Log.e(getClass().getSimpleName(), "Activity of " + getClass().getSimpleName() + "must implement ReplaceWith interface!", e);
throw e;
}
startSwitcherThread();
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_splash, container, false);
splashButton = (Button) rootView.findViewById(R.id.fragment_splash_button);
splashButton.setOnClickListener(this);
return rootView;
}
public void startSwitcherThread()
{
Thread splashDelay = new Thread()
{
#Override
public void run()
{
try
{
long millis = 0;
while (showSplash && millis < 4000)
{
sleep(100);
millis += 100;
}
showSplash = false;
switchToFirstScreen();
}
catch (Exception e)
{
e.printStackTrace();
}
}
};
splashDelay.start();
}
private void switchToFirstScreen()
{
activity_replaceWith.replaceWith(new FirstFragment());
}
#Override
public void onClick(View v)
{
if(v == splashButton)
{
if(showSplash == false)
{
switchToFirstScreen();
}
}
};
}
Where the ReplaceWith interface is the following:
import android.support.v4.app.Fragment;
public interface ReplaceWith
{
public void replaceWith(Fragment fragment);
}
And the replace function is implemented like so:
#Override
public void replaceWith(Fragment fragment)
{
getSupportFragmentManager()
.beginTransaction().replace(R.id.fragment_container, fragment)
.addToBackStack(null)
.commit();
}
Now, most people will say this is not a good approach if you're using multiple activities, and/or using multiple orientations and aren't just simply displaying a single Fragment in a single Activity no matter what. And they are completely right in saying so.
Multiple orientations would require the Activity to be responsible for knowing what is the "next" Fragment at a given replace call, and where to place it (which container, or to start it in a new Activity). So this is a valid approach only if you are certain that you only have one container and there is one Fragment shown at a given time.
So basically, if this does not apply to you, then you need to utilize the same approach (make a specific delay before you replace the current Fragment or Activity with another one, this specific code allows you that once the splash has been shown once, then clicking the button will automatically take you to the next screen - typical game splash setup, really), but use activity callbacks specific to the Fragment in order to swap one out for the other.
A Fragment setup I recommend and isn't relying on this special case can be seen here: Simple Android Project or its equivalent on Code Review: Fragment Start-Up Project
I am having trouble figuring out how to share data between my two fragments which are hosted on the same activity.
The objective:
I want to transfer string from the the selected position of a spinner and an image url string from a selected list view position from fragment A to fragment B.
The Attempt:
I read the fragments doc on this problem here http://developer.android.com/guide/components/fragments.html#CommunicatingWithActivity
And went ahead an created the following Interface to use betweeen the Fragments and the Host Activity.
public interface OnSelectionListener {
public void OnSelectionListener(String img, String comments );
}
Then I proceeded to implement it in my fragment A's onCreateView method like so:
postList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,
int position, long id) {
ListData link = data.get(position);
String permalink = link.getComments();
String largeImg = link.getImageUrl();
Fragment newFragment = new DetailsView();
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
// Commit the transaction
transaction.commit();
//pass data to host activity
selectionListener.OnSelectionListener(permalink,largeImg);
}
});
And also in the onAttach method
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
try {
selectionListener = (OnSelectionListener)getActivity();
} catch (ClassCastException e) {
throw new ClassCastException(activity.toString() + " must implement onSelectionListener");
}
}
In the Host activity I implemented the interface I wrote and overrided the method like so:
#Override
public void OnSelectionListener(String img, String comments) {
DetailsView detailsView = new DetailsView();
DetailsView dView = (DetailsView)getSupportFragmentManager().findFragmentByTag(detailsView.getCustomTag());
dView.setInformation(img, comments);
}
In Fragment B I set a "tag" the following way
private String tag;
public void setCustomTag(String tag)
{
this.tag = tag;
}
public String getCustomTag()
{
return tag;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setCustomTag("DETAILS_VIEW");
And my thinking is that that the information can be passed to Fragment B by calling this method from the host activity
void setInformation (String info, String img){
RedditDetailsTask detailsTask = new RedditDetailsTask(null,DetailsView.this);
detailsTask.execute(info);
setDrawable(img);
}
What I need:
I want to know how to properly use tags to get this to work, I dont have any fragment id's declared in my xml and rather opted to exchange fragments in a fragment_container.
I also am not sure if this is a good way to pass multiple strings between fragments. I am a newbie programmer so I know my logic probably looks pretty embarrassing but I am trying to do my best learn to do this right. I would appreciate it if you more senior developers can point me in the right direction for doing this.
You don't need to use tags. Take a look at this example. The Activity implements an interface that allows you to talk from Fragment1 back to the Activity, the Activity then relays the information into Fragment2.
I've left out all the android stuff about FragmentManager etc.
interface FragmentListener {
void onTalk(String s1);
}
public class MyActivity extends Activity implements FragmentListener {
Fragment2 fragment2;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_my);
// Find fragment2 and init
}
#Override
public void onTalk(String s1) {
fragment2.onListen(s1);
}
private static class Fragment1 extends Fragment {
private FragmentListener communication;
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
communication = (FragmentListener) activity;
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_one, container, false);
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
// or in an onClick listener
communication.onTalk("blah blah");
}
}
private static class Fragment2 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_two, container, false);
}
public void onListen(String s1) {
Log.d("TADA", s1);
}
}
}
My approach would be, when you get the callback in activity through the OnSelectionListener interface, I would create the Fragment B object and set arguments to it as follows:
#Override
public void OnSelectionListener(String img, String comments) {
DetailsView detailsView = new DetailsView();
Bundle args=new Bundle();
args.putString("img",img);
args.putString("comments",comments);
detailsView.setArguments(args);
//code here to replace the fragment A with fragment B
}
Then in Fragment B's onCreate method you can retrieve the values as follows:
#Override
public void onCreate(Bundle savedInstanceState) {
Bundle args=getArguments();
String img=args.getString("img");
String comments=args.getString("comments");
//do whatever you want to do with the varaibles
}
You could try to make two public static String's in your B fragment.
it Would look like something like this
public static String img;
public static String comment;
The you set the variables before making the transaction to fragment B
FragmentTransaction transaction = getFragmentManager().beginTransaction();
// Replace whatever is in the fragment_container view with this fragment,
// and add the transaction to the back stack
transaction.replace(R.id.fragment_container, newFragment);
transaction.addToBackStack(null);
SecondFragment.img = new String("imgString"); //Making a new string so incase you change the string in bfragment, the values wont change in here
SecondFragment.comment = new String("comment");
// Commit the transaction
transaction.commit();
Then in the onStop(), or onDestroy() - depending on when you want the variables to be null, check this - you set the the static variables to null, so they dont take memory space
public void onDestroy(){
super.onDestroy();
img = null;
comment = null;
}
I have this class here which calls the method setPoint
public class PointsList extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.listpoints, container, false);
public static class PointCreation extends Fragment {
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.point_creation, container, false);
setPoint(view, CREATE);
return view;
}
}
static final void setPoint(View view, int goal) {
final EditText SerialField = (EditText) view.findViewById(R.id.Serial);
if(goal == CREATE) {
Button buttonGuardar = (Button) view.findViewById(R.id.buttonGuardar);
buttonGuardar.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String Serial = SerialField.getText().toString();
pointsList.add(new Serial);
//go back to R.layout.listpoints
}
});
}
}
My goal is after I click the button to add the new Serial to the List, I can go back to the previous menu from
R.layout.point_creation to R.layout.listpoints
To move around fragments I generally use something like this:
Fragment fragment = new PointsList();
FragmentManager fragmentManager = getActivity().getSupportFragmentManager();
fragmentManager.beginTransaction()
.replace(R.id.container, fragment)
.commit();
But inside:
static final void setPoint(View view, int goal)
getActivity().getSupportFragmentManager();
cannot be referenced from a static context, and I don't know how to go around it with making the static class non-static? I've some global flags which I use in the static Classes (have 2 of them) that would be a bit painfull to export since
public class PointCreation(int something) extends Fragment
is something I can't do.
You can get the activity from view:
Activity activity = (Activity)view.getContext()
If you use FragmentActivity (it seems to be so), then cast Context to FragmentActivity (instead of regular Activity) and further you will able to call getSupportFragmentManager()
FragmentActivity activity = (FragmentActivity)view.getContext();
FragmentManager manager = activity.getSupportFragmentManager();
You can use by below code;
private static FragmentActivity myContext;
#Override
public void onAttach(Activity activity) {
myContext = (FragmentActivity) activity;
super.onAttach(activity);
}
You can use myContext as Context
You can't reference from a static to non-static objects. First thing, that comes to mind is to use singleton pattern for your fragment. In other words add to you fragment singleton snippet:
static PointsList instance;
public PointsList getInstace(){
if(instance == null){
instance = new PointsList ();
}
return instance;
}
and in your fragment onCreate method assign it to the instance:
instance = this;
after that you can remove static modifier from setPoint method. And call it from any part of your project like PointsList.getInstance().setPoint();
p.s. what goals from static you have to use? You should use static very carefully, many things can be done through singleton instead of using statics.