ExpandableListView sample from Google - android

I just tried the current Google sample for ExpandableListiew:
This sample seem very simple and easy to use, but what I would like to do is to say that one of the category has no child. I removed all the children but the problem is that the arrow still appears on this current line.
For instance, imagine that I remove all "Cat Names", the arrow is still there and when I click on it, the arrow just change. How to remove this arrow and launch an activity instead?

If you're talking about the arrow that is used to collapse/expand the list then you can remove it by using setGroupIndicator()
in the activity you can call
getExpandableListView().setGroupIndicator(null);
that will remove the arrow permanently though. If you want to only hide it if the list is empty you can do it through xml attributes like this
To launch an activity when the list is expanded/collapsed you can override onGroupExpanded (or collapsed) in your ListAdapter implementation and used that to fire up your activity

In general, if you want a specific area of your activity to be refreshed when you come back from another activity; you got to write in the onResume() method.
Steps:
1 – In the Activity you want it to be refreshed, override the onResume() method:
#Override
protected void onResume() {
super.onResume();
// TODO: PUT YOUR REFRESH CODE IN HERE
}
2- You will need to refresh your Expandable Adapter.
I suggest you take the same code you used to instantiate your expandable list and put it again, like follows:
SimpleExpandableListAdapter expandableListAdapter =
new SimpleExpandableListAdapter(
this,
YourPrentsList, // List of your parent
R.layout.parents_layout, // Layout for the Parents
new String[] { "groupKey" }, // Key in the Group Data maps to display
new int[] { 1 },
childrenList, // List of the children
R.layout.childs_layout, // Layout of the children
new String[] { "keys"},
new int[] { 1}
);
setListAdapter( expandableListAdapter );
I hope this solves your concern...
Thanks,
Mohamed.

Related

Empty list after orientation change

