我有一个扩展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);
}
}
非常感谢任何帮助。我可以通过实现一些其他侦听器并将其分配给每个列表项来获得所需的效果,但我认为这是正确的方法,它应该工作。我只是不知道它为什么不
先谢谢。
答案 0 :(得分:97)
如果布局中有一个项目可以窃取其他组件(如CheckBox)的输入,那么该组件需要定义为不可聚焦。
答案 1 :(得分:14)
将我的应用从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_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>
答案 2 :(得分:14)
这是一个优雅的解决方案,它允许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_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>
示例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_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>
答案 3 :(得分:4)
与其他人类似,我有一个带有ListFragment的CustomAdapter,它会覆盖getView()以提供每行的View。在返回的视图上设置监听器对我有用。例如:
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 :(得分:3)
到目前为止只有解决方法,使用Fragment
和ListView
,然后使用setOnItemClickListener()
,这会使ListFragment
及其所有'便利性过时......
在我创建了一个包含完全相同代码的新类(只更改了类名)并在另一台机器上构建项目后,它“神奇地”工作(仍然是相同的API级别)。下面的代码工作。我也尝试过project-&gt; 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);
}
}
答案 5 :(得分:3)
我也遇到了同样的问题。使用ListFragment
作为TabHost
内标签的标签内容进行了充实。
我的一个TabHost
有3个标签,onListItemClick()
被正确调用。
但另一个TabHost只有2个标签,onListItemClick()
未被调用。
所以我决定实施上面提到的Marcel和H9kDroid的工作,它运行良好。谢谢你的回答。
<强>更新强> 搞乱了几个小时后,似乎问题与列表项的布局和ListView的适配器有关。
在我的列表项布局中,有一个复选框,并且在适配器内部切换复选框的状态。我认为在切换后,ListView
搞砸了,无法将来电传递给onListItemClick()
。
我刚刚更改了布局,删除了复选框,它运行正常。
干杯。
答案 6 :(得分:2)
回应@auselen上面所说的内容,在我的情况下,android:textIsSelectable="true"
设置了TextView
,它正在窃取回调。
将此设置为false
解决了问题。
答案 7 :(得分:1)
我发现在列表项的任何子视图上将属性android:focusable设置为false可防止单击处理程序被触发。以下设置适用于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" />
答案 8 :(得分:1)
我花了好几个小时试图找出自己类似的问题。我有一个相当简单的复合文本视图,其中包含一个复选框和两个textview以及一个简单的自定义适配器。在尝试了建议的各种事情后,我拿出了一些视图,直到我只有一个textview。仍然没有工作。你知道吗?我刚刚删除了我正在使用的自定义视图的xml布局文件,并逐个重新制作。用相同的名字做同样的事情。出于某种原因,它只是不喜欢那个文件。你去吧我想Eclipse中有一些奇怪的故障。
答案 9 :(得分:1)
默认情况下,复选框是可聚焦的。这意味着单击列表项将被解释为切换CheckBox,并且不会到达您的onListItemClick(…)方法。 ListView的这种内部怪异意味着,应使出现在列表项布局中的所有可聚焦小部件(如CheckBox或Button)都不能变为焦点,以确保单击列表项将按预期工作。因为您的CheckBox仅报告信息,并且与任何应用程序逻辑无关,所以有一个简单的解决方案。您可以将CheckBox定义为不可聚焦的。
来自《大书呆子牧场指南》一书
答案 10 :(得分:0)
我调用了listfragment.getListView()。setOnItemClickListener()并且还覆盖了ListFragment上的onListItemClick(),导致onListItemClick未被调用。我删除了setOnItemClickListener()方法,而是调用了listFragment的onListItemClick方法。