DialogFragments と backstacks で本当に厄介な問題が発生しています。
ActiveMenuFragment をホストする MainActivity があります。
ActiveMenuFragment には、DialogFragmentActionSelector を起動するボタンがあります (すべてのメソッドは MainActivity 強制インターフェイス メソッドから実行されます)。
/**
* Opens the action selection dialog, so the user may pick an action to
* begin.
*/
@Override
public void openActionSelector() {
actionSelectorFragment = DialogFragmentActionSelector.newInstance();
Bundle args = new Bundle();
args.putSerializable("Profile", currentProfile);
actionSelectorFragment.setArguments(args);
FragmentTransaction ft = this.getFragmentManager().beginTransaction();
ft.addToBackStack("Action Selector");
actionSelectorFragment.show(ft, "Action Selector");
}
このフラグメントは、アクションのリストを含む ListView を表示します。この時点ではすべて問題ありません。以前に追加された新しいアクションもここに表示されます。ここで、ユーザーが新しいアクションを定義したい場合は、DialogFragmentDefineNewAction を起動する DialogFragment のボタンを押します。
/**
* Opens the action selection dialog, so the user may pick an action to
* begin.
*/
@Override
public void openActionSelector() {
actionSelectorFragment = DialogFragmentActionSelector.newInstance();
Bundle args = new Bundle();
args.putSerializable("Profile", currentProfile);
actionSelectorFragment.setArguments(args);
FragmentTransaction ft = this.getFragmentManager().beginTransaction();
ft.addToBackStack("Action Selector");
actionSelectorFragment.show(ft, "Action Selector");
}
ユーザーが新しいアクションのデータを入力した後、確認ボタンを押します。これは、前のフラグメント (DialogFragmenActionSelector) 内の ListView を更新するためにサポートされていますが、そうではありません。バックスタック経由で戻るたびに、同じ古い 8 つのアクションが表示されます。
/**
* Takes the action defined in the DefineNewAction fragment and adds it to
* the user dictionary.
*/
@Override
public void addNewActionToDictionary(Action toAdd) {
currentProfile.getDictionary().addDefinition(toAdd);
actionSelectorFragment.updateProfile(currentProfile);
}
参考までに、ArrayAdapter を含む DialogFragmentActionSelector 全体を次に示します。
public class DialogFragmentActionSelector extends DialogFragment implements
OnClickListener {
private EditText searchBar;
private Button defineNewActionBtn;
private ListView actionList;
private UserProfile user;
private ListViewInterface mInterface;
private ActionsAdapter adapter;
public interface ListViewInterface {
void openDefineNewAction();
void setActiveMenuActionBar(String action);
}
public void onAttach(Activity activity) {
super.onAttach(activity);
if (activity instanceof ListViewInterface) {
mInterface = (ListViewInterface) activity;
} else {
throw new ClassCastException(activity.toString()
+ " must implement ListViewInterface");
}
}
/**
* Constructor for the fragment.
*
* @return
*/
public static DialogFragmentActionSelector newInstance() {
DialogFragmentActionSelector dfm = new DialogFragmentActionSelector();
return dfm;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View toReturn = inflater.inflate(
R.layout.fragment_action_selector_layout, container, false);
user = (UserProfile) this.getArguments().get("Profile");
searchBar = (EditText) toReturn.findViewById(R.id.action_search_bar);
searchBar.addTextChangedListener(new TextWatcher() {
@Override
public void afterTextChanged(Editable arg0) {
}
@Override
public void beforeTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
}
@Override
public void onTextChanged(CharSequence arg0, int arg1, int arg2,
int arg3) {
adapter.updateList();
adapter.getFilter().filter(arg0);
}
});
defineNewActionBtn = (Button) toReturn
.findViewById(R.id.define_new_action);
defineNewActionBtn.setOnClickListener(this);
actionList = (ListView) toReturn.findViewById(R.id.actions_list);
ArrayList<Action> allActions = user.getDictionary().getAllActions();
adapter = new ActionsAdapter(this.getActivity(),
android.R.layout.simple_list_item_1, allActions, inflater);
actionList.setAdapter(adapter);
actionList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View clickedView,
int index, long id) {
Action selectedAction = (Action) parent.getItemAtPosition(index);
mInterface.setActiveMenuActionBar(selectedAction.getActionName());
dismiss();
}
});
return toReturn;
}
public void updateProfile(UserProfile updatedProfile) {
user = updatedProfile;
ArrayList<Action> allActions = user.getDictionary().getAllActions();
adapter = new ActionsAdapter(this.getActivity(),
android.R.layout.simple_list_item_1, allActions, getActivity()
.getLayoutInflater());
actionList.setAdapter(adapter);
actionList.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View clickedView,
int index, long id) {
Action selectedAction = (Action) parent.getItemAtPosition(index);
mInterface.setActiveMenuActionBar(selectedAction.getActionName());
dismiss();
}
});
// this.updateListView();
}
private void updateListView() {
adapter.updateList();
}
// TODO FIX CONSTRUCTOR. UPON RETURNING, IT REVERTS TO DEFAULT 8 ACTIONS
// RATHER THAN NEW ONES.
private class ActionsAdapter extends ArrayAdapter<Action> implements
Filterable {
LayoutInflater inflater;
ArrayList<Action> actionsList;
public ActionsAdapter(Context context, int resId,
ArrayList<Action> objects, LayoutInflater inflater) {
super(context, resId, objects);
this.actionsList = objects;
this.inflater = inflater;
}
@Override
public int getCount() {
return actionsList.size();
}
@Override
public Action getItem(int position) {
return actionsList.get(position);
}
@Override
public long getItemId(int position) {
return this.getItem(position).hashCode();
}
public void updateList() {
ArrayList<Action> allActions = user.getDictionary().getAllActions();
actionsList = allActions;
notifyDataSetInvalidated();
// notifyDataSetChanged();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
Action toWrap = actionsList.get(position);
if (convertView == null) {
holder = new ViewHolder();
convertView = inflater.inflate(
R.layout.listview_row_action_selector, parent, false);
holder.actionName = (TextView) convertView
.findViewById(R.id.action_title);
convertView.setTag(holder);
} else
holder = (ViewHolder) convertView.getTag();
holder.actionName.setText(toWrap.getActionName());
return convertView;
}
private class ViewHolder {
TextView actionName;
}
/**
* Filters the adapter's database.
*/
@Override
public Filter getFilter() {
Filter filter = new Filter() {
@Override
protected FilterResults performFiltering(CharSequence constraint) {
FilterResults results = new FilterResults();
ArrayList<Action> matchingActions = new ArrayList<Action>();
for (int i = 0; i < actionsList.size(); i++) {
Action actionToCheck = actionsList.get(i);
ArrayList<String> actionTags = actionToCheck.getTags();
String searchQuery = constraint.toString();
boolean foundMatch = false;
int k = 0;
int tagSize = actionTags.size();
if (tagSize != 0) {
while (!foundMatch && k < tagSize) {
if (actionTags.get(k).contains(searchQuery)) {
matchingActions.add(actionToCheck);
foundMatch = true;
}
k++;
}
}
}
results.count = matchingActions.size();
results.values = matchingActions;
return results;
}
@SuppressWarnings("unchecked")
@Override
protected void publishResults(CharSequence constraint,
FilterResults results) {
// actionsList.clear();
actionsList = (ArrayList<Action>) results.values;
adapter.notifyDataSetChanged();
}
};
return filter;
}
}
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.define_new_action:
mInterface.openDefineNewAction();
break;
default:
break;
}
}
public void addNewActionToDictionary(Action toAdd) {
user.getDictionary().addDefinition(toAdd);
adapter.add(toAdd);
adapter.updateList();
}
}
notifyDataSetChanged() または無効化を使用することになっていますか? 私のアダプターでは動作しないためです。
すべてのダイアログを閉じて再度開くと、更新されたアクションがリストビューに表示されます。なんで?
また、効率が悪く、必要以上に複雑にしているようにも感じます。UserProfile オブジェクトには、アクション ディクショナリと、ユーザーに関連するその他の情報も保持されます。ホスト アクティビティはこれを保持して変更する必要がありますか、それとも ActiveMenu フラグメントで処理する必要がありますか?