First off, I've already looked at this question. In my scenario, I have a fragment that extends support.v4.app.ListFragment. The fragment calls a custom adapter. I need to handle orientation changes on a device that will switch to a 2 pane layout. I believe I'm doing most things right as the view itself changes correctly and I am able to retrieve the data from savedInstanceState. However, my list is always empty. I tried the recommended answer in the linked question (calling setListShown(true)), but I get an exception, "Can't be used with a custom content view". The relevant code is below:
#Override
public void onConfigurationChanged(Configuration newConfig){
super.onConfigurationChanged(newConfig);
LayoutInflater inflater = LayoutInflater.from(getActivity());
ViewGroup group = ((ViewGroup) getView());
group.removeAllViews();
inflater.inflate(R.layout.activity_message_list, group);
if(!messages.isEmpty()){
mAdapter = new MessageListAdapter(getActivity().getApplicationContext(), messages);
}
setListAdapter(mAdapter);
}
The adapter's getView method is never invoked after the configuration change. What else do I need to do to re-hydrate the view? Let me know if you need to see any other code.
Since you are creating a new view then you have to redo all View's initialization that you do in the original piece of code (in onViewCreated or somewhere else). So, in order to initialize the ListView - you should do something like this:
View rootView = inflater.inflate(R.layout.activity_message_list, group);
if(!messages.isEmpty()){
mAdapter = new MessageListAdapter(getActivity().getApplicationContext(), messages);
}
ListView listView = (ListView) root.findViewById(R.id.<your_list_view_id>;
listView.setAdapter(mAdapter);
Just keep in mind that you also have to do all other initialization (creating references to Views and creating onClickListeners or whatever else you're doing)
There is a way to avoid app reload after device orientation changed, you just have to tell system that you'll handle screen orientation changed events on your own.
Have you tried to add parameter android:configChanges="orientation" inside activity tag in Manifest.xml?
With a ton of direction from Chaosit, I was able to achieve what I wanted. My app has a nav drawer activity and I swap the fragment (which is a master-detail type fragment) based on the selection. I added android:configChanges="orientation" so the fragment wouldn't revert back to the default selection I made in the onCreate method of the activity. So, I removed that line and made the adjustments to store the selection in the savedInstanceState bundle. Everything is working as I would have expected now.

What's the correct way of displaying ViewPager after associated ListView's item click?

I'm a beginner in Android, so I apologize for the mistakes and I'd appreciate any constructive criticism.
I'm writing a basic application with a ListView of images, and when the user clicks on an item in the list, I want to display that image in a ViewPager, where the user can swipe back and forth to browse the whole list of images. Afterwards when the user presses the back button, I want to switch back to the ListView.
I manage the business logic in the MainActivity, which uses MainActivityFragment for the ListView and ImageHolderFragment for ViewPager.
The simplified code so far is as follows:
#Override
protected void onCreate(final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListItems = new ArrayList<>();
mListItemAdapter = new ListItemAdapter(this, R.layout.list_item, R.id.list_item_name, mListItems);
mListView = (ListView) findViewById(R.id.list_view_content);
mListView.setAdapter(mListItemAdapter);
mDeletedListItems = new ArrayList<>();
mViewPager = (ViewPager) getLayoutInflater().inflate(R.layout.image_display, null, true);
mImageAdapter = new ImageAdapter(getSupportFragmentManager(), mListItems);
mViewPager.setAdapter(mImageAdapter);
mViewPager.setOffscreenPageLimit(3);
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mViewPager.setCurrentItem(position);
setContentView(mViewPager); // TODO: this is very wrong!
}
});
loadImages();
noContentText = (TextView) findViewById(R.id.no_content_text);
if (mListItems.isEmpty()) {
noContentText.setText(R.string.no_images);
} else {
mImageAdapter.notifyDataSetChanged();
}
}
Although this does work to some extent, meaning that it manages to display the ViewPager when an item in the list is clicked, there are two things about it ringing the alarm bells:
I've read that calling setContentView() for the second time in the same class is pretty much a sin. Nobody explained me why.
The back button doesn't work in this case. When it's pressed, the application is terminated instead of going back to the list view. I believe this is connected to the first point.
I would appreciate any help, explanations if my idea is completely wrong, and if my case is hopeless, I'd like to see a successful combination of ListView and ViewPager with transitions between each other.
Your activity already has R.layout.activity_main set as content view, which rightly displays the list view - that's what the responsibility of this activity is as you defined it. If we want to change what's shown on the screen, we should use a different instance of a building block (activity or fragment) to display the view pager images.
To say the least, imagine if you wanted to change the view to a third piece of functionality or UI, or a fourth... it would be a nightmare to maintain, extend and test as you're not separating functionality into manageable units. Fields that are needed in one view are mixed with those needed in another, your class file would grow larger and larger as each view brings its click listeners, callbacks, etc., you'd also have to override the back button so it does what you want - it's just not how the Android framework was designed to help you. And what if you wanted to re-use UI components in different contexts whilst tapping in to the framework's activity lifecycle callbacks? That's why fragments were introduced.
In your case, the list view could continue to run in your MainActivity and in your click listener, onItemClick you could start a new activity that will hold a viewPager:
Intent i = new Intent(MainActivity.this, MyLargePhotoActivityPager.class);
i.putExtra(KEY_POSITION, position);
// pass the data too
startActivityForResult(i, REQUEST_CODE);
Notice how you could pass the position to this activity as an int extra, in order for that second activity to nicely set the viewPager to the position that the user clicked on. I'll let you discover how to build the second activity and put the ViewPager there. You also get back button functionality assuming your launch modes are set accordingly, if needed. One thing to note is that when you do come back to the list View, you'd probably want to scroll to the position from the view pager, which is why you could supply that back as a result via a request code. The returned position can be supplied back to the list view.
Alternatively, you could use the same activity but have two fragments (see the link further above) and have an equivalent outcome. In fact, one of your fragments could store the list view, and the second fragment could be a fullscreen DialogFragment that stores a viewPager, like a photo gallery (some details here).
Hope this helps.
I've read that calling setContentView() for the second time in the
same class is pretty much a sin. Nobody explained me why.
Well, you kind of get an idea as to why.
When you use setContentView() to display another 'screen' you do no have a proper back stack.
You also keep references to Views (like mListView) that are not visible anymore and are therefore kind of 'useless' after you setContentView() for the second time.
Also keep in mind orientation changes or your app going to the background - you'll have to keep track of the state that your Activity was in which is way more complicated than it has to be if you have one Activity that does two different things.
You won't be arrested for doing things like you do right now, but it's just harder to debug and keep bug free.
I'd suggest using two different Activities for the two different things that you want to do, or use one Activity and two Fragments, swapping them back and forth.
If you insist on having it all in one Activity you need to override onBackPressed() (called when the user presses the back button) and restore the first state of your Activity (setContentView() again, pretty much starting all over).

