I am using a "Drawer Navigation" project using fragments and so far this method works:
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Navigation.findNavController(view).navigate(R.id.nav_gallery);
}
});
But I want to use the navigation method inside a fragment class calling a function like this:
boolean b;
public void fragmentNavigation() {
if (b) {
Navigation.findNavController(getView()).navigate(R.id.nav_gallery);
}
}
I am a newbie using the navigation architecture and I still don't know if I need to declare some type of action listener for that function or how to make it work.
You can do that, but be careful of :
using getView() is Nullable; so you must make sure that your fragment has already created the view.
You can solve this by a couple of ways
First: Overriding onViewCreated() which has a nonNull view argument
#Override
public void onViewCreated(#NonNull View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
fragmentNavigation(view);
}
boolean b;
public void fragmentNavigation(View view) {
if (b) {
Navigation.findNavController(view).navigate(R.id.nav_gallery);
}
}
Second: Create a View field in the fragment class, and set it within onCreateView()
View view;
#Nullable
#Override
public View onCreateView(#NonNull LayoutInflater inflater, #Nullable ViewGroup container,
#Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.main_fragment, container, false);
return view;
}
then always call fragmentNavigation(view); on that view field.
Your fragment is hosted by the NavHostFragment of the Navigation Graph; so that you can avoid potential IllegalStateException
Related
I got code from stack overflow to call a callback in a fragment from the Activity class.
I got the following code from stack overflow
fraKey fragment=(fraKey)getFragmentManager().findFragmentById(R.id.idFraKey);
I get the following error
inconvertable types cannot cast "android.app.fragment to com.example.mylib.keyfrag
the fragment code
public class fraKey extends Fragment implements View.OnClickListener {
int ted=0;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_fra_key, container, false);
}
public void onViewCreated(View view, Bundle savedInstanceState) {
View temp;
temp = view.findViewById(R.id.idUpdate);
temp.setOnClickListener(this);
temp = view.findViewById(R.id.idTest);
temp.setOnClickListener(this);
}
public void onClick(View v) {
ted++;
cKeys mKeys = new cKeys();
((actMyLib)getActivity()).updateKeya(mKeys);
}
} // end class
you just need this article. It's very helpful. Have a great day.
Maybe I am missing something small here, but I cannot get my binding to work. I set it up as follow:
public class Toolbar extends Fragment {
//Interaction handlers
//interface for interaction with Activity
public interface ToolBarInteraction{
public void Search(String screenName);
}
private ToolbarBind modelData;
private ToolBarInteraction mListener;
public static Toolbar newInstance() {
return new Toolbar();
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
FragmentToolbarBinding binding = DataBindingUtil.setContentView(getActivity(), R.layout.fragment_toolbar);
modelData = ToolbarBind.newInstance();
modelData.searchedText.set("Hello");
binding.setModelData(modelData);
}
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
checkMListener();
View view = inflater.inflate(R.layout.fragment_toolbar, container, false);
//get button to set onClick event
Button button = (Button)view.findViewById(R.id.btnSearch);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String hello = modelData.searchedText.get();
}
});
return view;
}
public void OnSearchClicked(View view){
mListener.Search(modelData.searchedText.get());
}
private void checkMListener(){
try{
mListener = (ToolBarInteraction) getActivity();
} catch (ClassCastException ex) {
throw new ClassCastException(getActivity().toString()
+ " must implement the ToolBarInteraction Interface");
}
}
}
Here is the code for ToolbarBind:
public class ToolbarBind extends BaseObservable {
private String _searchText;
public final ObservableField<String> searchedText = new ObservableField<String>();
//factory method
public static ToolbarBind newInstance(){ return new ToolbarBind(); }
}
And in my fragment, I set the binding up as follow, all within the layout tag:
<data>
<variable
name="modelData"
type="Common.CommonObjects.ToolbarBind"/>
</data>
And for binding to property:
<EditText
android:layout_width="match_parent"
android:layout_height="match_parent"
android:hint="Search"
android:text="#={modelData.searchedText}"/>
As can be seen, in the onCreate I set the text to "Hello", but even when the view displays on the phone, the EditText is not populated with this text. When I change the value, and click my button, the value I get back in the event is "Hello", not my new text entered while the app is running.
What am I missing?
The problem with your code is that you set the Activity's content view to something in onCreate(...) but you inflate and use something different in onCreateView(...) as your fragment's view, which gets the model data (not the other one you created in onCreate(...)). I don't know exactly what you try to achieve, but I'm gonna guess that you don't want to change the Activity's content view to something from the fragment, so I'm just gonna show you a variation of what could you use, however, you should change it to whatever pleases you.
Remove onCreate(...) completely then use only onCreateView(...) to inflate the fragment_toolbar layout using data binding:
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
checkMListener();
FragmentToolbarBinding binding = DataBindingUtil.inflate(inflater, R.layout.fragment_toolbar, container, false);
// FIXME you're losing data here; watch out: checking whether savedInstanceState == null is not enough because returning from backstack it will be null
modelData = ToolbarBind.newInstance();
// FIXME modify this so it sets the data from savedInstanceState when configuration changes
modelData.searchedText.set("Hello");
binding.setModelData(modelData);
//get button to set onClick event
binding.btnSearch.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
String hello = modelData.searchedText.get();
}
});
return binding.getRoot();
}
Watch out for the FIXME parts. You could move the modelData init to onCreate(...) which would save it from the backstack-return thing, however, configuration change will still call onCreate(...) unless you call setRetainInstance(true) on the fragment (do not).
I tried to make an application that shows a fragment at start then you can change that fragment to an other one with a button in the first fragment. But when I put the button action in the fragments java, it won't start and I get the nullpointerexception error. Could you tell me why?
public class Fragment_main extends Fragment {
FragmentManager fm = getFragmentManager();
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Button button_inditas = (Button) getView().findViewById(R.id.button_inditas);
button_inditas.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
fm.beginTransaction().replace(R.id.content_frame, new Fragment_1()).commit();
}
});
return inflater.inflate(R.layout.fragment_main,container,false);
}
}
But when I put the button action in the fragments java, it won't start and I get the nullpointerexception error. Could you tell me why?
Well I see some errors on your code...
First of all you can't call getView() if you haven't inflated one, it means that you MUST inflate a view as i'll put on my answer and then you can avoid the getView() and use that view itself.
And instead of returning the inflater you have to return your View
This is how your Fragment should look like:
public class Fragment_main extends Fragment {
public Fragment_main() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main,container,false);
Button button_inditas = (Button)rootView.findViewById(R.id.button_inditas);
button_inditas.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
FragmentManager fragmentManager = getFragmentManager ();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction ();
fragmentTransaction.add (R.id.content_frame, new Fragment_1());
fragmentTransaction.commit ();
});
return rootView;
}
}
You have to inflate the view first and use the view returned by that instead of getView ().
View view = inflater.inflate (...);
and then
... = (Button) view.findView(...);
This happens because the view that is returned by getView () hasn't been created yet, so getView() returns null.
Obviously, I am new to Android - XML programming... So I have a navigation drawer, and once item is selected from the drawer, a corresponding fragment on the right side will display. Inside that fragment, I have linear layouts. I'd like to get redirected to another activity once that linear layout has been tapped. I was able to make it work on activities, by using android:onClick on XML file, but can't make it work on fragment. Somebody help me please.
App interface, refer to this image:
http://i.stack.imgur.com/u6dHi.jpg
Code:
fragment_smart.xml - the display once item's selected. I am trying to use the onClick on xml.
<LinearLayout
android:id="#+id/linearLayoutsmart1"
android:layout_width="match_parent"
android:layout_height="30dp"
android:layout_below="#+id/smart_title"
android:layout_marginLeft="15dp"
android:layout_marginTop="15dp"
android:orientation="horizontal"
android:weightSum="1"
android:onClick="smart_recommended_link">
Here's my Java code:
public class FragmentSmart extends Fragment {
public static final String TAG = "stats";
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myfragment = inflater.inflate(R.layout.fragment_smart, container, false);
return myfragment;
}
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
public void smart_recommended_link(View view) {
Intent smartRecommendedIntent = new Intent(this, SmartRecommended.class);
startActivity(smartRecommendedIntent);
}
}
The app is crashing when I clicked on the linearlayout using this code. What's the best thing to do here? Thank you!
For fragments you need to add the listener programmatically:
public class MyFragment extends Fragment implements View.OnClickListener {
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
view.findViewById(R.id.my_layout).setOnClickListener(this);
}
#Override
public void onClick(View v) {
// Handle click based on v.getId()
}
}
First of all, linear layouts cant trigger onClick events by default.
Check this answer for more information: LinearLayout onClick.
You can get the LinearLayout from the view in your onCreateView() like this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View myfragment = inflater.inflate(R.layout.fragment_smart, container, false);
LinearLayout layout = (LinearLayout)myFragment.findViewById(R.id.linearLayoutsmart1);
// here you can set a listener of any type you want to the layout
return myfragment;
}
In fragments, you must use getActivity() method instead of this to reference the activity that the fragment is attached to.
public void smart_recommended_link(View view) {
Intent smartRecommendedIntent = new Intent(getActivity(), SmartRecommended.class);
startActivity(smartRecommendedIntent);
}
I have the following Activity:
public class MainActivity extends ActionBarActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new StartFragment())
.commit();
}
Button login = (Button) findViewById(R.id.loginButton);
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this,LoginActivity.class);
startActivity(intent);
}
});
}
I get a NPE when I try to invoke findViewByID for R.id.loginButton, and I'm guessing this is because loginButton is within a separate Fragment, which I have as:
public static class StartFragment extends Fragment {
public StartFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_main, container, false);
}
}
However, I am unsure of how to fix this so that I can find the loginButton ID. I haven't worked with fragments before, so I realize I may be using them/implementing them incorrectly. fragment_main contains a few buttons in a LinearLayout, and activity_main has nothing but a single FrameLayout.
Try to implement your onCreateView(...) in Fragment like
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
View something = rootView.findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });
return rootView;
}
The Button is in the fragment layout (fragment_main.xml) and not in the activity layout (activity_main.xml). onCreate() is too early in the lifecycle to find it in the activity view hierarchy, and a null is returned. Invoking a method on null causes the NPE.
Write code to initialize button from fragment becuase your button is into fragment layout not into activity's layout.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_main, container,
false);
Button login = (Button) rootView.findViewById(R.id.loginButton);
login.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View arg0) {
Intent intent = new Intent(MainActivity.this,
LoginActivity.class);
startActivity(intent);
}
});
return rootView;
}
And remove the login button related code from onCreate of Activity.
findViewById() works with reference to a root view.
Without having a view in the first place will throw a null pointer exception
In any activity you set a view by calling setContentView(someView);.
Thus when you call findViewById() , its with reference to the someView.
Also findViewById() finds the id only if its in that someView. So in you case null pointer exception
For fragments, adapters, activity, .... any view's findViewById() will only find if the id exixts in the view
Alternately if you are inflating a view, then you can also use inflatedView.findViewById() to get a view from that inflatedView
In short make sure you have the id in your layout you are referring to or make findViewById() call in appropriate place(Ex. adapters getView(), activity's onCreate() or onResume() or onPause() , fragments onCreateView(), ....)
Also have an idea about UI & background thread's as you cannot efficiently update UI in bg-threads