I have a working "folder explorer" for the internal storage only. But I want to improve that explorer to use internal and external storage. I want something like this:
Bellow is the basic code of my working folder explorer (internal storage only):
select_folders.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listView"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.fragment.app.FragmentContainerView
android:id="#+id/fragmentContainer"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
SelectFoldersActiviy.java
public class SelectMusicFolderActivity extends AppCompatActivity {
...
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (savedInstanceState == null) {
getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentContainer, FoldersFragment.newInstance(path))
.commit();
}
}
...
}
The following fragment class uses a ListView to list all subfolders for a folder. In the onItemClick() method of the ListView objet I instantiate a new fragment with the current item path as argument. Like so:
FoldersFragment.java
public class FoldersFragment extends Fragment {
...
private static final String RUTA_KEY = "ruta";
private final String PATH = "ruta";
public static FoldersFragment newInstance(String ruta) {
FoldersFragment fragment = new FoldersFragment();
Bundle args = new Bundle();
args.putString(RUTA_KEY, ruta);
fragment.setArguments(args);
return fragment;
}
...
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
ruta = getArguments().getString(RUTA_KEY);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
...
#Override
public void onItemClick(AdapterView<?> adapterView, View view, int i, long l) {
getActivity().getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentContainer,
FoldersFragment.newInstance(currentItemPath))
.addToBackStack(null)
.commit();
}
}
}
With what I can combine the viewpager2 to get the aproach of the image?
I tried with fragmentcontainerview inside viewpager2, but viewpager2 dont support direct childs.
I tried with NavHostFragment too, but that needs a predefined navigation graph, and the number of folders is undetermined.
I have two fragments and a main activity. The first fragment, FriendsFragment (extends ListFragment) is displayed on the screen and when the user clicks an item, the main Activity replaces the FriendsFragment with FeedFragment, then calls a method from FeedFragment to update the textView.
I'm getting an error that the textView object in the FeedFragment class is null even though I instantiate using the findViewById method.
I have looked at related questions and have tried the solutions but nothing is working. I've tried doing getView().findViewById(R.id.feed_view), getActivity().findViewById(R.id.feed_view), and I've tried putting these in onActivityCreated() and onCreateView()
The only thing that worked is writing this code in onActivityCreated():
text = getView().findViewById(R.id.feed_view);
text.setText("some string");
but this is not what I want
FeedFragments.java
public class FeedFragment extends Fragment {
private static String TAG = "Feed Fragment";
private TextView text;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
Log.i(TAG, "Entered FeedFragment.java onActivityCreated()");
super.onActivityCreated(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.i(TAG, "Entered FeedFragment.java onCreateView()");
View v = inflater.inflate(R.layout.fragment_feed, container,false);;
text = v.findViewById(R.id.feed_view);
return v;
}
public void updateDisplay(int position)
{
Log.i(TAG, "FeedFragment.java: updateDisplay()");
text.setText(position);
}
MainActivity.java
// previously declared feedFragment: feedFragment = new FeedFragment();
public void onItemSelected(int position)
{
Log.i(TAG, "Entered onItemSelected(" + position + ")");
fragManager = getFragmentManager();
FragmentTransaction fragTransaction = fragManager.beginTransaction();
fragTransaction.replace(R.id.fragment_container, feedFragment, "feed_fragment");
fragTransaction.addToBackStack(null);
fragTransaction.commit();
feedFragment.updateDisplay(position);
}
fragment_feed.xml
<ScrollView xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<TextView
android:id="#+id/feed_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="#string/greeting" />
</ScrollView>
activity_main.xml
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fragment_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
onViewcreated() is being called after updateDisplay() because the system doesn't run it right away.
you need to pass a value to the feedFragment on it's creation and store it in a bundle and retrieve it after the fragment internal code is run by the system.
the way you solve it is like this:
when you instantiate feedFragment you do it like this:
feedFragment = FeedFragment.newInstance(position)
and inside FeedFragment class you should have a static code:
private static final String ARG_PARAM1 = "param1";
public static FeedFragment newInstance(int param1) {
FeedFragment fragment = new FeedFragment();
Bundle args = new Bundle();
args.putInt(ARG_PARAM1, param1);
fragment.setArguments(args);
return fragment;
}
and non static code:
private int mParam1;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (getArguments() != null) {
mParam1 = getArguments().getInt(ARG_PARAM1);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
Log.i(TAG, "Entered FeedFragment.java onCreateView()");
View v = inflater.inflate(R.layout.fragment_feed, container,false);;
text = v.findViewById(R.id.feed_view);
text.setText(String.valueOf(mParam1));
return v;
}
I wrapped mParam1 in String.valueOf() because when you pass integer to setText it thinks you try to use a Strings.xml resource instead of the number you chose.
also I used very generic variable names. please change them to something meaningful so that your code makes sense.
I want to have an information fragment on a sliding tab fragment, and be able to pass in some parameter to the information fragment when creating it. I've put (hopefully all) the relevant code below:
InfoFragment.java:
public class InfoFragment extends Fragment
{
private static final String HEADER_ARG = "HEADER";
private String header;
/**
* Use this factory method to create a new instance of this fragment using the provided parameters.
*/
public static InfoFragment newInstance(String header)
{
InfoFragment fragment = new InfoFragment ();
Bundle args = new Bundle();
args.putString(HEADER_ARG, header);
fragment.setArguments(args);
return fragment;
}
/**
* Required empty public constructor
*/
public InfoFragment() {}
/**
* Gets the args out of the bundle
*/
#Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if (getArguments() != null)
{
this.header = getArguments().getString(HEADER_ARG);
}
else
{
Log.e("Frag", "Args = null");
}
}
// Rest of Fragment...
}
TabFragment.java:
public class TabFragment extends Fragment
{
/**
* Mandatory empty constructor for the fragment manager to instantiate the fragment (e.g. upon screen orientation changes).
*/
public TabFragment() {}
/**
* Create the Inventories tab view
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View view = inflater.inflate(R.layout.tab_2, container, false);
return view;
}
/**
* Dynamically add fragments
*/
#Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
InfoFragment infoFrag = InfoFragment.newInstance("MyHeader");
FragmentManager fm = getChildFragmentManager();
fm.beginTransaction().add(R.id.info_fragment, infoFrag);
}
// Rest of TabFragment...
}
tab_fragment.xml:
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<fragment
android:id="#+id/character_item_fragment_1"
android:name="lomax.mycharacter3.InfoFragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
The console log shows that the getArgs() = null, and I have no idea why. Any ideas?
Because you're using fragment tag, your fragment will be instantiated using empty constructor, also you forgot to call commit at the end on transaction, that's why you didn't get exception about view with missing id.
Fixed version:
tab_fragment.xml:
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/info_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
Code:
InfoFragment infoFrag = InfoFragment.newInstance("MyHeader");
FragmentManager fm = getChildFragmentManager();
fm.beginTransaction().add(R.id.info_fragment, infoFrag).commit();
This is a canonical question for a problem frequently posted on StackOverflow.
I'm following a tutorial. I've created a new activity using a wizard. I get NullPointerException when attempting to call a method on Views obtained with findViewById() in my activity onCreate().
Activity onCreate():
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
Layout XML (fragment_main.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"
android:paddingBottom="#dimen/activity_vertical_margin"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
tools:context="packagename.MainActivity$PlaceholderFragment" >
<View
android:layout_width="100dp"
android:layout_height="100dp"
android:id="#+id/something" />
</RelativeLayout>
The tutorial is probably outdated, attempting to create an activity-based UI instead of the fragment-based UI preferred by wizard-generated code.
The view 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.
The preferred solution is to move the code to the fragment onCreateView(), calling findViewById() on the inflated fragment layout rootView:
#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); // not activity findViewById()
something.setOnClickListener(new View.OnClickListener() { ... });
return rootView;
}
As a side note, the fragment layout will eventually be a part of the activity view hierarchy and discoverable with activity findViewById() but only after the fragment transaction has been run. Pending fragment transactions get executed in super.onStart() after onCreate().
Try OnStart() method and just use
View view = getView().findViewById(R.id.something);
or Declare any View using getView().findViewById method in onStart()
Declare click listener on view by anyView.setOnClickListener(this);
Try to shift your accessing views to the onViewCreated method of fragment because sometimes when you try to access the views in onCreate method they are not rendered at the time resulting null pointer exception.
#Override
public void onViewCreated(View view, #Nullable Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
Agreed, this is a typical error because people often don't really understand how Fragments work when they begin working on Android development. To alleviate confusion, I created a simple example code that I originally posted on Application is stopped in android emulator , but I posted it here as well.
An example is the following:
public class ContainerActivity extends FragmentActivity implements ExampleFragment.Callback
{
#Override
public void onCreate(Bundle saveInstanceState)
{
super.onCreate(saveInstanceState);
this.setContentView(R.layout.activity_container);
if (saveInstanceState == null)
{
getSupportFragmentManager().beginTransaction()
.add(R.id.activity_container_container, new ExampleFragment())
.addToBackStack(null)
.commit();
}
getSupportFragmentManager().addOnBackStackChangedListener(new OnBackStackChangedListener()
{
public void onBackStackChanged()
{
int backCount = getSupportFragmentManager().getBackStackEntryCount();
if (backCount == 0)
{
finish();
}
}
});
}
#Override
public void exampleFragmentCallback()
{
Toast.makeText(this, "Hello!", Toast.LENGTH_LONG).show();
}
}
activity_container.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"
android:orientation="vertical" >
<FrameLayout
android:id="#+id/activity_container_container"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</RelativeLayout>
ExampleFragment:
public class ExampleFragment extends Fragment implements View.OnClickListener
{
public static interface Callback
{
void exampleFragmentCallback();
}
private Button btnOne;
private Button btnTwo;
private Button btnThree;
private Callback callback;
#Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
try
{
this.callback = (Callback) activity;
}
catch (ClassCastException e)
{
Log.e(this.getClass().getSimpleName(), "Activity must implement Callback interface.", e);
throw e;
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
View rootView = inflater.inflate(R.layout.fragment_example, container, false);
btnOne = (Button) rootView.findViewById(R.id.example_button_one);
btnTwo = (Button) rootView.findViewById(R.id.example_button_two);
btnThree = (Button) rootView.findViewById(R.id.example_button_three);
btnOne.setOnClickListener(this);
btnTwo.setOnClickListener(this);
btnThree.setOnClickListener(this);
return rootView;
}
#Override
public void onClick(View v)
{
if (btnOne == v)
{
Toast.makeText(getActivity(), "One.", Toast.LENGTH_LONG).show();
}
else if (btnTwo == v)
{
Toast.makeText(getActivity(), "Two.", Toast.LENGTH_LONG).show();
}
else if (btnThree == v)
{
callback.exampleFragmentCallback();
}
}
}
fragment_example.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" >
<Button
android:id="#+id/example_button_one"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:layout_centerHorizontal="true"
android:layout_marginTop="30dp"
android:text="#string/hello"
android:layout_marginLeft="20dp"
android:layout_marginRight="20dp"/>
<Button
android:id="#+id/example_button_two"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/example_button_one"
android:layout_alignRight="#+id/example_button_one"
android:layout_below="#+id/example_button_one"
android:layout_marginTop="30dp"
android:text="#string/hello" />
<Button
android:id="#+id/example_button_three"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignLeft="#+id/example_button_two"
android:layout_alignRight="#+id/example_button_two"
android:layout_below="#+id/example_button_two"
android:layout_marginTop="30dp"
android:text="#string/hello" />
</RelativeLayout>
And that should be a valid example, it shows how you can use an Activity to display a Fragment, and handle events in that Fragment. And also how to communicate with the containing Activity.
The view "something" is in fragment and not in activity, so instead of accessing it in activity you must access it in the fragment class like
In PlaceholderFragment.class
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View root = inflater.inflate(R.layout.fragment_main, container,
false);
View something = root .findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... });
return root;
}
You are trying to access UI elements in the onCreate() but , it is too early to access them , since in fragment views can be created in onCreateView() method.
And onActivityCreated() method is reliable to handle any actions on them, since activity is fully loaded in this state.
Add the following in your activity_main.xml
<fragment
android:id="#+id/myFragment"
android:name="packagename.MainActivity$PlaceholderFragment"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</fragment>
Since you have declared your View in the fragment_main.xml,move that piece of code where you get the NPE in the onCreateView() method of the fragment.
This should solve the issue.
in the posted code above in the question there is a problem :
you are using R.layout.activity_main in oncreate method, but the xml files name is "fragment_main.xml" , means you are trying to get the view of fragment_main.xml file which is not being shown so it gives null pointer exception. change the code like :
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);// your xml layout ,where the views are
View something = findViewById(R.id.something);
something.setOnClickListener(new View.OnClickListener() { ... }); // NPE HERE
if (savedInstanceState == null) {
getSupportFragmentManager().beginTransaction()
.add(R.id.container, new PlaceholderFragment()).commit();
}
}
You have to remember important thing is :
NullPointerException occurs when you have declared your variable and trying to retreive its value before assigning value to it.
Use onViewCreated() Method whenever using or calling views from fragments.
override fun onViewCreated(view: View?, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
View v = view.findViewById(R.id.whatever)
}
I've got the same NullPointerException initializing a listener after calling findViewById() onCreate() and onCreateView() methods.
But when I've used the onActivityCreated(Bundle savedInstanceState) {...} it works. So, I could access the GroupView and set my listener.
I hope it be helpful.
Most popular library for finding views which is used by almost every developer.
ButterKnife
As I can their are enough answers explaining finding views with proper methodology. But if you are android developer and code frequently on daily basis then you can use butter-knife which saves a lot time in finding views and you don't have write code for it, With in 2-3 steps you can find views in milliseconds.
Add dependency in app level gradle:
implementation 'com.jakewharton:butterknife:8.8.1'
annotationProcessor 'com.jakewharton:butterknife-compiler:8.8.1'
Add plugin for butter knife:
File -> Settings -> plugins->
Then search for Android ButterKnife Zelezny and install plugin and restart your studio and you are done with it.
Now just go to Oncreate method of your activity and right click on your layout_name and tap on generate button and select butterknife injection option and your views references will be automatically created like mention below:
#BindView(R.id.rv_featured_artist)
ViewPager rvFeaturedArtist;
#BindView(R.id.indicator)
PageIndicator indicator;
#BindView(R.id.rv_artist)
RecyclerView rvArtist;
#BindView(R.id.nsv)
NestedScrollingView nsv;
#BindView(R.id.btn_filter)
Button btnFilter;
The gist of the issue is this: I'm trying to Launch a DialogFragment from a FragmentActivity. This DialogFragment's view contains a FrameLayout which I would like to populate with a Fragment. Basically the FragmentActivity launches the DialogFragment, then the DialogFragment populates it's FrameLayout with a Fragment. I've scoured the internet for tutorials and I've pieced together something that (in my mind) should work. However, no matter what I try I continuously get errors. This is what I have so far:
This is my FragmentActivity's layout (file name is "activity_interact"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/activity" >
<Button
android:id="#+id/btnLaunchDialog"
style="#style/btn" />
This is my DialogFragment's layout (file name is "dialog_overview"):
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/dialog" >
<FrameLayout
android:id="#+id/frameDisplay"
style="#style/frame" />
This is my Fragment's layout (file name is "fragment_stats"):
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
style="#style/table" >
<TableRow style="#style/table" >
<TextView
style="#style/display"
android:gravity="right"
android:text="#string/textStr" />
</TableRow>
Here is the java code for my FragmentActivity:
public class ActivityInteract extends FragmentActivity implements
OnClickListener {
Button btnLaunchDialog;
#Override
protected void onCreate(Bundle b) {
super.onCreate(b);
setContentView(R.layout.activity_interact);
btnLaunchDialog = (Button) findViewById(R.id.btnLaunchDialog);
btnLaunchDialog.setOnClickListener(this);
}
public void onClick(View v) {
switch (v.getId()) {
case R.id.btnLaunchDialog:
FragmentManager fm = getSupportFragmentManager();
DialogOverview dialogOverview = new DialogOverview();
dialogOverview.show(fm, "dialog_overview");
break;
}
}
}
Here is my DialogFragment's code:
public class DialogOverview extends DialogFragment implements OnClickListener {
public DialogOverview() {
}
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.dialog_overview, vg);
FragmentManager fm = getFragmentManager();
FragmentTransaction ft = fm.beginTransaction();
ft.add(R.id.frameDisplay, new FragmentStats());
ft.commit();
return view;
}
}
Here is my Fragment's code:
public class FragmentStats extends Fragment {
#Override
public View onCreateView(LayoutInflater li, ViewGroup vg, Bundle b) {
View view = li.inflate(R.layout.fragment_stats, vg);
return view;
}
}
And finally, here is the logcat error:
06-11 10:07:29.382: E/AndroidRuntime(30013): java.lang.IllegalArgumentException: No view found for id 0x7f060003 for fragment FragmentStats{4169c928 #1 id=0x7f060003}
I can see that it's saying that I don't have a view for the Fragment, but I do...(or do I?) I'm lost here, any help would be appreciated. Also, am I going about this the right way? Would it be more efficient to re-use a FragmentManager? (i.e. pass it from the FragmentActivity into the DialogFragment)
Update: I removed the code to load my Fragment into the DialogFragment and the DialogFragment displays without issue now. So obviously (as the logcat error suggests) there is something wrong with my Fragment itself...however, it matches examples that I've seen on the internet. Which is making me wonder: Is there an issue with nesting fragments in this way? A FragmentActivity displaying a DialogFragment that displays a Fragment makes me want to quip that "we can't go any deeper" but I don't know. Could I nest more fragments?
Actually, you CAN add nested fragment to a DialogFragment, BUT it cannot be based on a wrapped Dialog.
Instead of overriding onCreateDialog, inflate the View that contains the ViewGroup that will use the Fragment in onCreateView.
A consequence of this is that you cannot use a DialogFragment that wraps an AlertDialog - so if you want positive and negative buttons you need to manually create them in the content view.
Also, keep in mind that you cannot set the fragment in the XML. You need to declare the container view in XML and perform the fragment transaction programmatically.
public class MyDialogFragment extends DialogFragment {
[...]
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View view = inflater.inflate(R.layout.dialog_layout, container);
if (savedInstanceState == null) {
final ChildFragment fragment = [...];
getChildFragmentManager()
.beginTransaction()
.replace(R.id.fragment_container, fragment)
.commit();
}
return view;
}
}