Android Fragments default select first item in ListView

In Android Studio I have selected a new project based on master / detail flow. The project works as it should be. Now I want to extend it. As of now the content of the items in the detail fragment are only shown when I click an item on the list. I want that the top item is selected automaticaly when the app starts. I thought I put in ItemListFragment just a method call
#Override
public void onStart()
{
mCallbacks.onItemSelected(DummyContent.ITEMS.get(0).id);
}
that a click is simulated in the lifecycle once all objects are initialized. That fails. What is the best way of doing this?
You can select the first element by the following code
yourListView.setSelection(0);
yourListView.getSelectedView().setSelected(true);
Hope this helps. Cheers mate! :)
You can use SharedPreferences. You can save first item of listview as preference when create listview, then you can use it later.
Have you try it in onResume()? I always put it in onResume()
UPDATE #1
You can try my way to implement the selection. Normally i don't use the select of list view.
1.In your data model, create a boolean field called "selection".
2.When you apply data to your adapter, set the first data "selection" to true.
3.In your adapter getView callback, try to handles your data "selection" such as:
if (!dummyData.selection){
do something when not select....
}else{
do something when selected...
}
UPDATE #2
put this code in ItemListFragment
#Override
public void onResume() {
super.onResume();
mCallbacks.onItemSelected(DummyContent.ITEMS.get(0).id);
}

How to recreate the listview of a former ListFragment after pressing back button

