I know a lot have asked similar questions, but none of their solutions works for me.
I have a master/detail flow with a expandable list as the list. I can click the groups and expand them, but I can't click the children.
I have set them to selectable (at first I thought that was the problem), they do not have any checkboxes or images on them, just plain text. I've even set that text to not be focusable (as focusable elements seemed to be the problem for others).
This is what I have:
A custom BaseExpandableAdapter:
public class CabinetAndRacksExpandAdapter extends BaseExpandableListAdapter {
//(some fields that probably aren't relevant)
#Override
public int getGroupCount() {
return cabinets.size();
}
(more override methods here, probably not relevant)
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = inflater.inflate(R.layout.listrow_cabinet, null);
}
Cabinet cabinet = (Cabinet) getGroup(groupPosition);
((CheckedTextView) convertView).setText(cabinet.toString());
((CheckedTextView) convertView).setChecked(isExpanded);
return convertView;
}
#Override
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
final Rack rack = (Rack) getChild(groupPosition, childPosition);
TextView text = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.listrow_rack, null);
}
final View view = convertView;
text = (TextView) convertView.findViewById(R.id.textView1);
text.setText(rack.toString());
return convertView;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
I also have a fragment (the list fragment in the master/detail flow). It should listen to the clicks, both child and group, so it implements those listeners:
public class CabinetListFragment extends Fragment implements ExpandableListView.OnChildClickListener, ExpandableListView.OnGroupClickListener{
//(some stuff that's probably not relevant)
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_cabinet_list, container, false);
listView = (ExpandableListView) rootView;
listView.setOnChildClickListener(this);
listView.setOnGroupClickListener(this);
Bundle extras = getActivity().getIntent().getExtras();
if(extras != null)
{
String orderId = extras.getString(CabinetListActivity.ARG_ORDER_ID);
order = service.getOrderFromId(service.intFromString(orderId));
}
CabinetAndRacksExpandAdapter adapter = new CabinetAndRacksExpandAdapter(getActivity(), service.getCabinetsForOrder(order));
listView.setAdapter(adapter);
return rootView;
}
#Override
public boolean onChildClick(ExpandableListView parent, View v, int groupPosition, int childPosition, long id) {
Log.d("CabinetListFragment", "onChildClick");
mCallbacks.onChildSelected(groupPosition, childPosition);
return true;
}
#Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id)
{
Log.d("CabinetListFragment", "onGroupClick");
mCallbacks.onGroupSelected(groupPosition);
return false;
}
/**
* A callback interface that all activities containing this fragment must
* implement. This mechanism allows activities to be notified of item
* selections.
*/
public interface Callbacks {
/**
* Callback for when an item has been selected.
*/
public void onChildSelected(int groupPosition, int childPosition);
public void onGroupSelected(int groupPosition);
}
#Override
public void onAttach(Activity activity) {
super.onAttach(activity);
// Activities containing this fragment must implement its callbacks.
if (!(activity instanceof Callbacks)) {
throw new IllegalStateException("Activity must implement fragment's callbacks.");
}
mCallbacks = (Callbacks) activity;
}
}
This fragment belongs to an Activity that implements the Callback as it should:
public class CabinetListActivity extends FragmentActivity
implements CabinetListFragment.Callbacks{
/**
* Whether or not the activity is in two-pane mode, i.e. running on a tablet
* device.
*/
private boolean mTwoPane;
public static final String ARG_ORDER_ID = "order_id";
private Service service;
private Order order;
private String orderId;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_cabinet_list);
service = Service.getInstance();
if (findViewById(R.id.cabinet_detail_container) != null) {
mTwoPane = true;
}
Bundle extras = getIntent().getExtras();
if(extras != null)
{
String orderId = extras.getString(ARG_ORDER_ID);
order = service.getOrderFromId(service.intFromString(orderId));
this.orderId = orderId;
}
}
/**
* Callback method from {#link CabinetListFragment.Callbacks}
* indicating which item was selected.
*/
#Override
public void onChildSelected(int groupPosition, int childPosition) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putString(CabinetDetailFragment.ARG_CHILD_ID, childPosition+"");
arguments.putString(CabinetDetailFragment.ARG_GROUP_ID, groupPosition+"");
arguments.putBoolean(CabinetDetailFragment.ARG_RACKMODE, true);
CabinetDetailFragment fragment = new CabinetDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.cabinet_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Log.d("CabinetListActivity", "show rack");
Intent detailIntent = new Intent(this, CabinetDetailActivity.class);
detailIntent.putExtra(CabinetDetailFragment.ARG_CHILD_ID, childPosition + "");
detailIntent.putExtra(CabinetDetailFragment.ARG_GROUP_ID, groupPosition+"");
detailIntent.putExtra(CabinetDetailFragment.ARG_RACKMODE, true);
startActivity(detailIntent);
}
}
#Override
public void onGroupSelected(int groupPosition) {
if (mTwoPane) {
// In two-pane mode, show the detail view in this activity by
// adding or replacing the detail fragment using a
// fragment transaction.
Bundle arguments = new Bundle();
arguments.putString(CabinetDetailFragment.ARG_GROUP_ID, groupPosition+"");
arguments.putString(CabinetDetailFragment.ARG_ORDER_ID, orderId);
arguments.putBoolean(CabinetDetailFragment.ARG_RACKMODE, false);
CabinetDetailFragment fragment = new CabinetDetailFragment();
fragment.setArguments(arguments);
getSupportFragmentManager().beginTransaction()
.replace(R.id.cabinet_detail_container, fragment)
.commit();
} else {
// In single-pane mode, simply start the detail activity
// for the selected item ID.
Intent detailIntent = new Intent(this, CabinetDetailActivity.class);
detailIntent.putExtra(CabinetDetailFragment.ARG_GROUP_ID, groupPosition+"");
detailIntent.putExtra(CabinetDetailFragment.ARG_ORDER_ID, orderId);
detailIntent.putExtra(CabinetDetailFragment.ARG_RACKMODE, false);
startActivity(detailIntent);
}
}
//(more stuff that's probably not relevant)
}
I cannot see where my childClick event is different from the GroupClick event.
As stated, the GroupClick fires and is handled as intended, but the childClick is not even fired (I have tried logging from the onChildClick in the Fragment).
BTW, it does not seem to make any difference whether that method return true or false (naturally, since it is never called).
I have tried creating a listener directly on the childView, and that works. But I need to do stuff from within either the fragment or activity (preferably the activity, but it ought to be the fragment that listens for the event) upon click, so that won't do.
Why is my fragment not notified of the onChildClick event when it is registered as a listener? Is the event never fired for some reason? Or is it handled elsewhere?
Just a shot in the dark, but did you implement among those not relevant methods
int getChildrenCount(int groupPosition) {
// Return whatever the children's count is
}
and
public boolean areAllItemsEnabled() {
return true;
}
In the Layout XML R.layout.listrow_rack, have you by any chence disabled the item, or associated a click listener to it that fires silently?
Maybe, what your are trying to do is something similar to this:
public class FaltasFragmentActivity extends Activity {
//Lista de faltas
private ExpandableListView mExpandalbeView;
private ExpandableListAdapter listAdapter;
private int posicion;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.conduccion_faltas);
mExpandalbeView = (ExpandableListView) findViewById(R.id.expandable_faltas);
mExpandalbeView.setOnGroupClickListener(new ExpandableListView.OnGroupClickListener() {
#Override
public boolean onGroupClick(ExpandableListView parent, View v, int groupPosition, long id) {
// Codigo para cerra el ultiom grupo abierto antes de abrir el nuevo
Toast.makeText(FaltasFragmentActivity.this, "pulsado group: " + groupPosition, Toast.LENGTH_SHORT).show();
return false;
}
});
// Listview on child click listener
mExpandalbeView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
Toast.makeText(FaltasFragmentActivity.this, "pulsado child: (" + groupPosition + ", " + childPosition + ")", Toast.LENGTH_SHORT).show();
return false;
}
});
// Listview Group expanded listener
mExpandalbeView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
#Override
public void onGroupExpand(int groupPosition) {
Toast.makeText(FaltasFragmentActivity.this, "pulsado expand", Toast.LENGTH_SHORT).show();
}
});
// Listview Group collasped listener
mExpandalbeView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
#Override
public void onGroupCollapse(int groupPosition) {
Toast.makeText(FaltasFragmentActivity.this, "pulsado collapse", Toast.LENGTH_SHORT).show();
}
});
listAdapter = new ExpandableListAdapter(FaltasFragmentActivity.this, root, posicion, filtroBusqueda);
mExpandalbeView.setAdapter(listAdapter);
}
}
Where ExpandableListAdapter(...) is:
public class ExpandableListAdapter extends BaseExpandableListAdapter{...}
This worked for me!
Hope this helps!
Ok, so here is what was the problem:
This was my listrow_rack:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="40dp"
android:clickable="true"
android:orientation="vertical"
android:paddingLeft="40dp"
tools:context=".OrdreDetailActivity" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawablePadding="5dp"
android:gravity="center_vertical"
android:text="#string/hello_world"
android:textSize="14sp"
android:textStyle="bold">
</TextView>
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:background="#android:color/black" />
The problem was this specific line:
android:clickable="true"
I'm not sure why, but maybe it adds a listener, or maybe for some other reason. But when I removed it, it works.
Props to Andras Baláz Lathja for pointing me in the right direction.
Related
I have a fragment which contains a listview of some items. On click of an item in the list view an Activity is started to view the details about the list view. There is a back button on the Activity which brings us back to the fragment page. Once the back button is pressed and the activity page closes I want the data to be refreshed in the fragment list view. I have tried onResume() method but it does not refresh the data.
Can someone please help me how to achieve this am pretty new at this.
public class FavCommittee extends Fragment {
public ArrayList<Committee> favCommitteeData = new ArrayList<>();
#Nullable
#Override
public void onResume(){
super.onResume();
final SharedPreferences pref = getContext().getSharedPreferences("MyFav", 0);
final SharedPreferences.Editor editor = pref.edit();
View rootView =getView();
Map<String,?> entries = pref.getAll();
final Set<String> keys = entries.keySet();
int count=0;
for (String key:keys) {
String val = pref.getString(key, null);
String[] store = val.split(":");
if (store[0].equals("committee"))
count=count+1;
}
for (int i=0;i<keys.size();i++) {
String val = pref.getString(Integer.toString(i), null);
if (val != null) {
String[] store = val.split(":");
if (store[0].equals("committee")) {
count = count - 1;
new GetAllCommittees(getContext(), rootView).execute(store[1], Integer.toString(count));
}
}
} final ListView yourListView = (ListView) rootView.findViewById(R.id.house_listview);
yourListView.setOnItemClickListener(new android.widget.AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view,int position, long id) {
Committee obj = (Committee)yourListView.getItemAtPosition(position);
Intent intent = new Intent(FavCommittee.this.getActivity(), committee_info.class);
intent.putExtra("Committee",obj.getCommittee_id());
startActivity(intent);
}
});
}
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.house, container, false);
return rootView;
}
When you are transitioning between Fragments, call addToBackStack() as part of your FragmentTransaction:
FragmentTransaction tx = fragmentManager.beginTransation();
tx.replace( R.id.fragment, new MyFragment() ).addToBackStack( "tag").commit();
If you require more detailed control (i.e. when some Fragments are visible, you want to suppress the back key) you can set an OnKeyListener on the parent view of your fragment:
//You need to add the following line for this solution to work,
fragment.getView().setFocusableInTouchMode(true);
fragment.getView().requestFocus();
fragment.getView().setOnKeyListener( new OnKeyListener()
{
#Override
public boolean onKey( View v, int keyCode, KeyEvent event )
{
if( keyCode == KeyEvent.KEYCODE_BACK )
{
return true;
}
return false;
}
} );
getSupportFragmentManager().beginTransaction().add(R.id.fragContainer, new FavCommittee (), FavCommittee .class.getName()).commit();
Try this code.
Put the code where u add your Fragment in Activity
I have a listview with several items. When clicking on an item, an Activity is opened. However, when I click fast several times on the item, the activity, asociated with it, is opened several times. How to open the item only once?
I use the following code:
private OnItemClickListener newsfeedClickHandler = new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
MyObject newsfeed = adapter.getItem(position);
onNewsfeedClick(newsfeed);
}
};
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_newsfeed, container, false);
...
listView = (ListView) view.findViewById(R.id.newsfeeds);
listView.setOnItemClickListener(newsfeedClickHandler);
listView.setAdapter(adapter);
...
return view;
}
private void onNewsfeedClick(MyObject newsfeed) {
Intent intent = createIntent(newsfeed);
startActivity(intent);
}
The correct way to prevent calls to the ListView items would be to (obviously) disable the OnItemClickListener:
private void onNewsfeedClick(MyObject newsfeed) {
listView.setOnItemClickListener(null);
Intent intent = createIntent(newsfeed);
startActivity(intent);
}
Re-set the listener at onResume, when this activity is visible again.
#Override
protected void onResume() {
super.onResume();
listView.setOnItemClickListener(newsfeedClickHandler);
}
Maintain a boolean value say isItemClicked and reset it when you return back to the listview.
private boolean isItemClicked;
private OnItemClickListener newsfeedClickHandler = new OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (!isItemClicked) {
isItemClicked = true;
MyObject newsfeed = adapter.getItem(position);
onNewsfeedClick(newsfeed);
}
}
};
private void resetIsItemClicked() {
isItemClicked = false;
}
To prevent multiple clicks on ListView Items, add the following code in your listview click listener:
if (SystemClock.elapsedRealtime() - mLastClickTimeListViewItem < 1000){
return;
}
I want to programmatically (re)highlight selected list item after screen rotation.
public class MyListFragment extends ListFragment {
private static final String tag = MyListFragment.class.getName();
private static final String indexTag = "index";
private int index = -1;
#Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
if (savedInstanceState != null) {
index = savedInstanceState.getInt(indexTag, -1);
Log.d(tag, "Restored index " + index + " from saved instance state.");
}
}
#Override
public void onResume() {
super.onResume();
if (index >= 0) {
showDetails(index);
}
}
#Override
public void onListItemClick(ListView l, View v, int position, long id) {
showDetails(position);
}
private void showDetails(int index) {
this.index = index;
getListView().setItemChecked(index, true);
// update details panel
}
#Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt(indexTag, index);
}
}
I use CheckedTextView as item view in my custom adapter:
public class MyListAdapter extends BaseAdapter {
private static final String tag = MyListAdapter.class.getName();
#Override
public CheckedTextView getView(int position, View convertView, ViewGroup parent) {
if (convertView == null || !(convertView instanceof CheckedTextView)) {
final LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = inflater.inflate(R.layout.simple_list_item_single_choice, parent, false);
}
((CheckedTextView)convertView).setText("test");
return (CheckedTextView)convertView;
}
}
After screen rotation showDetails() is called and details panel updates but setItemChecked()does nothing and the item is still not highlighted. Also I noticed that when item it clicked by touch event setItemChecked() is not needed and the row highlights anyway.
So how can I programmatically check the item during onResume stage?
put showIndex(index) in your onActivityCreate() because on screen rotation Android destroys current activity and create another one saving current state through Bundle savedInstanceState
I solved the problem. I forgot that I'm setting list adapter through AsyncTask on my activity so when showDetails() is called during onResume stage my fragment still has empty list.
So I removed onResume method from my fragment, made showDetails() public and call it from my activity after setting the adapter:
public void onListLoadDone(...) {
final MyListAdapter adapter = new MyListAdapter(...);
myListFragment.setListAdapter(adapter);
myListFragment.showDetails();
}
I've just gotten an ExpandableListView setup and everything works fine so far. On the group/parent I have a TextView and and Button. The purpose of the list is to have people sample different sounds that are included in the app, and it they click the button then the sounds will be saved to the SD Card. Here's a link to what I have so far: http://imgur.com/djSCIrG
My question is whether or not it's possible that after someone clicks the button and chooses to purchase the pack if it's possible to hide just that one button and not all of the buttons in every group.
Here's is my main layout (expandablelistview_main.xml):
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="fill_parent"
android:layout_height="fill_parent" >
<TextView
android:id="#+id/soundpacktitle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="#dimen/expandablelistview_main_soundpacktitle_topmargin"
android:layout_centerHorizontal="true"
android:text="#string/soundpacktitle"
android:textSize="#dimen/expandablelistview_main_soundpacktitle_textsize" />
<ExpandableListView
android:id="#+id/soundpacklist"
android:layout_width="fill_parent"
android:layout_height="0dp"
android:layout_below="#+id/soundpacktitle"
android:layout_above="#+id/soundpackbottombar"
android:layout_marginTop="#dimen/expandablelistview_main_soundpacklist_topmargin"
android:transcriptMode="disabled"
android:cacheColorHint="#00000000"
android:listSelector="#android:color/transparent" />
</RelativeLayout>
Here is my group/parent layout (expandablelistview_group.xml):
<?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="?android:attr/listPreferredItemHeight"
android:orientation="horizontal" >
<TextView
android:id="#+id/grouptextview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:gravity="center_vertical"
android:layout_marginLeft="#dimen/expandablelistview_group_grouptextview_leftmargin"
android:textSize="#dimen/expandablelistview_group_grouptextview_textsize" />
<Button
android:id="#+id/buypackbutton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center_vertical"
android:layout_alignParentRight="true"
android:focusable="false"
android:focusableInTouchMode="false"
android:text="#string/buypack"
android:padding="#dimen/expandablelistview_group_buypackbutton_padding"
android:textSize="#dimen/expandablelistview_group_buypackbutton_textsize"
android:textStyle="bold" />
</RelativeLayout>
Here is my java class:
public class InAppSounds extends Activity {
private ExpandableListView soundpacklist;
private ArrayList<String> groups;
private ArrayList<ArrayList<ArrayList<String>>> childs;
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.expandablelistview_main);
TextView soundpacktitle = (TextView) findViewById(R.id.soundpacktitle);
soundpacktitle.setTypeface(printbold);
// Declare the ExpandableListView and set's the indicator to the list arrows
soundpacklist = (ExpandableListView) findViewById(R.id.soundpacklist);
soundpacklist.setGroupIndicator(getResources().getDrawable(R.drawable.list_groupselector));
LoadData();
myExpandableAdapter adapter = new myExpandableAdapter(this, groups, childs);
soundpacklist.setAdapter(adapter);
}
// Loads the ExpandableListView with parent and children groups
private void LoadData() {
groups = new ArrayList<String>();
childs = new ArrayList<ArrayList<ArrayList<String>>>();
// String array that stores the parent and child names
String[] soundpackgroups = getResources().getStringArray(R.array.soundpackgroups);
String[] soundpack1 = getResources().getStringArray(R.array.soundpack1);
String[] soundpack2 = getResources().getStringArray(R.array.soundpack2);
String[] soundpack3 = getResources().getStringArray(R.array.soundpack3);
// First Sound Pack and their songs
groups.add(soundpackgroups[0]);
childs.add(new ArrayList<ArrayList<String>>());
for (int a = 0; a < soundpack1.length; a++) {
childs.get(0).add(new ArrayList<String>());
childs.get(0).get(a).add(soundpack1[a]);
}
// Second Sound Pack and their songs
groups.add(soundpackgroups[1]);
childs.add(new ArrayList<ArrayList<String>>());
for (int a = 0; a < soundpack2.length; a++) {
childs.get(1).add(new ArrayList<String>());
childs.get(1).get(a).add(soundpack2[a]);
}
// Third Sound Pack and their songs
groups.add(soundpackgroups[2]);
childs.add(new ArrayList<ArrayList<String>>());
for (int a = 0; a < soundpack3.length; a++) {
childs.get(2).add(new ArrayList<String>());
childs.get(2).get(a).add(soundpack3[a]);
}
}
public class myExpandableAdapter extends BaseExpandableListAdapter {
private final ArrayList<String> groups;
private final ArrayList<ArrayList<ArrayList<String>>> children;
private final Context context;
public myExpandableAdapter(Context context, ArrayList<String> groups,
ArrayList<ArrayList<ArrayList<String>>> children) {
this.context = context;
this.groups = groups;
this.children = childs;
}
#Override
public boolean areAllItemsEnabled() {
return true;
}
#Override
public ArrayList<String> getChild(int groupPosition, int childPosition) {
return children.get(groupPosition).get(childPosition);
}
#Override
public long getChildId(int groupPosition, int childPosition) {
return childPosition;
}
#Override
public View getChildView(int groupPosition, final int childPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
String child = getChild(groupPosition, childPosition).get(0);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.expandablelistview_child, null);
}
// TypeFace variable for the PrintBold
printbold = Typeface.createFromAsset(getAssets(), "fonts/PrintBold.otf");
TextView childtxt = (TextView) convertView.findViewById(R.id.childtextview);
childtxt.setTypeface(printbold);
childtxt.setText(child);
return convertView;
}
#Override
public int getChildrenCount(int groupPosition) {
return children.get(groupPosition).size();
}
#Override
public String getGroup(int groupPosition) {
return groups.get(groupPosition);
}
#Override
public int getGroupCount() {
return groups.size();
}
#Override
public long getGroupId(int groupPosition) {
return groupPosition;
}
#Override
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
final String group = getGroup(groupPosition);
if (convertView == null) {
LayoutInflater infalInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = infalInflater.inflate(R.layout.expandablelistview_group, null);
}
// TypeFace variable for the PrintBold
printbold = Typeface.createFromAsset(getAssets(), "fonts/PrintBold.otf");
TextView grouptxt = (TextView) convertView.findViewById(R.id.grouptextview);
grouptxt.setTypeface(printbold);
grouptxt.setText(group);
final Button buypackbutton = (Button) convertView.findViewById(R.id.buypackbutton);
buypackbutton.setClickable(true);
buypackbutton.setOnClickListener(new OnClickListener() {
#Override
public void onClick(View view) {
AlertDialog.Builder alert = new AlertDialog.Builder(InAppSounds.this);
if (group.equals("Pack #1")) {
alert.setCancelable(false);
alert.setTitle(getString(R.string.buypacktitle));
alert.setIcon(getResources().getDrawable(R.drawable.ic_audioicon));
alert.setMessage(getString(R.string.buypackmsg));
alert.setPositiveButton("Yes", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
// check to make sure the SD card is mounted
// if not display an AlertDialog
if (!isSDPresent()) {
sdcardalert();
}
else {
// this will erase the button in all the groups, not just this group
buypackbutton.setVisibility(View.INVISIBLE);
}
}
});
alert.setNegativeButton("No", new DialogInterface.OnClickListener() {
#Override
public void onClick(DialogInterface dialog, int which) {
dialog.cancel();
}
});
alert.show();
}
}
});
return convertView;
}
#Override
public boolean hasStableIds() {
return true;
}
#Override
public boolean isChildSelectable(int groupPosition, int childPosition) {
return true;
}
}
}
I would appreciate any guidance in this matter. Thanks
Yes. It's easy. All that you have to do is get a reference to your button and set the visibility to gone. Like this:
Button sampleButton = (Button) findViewById(R.id.sample_button);
sampleButton.setVisiblity(View.GONE);
Note: when you set it to View.GONE the layout space that was initially given to it is also removed. If you just want to remove the button and keep the layout space use
View.INVISIBLE instead.
EDIT: Here's how I would keep the button from reappearing: First, I would use a boolean to track the status of the button while the activity is active. Then in your override of getChildView I would check this boolean and set the visibility accordingly. Maybe insert something like this into the getChildView callback to keep the button from reappearing when the list item is clicked:
if (!showButton) {
Button button = (Button) findViewById(R.id.sample_button);
button.setVisibility(View.GONE);
}
As for coming back to the screen. To keep track of the whether not to show the button I would use a boolean and store it in SharedPreferences. Then, also in the getChildView callback, check the status of the boolean and set it accordingly. Something like this:
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
boolean showButtonStatusPref = settings.getBoolean("showButton", true);
if(!showButtonStatusPref) {
Button button = (Button) findViewById(R.id.sample_button);
button.setVisibility(View.GONE);
}
The only other thing you need to do is manage the status of each button.
EDIT 2: I completely overlooked the fact that the same layout is used for the child views (duh! brain cramp :)).
You could still use shared preferences to keep track of which samples have been downloaded (you could use Set for this). You would also need to create a way to assign "identifiers" to each sample. From there all that you would have to do is perform a check every time getChildView() is called and, if the Set contains the selected sample identifier, set the button visibility to gone. That should take care of showing the button when the sample hasn't been downloaded and not showing the button when the sample has been downloaded. Maybe something like this in the getChildView():
Set<String> defaultSet = new SortedSet<String>();
defaultSet.add("Nothing downloaded");
SharedPreferences settings = getSharedPreferences(PREFS_NAME, 0);
SortedSet<String> listOfDowloaded = settings.getStringSet("isDownloadedList", );
if (listOfDownLoaded.contains(sampleDownloadIdentifier)) {
Button button = (Button) findViewById(R.id.some_id);
button.setVisiblity(View.GONE);
}
I have an Activity that hosts multiple fragments using the actionbar's tab functionality. One of those fragments contains a ListView. Upon this tab being selected, I'd like to select a certain item.
To do this programmatically, I use the following code (where calls is the ListView)
private void selectItem(int position)
{
long itemId = calls.GetItemIdAtPosition(position);
calls.PerformItemClick(calls, position, itemId);
}
If this ListView has been rendered, and I'm calling this, no problem. However, if I call it from onResume, then the code executes but nothing is selected in the end. I figure this is because at the point where I'm calling selectItem, not all items of the ListView have been rendered yet. If however I start off a background thread, sleep for a couple hundred milliseconds, then run the same code (in the ui thread of course), everything is fine, but this is an ugly hack.
Now you might be wondering, "why isn't he using calls.setSelection"? The thing is, I'm using a custom layout that performs expansion - so I need to actually click on the item I want selected (which in turn triggers the layout expansion for the item selected). However, I can call the code that is performed on PerformItemClick directly, the results will be the same (the layout expansion isn't performed).
Isn't there any way for me to catch the "Listview has finished rendering all viewable items" point in time, and then execute my selectItem call at that point? In ASP.NET, I have an event on every UI item telling me when it is done rendering, so I do item selection at that point but I haven't found anything.
Regards
Stephan
Here's the Adapter I'm using
public class ActiveCallsAdapter: ObservableAdapter<Call>
{
public ActiveCallsAdapter(Activity activity, ObservableCollection<Call> calls)
: base(activity, calls)
{
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
//View view = convertView;
//if (view == null) // no view to re-use, create new
// view = context.LayoutInflater.Inflate(Resource.Layout.Call, null);
SetTextView(view, Resource.Id.CallerName, item.CallerName);
SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);
SetTextView(view, Resource.Id.CallStatus, item.State.ToString());
SetTextView(view, Resource.Id.CallDuration, item.Duration);
return view;
}
public void Update(LinearLayout view, Call item)
{
SetTextView(view, Resource.Id.CallerName, item.CallerName);
SetTextView(view, Resource.Id.CallerNumber, item.CallerNumber);
string identifier = "callState_" + item.State.ToString();
int resourceId = Application.Context.Resources.GetIdentifier(identifier, "string", Application.Context.PackageName);
string callStateString = item.State.ToString();
if (resourceId != 0)
{
try
{
callStateString = Application.Context.Resources.GetString(resourceId);
}
catch (Exception e)
{
AndroidLogModel.Model.AddLogMessage("ActiveCallsAdapter", "Unable to find call state string with resource id " + resourceId + " state string: " + identifier, 3);
}
}
SetTextView(view, Resource.Id.CallStatus, callStateString);
//SetTextView(view, Resource.Id.CallDuration, item.Duration);
}
public void UpdateDuration(LinearLayout view, Call item)
{
SetTextView(view, Resource.Id.CallDuration, item.Duration);
}
}
And the base class of that adapter
public class ObservableAdapter<T>: BaseAdapter<T>
{
protected readonly Activity context;
protected readonly ObservableCollection<T> items;
public ObservableAdapter(Activity context, ObservableCollection<T> collection)
{
this.context = context;
this.items = collection;
//this.collection.CollectionChanged += new System.Collections.Specialized.NotifyCollectionChangedEventHandler(collection_CollectionChanged);
this.items.CollectionChanged += (sender, e) => NotifyDataSetChanged();
}
void collection_CollectionChanged(object sender, System.Collections.Specialized.NotifyCollectionChangedEventArgs e)
{
NotifyDataSetChanged();
}
public override T this[int position]
{
get { return items[position]; }
}
public override int Count
{
get { return items.Count; }
}
public override long GetItemId(int position)
{
return position;
}
public override View GetView(int position, View convertView, ViewGroup parent)
{
var item = items[position];
var view = (convertView ?? context.LayoutInflater.Inflate(Resource.Layout.Call, parent, false)) as LinearLayout;
// configure view here
return view;
}
protected void SetTextView(LinearLayout view, int id, string text)
{
var textView = view.FindViewById<TextView>(id);
if (textView != null)
textView.SetText(text, TextView.BufferType.Normal);
}
}
My Mono skills are limited so I don't know if I fully understood your adapter, anyway I've adapted some old code and made an adapter that expands a single item when click, also it will move the ListView in onResume to a desired position:
private static class CustomAdapter extends BaseAdapter {
// the data
private ArrayList<String> mData;
// an int pointing to a position that has an expanded layout,
// for simplicity I assume that you expand only one item(otherwise use
// an array or list)
private int mExpandedPosition = -1; // -1 meaning no expanded item
private LayoutInflater mInflater;
public CustomAdapter(Context context, ArrayList<String> items) {
mInflater = LayoutInflater.from(context);
mData = items;
}
public void setExpandedPosition(int position) {
// if the position equals mExpandedPosition then we have a click on
// the same row so simply toggle the row to be gone again
if (position == mExpandedPosition) {
mExpandedPosition = -1;
} else {
// else change position of the row that was expanded
mExpandedPosition = position;
}
// notify the adapter
notifyDataSetChanged();
}
#Override
public int getCount() {
return mData.size();
}
#Override
public String getItem(int position) {
return mData.get(position);
}
#Override
public long getItemId(int position) {
return position;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
convertView = mInflater.inflate(R.layout.ad_expandedelement,
parent, false);
}
((TextView) convertView.findViewById(R.id.textView1))
.setText(getItem(position));
// see if there is an expanded position and if we are at that
// position
if (mExpandedPosition != -1 && mExpandedPosition == position) {
// if yes simply expand the layout
convertView.findViewById(R.id.button1).setVisibility(
View.VISIBLE);
} else {
// this is required, we must revert any possible changes
// otherwise the recycling mechanism will hurt us
convertView.findViewById(R.id.button1).setVisibility(View.GONE);
}
return convertView;
}
}
The onListItemClick will simply be:
#Override
protected void onListItemClick(ListView l, View v, int position, long id) {
// set the expanded(or collapsed if it's a click on the same row that
// was previously expanded) row in the adapter
((CustomAdapter) getListView().getAdapter())
.setExpandedPosition(position);
}
and in onResume will have:
#Override
protected void onResume() {
super.onResume();
// set the position to the desired element
((CustomAdapter) getListView().getAdapter()).setExpandedPosition(15);
// set the selection to that element so we can actually see it
// this isn't required but has the advantage that it will move the
// ListView to the desired
// position if not visible
getListView().setSelection(15);
}
The R.layout.ad_expandedelement is a simple vertical LinearLayout with a TextView and an initially hidden(visibility set to gone) Button. For this Button I change the visibility to simulate expanding/collapsing a row in the ListView. You should be able to understand my code, if you want I can post on github the full sample.
While I'm not sure of the exact equivalent in C#/Mono, the Android framework provides a callback on Activity called onWindowFocusChanged() that indicates the period when the Window associated with a given Activity is visible to the user. You may have better luck waiting to call your selection method until that time, as the ListView should be measured and laid out by that point. In Java, it would be something like this:
#Override
public void onWindowFocusChanged (boolean hasFocus) {
if (hasFocus) {
selectItem(position);
}
}
You may need to have a bit more logic in there, this callback is directly associated with window focus and isn't a true lifecycle method. I can get called multiple times if you are displaying Dialogs or doing other similar operations.