For my app, I'm trying to show a list, and as soon as this list ends, the second one shall begin. The Lists are being displayed using a ListAdapter, which again is part of a fragment. Everything works very well, the lists appear correctly, but I can't figure out a way to put one list under the other. I thought this shouldn't be all too hard.
Summary:
What I have:
A FragmentPagerAdapter with 3 Fragments
Two Fragments, which contain one ListView each
My searches:
Apart from multiple searches on this site, this guy came closest to what I'm seeking:
This guy here Fragmenttransaction in 1 tab of a Fragmentpageradapterhas had the same problem, but it wasn't satisfyingly answered, so I thought I can make a valid question here.
My question:
How can I place two ListViews in one Fragment? The big deal is that for example if the first ListView is bigger than the screen, I don't want the second ListView to show up before the first is completely scrolled down.
Current output:
Currently, both ListViews are in the same position, meaning that one ListView is on top of the other, making both unreadable
I thought that I can maybe use a specified layout for the FragmentTransaction. But I just can't figure out how.
This is the Fragment where I combine my top and bottom ListViews
public class LeaguePageTransactionsAdapter extends Fragment{
Global global_var;
ListView list, list_flat;
List <League> leagues = null, leaguesFlat = null;
ListAdapter adapter = null, adapter_flat = null;
View rootView;
FragmentTransaction fragmentTransaction;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.league_page, container, false);
fragmentTransaction = getFragmentManager().beginTransaction();
fragmentTransaction.add(rootView.getId(), new LeaguePageTop(), "TopFragment");
fragmentTransaction.add(rootView.getId(), new LeaguePageBottom(), "BottomFragment");
fragmentTransaction.commit();
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onViewCreated(view, savedInstanceState);
}
}
This is corresponding the xml layout file.
<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" />
This is one of my two ListViews
public class LeaguePageTop extends Fragment{
ListView list;
List <League> leagues = null;
ListAdapter adapter = null;
View rootView;
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
rootView = inflater.inflate(R.layout.league_page_top, container, false);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
// TODO Auto-generated method stub
super.onViewCreated(view, savedInstanceState);
list = (ListView) rootView.findViewById(R.id.listView1);
try {
leagues = Leagues_Parser.parse(getActivity().getAssets().open("league_raw.xml"), 0);
} catch (IOException e) {
e.printStackTrace();
}
adapter = new LeagueAdapter (getActivity(), R.layout.list_row, leagues);
list.setAdapter(adapter);
list.setOnItemClickListener(new OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int arg2,
long arg3) {
// TODO Auto-generated method stub
Global.mViewPager.setCurrentItem(1, true);
}
});
}
}
This is the corresponding xml file
<?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" >
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
Thank you very much for reading and thinking about it!
The reason is because you have set the LinearLayout in the XML file you have shown to match_parent which will occupy all available space and then comes your next ListView with it's own LinearLayout (which I presume is set to match_parent as well) and hence there is no space for it to display. FrameLayout and LinearLayout follows an eldest child first approach meaning that the first layout occupies as much space as it requests. Set the LinearLayout you have to wrap_content and I think that should solve your fragment.
Firstly use a view filpper.you can load your first list view in fragment and in your adapterview on item click listener you can flip view by flipper.setdisplayedchild(1) which will show your desired second list.
Related
I'm trying to create an Android up that has a Tab layout achieved using ViewPager.
I can't understand where i have to handle the layout of any tab.
I know there's the function onCreateView() in the PageFragment class that does that and i already done this:
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_page, container, false);
switch(mPage)
{
case 1:
ScheduleList(view);
return view;
case 2:
// Do something.
return view;
case 3:
// Do something.
return view;
}
return view;
}
public void ScheduleList(View v) {
ItemsLst = (ListView) v.findViewById(R.id.listview);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(getActivity(), android.R.layout.simple_list_item_1, Items);
ItemsLst.setAdapter(adapter);
}
This simply shows a ListView in the first Tab. This action is handled in the switch inside the onCreateView() method.
Here is the XML layout for the ListView:
<?xml version="1.0" encoding="utf-8"?>
<ListView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/listview"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
The issue is that i can have only the ListView in the XML file so i can't manage other case: in the switch for showing other UI elements.
For example, the first tab shows the ListViewand i want to show some text in the second Tab and some other in the third.
How can i do it?
Thank you in advance, hope i explained myself.
It looks like you have already handled the layout file :/, you are supposed to give a layout file for each fragment.
View view = inflater.inflate(R.layout.fragment_page, container, false);
the code above sets the fragment layout to res/layout/fragment_page.xml
if you are still a bit confused, I would recommend following this tutorial :D - http://www.exoguru.com/android/material-design/navigation/android-sliding-tabs-with-material-design.html
I have a ListView (inside a ListFragment) loading data with the help of a LoaderManager, to which I attach a headerView and a footerView. My problem is that after doing that, it is displaying a lot of blank space (like almost an entire screen) after the footer is displayed.
Some code from my ListFragment:
public class NewsDetailsFragment extends ListFragment implements
LoaderManager.LoaderCallbacks<Cursor> {
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
if (getArguments().containsKey(Const.ARG_ITEM_ID)) {
mId = getArguments().getLong(Const.ARG_ITEM_ID);
getLoaderManager().initLoader(NEWS_DETAILS_LOADER_ID, null, this);
getLoaderManager().initLoader(COMMENTS_LOADER_ID, null, this);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
mCommentsAdapter = new CommentsListAdapter(getActivity(), null, 0);
View rootView = inflater.inflate(R.layout.frag_news_details_comments,
container, false);
return rootView;
}
#Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
mHeaderView = LayoutInflater.from(getActivity()).inflate(
R.layout.header, getListView(), false);
mFooterView = LayoutInflater.from(getActivity()).inflate(
R.layout.footer, getListView(), false);
setUI();
setAction();
if (mHeaderView != null) {
getListView().addHeaderView(mHeaderView);
}
if (mFooterView != null) {
getListView().addFooterView(mFooterView);
}
setListAdapter(mCommentsAdapter);
}
Obviously, this is not the entire code from my ListFragment class, but the other stuff is mostly related to how the data is being manipulated (Loader Callbacks and others). If there is a need to add full code in order to be able to get the correct answer, I will add it on demand.
As for a general idea of how the screen should look like, the header displays some data about the article (which contains a bunch of TextViews, ImageViews and a Webview), the ListView displays comments on this article (if there are any), and the footer displays a form with writing a comment.
LE: This is how it looks like:
LE2: This screenshot shows how headerView and footerView are limited.
headerView ends at "0 comments on this post:" and after that comes the footerView. In between these two, should be displayed the list of comments (if there are any)
LE3: To make myself even more clear: I will display the wrong (red), and right (green) way it should be displayed.
Turns out I had this background on footerView (the same of the ListView), just to test if everything looked fine, with textColor and stuff, and I forgot to remove it afterwards. Then it took its height, which was larger than the form in the footer, thus creating the empty space. Sry to have bothered your eyes with such stupidity.
I am working through the Big Nerd Ranch guide for android programming, and I am at the challenge for Chapter 16. The challenge is to make an EmptyView for a ListView, and then make a button on the EmptyView that adds stuff. I got the EmptyView to work but I can't figure out where I should make my button. Here is my code.
public View onCreateView(LayoutInflater inflater, ViewGroup parent,
Bundle savedInstanceState) {
View v= super.onCreateView(inflater, parent, savedInstanceState);
inflater.inflate(R.layout.list_frame_layout, parent);
return v;
}
and here is my XML.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="#android:id/list"
android:layout_width="wrap_content"
android:layout_height="wrap_content" >
</ListView>
<LinearLayout android:id="#android:id/empty"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:gravity="center">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="24dp"
android:text="#string/empty_no_crime" />
<Button
android:id="#+id/empty_new_crime"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/empty_new_crime">
</Button>
</LinearLayout>
</FrameLayout>
The book is telling us to use fragments, hence the inflate. I figure the code should be
mNewCrime=(Button)getView().findViewById(R.id.empty_new_crime)
but that isn't working. Any ideas?
Edit*: Hmmm, apparently this also really isn't working that well. When I do add stuff, the EmptyView does not go away, it just gets pushed down while items are listed. Any ideas on how to make the EmptyView go away as soon as I add things?
I had trouble with this challenge at first as well. I over thought it! You have probably solved this issue by now but I thought it would be useful to post an answer for others. The following worked for me:
Create a new XML file specifying the "empty" and "list" views as you have done already.
Modify your existing onCreateView method to inflate the new modified layout which contains the "empty" and "list" views you have defined in your XML.
Create a new button and setup the onClickListener for the button.
Here is the code:
#TargetApi(11)
#Override
// We override the onCreateView to set the subtitle by default if we are rocking >3.0
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState)
{
super.onCreateView(inflater, parent, savedInstanceState);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
if(mSubtitleVisible){
getActivity().getActionBar().setSubtitle(R.string.subtitle);
}// End inner if
}// End if
View v = inflater.inflate(R.layout.empty_layout, parent, false);
mNewCrimeButton = (Button)v.findViewById(R.id.add_crime);
//Define an click event listener for the button and launch the new crime fragment when clicked
mNewCrimeButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v){
Crime crime = new Crime();
//Get the crimelab from the activity and add the crime
CrimeLab.get(getActivity()).addCrime(crime); //getActivity returns the activity this fragment is attached to
Intent i = new Intent(getActivity(), CrimePagerActivity.class);
startActivityForResult(i,0);
}//End onClick
});
return v;
}// End onCreateView
This should work with your existing xml layout. I hope this helps.
I too struggled initially with this, essentially solving it the same way the above poster did. However my problem was a bit different. I was getting bombed out of the application on startup, because my code that set up the onClick listener looked like this:
Button mCrimeButton = (Button)v.findViewById(R.id.crime_button);
mCrimeButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
initiateCrimeRecord();
}
});
It wasn't until I moved the declaration of mCrimeButton up to the class level making it an instance variable of the class that I was able to successfully execute the app:
public class CrimeListFragment extends ListFragment {
private static final String TAG = "CrimeListFragment";
private ArrayList<Crime> mCrimes;
private boolean mSubtitleVisible;
private Button mCrimeButton;
*
*
*
#TargetApi(11)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup parent, Bundle savedInstanceState) {
View v = inflater.inflate(R.layout.fragment_empty_crime_list, parent, false);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) {
if ( mSubtitleVisible) {
getActivity().getActionBar().setSubtitle(R.string.subtitle);
} else {
getActivity().getActionBar().setSubtitle(null);
}
}
// Set the button up on the empty view
mCrimeButton = (Button)v.findViewById(R.id.crime_button);
mCrimeButton.setOnClickListener(new View.OnClickListener(){
public void onClick(View v) {
initiateCrimeRecord();
}
});
return v;
}
I then went back and noticed that in all the other examples in the book, the widgets that get manipulated are declared as private instances of the class. Why is this? Android doesn't allow you to just get a local instance to attach the listener?
Previously, I have a Fragment, which update its UI based on time consuming fetch data.
public class HomeMenuFragment extends SherlockFragment {
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
Infos arg1 = performTimeConsumingDataStructureLoading();
// Add in various UI views in v, according to information in loaded
// data structure.
View v = inflater.inflate(R.layout.home_menu, container, false);
// ...
// Adding several LinearLayout and TextView into v, based on
// information from loaded data structure (arg1)
// ...
return v;
}
....
}
This is really not a good design. As sometimes, performTimeConsumingDataStructureLoading might take more than 5 seconds, and I will get ANR (Application Not Responding)
By referring http://developer.android.com/reference/android/content/AsyncTaskLoader.html, I end up with better solution. The example I encounter is, it updates its UI indirectly through an ArrayAdapter.
public class HomeMenuFragment extends SherlockFragment implements LoaderManager.LoaderCallbacks<HomeMenuFragment.Infos> {
#Override
public void onLoadFinished(Loader<Infos> arg0, Infos arg1) {
LayoutInflater inflater = this.getLayoutInflater(null);
ViewGroup container = (ViewGroup)this.getView().getParent();
// Remove previous old views, as onLoadFinished might be triggered
// by onContentChanged too.
container.removeAllViews();
View v = inflater.inflate(R.layout.home_menu, container, false);
// ...
// Adding several LinearLayout and TextView into v, based on
// information from loaded data structure (arg1)
// ...
container.addView(v);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return super.onCreateView(inflater, container, savedInstanceState);
}
....
}
I do not have an ArrayAdapter as I'm not using ListFragment. What I want to do is adding few TextView into few LinearLayout. Then, I will add LinearLayout to the Fragment's container.
However, the following code gives me null pointer exception.
ViewGroup container = (ViewGroup)this.getView().getParent();
What I know what is the correct way to update Fragment UI dynamically, after onLoadFinished from AsyncTaskLoader?
However, most example I encounter is, they update their UI indirectly
through an ArrayAdapter.
Using a Loader doesn't require that you must use a ListView, you can update whatever view you target in your application.
What I know what is the correct way to update Fragment UI dynamically,
after onLoadFinished from AsyncTaskLoader?
Get a reference to the container from the Fragment's view where you'll place the extra views and add the new views.
#Override
public void onLoadFinished(Loader<Infos> arg0, Infos arg1) {
// for some reason the fragment's view isn't built yet so abort things
if (getView() == null) {
ViewGroup container = (ViewGroup) getView().findViewById(R.id.containerId);
ViewGroup container = getViewcontainer.removeAllViews();
// ...
// Adding several LinearLayout and TextView into v, based on
// information from loaded data structure (arg1)
// ...
container.addView(v);
}
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.home_menu, container, false);;
}
For a while I was also trying to achieve the same. Based on the above answer, what worked for me was this:
#Override
public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
adapter.swapCursor(cursor);
ViewGroup container = (ViewGroup) getView().findViewById(R.id.fragment_oam);
if (container != null) {
if (adapter.getCount() == 0) {
Log.v(TAG, "Count is: " + adapter.getCount());
container.setBackgroundColor(Color.BLUE);
} else {
Log.v(TAG, "Count is: " + adapter.getCount());
container.setBackgroundColor(Color.WHITE);
}
} else {
Log.v(TAG, "Container is null");
}
}
And my Layout xml is as follows:
<?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:id="#+id/background"
android:background="#FFF">
<fragment
android:name="com.myapp.OAMCursorLoader"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/fragment_oam">
</fragment>
Now when ContentResolver updates the cursor and onLoadFinished checks if the cursor is empty or has some content in it, based on this background of the visible screen, i.e. ListFragment is changed.
You can play around with background settings, use a drawable image instead of colors, it is up to you.
I'm planning to convert an existing android application to fragments layout.
The idea is to have the classical two panel layout (item list on left, and details on right).
Actually the application is composed by 4 activites:
A ChoiceListActivity with all the available options
3 different activities, one for each operation available on the tool.
Now i started to work on the conversion and i created a FragmentActivity classs, that is the main class:
public class MainFragment extends FragmentActivity {
private static final String TAG = "MainFragment";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.fragment_main);
if(findViewById(R.id.fragment_container)!=null){
Log.i(TAG, "No Tablet");
Intent i = new Intent(MainFragment.this, main.ChoiceActivity.class);
startActivity(i);
} else {
Log.i(TAG, "Tablet");
}
}
}
And i created a ChoiceListFragment:
`
public class ChoiceListFragment extends ListFragment {
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
Toast.makeText(getActivity(), getListView().getItemAtPosition(position).toString(), Toast.LENGTH_LONG).show();
super.onListItemClick(l, v, position, id);
}
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
String[] options = getResources().getStringArray(R.array.listitems);
ArrayAdapter<String> adapter = new ArrayAdapter<String>(inflater.getContext(), R.layout.list_item, options);
setListAdapter(adapter);
return super.onCreateView(inflater, container, savedInstanceState);
}
}
That fragment will be the left side of the panel.
My problem is for the right side. The idea is that for every element of the list the corresponding activity (or fragment?) will be shown.
So what is the correct way?
Is a good idea to start an activity in the right fragment when the user select an item?
Or i must switch between fragments programmatically? And how to do that (i found many tutorials, but they use always the same activity for the right panel changing some data inside it)?
I have created the following class for the right fragment (but i'm not sure that i'm doing it correctly):
public class RightFragment extends Fragment {
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
}
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
return inflater.inflate(R.layout.main, container, false);
}
}
I noticed that i can eventually change the layout using the LayoutInflater object during onCreate method, but this simply siwtch the layout on the screen, The objects declared in the layout aren't initialized (nor eventListener added, etc). So how to do that?
Maybe i should Create an Intent and use startActivity to launch the existing activities, or this is a bad idea into a fragment?
Actually the xml layout is:
<?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="horizontal" >
<fragment
android:id="#+id/choicelist_fragment"
android:name="main.fragments.ChoiceListFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<fragment
android:id="#+id/right_fragment"
android:name="main.fragments.RightFragment"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="2" />
</LinearLayout>
Ok i found myself the solution, it was not clear on a first moment, but reading some documentation, looking at many tutorials maybe i understand how it works.
First of all i removed the second fragment *right_fragment* from the layout (check the question), i replaced it with an empty FrameLayout called *activity_container* that will be the container of my fragments.
The idea behind is simply use the FragmentManager to replace the fragment inside the container.
So i updated the onListItemClick method into the ChoiceListFragment, and depending on what is the list item tapped, it creates a new Fragment and replace it into the *activity_container*. The updated method is similar to the following:
public void onListItemClick(ListView l, View v, int position, long id) {
String itemName = getListView().getItemAtPosition(position).toString();
switch(position){
case OPTION_ONE: getFragmentManager().beginTransaction().replace(R.id.activity_container, new OptionOneFragment()).commit();
break;
case RESISTOR_VALUE:
getFragmentManager().beginTransaction().replace(R.id.activity_container, new OptionTwoFragment()).commit();
break;
default:
Toast.makeText(getActivity(), getListView().getItemAtPosition(position).toString(), Toast.LENGTH_LONG).show();
break;
}
super.onListItemClick(l, v, position, id);
}
In that way every component of the application has its own fragment, handled by a different class.
You're on the right track. On small screens, clicking a list item starts a DetailActivity, which is a simple wrapper around a DetailFragment. On a larger screen, clicking the list item would replace the right hand side with a new instance of DetailFragment.
If you are using eclipse and ADT, I would suggest taking a look at the MasterDetailFlow template, which can be accessed by creating a new Android project or a new Android Activity.