I'm making a downloader app, and i got child's parents error after the onCreateView return section.
I tried a lots of things, but nothing help. I tried:
((ViewGroup)dlistView.getParent()).removeView(dlistView);
after that the compiler says:
01-12 12:05:15.558: E/AndroidRuntime(10740): java.lang.RuntimeException: Your content must have a ListView whose id attribute is 'android.R.id.list'
or remove the container/ i got an npe/ or remove the V but the V.getParent() is null.
Which view is the parent, and which is the child?
The code:
public static class DownloadingFragment extends ListFragment {
public static final String ARG_SECTION_NUMBER = "section_number";
public DownloadingFragment() {
}
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View V = inflater.inflate (R.layout.list, null);
ListView dlistView = (ListView) V.findViewById(R.id.dlist);
List<DownloadInfo> downloadInfo = new ArrayList<DownloadInfo>();
downloadInfo.add(new DownloadInfo("File", 1000));
DownloadInfoArrayAdapter diaa = new DownloadInfoArrayAdapter(
getActivity().getApplication(),R.layout.list, downloadInfo);
dlistView.setAdapter(diaa);
return dlistView; // throw exception
}
}
//...
}
Thank you in advance for your help.
EDIT:
the new code:
public static class DownloadingFragment extends Fragment {
public static final String ARG_SECTION_NUMBER = "section_number";
public DownloadingFragment() {
}
#Override
public View onCreateView (LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View V = inflater.inflate (R.layout.list, null);
return V;
}
#Override
public void onStart() {
// TODO Auto-generated method stub
super.onStart();
ListView dlistView = (ListView) getView().findViewById(R.id.dlist);
List<DownloadInfo> downloadInfo = new ArrayList<DownloadInfo>();
downloadInfo.add(new DownloadInfo("File", 1000));
DownloadInfoArrayAdapter diaa = new DownloadInfoArrayAdapter(
getActivity().getApplication(),R.layout.list, downloadInfo);
dlistView.setAdapter(diaa);
}
}
//...
}
that's because you return the listview which has a parent (somewhere in V's layout tree) .
for any type of fragment , onCreateView should return a view that doesn't have any parent .
in this case , since it's a listFragment , create the listview (either using xml or programmatically ) , and then return it . do not allow it to have any parents since it needs to have a parent as a fragment .
read here for more information :
The system calls this when it's time for the fragment to draw its
user interface for the first time. To draw a UI for your fragment, you
must return a View from this method that is the root of your
fragment's layout. You can return null if the fragment does not
provide a UI.
Note that the third argument of inflate is false.
so replace this line
View V = inflater.inflate (R.layout.list, null);
with this
View V = inflater.inflate (R.layout.list, null,false);
and you are done.hope this helps.
Related
I'm new to fragments and they are a bit confusing to me right now.
I have read alot and actually built a test app using this tutorial
I was able to add more tabs and so on. But I don't want to display static
content (xmls). I want to be able to modify the UI add listviews, load json data with asynctasck and so on but the very first attempt I made has failed.
So on Tab1:
public class Tab1 extends Fragment {
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.tab_1,container,false);
return v;
}
}
I tried to declare a textview and edit it's text with settext but
I get a crash and also findviewbyid is not available as a method.
It didn't throw and error when I typed getView().findViewById but
that's not the problem.
Are fragments limited or very different from traditional activities?
Do I need to make never ending customizations
in order to get a listview loading?
Thank you.
You should declare your TextView as a Member variable for your class and get a reference to that view in onCreateView like:
public class Tab1 extends Fragment {
// your TextView member variable
private TextView mTextView;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, #Nullable Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.tab_1, container, false);
mTextView = (TextView) v.findViewById(R.id.my_text_view);
return v;
}
// It is safe to access the views here, not in onCreate since it is called before onCreateView
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
mTextView.setText("Hello, World!");
}
}
For reference on the Fragment lifecycle
Are fragments limited or very different from traditional activities?
A Fragment has its own lifecycle like that of an Activity but it is dependent upon the lifecycle of its parent Activity, so to answer your question they are not very different, it is just a good practice for modularizing code.
Do I need to make never ending customizations in order to get a listview loading?
A little unclear what you mean by never ending customizations but loading a dynamic ListView is very simple in Android.
For Example:
using fragment_one.xml we have just a FrameLayout
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/fl_list_container"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
And the code:
public class Tab1 extends Fragment {
// your FrameLayout member variable
private FrameLayout mFlParent;
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, #Nullable Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_one, container, false);
mFlParent = (FrameLayout) v.findViewById(R.id.fl_list_container);
return v;
}
// It is safe to access the views here, not in onCreate since it is called before onCreateView
#Override
public void onActivityCreated(Bundle savedInstanceState){
super.onActivityCreated(savedInstanceState);
// Create a new list, adapter and add a item click listener
ListView myList = new ListView(context); // context needed
myList.setAdapter(new ArrayAdapter<String>(context, android.R.id.simple_list_item_1, new String []{"Item 1", "Item 2", "Item 3", "Item 4"}));
myList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
//... handle clicks on list items as usual
}
});
// add the view to the FrameLayout
mFlParent.addView(myList); // may want to call mFlParent.removeAllViews(); before adding just to be safe
}
}
Good luck and happy coding.
Fragment doesn't have an activity methods.
If you want to use findViewById methods, you should use a class field.
public class Tab1 extends Fragment {
private View mView;
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
mView = inflater.inflate(R.layout.tab_1,container,false);
return mView;
}
#Override
public void onCreate() {
// here you can use `findViewById` method via `mView` variable
mView.findViewById(YOUR_VIEW_ID);
}
}
There is basically 2 method to be use in fragment
1. onCreateView
2. onViewCreated
First one is use to inflate layout on your fragment
Second one is use to find all view used in your layout.
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, #Nullable Bundle savedInstanceState) {
View v =inflater.inflate(R.layout.tab_1, container, false);
return v;
}
#Override
public void onViewCreated(View v){
mTextView = (TextView) v.findViewById(R.id.my_text_view);
mTextView.setText("Hello, World!");
}
How do I find the fragment that includes a particular view?
It is easy to find a view knowing the fragment and the view id:
fragment.getView().findViewById(R.id.foo)
But what I want to do is the reversal: knowing the view, find the fragment.
So far the best idea is:
I. Set the root view's tag to the fragment that inflated it:
// in a class that extends Fragment:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_id, container, false);
rootView.setTag(this); // <======= !!!
return rootView;
}
II. Then, it's relatively easy to find that root view among view's parents:
// in some utility class:
public static Fragment getTagFragment(View view) {
for (View v = view; v != null; v = (v.getParent() instanceof View) ? (View)v.getParent() : null) {
Object tag = v.getTag();
if (tag != null && tag instanceof Fragment) {
return (Fragment)tag;
}
}
return null;
}
III. Usage:
Fragment f = getTagFragment(view);
PS I can even place an onClick handler into a Fragment, and it works!
I have an Activity with a layout made of two Fragment. One of the fragments layout contains a GridView.
public class PicturesGallery extends FragmentActivity {
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.gallery_frames);
Fragment galleryFragment = fm.findFragmentById(R.id.gallery_fragment);
if (galleryFragment == null) {
fm.beginTransaction().add(R.id.gallery_fragment, new GalleryFragment()).commit();
}
...
When this fragment is called it instantiates the adapter for the GridView, inflates the GridView and sets the adapter (which has been fed with an ArrayList with 15 items to be displayed).
public class GalleryFragment extends Fragment {
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
// Initialize the layout Adapter
PicturesGallery pga = (PicturesGallery) getActivity();
mAdapter = new ImageAdapter(pga.getApplication(), pga.mMemoryCache,
galleryDir);
mAdapter.mPictureNames = new ArrayList<String>(pictureNames);
// Inflate the layout and set the adapter
LayoutInflater inflater = (LayoutInflater) getActivity()
.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
GridView gridView = (GridView) inflater.inflate(R.layout.pictures_gallery_fragment, null);
gridView.setAdapter(mAdapter);
}
....
But when the activity is executed nothing happens (no errors in the logcat but nothing displayed on the GridView). In the adapter class, using Log.d() inside getView() indicates that the method is never called. However getCount() returns the right number of items. The relevant code follows.
public class ImageAdapter extends BaseAdapter {
...
public ArrayList<String> mPictureNames = new ArrayList<String>();
public ImageAdapter(Context c, LruCache<String, Bitmap> mCache, File gallery) {
mMemoryCache = mCache;
mGalleryDir = gallery;
mInflater = (LayoutInflater) c.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
}
public View getView(int position, View convertView, ViewGroup parent) {
Log.d("dupa", "getView");
ViewHolder vh;
View cell = convertView;
if (cell == null) {
vh = new ViewHolder();
cell = mInflater.inflate(R.layout.galleryitem, null);
// Populate the ViewHolder
vh.checkBox = (CheckBox) cell.findViewById(R.id.itemCheckBox);
...
cell.setTag(vh);
...
} else {
vh = (ViewHolder) cell.getTag();
}
// Update the cell View state
vh.checkBox.setTag(position);
...
return cell;
}
I've not included xml files because the post is lengthy. If you think they are needed (or you need more code) just tell me. Any help will be greatly appreciated. TIA.
You need to implement onCreateView() in your fragment and return the inflated layout there (note to use the inflate method that doesnt add the inflated view to the container.
http://developer.android.com/reference/android/app/Fragment.html#onCreateView(android.view.LayoutInflater, android.view.ViewGroup, android.os.Bundle)
Just like in the example at the top of the page
/**
* The Fragment's UI is just a simple text view showing its
* instance number.
*/
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.hello_world, container, false);
View tv = v.findViewById(R.id.text);
((TextView)tv).setText("Fragment #" + mNum);
tv.setBackgroundDrawable(getResources().getDrawable(android.R.drawable.gallery_thumb));
return v;
}
There you can remember the instance to the GridView and then in onActivityCreated (or potentially in onAttach() you can set the adapter.
I am trying to implement Fragments in my Project using the https://github.com/JakeWharton/Android-ViewPagerIndicator plugin.
My Fragment
public final class NearByFragment extends Fragment {
private static String TAG = "bMobile";
public static NearByFragment newInstance(String content) {
NearByFragment fragment = new NearByFragment();
return fragment;
}
#Override
public void onCreate(Bundle savedInstanceState) {
Log.d(TAG, "onCreate");
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
return inflater.inflate(R.layout.fragment_search_nearby, null);
}
}
Now I want to execute some code like start start a new thread and download a JSON from the server and then assign a list view from the content I download. In fragments we are assigning views in onCreateView. so where should I write the
listview = (ListView) findViewById(R.id.list_items);
((TextView) findViewById(R.id.title_text))
.setText(R.string.description_blocked);
and other code to generate the Fragment view ?
You can search within the view that you just inflated:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Log.d(TAG, "onCreateView");
View v = inflater.inflate(R.layout.fragment_search_nearby, null);
listview = (ListView)v.findViewById(R.id.list_items);
((TextView)v.findViewById(R.id.title_text))
.setText(R.string.description_blocked);
return v;
}
You can also use the Fragment.getView() function elsewhere in your code and call that view's findViewById() member.
You can use getActivity().findByView() from the Fragment if you want to have access to the layout elements. Alternatively, you can just put the findByView() call in the main Activity.
I'm working on 3.0 for the first time.
I want to add fragments dynamically , but its shown error:-
10-18 18:29:11.215: ERROR/AndroidRuntime(3550): java.lang.IllegalStateException: The specified child already has a parent. You must call removeView() on the child's parent first.
XML code
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/frags">
<ListView
android:id="#+id/number_list"
android:layout_width="250dip"
android:layout_height="match_parent" />
<FrameLayout
android:id="#+id/the_frag"
android:layout_width="match_parent"
android:layout_height="match_parent" />
Activity
public class FragmentExampleActivity extends Activity implements
OnItemClickListener {
/** Called when the activity is first created. */
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
ListView l = (ListView) findViewById(R.id.number_list);
ArrayAdapter<String> numbers = new ArrayAdapter<String>(
getApplicationContext(), android.R.layout.simple_list_item_1,
new String[] { "one", "two", "three", "four", "five", "six" });
l.setAdapter(numbers);
l.setOnItemClickListener(this);
}
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Fragment f = new FragmentExample();
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.the_frag, f);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.addToBackStack(null);
ft.commit();
}
}
public class FragmentExample extends Fragment {
private int nAndroids=1;
public FragmentExample() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle saved) {
int n;
View l = inflater.inflate(R.layout.textlay,container);
TextView tv = (TextView) l.findViewById(R.id.textView1);
tv.setText("value "+nAndroids);
return l;
}
}
Plz put some light on , where i'm going wrong :(
Instead of using
View l = inflater.inflate(R.layout.textlay,container);
you should use
View l = inflater.inflate(R.layout.textlay, container, false);
or alternatively
View l = inflater.inflate(R.layout.textlay, null );
I strongly suggest you to use, the version of inflate which takes three parameters. Android use the container only for the layout's params purpose. Passing false as third parameter, prevents the addition of textlay, to container and fixes your issue
if in onCreateView() , you want to reuse view , then you can deal with onDestroyView()
#Override
public void onDestroyView() {
super.onDestroyView();
if (view != null) {
ViewGroup parentViewGroup = (ViewGroup) view.getParent();
if (parentViewGroup != null) {
parentViewGroup.removeAllViews();
}
}
}
or you can use third argument and pass it as a false.
inflater.inflate(R.layout.sample_fragment, container,false);
it means that don't attach current view to root.
The following solves multiple problems:
public static View contentView;
public static class MenuCardFrontFragment extends Fragment {
public MenuCardFrontFragment() {
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
try
{
LinearLayout frontLayout = (LinearLayout)inflater.inflate(R.layout.content_card, container, false);
// modify layout if necessary
return contentView = frontLayout;
} catch (InflateException e) {
return contentView;
}
}
#Override
public void onDestroyView() {
super.onDestroyView();
if (contentView != null) {
ViewGroup parentViewGroup = (ViewGroup) contentView.getParent();
if (parentViewGroup != null) {
parentViewGroup.removeAllViews();
}
}
}
}
If fragments shows with animation and user forced to repeat the animation before it's over. ex: pressing menu button twice before the menu fragment finishes animating and appear. (this got solved by the onDestryView from user2764384)
If inflate method called with specifying the container to get container lay outing. it looks like that cases a problem because at the 2nd time that happened the old container already have a view. so if that exception happen it will be caught and returns the old view contentView.