web-dev-qa-db-fra.com

ListFragment OnListItemClick n'étant pas appelé

J'ai une classe qui étend ListFragment et remplace la méthode OnListItemClick. Je le fais également dans un autre ListFragment de la même manière (et la méthode est appelée). Je me demande pourquoi la méthode n'est pas appelée lorsque je clique sur l'élément de la liste.

Voici le 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 fantasyLeague 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);
    }


}

Toute aide serait grandement appréciée. Je peux obtenir l'effet souhaité en mettant en place quelques autres écouteurs et en les affectant à chaque élément de la liste, mais je pense que c'est la bonne façon de le faire et que cela DEVRAIT fonctionner. Je ne sais pas pourquoi ça ne marche pas

Merci d'avance.

32
Steve

Si vous avez un élément dans votre mise en page qui peut voler des entrées d'autres composants, comme un CheckBox, ce composant doit être défini comme non focusable.

97
auselen

Voici une solution élégante qui permet à onListItemClick de déclencher ce que vous pensez, et permet également à toute vue enfant de recevoir des événements (ou non, en fonction de la manière dont vous voulez le définir). la sous-vue de la case à cocher. Veuillez prendre le soin de déterminer s'il s'agit d'une expérience intuitive pour les utilisateurs.

Sur le ViewGroup racine de tous les éléments de la liste, définissez l'attribut descendantFocusability en XML:

Android:descendantFocusability="blocksDescendants"

Documentation SDK Android de descendantFocusability . Fait partie du SDK depuis Android 1.0. Les autres paramètres de Android:descendantFocusability incluent "beforeDescendants" et "afterDescendants".

Exemple cluster_layout.xml qui définit descendantFocusability (Liste des éléments appliqués à cluster_list.xml via un 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>

Exemple cluster_list.xml, qui n'a aucune incidence sur cette solution si ce n'est de montrer qu'elle est destinée à être utilisée dans un ListFragment en ayant un élément ListView avec l'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>
14
joweeba

J'ai rencontré le même problème après la conversion de mon application ListActivity en ListFragment.

Le ListFragment était le seul fragment de l'activité (pas d'onglets, etc.), mais le ListFragment ne voyait aucun clique.

Le problème était que mon activité était toujours définie en tant que ListActivity. Changé en activité corrigé, et maintenant le ListFragment voit les clics.

    public class MyActivity extends ListActivity {
        @Override
        public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
            setContentView(R.layout.my_layout);
    }

aurait du être:

    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>
14
davidmarkscott

Semblable à d'autres, j'ai un CustomAdapter avec mon ListFragment, qui remplace getView () pour fournir la vue pour chaque ligne. Régler les auditeurs sur la vue renvoyée a fonctionné pour moi. Exemple:

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;
}
}
4
Todd DeLand

J'ai aussi rencontré le même problème. Utilisation de ListFragment qui a été gonflé en tant que contenu d'onglet d'un onglet à l'intérieur de TabHost

Un de mes TabHost a 3 onglets, la onListItemClick() a été appelée correctement. 

Mais l'autre TabHost n'a que 2 onglets, la onListItemClick() n'a pas été appelée.

J'ai donc décidé de mettre en œuvre le travail autour de Marcel et H9kDroid mentionnés ci-dessus et cela a bien fonctionné. Merci pour votre réponse.

Mise à jour: Après quelques heures de jeu, il semble que le problème soit lié à la présentation de l'élément de liste et à l'adaptateur de ListView.

Dans la disposition de mon élément de liste, il y a une case à cocher et l'état de la case à cocher est basculé à l'intérieur de l'adaptateur. Je pense qu'après la bascule, la ListView a été gâchée et ne peut pas livrer l'appel à onListItemClick().

Je viens de changer la mise en page, décochez la case et cela fonctionne bien.

À votre santé.

3
ck1910

Seule solution de contournement à ce jour, utilisez Fragment et ListView, puis setOnItemClickListener(), ce qui rendrait la ListFragment et toutes ses commodités obsolètes ... Après avoir créé une classe contenant exactement le même code (seul le nom de la classe a été modifié) et construit le projet sur une machine différente, il a fonctionné "magiquement" (toujours le même niveau API). Ci-dessous le code qui a fonctionné. J'ai aussi essayé project-> clean qui résout généralement la plupart des problèmes, mais cela n'a pas fonctionné.

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);
    }


}
3
Marcel

En écho à ce que @auselen a dit ci-dessus, dans mon cas, j'avais Android:textIsSelectable="true" défini dans un TextView et le vol était rappelé. 

Définir ceci à false a résolu le problème.

2
Jonathan Lin

J'ai découvert que définir la propriété Android: focusable sur false sur toute vue enfant d'un élément de liste empêche le gestionnaire de clics d'être déclenché. La configuration suivante fonctionne avec un 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" />

1
s.froehlich

J'ai passé des heures à essayer de résoudre mon propre problème similaire. J'avais une vue de texte composé assez simple contenant une case à cocher, deux vues de texte et un simple adaptateur personnalisé. Après avoir essayé diverses choses qui ont été suggérées, j'ai retiré des points de vue jusqu'à ce que je ne dispose plus que d'une seule vue. Je n'ai toujours pas travaillé. Sais-tu ce que tu as fait? Je viens de supprimer le fichier de mise en page XML de la vue personnalisée que j'utilisais et je l'ai refait, pièce par pièce. Fait exactement la même chose avec le même nom. Il n'a tout simplement pas aimé ce fichier pour une raison quelconque. Alors voilà. Un petit problème étrange dans Eclipse, je suppose.

1
slida

J'avais appelé listfragment.getListView (). SetOnItemClickListener () et également surchargé onListItemClick () sur le ListFragment, ce qui a empêché onListItemClick de s'appeler. J'ai supprimé la méthode setOnItemClickListener () et la méthode onListItemClick de listFragment a été appelée à la place.

0
flobacca