ListFragment OnListItemClick 未被调用

Posted

技术标签:

【中文标题】ListFragment OnListItemClick 未被调用【英文标题】:ListFragment OnListItemClick not being called 【发布时间】:2011-11-08 14:36:26 【问题描述】:

我有一个扩展 ListFragment 的类,它覆盖了 OnListItemClick 方法。我也在另一个 ListFragment 中以相同的方式执行此操作(并且该方法被调用)。我想知道为什么单击列表项时没有调用该方法?

代码如下:

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



任何帮助将不胜感激。我可以通过实现一些其他侦听器并将其分配给每个列表项来获得所需的效果,但我认为这是正确的方法,它应该可以工作。我只是不知道为什么它没有

提前致谢。

【问题讨论】:

我不知道确切的解决方案,但没有必要实现 OnItemClickListener。我认为 onItemClick 方法也是多余的。我有一个类似的 ListFragment 实现这样做,它工作正常。您肯定在 logcat 中看不到行吗? 不。没有行显示,并且项目不会被选中。我在我的另一个 ListFragment 中做同样的事情,它工作正常。我只有 OnItemClickListener 在那里,因为原来的方法不起作用。 同样的问题 - 实现 OnListItemClickListener 而不是覆盖该方法也不起作用,它只是没有被调用。 使用其他帖子中的信息解决了这个问题:***.com/questions/1821871/… 【参考方案1】:

如果您的布局中有一个项目可以从其他组件(如 CheckBox)窃取输入,则需要将该组件定义为不可聚焦。

【讨论】:

完全正确。这是我的情况。我有一个复选框,点击没有注册。我删除了它,现在就像一个魅力。 这听起来像一个错误?为什么单击元素 X 不会触发事件,因为同一页面上有一个元素 Y 具有焦点?我们可以在哪里举报? @Nilzor 我不认为这是一个错误,但您可以在code.google.com/p/android/issues/list 我在列表单元格中使用了 ImageButton 而不是 CheckBox。有了那个,即使将 focusable 设置为 false 也不起作用。我不得不让该控件作为 ImageView 工作,无论如何这更好。 我永远不会猜到,谢谢你,谢谢你 auselen。【参考方案2】:

这是一个优雅的解决方案,它允许onListItemClick 以您认为应该的方式触发,并且还允许任何子视图接收事件(或不接收事件,取决于您希望如何设置。)对于提出的问题,这将允许保留复选框子视图。请注意考虑这对用户来说是否是一种直观的体验。

在所有列表项的根 ViewGroup 上,设置 XML 中的descendantFocusability 属性:

android:descendantFocusability="blocksDescendants"

Android SDK documentation of descendantFocusability。自 Android 1.0 以来一直是 SDK 的一部分。 android:descendantFocusability 的其他设置包括 "beforeDescendants""afterDescendants"

示例 cluster_layout.xml 设置 descendantFocusability(通过 ArrayAdapter 应用到 cluster_list.xml 的列表项):

<?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_
    android:layout_
    android:orientation="horizontal"
    android:background="#e0e0e0"
    android:clickable="false"
    android:descendantFocusability="blocksDescendants" >

    <FrameLayout
        android:id="@+id/cluster_sentiment_handle"
        android:layout_
        android:layout_
        android:background="@color/handle_red" />
    <!-- other elements -->
</LinearLayout>

示例 cluster_list.xml,它与此解决方案无关,只是通过具有 ID 为 @id/android:list 的 ListView 元素来表明它旨在用于 ListFragment:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_
    android:layout_
    android:orientation="vertical"
    android:padding="8dp"
    android:background="#f6f6f6" >
    <include layout="@layout/cluster_header" />
    <ListView
        android:id="@id/android:list"
        android:layout_
        android:layout_
        android:layout_gravity="top|center_horizontal"
        android:gravity="center_horizontal"
        android:divider="@android:color/transparent"
        android:dividerHeight="8dp"
        android:fastScrollEnabled="true" />
</LinearLayout>

【讨论】:

冠军小伙子!由于复杂的列表视图,我遇到了很多问题,这个解决方案是一个很好的解决方案。 谢谢!这解决了我的问题。我在列表项的父布局中添加了android:descendantFocusability="blocksDescendants" 这是我最喜欢的解决方案。 +1 用于公开 API 鲜为人知的部分。【参考方案3】:

在将我的应用从 ListActivity 转换为 ListFragment 后,我​​遇到了同样的问题。

ListFragment 是活动的唯一片段(没有标签等),但 ListFragment 没有看到任何点击。

