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'm trying to use Google's Place Autocomplete (https://developers.google.com/places/android-api/autocomplete) to implement into a fragment in my app. But I got this error when navigating from this fragment to other fragment and then coming back into this fragment
Caused by: java.lang.IllegalArgumentException: Binary XML file line #30: Duplicate id 0x7f0f010f, tag null, or parent id 0x7f0f00c0 with another fragment for com.google.android.gms.location.places.ui.PlaceAutocompleteFragment
at android.app.FragmentManagerImpl.onCreateView(FragmentManager.java:2148)
This is the java code
private PlaceAutocompleteFragment mSearchPAF;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.some_layout, container, false);
mSearchLocationPAF = (PlaceAutocompleteFragment) parentActivity.getFragmentManager().findFragmentById(R.id.place_autocomplete_fragment);
}
This is the XML file
<RelativeLayout
android:id="#+id/someRelativeLayout"
android:layout_width="wrap_content"
android:layout_height="36dp"
android:layout_marginLeft="10dp"
android:layout_marginRight="10dp"
android:layout_marginTop="30dp"
android:background="#drawable/some_drawable">
<fragment
android:id="#+id/place_autocomplete_fragment"
android:name="com.google.android.gms.location.places.ui.PlaceAutocompleteFragment"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</RelativeLayout>
I also have another map fragment in this same java and xml file but I managed to solve it by following the answers in this post (Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment) but I can't find any solution for this Place Autocomplete fragment
I have solved this problem myself by following one of the answers here
Duplicate ID, tag null, or parent id with another fragment for com.google.android.gms.maps.MapFragment
private PlaceAutocompleteFragment mSearchPAF;
#Override
public void onDestroyView() {
super.onDestroyView();
PlaceAutocompleteFragment f = (PlaceAutocompleteFragment) getFragmentManager()
.findFragmentById(R.id.place_autocomplete_fragment);
if (f != null)
getFragmentManager().beginTransaction().remove(f).commit();
}
I am currently working on my Project and implemented my main activity with multiple fragments(Still a novice in Android Studio). In my Navigation fragment, I used an googleAutocomplete provided by Google.
This code here is implemented inside the OnCreateView().
mPlace_Starting = (PlaceAutocompleteFragment)
this.getChildFragmentManager().findFragmentById(R.id.place_starting);
AutocompleteFilter countryFilter = new AutocompleteFilter.Builder()
.setTypeFilter(Place.TYPE_COUNTRY)
.setCountry("PH")
.build();
mPlace_Starting.setFilter(countryFilter);
mPlace_Starting.setOnPlaceSelectedListener(new PlaceSelectionListener() {
#Override
public void onPlaceSelected(Place place) {
// TODO: Get info about the selected place:
Log.i(TAG, "Starting Place:" + place.getName());
Toast.makeText(getActivity(), "Starting Place: " + place.getName(),
Toast.LENGTH_SHORT).show();
double mLongitude = place.getLatLng().longitude;
double mLatitude = place.getLatLng().latitude;
mStartingpoint = new Waypoint(mLongitude,mLatitude);
mStartpoint = new GeoCoordinate(mLongitude,mLatitude);
}
#Override
public void onError(Status status) {
//TODO: Handle the Error.
Log.i(TAG, "An error occured: " + status);
}
});
I inflated my PlaceAutocompleteFragment widget from Google by using getChildFragmentManager() since the widget is a fragment to be inflated inside another fragment(my Navigation Fragment).
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;
I'm new to Android development and I'm searching the best way to manage multiple webview at the same time.
My goal is to create a simple web browser with a small menu listing my opened "tab" and replace my content section inside my layout by the user's selection. I don't want to have visible tab like Chrome and the stock browser on a tablet.
What is the best way to manage multiple view, switch between them and keep their state ?
Should I use multiple fragment (each one containing a webview) and FragmentManager to replace the content ?
Manage manually a framelayout by removing and adding the selected webview ?
UPDATE:
I played with Fragment today and I made a quick test project.
Here some code:
My Layout:
<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" >
<FrameLayout android:id="#+id/fff"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
</FrameLayout>
</RelativeLayout>
My Activity:
public class MainActivity extends Activity {
private static final String TAG = "MainActivity";
private WebFragment f1;
private WebFragment f2;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
f1 = new WebFragment();
f2 = new WebFragment();
FragmentManager fragmentManager = getFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.fff, f1);
fragmentTransaction.commit();
f1.loadUrl("file:///android_asset/home.html");
f2.loadUrl("file:///android_asset/home.html");
}
private void switchFragment(int id) {
FragmentTransaction trx = getFragmentManager().beginTransaction();
if (id == 1) {
trx.replace(R.id.fff, f1);
} else if (id == 2) {
trx.replace(R.id.fff, f2);
}
trx.commit();
}
...
}
My Fragment:
<?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" >
<WebView android:id="#+id/ww"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
/>
</LinearLayout>
public class WebFragment extends Fragment {
private static final String TAG = "WebFragment";
private View v = null;
private WebView ww;
private String url = null;;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
Log.d(TAG, "WebFragment onCreateView");
if (v == null) {
v = inflater.inflate(R.layout.test_layout, container, false);
this.ww = (WebView) v.findViewById(R.id.ww);
// WebView settings here...
this.ww.setWebViewClient(new WebViewClient());
if (this.url != null)
this.ww.loadUrl(url);
}
return v;
}
public void loadUrl(String url) {
this.url = url;
if (this.ww != null)
this.ww.loadUrl(url);
}
}
By using the menu of my activity, I can switch to one or the other fragments.
Obviously, I will need some kind of controller to manage the fragment and a "bridge" to populate the activity's events but it seems to be ok.
Is it ok to keep multiple fragments (each one contain a webview) like that in memory ?
I need to keep the webview and their content but the fragment is only use in this case as a container.
Still open to some comments or suggestions.
Thanks again for your help.
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;
}
}