I have a ListFragment with a listview in it, that contains a navigation-structure. By selecting an item of the list, the next navigation hierarchy stage should be displayed. And so on.
The next hierarchy stage is created by a new fragment (remove the old fragment and add the same fragment new via fragmant transactions by using addToBackStack), in which the arrayadapter of the new listview is updated to the items of the next navigation hierarchy. That work pretty fine.
I want the user to have the possibility to navigate back via the back button. And here start my problems. I have no clue how to save the listview so it could be recreated after using the back button.
I thought using onSaveInstanceState would be a good idea. But onSaveInstanceState ist not called when the transaction gets comitted. I checked it by placing a Log.d("..","...) in the method. It´s not called. (What I found out, is, that onSaveInstanceState is called when rotating from portrait- to landscape-view and vice-versa. But that´s no help for my problem).
So what would be the best idea to store the elements of the listview and get it back to recreate the listview after using the back button and the former fragment is getting created? All items are Strings that are stored in an ArrayList that is bound to the ListAdapter.
Here my code. I implemented an interface, via the click on an item in the listview calls a method in the parent-activity. In this method I first call a method to fill the already existing ArrayList (navigationsItems) with the new items that are content of the next navigation-stage:
The code of my ArrayAdapter:
arrayAdapterFuerNaviList = new ArrayAdapter<String>(
BRC_BoardOverviewActivity.this,
android.R.layout.simple_list_item_1,
navigationsItems);
In this method I first call a method to fill an ArrayList (navigationsItems) with the items of the next navigation-stage:
// load new itemlist to navigation-
public void inhaltAktuelleNaviListeInArrayLaden() {
navigationsItems.clear();
for (int i = 0; i < prefs.getInt("aktuelleNaviListe_size", 0); i++) {
(...)
navigationsItems.add(titel);
}
}
Then I call the method, with which I load the new ListFragment and bind the new ArrayAdapter to it:
// push navifragment to BackStack and create a new instance of it
public void naviFragmenteNeuLaden() {
// get a reference to the actual ListFragment
Fragment removefragment = frgm.findFragmentById(R.id.navi_container);
// initialize ne ListFragment
BRC_NavigationsFragment navifragment = (BRC_NavigationsFragment) frgm
.findFragmentById(R.id.navi_container);
// remove the old and bind the new ListFragment from/to the container ...
FragmentTransaction frgmta = frgm.beginTransaction();
frgmta.remove(removefragment);
frgmta.add(R.id.navi_container, navifragment);
// ... push the old ListFragment on the BackStack and commit ...
frgmta.addToBackStack(null);
frgmta.commit();
frgm.executePendingTransactions();
// ... bind the updated ArrayAdapter to the new ListFragment ...
try {
navifragment.setListAdapter(arrayAdapterFuerNaviList);
} catch (Exception e) {
// TODO
e.printStackTrace();
}
// ... and notify
try {
arrayAdapterFuerNaviList.notifyDataSetChanged();
} catch (Exception e) {
// TODO
e.printStackTrace();
}
}
As mentioned, forward-navigating is no problem. But when i press back button for e.g. in the 2nd navigation-stage, the former ListFragment gets loaded (verfified with Log.d("..."."...)-Messages in onCreatView() of ListFragment), but the former listview which was in it isn´t created.
I am currently working on a new approach. When you click on an item in the list, I write the current list, together with a reference to the current ListFragment into a vector based stack. Then I catch with "onBackPressed()"-method the press of the back button. So now when the button is pressed, I call on the already mentioned methods (see above), write the item-data from the stack back into the list, call via the stored reference the forme ListFragmt and bind the updated ArrayAdapter to it. This is work in progress actually. I will write the result of this approach when finished.
I solved the problem.
For the different navigation contents of the ListView I have defined a class "Naviliste", in which all the items are stored. If someone clicks on an item, then the a new listview with the correspondingly new list will be generated. These lists are indexed, so each list get´s a fixed ID assigned.
By clicking an item in the ListView, the ID of the current Naviliste is pushed onto a stack. After that a new fragment will be created an the new list bound to it. The same procedure repeats if the user goes a step further, and so on ...
If the user pushes the back button to go a step back, the following is done:
The push of the back button is catched via the onPressedBack()-method placed in the parent activity. Via this method an own method is called. This pulls the last ID that was pushed onto the stack and then builds with it in a new created fragment the former list.
At the same time I will run along a counter that counts in which navigation depth I am. In each navigation step forward it get´s inceased by 1. And in reverse just reduced by 1. With this counter I am able to query if the user is in the navigation-root (counter = 0) and once more pushes the back button to leave the current activity. If so, i call finish() and the parent activity with its fragments get´s closed. Voilá!
It works great. Took a little bit time to get over it. But now I am happy that my solution works. :)

How to notify Intent that data in changed?

I am using LocalActivityManager activityManager = getLocalActivityManager();, I put all my activities to this by View view = activityManager.startActivity("123", myIntent).
There are many activities in this activityManger which are identical by their ID e.g(123).
1 activity hold favorite Items name FavoriteActivty. Another activity hold items. All I want to do is to notify FavoriteActivty that data has been changed so activity has to refresh itself...
How to do this? I dont want to create that activity again.
I'm not 100% sure if I got what you need.
For my understanding you need to set an adapter to this activity...
then you can just handle a list and give the adapter a "notification"
Example:
adapter = new ArrayAdapter<Module>(this,
android.R.layout.simple_list_item_1, moduleList);
setListAdapter(adapter);
and if you change something (I add here an entry to the list):
moduleList.add(m);
adapter.notifyDataSetChanged();
OK. I finally got my answer. I used the following method:
onAttachedToWindow();
It worked for me. As the window comes to front I check look for the changes.
onResume() ;
onWindowFocusChanged(boolean hasFocus)

Categories

Resources