Introduction
I want to have different fragments depending on the user's choice from the navigation drawer. For every item in the navigation drawer is a different site that should called. I want to achieve this by fragments.
The main fragment will get displayed first by doing the following in the onCreate-method
if ( savedInstanceState == null ) {
FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
Fragment fragment = MainFragment.passList(hashMap);
ft.replace(R.id.content_frame, new MainFragment());
ft.commit();
}
As you can see I'm passing data (a hashmap) to a method of the fragment which works perfectly. The data then gets put into string arrays. This works and the data can also be used in the onCreate-method of the fragment.
This is the xml of the main activity
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<FrameLayout
android:id="#+id/content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<ListView
android:id="#+id/left_drawer"
android:layout_width="240dp"
android:layout_height="match_parent"
android:layout_gravity="start"
android:choiceMode="singleChoice"
android:divider="#android:color/darker_gray"
android:dividerHeight="0.1dp" />
</android.support.v4.widget.DrawerLayout>
And this is the XML of the main fragment
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainRelativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?android:attr/activatedBackgroundIndicator"
android:gravity="center_vertical"
android:paddingRight="10dp"
>
<ScrollView
android:id="#+id/mainScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1.03" >
<LinearLayout
android:id="#+id/mainListView"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
</LinearLayout>
</ScrollView>
</RelativeLayout>
What I want to achieve now is to display data in the scroll view. But the question I have, is how do I populate that and how do I initliaze it?
Normally I would go and just create them like that - because findViewById doesn't work in a Fragment (Have to write the method by my own)
ScrollView sv = new ScrollView(getActivity());
LinearLayout ll = new LinearLayout ( getActivity() );
sv.addView(ll);
ll.addView(SAMPLETEXTVIEW);
View mainView = inflater
.inflate(R.layout.main_fragment, container, false);
return mainView;
but that just leaves me with a blank screen and no actual text added. How can I achieve this? The data is there - I know that, because it gets printed out with System.out.println - I just need a way to display it. Thx in advance!
EDIT
When I do it like that
View rootView = inflater.inflate(R.layout.main_fragment, container, false);
ScrollView sv = (ScrollView) rootView.findViewById(R.id.mainScrollView);
LinearLayout ll = (LinearLayout) rootView.findViewById(R.id.mainListView);
sv.addView(ll);
I get just an IllegalStateException that ScrollView can only host one direct child... But I don't get it. The linearlayout is the only thing that is the direct child. Why is there a problem?
Here is the complete onCreateView-method
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.main_fragment, container, false);
ScrollView sv = (ScrollView) rootView.findViewById(R.id.mainScrollView);
LinearLayout ll = (LinearLayout) rootView.findViewById(R.id.mainListView);
sv.addView(ll);
source = new TextView[hashMapSize];
for (int i = 0; i < hashMapSize; i++) {
source[i] = new TextView(getActivity());
source [i].setText( "Source: " + sourceArr [i] );
ll.addView(source[i]
}
return rootView;
}
The reason you get the IllegalStateException is because you inflate your R.layout.main_fragment which already contains your ScrollView that already has one direct child (declared in you layout xml).
You shouldn't add in code with sv.addView(ll) it's already there. All you need to do is get the references as you already do with findViewById.
Remove the sv.addView(ll); and you should be fine.
Related
I am trying to spawn some XML view for each instance of POJO data objects in a list, similar to how a ListView would do it, but it's not a ListView actually, but an empty LinearLayout instead, which is inside a ScrollView below other Fragment's content.
This is the item.xml file I want to inflate for each element of the list:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="#+id/item_label"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="0dp"
android:text="ITEM_LABEL"
android:textSize="16sp" />
<TextView
android:id="#+id/item_text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:text="ITEM_TEXT_1"
android:textSize="14sp" />
<TextView
android:id="#+id/item_text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:layout_marginLeft="8dp"
android:layout_marginTop="4dp"
android:text="ITEM_TEXT_2"
android:textSize="14sp" />
</LinearLayout>
...and this is the way I am trying to inflate it in the Fragment Java class:
public class MyFragment extends Fragment {
// we skip the "boilerplate"...
// [...]
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
// Inflate the layout for this fragment
View view = inflater.inflate(R.layout.my_fragment, container, false);
// XXX I use LiveData<> with Room to retrieve
// rows from DB inside POJO objects
LiveData<List<MyItem>> items_list = itemsViewModel.getItemsByParentId(some_id);
// The "parent" is not relevant, let's just
// focus on the List
LinearLayout items_list_layout = view.findViewById(R.id.items_list_layout);
items_list.observe(getActivity(), new Observer<List<MyItem>>() {
#Override
public void onChanged(List<MyItem> items) {
// Just in case, (maybe I'm wrong but) I think otherwise
// previous inflated objects remain on loading this same
// fragment for another parent
objs_list_layout.removeAllViews()
for (MyItem item : items) {
// XXX Here I inflate item.xml as a LinearLayout
LinearLayout obj_item = (LinearLayout) View.inflate(view.getContext(), R.layout.item, items_list_layout);
// XXX ... and try to change it's TextView children
TextView item_label = obj_item.findViewById(R.id.item_label);
item_label.setText(item.item_label);
TextView item_text1 = obj_item.findViewById(R.id.item_text1);
item_text1.setText(item.item_text1);
TextView item_text2 = obj_item.findViewById(R.id.item_text2);
item_text2.setText(item.item_text2);
}
}
});
}
}
This works well when the method getItemsByParentId only return a List with one only MyItem instance. But when the query returns more than one Item, it works unexpectedly wrong:
The first inflated item.xml element (or the one shown first, on top) has its TextViews modificated as expected, but for the last item in the list.
The rest of "inflated elements" have not been changed (it shows just ITEM_LABEL, ITEM_TEXT1 and ITEM_TEXT2 hardcoded strings as they are in the template XML file).
However, it inflates the XML template items.size() times as I planned.
Actually, the objective is that each "inflated item.xml" is edited with attributes of each corresponding Item. Just like a ListView would do, but without using ListView at all, because the goal is, actually, bloating an existing ScrollView (the main container in the fragment) that shows other different Views outside of the "LinearLayout" dedicated to generate this "list". How can I make it work without these errors?
View.inflate imho is a not the clearest method ) it returns the root View of the inflated hierarchy. If root was supplied, this is the root View; otherwise it is the root of the inflated XML file.
So obj_item is not an item view. It is an items_list_layout view. Inside it you find R.id.item_label in first item view and set it. Other item views are not initialized because findViewById returns first item it found.
Change this part:
LinearLayout obj_item = (LinearLayout) View.inflate(view.getContext(), R.layout.item, items_list_layout);
to
LinearLayout obj_item = LayoutInflator.from(view.getContext()).inflate(R.layout.item, items_list_layout, false);
I prefer to add child views explicitly to make code more readable. So and add this code to add item view to parent layout:
items_list_layout.addView(obj_item)
Alright so I have a fragment and I'd like to include, within its' xml file another layout which is programmatically inflated
Fragment:
public class zGoal_Fragment extends Fragment{
private LinearLayout todayView;
private View view;
#Nullable
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
view = inflater.inflate(R.layout.main_goal_view, container, false);
todayView = (LinearLayout)view.findViewById(R.id.todayView);
return view;
}
}
xml's file for fragment:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="#+id/todayView"
>
</LinearLayout>
and xml layout I want included within the above xml programmatically:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/goalEditLayout"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="100dp"
android:background="#color/test_color_two"
>
</LinearLayout>
I've tried a couple different methods all of which led to " on a null object reference" errors...
plz help :))
I suppose the solution which you are looking for are Android ViewStubs. These are dynamically inflated layouts.
For more information you could refer these :
How to use View Stub in android
https://developer.android.com/reference/android/view/ViewStub.html
However, if you don't wish to inflate one layout inside another during runtime, you could try this the include tag:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="fill_parent"
android:id="#+id/todayView">
<include layout="#layout/your_layout_file_name"/>
</LinearLayout>
Have you tried something like this:
LinearLayout child = getLayoutInflater().inflate(R.layout.goalEditLayout, null);
todayView.addView(child);
Edit:
Inside onCreateView:
LinearLayout inflatedLayout = (LinearLayout) inflater.inflate(R.layout.goalEditLayout, todayView, true);
Recently I asked a question on how to have one style for a TextView and display it multiple times for other texts as well. The solution was, to create an XML-Layout where I design the textview and then use an ArrayAdapter to fill it with contents. I'm using the ArrayAdapter in a fragment because I have multiple fragments that replace the main fragment dependent on the menu click.
But I'm stuck. I don't really know how to achieve that. I'm writing all my values into an array and then I'm assigning them to my ArrayAdapter. I have seen plenty solutions, but none of them fixed my problem and they all used an other way.
This is the xml of the fragment
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/mainRelativeLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content" >
<ScrollView
android:id="#+id/mainScrollView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_weight="1.03" >
<TextView
android:id="#+id/sources"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#+id/label"
android:textSize="20px" >
</TextView>
</ScrollView>
</RelativeLayout>
The TextView is the textview I want to populate. There is no style yet, I'm doing it just for test purposes. Now I did this in the Fragment.
This is my onCreateView method
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
StrictMode.setThreadPolicy(policy);
View rootView = inflater.inflate(R.layout.main_fragment, null);
RelativeLayout ll = new RelativeLayout(getActivity());
ScrollView sv = new ScrollView(getActivity());
ArrayAdapter<String> sourceAdapter = new ArrayAdapter<String>(getActivity(), R.id.mainScrollView, R.id.sources, sourceArr);
return rootView;
}
sourceArr is the array with the contents. How exactly do I assign all values to the TextView so it gets displayed multiple times? Thanks
You are having textview in scrollview. Either loop through the array and append the data to textview.
Or
Just Have a listview in main_fragment.xml initialize listview and adapter and set the adapter to listview.
View rootView = inflater.inflate(R.layout.main_fragment, null);
ListView lv = (ListView) rootView.findViewById(R.id.lv);
ArrayAdapter<String> sourceAdapter = new ArrayAdapter<String>(getActivity(),android.R.layout.simple_list_item1, sourceArr);
lv.setAdapter(sourceAdapter);
return rootView;
Also remove ScrollView sv = new ScrollView(getActivity()) and RelativeLayout ll = new RelativeLayout(getActivity()).
I currently wanted to follow this layout but not sure how to control the buttons below. My problem is that the image stretched itself whatever the listview's width is. I wanted to follow the layout below. I am using Android's back button.
I am not quite familiar in programmatically controlling the position of the image and it's default size. The button is inside addFooterView(btnLoadMore);
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.load_main_groups_activty, container, false);
// Getting listview from xml
ListView lv = (ListView) rootView.findViewById(android.R.id.list);
// Creating a button - Load More
Button btnLoadMore = new Button(mContext);
btnLoadMore.setBackgroundResource(R.drawable.navigation_back);
RelativeLayout.LayoutParams params2 = new RelativeLayout.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
params2.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
btnLoadMore.setLayoutParams(params2);
// Adding button to listview at footer
lv.addFooterView(btnLoadMore);
return rootView;
}
load_main_groups_activty
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#color/white"
>
<!-- Main ListView
Always give id value as list(#android:id/list)
-->
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
>
</ListView>
<FrameLayout
android:id="#+id/contentFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_weight="1" />
</RelativeLayout>
Try to implement this:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical"
android:background="#color/white"
>
<!-- Main ListView
Always give id value as list(#android:id/list)
-->
<ListView
android:id="#android:id/list"
android:layout_width="fill_parent"
android:layout_alignParentTop="true"
android:layout_alignParentBottom="true"
android:layout_height="wrap_content"
android:listSelector="#drawable/list_selector">
</ListView>
<ImageButton
android:id="#+id/imageButton1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentBottom="true"
android:background="#null"
android:src="#drawable/navigation_back" />
</RelativeLayout>
I am sure it'll help- you. Thanks
To keep the image from stretching, you can create a LinearLayout and place the button inside it. By setting the gravity attribute, you can position the button in the center, left or right of the ListView footer. After adding the button to the LinearLayout, the LinearLayout can be added to ListView footer.
See if the following code gets you the layout you want:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.load_main_groups_activty, container, false);
// Getting listview from xml
ListView lv = (ListView) rootView.findViewById(android.R.id.list);
// Creating a button - Load More
Button btnLoadMore = new Button(mContext);
btnLoadMore.setBackgroundResource(R.drawable.navigation_back);
LinearLayout llFooter = new LinearLayout(mContext);
LinearLayout.LayoutParams paramsBtn = new
LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);
paramsBtn.gravity = Gravity.LEFT;
llFooter.addView(btnLoadMore, paramsBtn);
//RelativeLayout.LayoutParams params2 = new
//RelativeLayout.LayoutParams(RelativeLayout.LayoutParams.MATCH_PARENT,
//RelativeLayout.LayoutParams.MATCH_PARENT);
//llFooter.setLayoutParams(params2);
// Adding button to listview at footer
lv.addFooterView(llFooter);
return rootView;
}
I have layout with controls:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="#+id/contact_phones_layout_row"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<EditText
android:id="#+id/contact_phone"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="3"
android:inputType="phone" />
<Spinner
android:id="#+id/contact_phone_type"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
And I want to inflate it into another layout on the fragment. Quantity of these controls depends on values in a array. Array contains controls objects. So every time onCreateView rises I filled the layout from the array:
private void addLine(PhoneLine line) {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = line.getParent();
((ViewGroup) view.getParent()).removeView(view);
layout.addView(view, layout.getChildCount() - 1);
setButtonVisible(false);
}
if line is null controls are created this way:
private void addLine() {
LinearLayout layout = (LinearLayout) mLayout.findViewById(R.id.contact_phones_layout);
View view = inflater.inflate(R.layout.contact_phone_line, layout, false);
EditText phoneEditText = (EditText) view.findViewById(R.id.contact_phone);
Spinner phoneType = (Spinner) view.findViewById(R.id.contact_phone_type);
phoneLines.add(new PhoneLine(phoneEditText, phoneType, view));
layout.addView(view, layout.getChildCount() - 1);
}
But after that I get same values in all EditText/Spinner controls, values equal to last element in the array. What can be wrong? May be there is more pure way to add controls dynamically?
I fixed it by adding controls when onResume rises, not onCreateView. But I still don't understand why onCreateView changes all values.