问题是我的 Activity 仍然被定义为 ListActivity。将其更改为修复它的活动,现在 ListFragment 可以看到点击次数。

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

应该是:

    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_
        android:layout_
        android:orientation="vertical" >        
        <fragment class="com.davidmarkscott.myapp.MyFragment"
        android:id="@+id/fragment"
        android:layout_
        android:layout_ />
    </LinearLayout>

【讨论】:

这解决了我的问题。大佬:D【参考方案4】:

与其他人类似,我有一个带有 ListFragment 的 CustomAdapter,它覆盖 getView() 以提供每一行的视图。在返回的视图上设置侦听器对我有用。示例:

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;


【讨论】:

【参考方案5】:

目前只有解决方法,使用FragmentListView,然后使用setOnItemClickListener(),这将使ListFragment 及其所有便利性过时... 在我创建了一个包含完全相同代码的新类(仅更改了类名)并在另一台机器上构建了项目之后,它“神奇地”工作(仍然是相同的 API 级别)。在有效的代码下方。我还尝试了 project->clean,它通常可以解决大多数问题,但这并没有奏效。

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



【讨论】:

【参考方案6】:

我也遇到了同样的问题。使用ListFragment,它被夸大为TabHost 内选项卡的选项卡内容。

我的一个TabHost 有3 个标签,onListItemClick() 被正确调用。

但是另一个 TabHost 只有 2 个标签,onListItemClick() 没有被调用。

所以我决定围绕上面提到的 Marcel 和 H9kDroid 实施工作,并且效果很好。谢谢你的回答。

更新: 折腾了几个小时,好像是列表项的布局和ListView的适配器的问题。

在我的列表项布局中,有一个复选框,复选框的状态在适配器内切换。我认为切换后,ListView 搞砸了,无法将呼叫传递给onListItemClick()

我刚刚改了布局,去掉复选框就可以了。

干杯。

【讨论】:

【参考方案7】:

与上面@auselen 所说的相呼应,在我的情况下,我在TextView 中设置了android:textIsSelectable="true",它正在窃取回调。

将此设置为false 解决了问题。

【讨论】:

【参考方案8】:

我发现在列表项的任何子视图上将属性 android:focusable 设置为 false 会阻止点击处理程序被触发。以下设置适用于 ListFragment。

navigation_fragment.xml:

<ListView xmlns:android="http://schemas.android.com/apk/res/android"
    android:id="@+id/navigationEntries"
    android:layout_
    android:layout_ />

navigation_entry.xml:

    <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
            android:id="@+id/navigationEntry"
            android:orientation="vertical"
            android:layout_
            android:layout_
            <!-- enhance this -->
            <ImageView
                    android:layout_
                    android:layout_
                    android:background="@android:color/darker_gray" />

【讨论】:

【参考方案9】:

我花了几个小时试图找出我自己的类似问题。我有一个相当简单的复合文本视图,其中包含一个复选框和两个文本视图以及一个简单的自定义适配器。在尝试了各种建议后,我取出视图,直到只有一个文本视图。仍然没有工作。你知道做了什么吗?我刚刚删除了我正在使用的自定义视图的 xml 布局文件,然后逐个重新制作。用同样的名字做了同样的东西。它只是出于某种原因不喜欢那个文件。所以你去。我猜 Eclipse 中有一些奇怪的故障。

【讨论】:

【参考方案10】:

我调用了 listfragment.getListView().setOnItemClickListener() 并且还覆盖了 ListFragment 上的 onListItemClick() ,导致 onListItemClick 没有被调用。我删除了 setOnItemClickListener() 方法,而是调用了 listFragment 的 onListItemClick 方法。

【讨论】:

【参考方案11】:

CheckBox 默认是可聚焦的。这意味着单击列表项将被解释为切换 CheckBox,并且不会到达您的 onListItemClick(...) 方法。 ListView 的这种内部怪癖意味着任何出现在列表项布局(如 CheckBox 或 Button)中的可聚焦小部件都应设为不可聚焦,以确保单击列表项将按预期工作。因为您的 CheckBox 只报告信息并且不依赖于任何应用程序逻辑,所以有一个简单的解决方案。您可以将 CheckBox 定义为不可聚焦。

来自 The Big Nerd Ranch 指南

【讨论】:

以上是关于ListFragment OnListItemClick 未被调用的主要内容,如果未能解决你的问题,请参考以下文章

突出显示“ListFragment”中的选定项目?

Activity 完成时更新 ListFragment

长按 ListFragment

为啥 ListFragment 在没有 ListView 标签的情况下工作?

使用 SQLite 的 Android 简单 ListFragment

来自 xml 的 ListFragment 布局