it-swarm.com.de

ListFragment OnListItemClick wird nicht aufgerufen

Ich habe eine Klasse, die ListFragment erweitert, und überschreibt die OnListItemClick-Methode. Ich mache das auch in einem anderen ListFragment auf die gleiche Weise (und die Methode wird aufgerufen). Ich frage mich, warum die Methode nicht aufgerufen wird, wenn ich auf das Listenelement klicke?

Hier ist der 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);
    }


}

Jede Hilfe wäre sehr dankbar. Ich kann den gewünschten Effekt erzielen, indem ich einige andere Zuhörer implementiere und sie jedem Listenelement zuweise, aber ich denke, dass dies der richtige Weg ist, und es sollte funktionieren. Ich weiß einfach nicht, warum es nicht so ist

Danke im Voraus.

32
Steve

Wenn sich in Ihrem Layout ein Element befindet, das Eingaben anderer Komponenten wie einer CheckBox stehlen kann, muss diese Komponente als nicht fokussierbar definiert werden.

97
auselen

Hier ist eine elegante Lösung, die es onListItemClick erlaubt, so zu schießen, wie Sie es für richtig halten, und auch, dass untergeordnete Ansichten Ereignisse empfangen (oder nicht, je nachdem, wie Sie sie festlegen möchten.) die Checkbox-Unteransicht. Bitte achten Sie darauf, ob dies für Benutzer eine intuitive Erfahrung ist.

Legen Sie in der Stamm-Ansichtsgruppengruppe aller Listenelemente das Attribut descendantFocusability in XML fest:

Android:descendantFocusability="blocksDescendants"

Android-SDK-Dokumentation von descendantFocusability . Ist seit Android 1.0 Teil des SDK. Andere Einstellungen für Android:descendantFocusability umfassen "beforeDescendants" und "afterDescendants".

Beispiel cluster_layout.xml, das descendantFocusability setzt (Auflisten von Elementen, die über einen ArrayAdapter auf cluster_list.xml angewendet wurden):

<?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>

Beispiel cluster_list.xml, das keine Auswirkung auf diese Lösung hat, außer um zu zeigen, dass sie zur Verwendung in einem ListFragment vorgesehen ist, indem ein ListView-Element mit der ID 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

Beim Konvertieren meiner App von einer ListActivity in ein ListFragment ist das gleiche Problem aufgetreten.

Das ListFragment war das einzige Fragment der Aktivität (keine Registerkarten usw.), aber das ListFragment sah keine Klicks.

Das Problem war, dass meine Aktivität immer noch als ListActivity definiert wurde. Es wurde zu einer Aktivität geändert, und das ListFragment sieht nun die Klicks.

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

gewesen sein sollte:

    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

Ähnlich wie bei anderen habe ich mit meinem ListFragment einen CustomAdapter, der getView () überschreibt, um die View für jede Zeile bereitzustellen. Die Zuhörer auf die Ansicht einzustellen, die für mich zurückgegeben wurde, funktionierte für mich. Beispiel:

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

Ich bin auch auf das gleiche Problem gestoßen. Verwenden von ListFragment, das als Tabulatorinhalt einer Registerkarte innerhalb von TabHost aufgeblasen wurde. 

Eine meiner TabHost hat 3 Registerkarten, die onListItemClick() wurde korrekt aufgerufen. 

Der andere TabHost hat jedoch nur 2 Registerkarten, die onListItemClick() wurde nicht aufgerufen.

Also beschloss ich, die Arbeit um das oben erwähnte Marcel und H9kDroid herum zu implementieren und es hat gut funktioniert. Danke für deine Antwort.

Update: Nachdem Sie einige Stunden herumgespielt hatten, schien das Problem auf das Layout des Listenelements und des Adapters der ListView zurückzuführen zu sein.

In meinem Listenelement-Layout gibt es ein Kontrollkästchen, und der Status des Kontrollkästchens wird im Adapter umgeschaltet. Ich denke, nach dem Umschalten ist die ListView durcheinander geraten und kann den Anruf nicht an onListItemClick() übergeben.

Ich habe gerade das Layout geändert, das Kontrollkästchen entfernt und es funktioniert einwandfrei.

Prost.

3
ck1910

Als bisherige Problemumgehung verwenden Sie eine Fragment und eine ListView und dann setOnItemClickListener(), wodurch die ListFragment und ihre gesamte Bequemlichkeit obsolet werden ... Nachdem ich eine neue Klasse erstellt habe, die genau denselben Code enthält (nur der Klassenname wurde geändert) und baute das Projekt auf einer anderen Maschine, die "magisch" funktionierte (immer noch auf gleicher API-Ebene). Unter dem Code, der funktioniert hat. Ich habe auch project-> clean ausprobiert, was meistens die meisten Probleme behebt, aber das hat eiter nicht funktioniert.

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

In Anlehnung an @auselen oben hatte ich Android:textIsSelectable="true" in eine TextView gesetzt, und der Callback wurde gestohlen. 

Wenn Sie hier false einstellen, wurde das Problem gelöst.

2
Jonathan Lin

Ein CheckBox ist standardmäßig fokussierbar. Dies bedeutet, dass ein Klick auf ein Listenelement als Umschalten der CheckBox interpretiert wird und nicht zu Ihrer onListItemClick (…) -Methode führt. Diese interne Besonderheit von ListView bedeutet, dass jedes fokussierbare Widget, das in einem Listenelement-Layout angezeigt wird (z. B. ein Kontrollkästchen oder eine Schaltfläche), nicht fokussierbar sein sollte, um sicherzustellen, dass ein Klick auf ein Listenelement wie erwartet funktioniert. Da Ihre CheckBox nur Informationen meldet und nicht an eine Anwendungslogik gebunden ist, gibt es eine einfache Lösung. Sie können die CheckBox als nicht fokussierbar definieren.

aus dem The Big Nerd Ranch Reiseführer

1
Xenon Kfr

Ich habe herausgefunden, dass das Festlegen der Eigenschaft Android: fokussierbar auf false für jede untergeordnete Ansicht eines Listenelements das Auslösen des Klick-Handlers verhindert. Das folgende Setup funktioniert mit einem 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

Ich habe stundenlang versucht, mein ähnliches Problem herauszufinden. Ich hatte eine ziemlich einfache zusammengesetzte Textansicht, die ein Kontrollkästchen und zwei Textansichten sowie einen einfachen benutzerdefinierten Adapter enthielt. Nachdem ich verschiedene vorgeschlagene Dinge ausprobiert hatte, nahm ich Ansichten heraus, bis ich nur eine Textansicht hatte. Immer noch nicht funktioniert. Weißt du was? Ich habe gerade die XML-Layout-Datei für die benutzerdefinierte Ansicht gelöscht und Stück für Stück neu erstellt. Genau das gleiche mit demselben Namen gemacht. Es hat diese Datei aus irgendeinem Grund einfach nicht gefallen. Hier bitteschön. Irgendein seltsamer Fehler in Eclipse, denke ich.

1
slida

Ich hatte listfragment.getListView (). SetOnItemClickListener () aufgerufen und auch onListItemClick () auf ListFragment überschrieben, was dazu führte, dass onListItemClick nicht aufgerufen wurde. Ich habe die Methode setOnItemClickListener () gelöscht und stattdessen die Methode onListItemClick von listFragment aufgerufen.

0
flobacca