I have a class that extends ListFragment, and it overrides the OnListItemClick method. I am also doing this in another ListFragment the same way (and the method gets called). I am wondering why the method does not get called when I click on the list item?
Here is the code:
package org.doraz.fdboard.activity;
import java.sql.SQLException;
import java.util.Collection;
import org.doraz.fdboard.FantasyDraftBoardApplication;
import org.doraz.fdboard.R;
import org.doraz.fdboard.activity.DraftBoardActivity.PlayerDetailsActivity;
import org.doraz.fdboard.domain.FantasyLeague;
import org.doraz.fdboard.domain.FantasyTeam;
import org.doraz.fdboard.domain.Player;
import org.doraz.fdboard.domain.Position;
import org.doraz.fdboard.repository.FantasyDraftBoardRepository;
import org.doraz.fdboard.view.PlayerAdapter;
import org.doraz.fdboard.view.PlayerCursorAdapter;
import android.app.FragmentTransaction;
import android.app.ListFragment;
import android.app.ProgressDialog;
import android.content.Intent;
import android.database.Cursor;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Adapter;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ListView;
public class ListPlayersFragment extends ListFragment implements OnItemClickListener {
private final static String TAG = "ListPlayersFragment";
private boolean mDualPane;
private int curSelectedPlayerPosition = 0;
private PlayerCursorAdapter playerAdapter;
private QueryPlayersTask currentTask;
private FantasyDraftBoardRepository repository;
private FantasyLeague fantasyLeague;
private FantasyTeam fantasyTeam;
private Position position;
private ProgressDialog progressDialog;
/* (non-Javadoc)
* @see android.app.ListFragment#onActivityCreated(android.os.Bundle)
*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
//Check for the view players view along with the pane
View teamListFragment = getActivity().findViewById(R.id.team_list_fragment);
mDualPane = teamListFragment != null && teamListFragment.getVisibility() == View.VISIBLE;
if(mDualPane) {
Log.i(TAG, "Setting list select mode to single");
getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);
}
getListView().setSmoothScrollbarEnabled(false);
}
/* (non-Javadoc)
* @see android.app.ListFragment#onListItemClick(android.widget.ListView, android.view.View, int, long)
*/
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Log.i(TAG, "[onListItemClick] Selected Position "+ position);
selectPlayer(position);
}
/* (non-Javadoc)
* @see android.app.Fragment#onSaveInstanceState(android.os.Bundle)
*/
@Override
public void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
outState.putInt("curSelectedPlayerPosition", curSelectedPlayerPosition);
outState.putInt("fantasyLeague", fantasyLeague.getLeaguePID());
if(!Position.ALL.equals(position)) {
outState.putInt("position", position.getPositionPID());
}
if(!(FantasyTeam.ALL_AVAILABLE_TEAM.equals(fantasyTeam) || FantasyTeam.ALL_TEAM.equals(fantasyTeam))) {
outState.putInt("fantasyTeam", fantasyTeam.getTeamPID());
}
}
/**
* Selects the player at this position in the current list
* @param listPosition
*/
public void selectPlayer(int listPosition) {
curSelectedPlayerPosition = listPosition;
Player player = (Player) playerAdapter.getItem(listPosition);
Log.i(TAG, "Select Player ("+ listPosition +", "+ player.getName() +") called");
//Get the player url
String mPlayerUrl = player.getPlayerUrl();
Log.d(TAG, "Selected Player URL: "+mPlayerUrl);
if(mDualPane) {
if(getListView().getSelectedItemPosition() == listPosition) {
//Remove the selected item
return;
}
//Select the item
getListView().setItemChecked(listPosition, true);
Log.d(TAG, "Creating ViewPlayerFragment");
ViewPlayerFragment vpf = ViewPlayerFragment.newInstance(mPlayerUrl);
ListTeamsFragment ltf = (ListTeamsFragment) getFragmentManager().findFragmentById(R.id.team_list_fragment);
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.player_web_view_fragment, vpf);
if(ltf != null && !ltf.isHidden()) {
//Hide the list of teams
ft.hide(ltf);
ft.addToBackStack(null);
}
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_OPEN);
ft.commit();
Log.d(TAG, "Committed to ViewPlayerFragment");
}
else {
Log.d(TAG, "Launching new activity to view player");
Intent intent = new Intent();
intent.setClass(getActivity(), PlayerDetailsActivity.class);
intent.putExtra("playerURL", mPlayerUrl);
startActivityForResult(intent, 0);
}
}
public void clearSelectedPlayer() {
Log.i(TAG, "Clearing selected player");
curSelectedPlayerPosition = -1;
//Clear the list view
getListView().clearChoices();
ViewPlayerFragment vpf = (ViewPlayerFragment) getFragmentManager().findFragmentById(R.id.player_web_view_fragment);
if(vpf != null) {
Log.d(TAG, "Closing ViewPlayerFragment");
//Close the ViewPlayersFragment
FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.remove(vpf);
ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_CLOSE);
ft.commit();
Log.d(TAG, "Closed ViewPlayerFragment");
}
}
/**
* Initializes the player adapter
*/
private void initializePlayerAdapter(Cursor cursor) {
if(playerAdapter != null)
return;
playerAdapter = new PlayerCursorAdapter(getActivity(), cursor, (DraftBoardActivity)getActivity(), repository);
setListAdapter(playerAdapter);
setEmptyText(getText(R.string.no_players_msg));
}
/**
* Initializes the player adapter
*/
public void setPlayersCursor(Cursor cursor) {
if(playerAdapter == null) {
initializePlayerAdapter(cursor);
}
else {
playerAdapter.changeCursor(cursor);
}
}
/**
* Drafts a player
*
* @param mPlayer the player to draft
* @param fantasyTeam the fantasy team
* @param value the draft value
*/
public void draftPlayer(Player mPlayer, FantasyTeam fantasyTeam, Double value) {
mPlayer.setFantasyTeam(fantasyTeam);
mPlayer.setDraftValue(value);
mPlayer.setDrafted(true);
fantasyTeam.draftPlayer(mPlayer);
try {
repository.savePlayer(mPlayer);
repository.saveFantasyTeam(fantasyTeam);
} catch (SQLException e) {
Log.e(TAG, "Error drafting player", e);
}
//Refresh the query
refresh();
}
/**
* Refreshes the players list
*/
public void refresh(){
if(fantasyLeague == null) {
fantasyLeague = ((FantasyDraftBoardApplication) (getActivity().getApplication())).getCurrentFantasyLeague();
}
if(fantasyTeam == null) {
fantasyTeam = FantasyTeam.ALL_AVAILABLE_TEAM;
}
if(position == null) {
position = Position.ALL;
}
if(currentTask != null) {
currentTask.cancel(true);
}
if(progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
}
progressDialog = ProgressDialog.show(getActivity(), null, "Loading...");
currentTask = new QueryPlayersTask(fantasyLeague, fantasyTeam, position, repository);
currentTask.execute();
}
/**
* Sets the fantasyLeague
* @param fantas开发者_JAVA技巧yLeague the fantasyLeague to set
*/
public void setFantasyLeague(FantasyLeague fantasyLeague) {
this.fantasyLeague = fantasyLeague;
}
/**
* Sets the fantasyTeam
* @param fantasyTeam the fantasyTeam to set
*/
public void setFantasyTeam(FantasyTeam fantasyTeam) {
this.fantasyTeam = fantasyTeam;
}
/**
* Sets the position
* @param position the position to set
*/
public void setPosition(Position position) {
this.position = position;
}
/**
* Sets the repository
* @param repository the repository to set
*/
public void setRepository(FantasyDraftBoardRepository repository) {
this.repository = repository;
}
private class QueryPlayersTask extends AsyncTask<Integer, Integer, Cursor> {
private FantasyLeague fantasyLeague;
private FantasyTeam fantasyTeam;
private Position position;
private FantasyDraftBoardRepository repository;
public QueryPlayersTask(FantasyLeague fantasyLeague, FantasyTeam fantasyTeam, Position position, FantasyDraftBoardRepository repository) {
this.fantasyLeague = fantasyLeague;
this.fantasyTeam = fantasyTeam;
this.position = position;
this.repository = repository;
}
@Override
protected Cursor doInBackground(Integer... params) {
try {
return repository.queryForPlayersCursor(position, fantasyLeague, fantasyTeam);
} catch (SQLException e) {
Log.e("QueryPlayersTask", "Unable to query for players", e);
}
return null;
}
/* (non-Javadoc)
* @see android.os.AsyncTask#onPostExecute(java.lang.Object)
*/
@Override
protected void onPostExecute(Cursor result) {
super.onPostExecute(result);
if(!isCancelled()) {
//Update the player cursor
updatePlayerCursor(result);
}
}
}
/**
* Updates the player cursor
* @param c the player cursor
*/
private final void updatePlayerCursor(Cursor c){
Log.d(TAG, "Updating player cursor.");
if(playerAdapter == null)
initializePlayerAdapter(c);
else
playerAdapter.changeCursor(c);
if(progressDialog != null) {
progressDialog.dismiss();
progressDialog = null;
}
//Clear the current task
currentTask = null;
}
@Override
public void onItemClick(AdapterView<?> adapter, View arg1, int listPosition, long id) {
Log.d(TAG, "[onItemClick] Clicked item "+position);
selectPlayer(listPosition);
}
}
Any assistance would be much appreciated. I can get the desired effect by implementing a few other listeners and assigning it to each list item, but I think this is the correct way to do it and it SHOULD work. I just don't know why it doesn't
Thanks in Advance.
If you have an item in your layout that can steal input from other components like a CheckBox, that component needs to be defined as not focusable.
Here is an elegant solution which allows onListItemClick
to fire the way you think it should, and also allows any child views to receive events (or not, depending on how you want to set it.) For the question posed, this would allow for keeping the checkbox subview. Please take care to consider whether this is an intuitive experience for users.
On the root ViewGroup of all list items, set the descendantFocusability
attribute in XML:
android:descendantFocusability="blocksDescendants"
Android SDK documentation of descendantFocusability. Has been part of the SDK since Android 1.0. Other settings for android:descendantFocusability
include "beforeDescendants"
and "afterDescendants"
.
Example cluster_layout.xml which sets descendantFocusability
(List items applied to cluster_list.xml via an ArrayAdapter):
<?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:id="@+id/cluster_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:background="#e0e0e0"
android:clickable="false"
android:descendantFocusability="blocksDescendants" >
<FrameLayout
android:id="@+id/cluster_sentiment_handle"
android:layout_width="40dp"
android:layout_height="match_parent"
android:background="@color/handle_red" />
<!-- other elements -->
</LinearLayout>
Example cluster_list.xml, which has no bearing on this solution other than to show that it's intended for use in a ListFragment by having a ListView element with the id @id/android:list:
<?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"
android:padding="8dp"
android:background="#f6f6f6" >
<include layout="@layout/cluster_header" />
<ListView
android:id="@id/android:list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="top|center_horizontal"
android:gravity="center_horizontal"
android:divider="@android:color/transparent"
android:dividerHeight="8dp"
android:fastScrollEnabled="true" />
</LinearLayout>
I encountered the same problem after converting my app from a ListActivity to a ListFragment.
The ListFragment was the only fragment of the activity (no tabs, etc), but the ListFragment was not seeing any of the clicks.
The problem was that my Activity was still defined as a ListActivity. Changed it to a activity fixed it, and now the ListFragment sees the clicks.
public class MyActivity extends ListActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
}
should have been:
public class MyActivity extends Activity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_layout);
}
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:orientation="vertical" >
<fragment class="com.davidmarkscott.myapp.MyFragment"
android:id="@+id/fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Similar to others, I have a CustomAdapter with my ListFragment, which overrides getView() to provide the View for each row. Setting the listeners on the view which is returned worked for me. Example:
public class MyAdapter extends ArrayAdapter<SomeType> {
private Context context;
private List<SomeType> objects;
public MyAdapter(Context context, int resource, List<SomeType> objects) {
super(context, resource, objects);
this.context = context;
this.objects = objects;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LayoutInflater inflater = (LayoutInflater)context.getSystemService (Context.LAYOUT_INFLATER_SERVICE);
View rowView = inflater.inflate(R.layout.list_subscription_item, null);
TextView subTo = (TextView)rowView.findViewById(R.id.itemName);
SomeType sub = objects.get(position);
subTo.setText(sub.getName() + sub.getId());
rowView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
Toast.makeText(context, "from long click", Toast.LENGTH_SHORT).show();
return false;
}
});
rowView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Toast.makeText(context, "from click", Toast.LENGTH_SHORT).show();
}
});
return rowView;
}
}
Only workaround so far, use a Fragment
and a ListView
then use setOnItemClickListener()
which would make the ListFragment
, and all its' convenience obsolete...
After I created a new class containing exactly the same code (only the class name changed) and built the project on a different machine it "magically" worked (Still same API-Level). Below the code that worked. I also tried project->clean which usually fixes most problems, but that did not work eiter.
public class ContainerFragment extends ListFragment {
private ContainerFragmentListener listener;
public ContainerFragment(ContainerFragmentListener listener) {
super();
this.listener = listener;
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
setListAdapter(new ContainerAdapter(getActivity()));
}
@Override
public void onListItemClick(ListView l, View v, int position, long id) {
super.onListItemClick(l, v, position, id);
Container container = (Container) getListAdapter().getItem(position);
listener.containerSelected(container.id);
}
}
I also encountered the same problem. Using ListFragment
which was inflated as a tab content of a tab inside a TabHost
.
One of my TabHost
has 3 tabs, the onListItemClick()
was called correctly.
But the other TabHost has only 2 tabs, the onListItemClick()
was not called.
So I decided to implement the work around that Marcel and H9kDroid mentioned above and it worked fine. Thanks for your answer.
Update: After messing around for a few hours, it seems that the problem related to the layout of the list item and the adapter of the ListView.
Inside my list item layout, there is one check box and the state of the check box is toggled inside the adapter. I think after the toggling, the ListView
messed up and cannot deliver the call to onListItemClick()
.
I just changed the layout, remove the check box and it works fine.
Cheers.
Echoing what @auselen said above, in my case I had android:textIsSelectable="true"
set in a TextView
, and it was stealing the callback.
Setting this to false
solved the problem.
I found out that setting the property android:focusable to false on any child view of a list item prevents the click handler from being fired. The following setup works with a ListFragment.
navigation_fragment.xml:
<ListView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigationEntries"
android:layout_height="wrap_content"
android:layout_width="fill_parent" />
navigation_entry.xml:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/navigationEntry"
android:orientation="vertical"
android:layout_height="wrap_content"
android:layout_width="fill_parent"
<!-- enhance this -->
<ImageView
android:layout_width="fill_parent"
android:layout_height="1dp"
android:background="@android:color/darker_gray" />
I spent hours trying to figure out my own similar problem. I had a fairly simple compound text view that contained a checkbox and two textviews and a simple custom adapter. After trying various things that were suggested, I took out views until I just had one textview. Still didnt work. Do you know what did? I just deleted the xml layout file for the custom view I was using and made it again, piece by piece. Made the same exact thing with the same name. It just didn't like that file for some reason. So there you go. Some weird glitch in Eclipse I guess.
I had called listfragment.getListView().setOnItemClickListener() and also overridden onListItemClick() on the ListFragment and that resulted in onListItemClick not being called. I deleted the setOnItemClickListener() method and the listFragment's onListItemClick method was called instead.
A CheckBox is focusable by default. This means that a click on a list item will be interpreted as toggling the CheckBox and will not reach your onListItemClick(…) method. This internal quirk of ListView means that any focusable widget that appears in a list item layout (like a CheckBox or a Button) should be made non-focusable to ensure that a click on a list item will work as you expect. Because your CheckBox only reports information and is not tied to any application logic, there is a simple solution. You can define the CheckBox as not focusable.
from The Big Nerd Ranch Guide book
精彩评论