App not responding error due to Fragment code - java

So my code for schedule fragment are listed below. These codes are fixed from my later error, but the problem is, it gives a Not responding error once I click "My Schedule" in my navigation drawer. Remember that my Schedule Fragment is connected to Navigation Drawer it self.
#TargetApi(Build.VERSION_CODES.N)
public class ScheduleFragment extends Fragment {
CompactCalendarView compactCalendar;
private SimpleDateFormat dateFormatMonth = new SimpleDateFormat("MMM - yyyy", Locale.getDefault());
public ScheduleFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState){
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_schedule, container, false);
final ActionBar actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setTitle(null);
compactCalendar = (CompactCalendarView) v.findViewById(R.id.compactcalendar_view);
compactCalendar.setUseThreeLetterAbbreviation(true);
Event ev1 = new Event(Color.RED, 1477040400000L, "Teachers' Professional Day");
compactCalendar.addEvent(ev1);
compactCalendar.setListener(new CompactCalendarView.CompactCalendarViewListener() {
#Override
public void onDayClick(Date dateClicked) {
Context c = getContext().getApplicationContext();
if (dateClicked.toString().compareTo("Fri Oct 21 00:00:00 AST 2016") == 0) {
Toast.makeText(c, "Teachers' Professional Day", Toast.LENGTH_SHORT).show();
} else {
Toast.makeText(c, "No Events Planned for that day", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onMonthScroll(Date firstDayOfNewMonth) {
actionBar.setTitle(dateFormatMonth.format(firstDayOfNewMonth));
}
});
return v;
}
}
What seems to be the problem? Do I need to implement the "extends ActionBarActivity" or "AppCompatActivity"? Either of these to will result in error of #Override method.

Related

Using Bundle to send data from activity to fragment

I am trying to use Bundle to send data from an activity to a fragment. The activity is receiving the input from a dialogbox when the user clicks on the actionbar add icon. The button also opens the dialogbox but it sends the data straight to the fragment (I'm trying to learn the difference between activity and fragment and to interact with the dialogfragment). None of the solutions on the internet have worked for me, and I was hoping someone can help
I have provided a visualization to aid in my explanation of the issue. So initially, I click the action add icon that opens the dialogbox (2nd pic), when I enter an input, it doesn't alter the data on the fragment. Only when I press the action add icon for a second time, does the first input get updated (3rd pic). Also you may notice that it says "Bundle{[Dialog Input = First Input]}" where First Input is the user input. How do I change this to just, First Input. I tried clearing the textview before setting the value, but that doesn't work. Now lastly when I press the button, it opens the dialogbox and when I enter in data, the data from the action add icon (handled in activity then data sent to fragment) overlaps with the data from the button (data sent straight to fragment). Any help would be appreciated. Thanks in advance.
MainActivity:
public class MainActivity extends AppCompatActivity implements
MyCustomDialog.OnInputSelected{
public String dialogInput;
FragmentManager fragmentManager;
#Override
public void sendInput(String input) {
dialogInput = input;
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
fragmentManager = getSupportFragmentManager();
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
//Inflate the menu, this adds items to the action bar if it is present
getMenuInflater().inflate(R.menu.menu, menu);
return super.onCreateOptionsMenu(menu);
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
//Handle action bar clicks here. The action bar will automatically handle clicks on the home/up button
//so long as you specify a parent activity in AndroidManifest.xml
switch(item.getItemId()){
case R.id.action_add:
MyCustomDialog dialog = new MyCustomDialog();
dialog.show(getSupportFragmentManager(), "MyCustomDialog");
//Trying Bundle to pass data, dialog input between activity and fragment
Bundle bundle = new Bundle();
bundle.putString("Dialog Input", dialogInput);
//Set Fragment class arguments
MainFragment fragment = new MainFragment();
fragment.setArguments(bundle); //set argument bundle to fragment
fragmentManager.beginTransaction().replace(R.id.MainFragment,fragment).commit(); //now replace Mainfragment
Toast.makeText(this, "Action_Add Clicked Successfully", Toast.LENGTH_SHORT).show();
}
return super.onOptionsItemSelected(item);
}
}
MainFragment:
public class MainFragment extends Fragment implements MyCustomDialog.OnInputSelected{
TextView InputDisplay;
Button OpenDialog;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_main, container, false);
InputDisplay = view.findViewById(R.id.InputDisplay);
OpenDialog = view.findViewById(R.id.Open_Dialog);
//Getting Main Activity dialog information with Bundle, that was received from toolbar add
Bundle bundle = getArguments();
if(bundle != null){
String dialogInput = bundle.toString();
//Clearing since Fragment call and activity call overlap each other.
InputDisplay.setText("");
InputDisplay.clearComposingText();
InputDisplay.setText(dialogInput);
}
//String dialogInput = this.getArguments().getString("Dialog Input");
OpenDialog.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Log.d("MainFragment", "onClick: opening dialog");
MyCustomDialog customDialog = new MyCustomDialog();
customDialog.setTargetFragment(MainFragment.this, 1);
customDialog.show(getFragmentManager(), "MyCustomDialog");
}
});
return view;
}
#Override
public void sendInput(String input) {
InputDisplay.setText("");
InputDisplay.setText(input);
}
}
My Custom Dialog:
public class MyCustomDialog extends DialogFragment {
private EditText Input;
private TextView ActionOK, ActionCANCEL;
private OnInputSelected onInputSelected;
public interface OnInputSelected{
void sendInput(String input);
}
#Override
public void onAttach(Context context) {
super.onAttach(context);
try{
Fragment onInputSelected_fragment = getTargetFragment();
Activity onInputSelected_activity = getActivity();
if(onInputSelected_fragment != null){
onInputSelected = (OnInputSelected) onInputSelected_fragment;
}else{
onInputSelected = (OnInputSelected) onInputSelected_activity;
}
//throw new RuntimeException("Custom Dialog onAttach Listener was NULL");
}catch(ClassCastException e){
Log.e("Custom Dialog", "onAttach: ClassCastException: " + e.getMessage());
}
}
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog_my_custom, container, false);
Input = view.findViewById(R.id.Input);
ActionOK = view.findViewById(R.id.Action_OK);
ActionCANCEL = view.findViewById(R.id.Action_CANCEL);
ActionCANCEL.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
getDialog().dismiss();
}
});
ActionOK.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
onInputSelected.sendInput(Input.getText().toString());
getDialog().dismiss();
}
});
return view;
}
}
How do I change this to just, First Input.
your output is printed like this "Bundle{[Dialog Input = First Input]}" because you are directly doing bundle.toString(); instead of getting the value you have stored in the bundle.
change the above to this
String dialogInput = bundle.getString("Dialog Input")
InputDisplay.setText(dialogInput);
the data from the action add icon overlaps with the data from the button
Clear the existing text in the text view before setting the new value like this
String dialogInput = bundle.getString("Dialog Input")
InputDisplay.setText(");
InputDisplay.setText(dialogInput);
Also, I noticed that all the variable names that you have used are not following camel case I suggest you correct that as well.

error: cannot find symbol method getSupportActionBar(),findViewById(int),getApplicationContext()

So I have this code inside my ScheduleFragment in which my objective was to view a simple calendar viewer. But unfortunately, it gets an error mentioned in the title. Even if I change "public class ScheduleFragment extends Fragment" to "public class ScheduleFragment extends AppCompatActivity", it gives an error to #Override.
#TargetApi(Build.VERSION_CODES.N)
public class ScheduleFragment extends Fragment {
CompactCalendarView compactCalendar;
private SimpleDateFormat dateFormatMonth = new SimpleDateFormat("MMM - yyyy", Locale.getDefault());
public ScheduleFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_schedule, container, false);
final ActionBar actionBar = getSupportActionBar();
actionBar.setDisplayHomeAsUpEnabled(false);
actionBar.setTitle(null);
compactCalendar = (CompactCalendarView) findViewById(R.id.compactcalendar_view);
compactCalendar.setUseThreeLetterAbbreviation(true);
Event ev1 = new Event(Color.RED, 1477040400000L, "Teachers' Professional Day");
compactCalendar.addEvent(ev1);
compactCalendar.setListener(new CompactCalendarView.CompactCalendarViewListener() {
#Override
public void onDayClick(Date dateClicked) {
Context context = getApplicationContext();
if (dateClicked.toString().compareTo("Fri Oct 21 00:00:00 AST 2016") == 0) {
Toast.makeText(context, "Teachers' Professional Day", Toast.LENGTH_SHORT).show();
}else {
Toast.makeText(context, "No Events Planned for that day", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onMonthScroll(Date firstDayOfNewMonth) {
actionBar.setTitle(dateFormatMonth.format(firstDayOfNewMonth));
}
});
}
}
In fragments, you can not directly call findViewById() and have to use the view that you just inflated and call findViewById() on it.
So your onCreateView code will be,
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View v = inflater.inflate(R.layout.fragment_schedule, container, false);
compactCalendar = (CompactCalendarView) v.findViewById(R.id.compactcalendar_view);
//Make sure that the view inflated above has these view elements which you are trying to initialize
.
.
.
}
Similarly for getApplicationContext(), you first need to call getContext() and then call getApplicationContext() on it
So it becomes,
Context c = getContext().getApplicationContext();
Same goes for getSupportActionBar();
In my opinion, you should inflate the view and use view.findViewByID(R.id.id),
then call return at the end of the method .

Android Fragment Back press

I have a couple of fragments. But all the fragments lead to PeriodFragment when they press a button. My question is how do I implement so that when the user presses the BACK button on his mobile phone, the PeriodFragment will switch back to the fragment it was entered from.
This is the java code from PeriodFragment.java:
public class PeriodFragment extends Fragment {
Button btnPretrazi;
public PeriodFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.fragment_period, container, false);
//buttonPretraziPeriod
btnPretrazi = (Button) view.findViewById(R.id.buttonPretraziPeriod);
btnPretrazi.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Toast.makeText(getContext(),"test", Toast.LENGTH_SHORT).show();
}
});
return view;
}
}
This is my TvrdjavaFragment.java (one of the fragments that have the button to switch to PeriodFragment.java) :
package com.example.ivanp.msnis;
public class TvrdjavaFragment extends Fragment {
Button btnIdinaperiod;
public TvrdjavaFragment() {
// Required empty public constructor
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_tvrdjava, container, false);
// Inflate the layout for this fragment
//show date
TextView datumprikaz = (TextView) view.findViewById(R.id.datumprikaz);
Date danas = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("dd.MM.yyyy");
String novDatum = sdf.format(danas);
datumprikaz.setText(novDatum);
//End of show date
btnIdinaperiod = (Button) view.findViewById(R.id.buttonIdinaperiod);
btnIdinaperiod.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
PeriodFragment periodFragment = new PeriodFragment();
FragmentTransaction periodFragmentTransaction = getActivity().getSupportFragmentManager().beginTransaction();
periodFragmentTransaction.replace(R.id.frame, periodFragment);
periodFragmentTransaction.commit();
}
});
return view;
}
}
I'm new to android studio, so please tell the details.
According to android docs When using fragments in your app, individual FragmentTransaction objects may represent context changes that should be added to the back stack. For example, if you are implementing a master/detail flow on a handset by swapping out fragments, you should ensure that pressing the Back button on a detail screen returns the user to the master screen. To do so, call addToBackStack() before you commit the transaction:
// Works with either the framework FragmentManager or the
// support package FragmentManager (getSupportFragmentManager).
getSupportFragmentManager().beginTransaction()
.add(detailFragment, "detail")
// Add this transaction to the back stack
.addToBackStack()
.commit();
Adding to backstack will solve the problem:
periodFragmentTransaction.replace(R.id.frame, periodFragment);
periodFragmentTransaction.addToBackStack(null);
periodFragmentTransaction.commit();

How to update custom RecyclerView from FragmentDialog?

I have an Activity A with a fragment frag2. Inside the fragment I have a RecyclerView and Adapter to show a list of custom class objects. Adding objects to the adapter is handled programmatically. I have a button inside TwoFragment that opens a FragmentDialog. I'd like to add an object to my Adapter by confirming this dialog, but it seems that the adapter is null when called from the FragmentDialog.
The same adapter is not null, and works if I call it from the fragment OnClick.
Moreover the adapter is null only after screen rotation, it works fine before rotating.
To communicate between the two Fragments I implement a communicator class in activity A.
Activity A
public void respond(String type) {
frag2.addSupport(type);
}
frag2
public RecyclerView rv;
public ArrayList<support> supports;
public myAdapter adapter;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supports = new ArrayList<>();
adapter = new myAdapter(supports);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View layout = inflater.inflate( R.layout.fragment_two, container, false);
layout.setId(R.id.frag2);
if (savedInstanceState!=null)
{
supports = savedInstanceState.getParcelableArrayList("supports");
}
rv = (RecyclerView) layout.findViewById(R.id.rv);
adapter = new myAdapter(supports);
rv.setAdapter(myAdapter);
rv.setLayoutManager(new LinearLayoutManager(getActivity()));
rv.setItemAnimator(new DefaultItemAnimator());
#Override
public void onClick(View v) {
int id = v.getId();
switch (id){
case R.id.button1:
addSupport(type); // THIS WORKS ALWAYS, even after screen rotate
break;
case R.id.button2:
showDialog();
break;
}
}
public void showDialog(){
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.show(manager, "dialog");
}
public void addSupport(String type){
adapter.addItem(new support(type)); // this line gives null pointer on adapter, but only if called after screen rotate and only if called from the dialog
}
dialog
communicator comm;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.dialog, null);
comm = (myCommunicator) getActivity();
create = (Button) view.findViewById(R.id.button_ok);
create.setOnClickListener(this);
return view;
}
#Override
public void onClick(View v) {
if(v.getId()==R.id.button_ok)
{
// some controls to set type
comm.respond(type)
dismiss();
}
else {
dismiss();
}
myAdapter
public class myAdapter extends RecyclerView.Adapter<myAdapter.VH> {
private LayoutInflater inflater;
private ArrayList<support> data = new ArrayList<>();
// settings for viewholder
public myAdapter (ArrayList<support> data)
{
this.data=data;
}
public void addItem(support dataObj) {
data.add(dataObj);
notifyItemInserted(data.size());
}
}
logcat
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
I hope there are no mistakes, I shortened the code for better understanding. Keep in mind that everything works if I never rotate the screen.
I'm a beginner with android and I'm stuck with this for several days now. Please, help.
To understand the problem, it's as you say:
.. everything works if I never rotate the screen
So firstly to understand what happens on rotation, this is a quote from the Android Developer website:
Caution: Your activity will be destroyed and recreated each time the user rotates the screen. When the screen changes orientation, the system destroys and recreates the foreground activity because the screen configuration has changed and your activity might need to load alternative resources (such as the layout).
Ok, now to understand the error:
FATAL EXCEPTION: main
java.lang.NullPointerException: Attempt to invoke virtual method 'myAdapter.addItem(myObject)' on a null object reference
Essentially, in your dialog class, you have created a strong dependency by declaring :
comm = (myCommunicator) getActivity();
because comm references objects which would have been destroyed on rotation, hence the NullPointerException.
To further understand runtime changes, such as orientation changes, I'd recommend going through Handling Runtime Changes.
Update
Thank you for your answer, what would you recommend instead of comm = (myCommunicator) getActivity(); ?
The solution comes in 3 parts:
Make sure the onCreate of Activity A has the following:
#Override
public void onCreate(Bundle savedInstanceState) {
......
// find the retained fragment on activity restarts
FragmentManager fm = getFragmentManager();
frag2 = (Frag2) fm.findFragmentByTag(“frag2”);
// create frag2 only for the first time
if (frag2 == null) {
// add the fragment
frag2 = new Frag2();
fm.beginTransaction().add(frag2 , “frag2”).commit();
}
......
}
Add setRetainInstance(true) to the onCreate of frag2.
Remove the implicit referencing i.e. comm = (myCommunicator) getActivity();, and implement something more loosely coupled for dialog.
dialog
public interface Communicator {
void respond(String type);
}
Communicator comm;
....
public void addCommunicator(Communicator communicator) {
comm = communicator;
}
public void removeCommunicator() {
comm = null;
}
#Override
public void onClick(View v) {
if((v.getId()==R.id.button_ok) && (comm!=null))
{
// some controls to set type
comm.respond(type);
}
// Regardless of what button is pressed, the dialog will dismiss
dismiss();
}
This allows you do the following in frag2 (or any other class for that matter):
frag2
<pre><code>
public class Frag2 extends Fragment implements dialog.Communicator {
........
public void showDialog() {
FragmentManager manager = getFragmentManager();
myDialog dialog = new myDialog();
dialog.addCommunicator(this);
dialog.show(manager, "dialog");
}
#Override
public void respond(String type){
adapter.addItem(new support(type));
}
}

Using Fragments and Creating "Starting Page"

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

Categories