を組み込みで実装しSherlockFragmentActivity
ています。ViewPager
ViewPager は正しく表示されますが、その中のコンテンツ (フラグメント自体) が混在しています。
私の状況では、3 つのタブを持つビュー ページャーがあり、各タブのフラグメントをロードしたいと考えています。何が起こるか: 2 番目のフラグメントが最初のタブに読み込まれ、3 番目のタブをクリックして 2 番目のタブに戻ると、そこに表示されます。
また、2 番目のタブをクリックすると、最初のタブのフラグメントが読み込まれ、突然 2 番目のタブのコンテンツが読み込まれ、前のコンテンツが上書きされます。
別のタブで間違ったコンテンツを読み込んでいます。
アップデート
デバッグ中に、( as に追加された) 2 番目のタブを開くと、2 番目TabsAdapter
ではなく最初のタブであるのアダプター クラスのメソッドContactActivityFragment
が呼び出されることがわかりました。そのため、最初のタブのコンテンツが間違った場所に読み込まれます。onActivityCreated
getView
ContactInfoFragment
これが私のコードです。何が起こっているのでしょうか?
ContactActivity.java
public class ContactActivity extends SherlockFragmentActivity implements OnCreateOptionsMenuListener
{
public ActionBarSherlock mSherlock = ActionBarSherlock.wrap(this);
private TabHost mTabHost;
private ViewPager mViewPager;
private TabsAdapter mTabsAdapter;
private TextView mContactNameTextView;
private ImageView mContactIconImageView;
private Context mContext;
private final String FRAGMENT_POSITION = "fragment_position";
private final String CONTACT_ID = "contact_id";
private Long mContactId;
private ContactController mContactController;
private Contact mContact;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
mSherlock.setUiOptions(ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW);
setContentView(R.layout.contact_activity);
// retrieve the extra values from the intent
mContactId = getIntent().getExtras().getLong(CONTACT_ID);
// set the tab host to hold the fragments and the viewpager
mTabHost = (TabHost)findViewById(android.R.id.tabhost);
mTabHost.setup();
mViewPager = (ViewPager)findViewById(R.id.pager);
mTabsAdapter = new TabsAdapter(this, mTabHost, mViewPager);
// create the bundle to set the values to send to the fragment
Bundle bundle = new Bundle();
bundle.putLong(CONTACT_ID, mContactId);
mTabsAdapter.addTab(mTabHost.newTabSpec("info").setIndicator(getString(R.string.fragment_contact_title_info)), ContactInfoFragment.class, bundle);
mTabsAdapter.addTab(mTabHost.newTabSpec("activity").setIndicator(getString(R.string.fragment_contact_title_activity)), ContactActivityFragment.class, bundle);
mTabsAdapter.addTab(mTabHost.newTabSpec("stats").setIndicator(getString(R.string.fragment_contact_title_stats)), ContactStatsFragment.class, bundle);
mContext = getSupportActionBar().getThemedContext();
// Set the action bar properties
getSupportActionBar().setDisplayShowTitleEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setNavigationMode(ActionBar.DISPLAY_SHOW_TITLE);
// initialize the elements
mContactNameTextView = (TextView) findViewById(R.id.textViewContactName);
mContactIconImageView = (ImageView) findViewById(R.id.imageViewContactIcon);
// initialize the contact controller
mContactController = new ContactController(mContext);
// initialize the contact object
mContact = new Contact();
}
@Override
protected void onSaveInstanceState(Bundle outState)
{
super.onSaveInstanceState(outState);
outState.putString("tab", mTabHost.getCurrentTabTag());
}
@Override
public boolean onOptionsItemSelected(MenuItem item)
{
switch (item.getItemId())
{
// If the user clicks the home icon
case android.R.id.home:
Intent i = new Intent(this, HomeActivity.class);
i.putExtra(FRAGMENT_POSITION, "4");
i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
startActivity(i);
break;
default:
break;
}
return true;
}
@Override
public void onResume()
{
super.onResume();
mContact = mContactController.getContactById(mContactId);
// set the action bar title as the contact name.
// if the name is null, print the phone number which is set as
// the id of the contact in case it's an unmapped contact
getSupportActionBar().setTitle(mContact.getmDisplayName() == null ? mContact.getM_id().toString() : mContact.getmDisplayName());
// set the contact name.
// if the name is null, print the phone number which is set as
// the id of the contact in case it's an unmapped contact
mContactNameTextView.setText(mContact.getmDisplayName() == null ? mContact.getM_id().toString() : mContact.getmDisplayName());
// retrieve the contact image based on the URI
Bitmap contactIconBitmap = mContactController.getContactPicture(mContactId);
mContactIconImageView.setImageBitmap(contactIconBitmap);
}
@Override
public boolean onCreateOptionsMenu(Menu menu)
{
// do nothing, since the menu items must be added on specific fragments
return true;
}
/**
* This is a helper class that implements the management of tabs and all
* details of connecting a ViewPager with associated TabHost. It relies on a
* trick. Normally a tab host has a simple API for supplying a View or
* Intent that each tab will show. This is not sufficient for switching
* between pages. So instead we make the content part of the tab host
* 0dp high (it is not shown) and the TabsAdapter supplies its own dummy
* view to show as the tab content. It listens to changes in tabs, and takes
* care of switch to the correct paged in the ViewPager whenever the selected
* tab changes.
*/
public static class TabsAdapter extends FragmentPagerAdapter implements TabHost.OnTabChangeListener, ViewPager.OnPageChangeListener
{
private final Context mContext;
private final TabHost mTabHost;
private final ViewPager mViewPager;
private final ArrayList<TabInfo> mTabs = new ArrayList<TabInfo>();
static final class TabInfo
{
private final String tag;
private final Class<?> clss;
private final Bundle args;
TabInfo(String _tag, Class<?> _class, Bundle _args)
{
tag = _tag;
clss = _class;
args = _args;
}
}
static class DummyTabFactory implements TabHost.TabContentFactory
{
private final Context mContext;
public DummyTabFactory(Context context)
{
mContext = context;
}
public View createTabContent(String tag)
{
View v = new View(mContext);
v.setMinimumWidth(0);
v.setMinimumHeight(0);
return v;
}
}
public TabsAdapter(FragmentActivity activity, TabHost tabHost, ViewPager pager)
{
super(activity.getSupportFragmentManager());
mContext = activity;
mTabHost = tabHost;
mViewPager = pager;
mTabHost.setOnTabChangedListener(this);
mViewPager.setAdapter(this);
mViewPager.setOnPageChangeListener(this);
}
public void addTab(TabHost.TabSpec tabSpec, Class<?> clss, Bundle args)
{
tabSpec.setContent(new DummyTabFactory(mContext));
String tag = tabSpec.getTag();
TabInfo info = new TabInfo(tag, clss, args);
mTabs.add(info);
mTabHost.addTab(tabSpec);
notifyDataSetChanged();
}
@Override
public int getCount()
{
return mTabs.size();
}
@Override
public Fragment getItem(int position)
{
TabInfo info = mTabs.get(position);
return Fragment.instantiate(mContext, info.clss.getName(), info.args);
}
public void onTabChanged(String tabId)
{
int position = mTabHost.getCurrentTab();
mViewPager.setCurrentItem(position);
}
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels)
{
}
public void onPageSelected(int position)
{
// Unfortunately when TabHost changes the current tab, it kindly
// also takes care of putting focus on it when not in touch mode.
// The jerk.
// This hack tries to prevent this from pulling focus out of our
// ViewPager.
TabWidget widget = mTabHost.getTabWidget();
int oldFocusability = widget.getDescendantFocusability();
widget.setDescendantFocusability(ViewGroup.FOCUS_BLOCK_DESCENDANTS);
mTabHost.setCurrentTab(position);
widget.setDescendantFocusability(oldFocusability);
}
public void onPageScrollStateChanged(int state)
{
}
}
}
ContactInfoFragment.java
public class ContactInfoFragment extends SherlockListFragment
{
private Activity mActivity;
private Menu mOptionsMenu;
private ListView mListView;
private final String CONTACT_ID = "contact_id";
private Long mContactId;
private List<Contact> mContactsData = new ArrayList<Contact>();
private List<PhoneNumber> mPhoneNumbers = new ArrayList<PhoneNumber>();
private final String[] mListHeaders = { "Phone Numbers" };
private ContactController mContactController;
private PhoneNumberController mPhoneNumberController;
private Contact mContact;
private PhoneNumber mPhoneNumber;
private SeparatedListAdapter mSeparatedListAdapter;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
savedInstanceState = getArguments();
// set the contact id
mContactId = savedInstanceState.getLong(CONTACT_ID);
// initialize controllers
mContactController = new ContactController(getActivity().getApplicationContext());
mPhoneNumberController = new PhoneNumberController(getActivity().getApplicationContext());
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("class_name", "ContactInfoFragment");
}
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
mActivity = activity;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// inflate the layout
View view = inflater.inflate(R.layout.contact_info_fragment, container, false);
return view;
}
@Override
public void onStart()
{
super.onStart();
}
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
mContact = mContactController.getContactById(mContactId);
mPhoneNumbers = mPhoneNumberController.getPhoneNumbersByContact(mContact);
mListView = (ListView) mActivity.findViewById(android.R.id.list);
ContentAdapter mAdapter = new ContentAdapter(getActivity().getApplicationContext(), android.R.id.list, mPhoneNumbers);
// add the items for the adapter
for (PhoneNumber phoneNumber : mPhoneNumbers)
{
mAdapter.add(phoneNumber);
}
mSeparatedListAdapter = new SeparatedListAdapter(mActivity.getApplicationContext());
// Add Sections
mSeparatedListAdapter.addSection("Phone Numbers", mAdapter);
mAdapter.notifyDataSetChanged();
mSeparatedListAdapter.notifyDataSetChanged();
mListView.setAdapter(mSeparatedListAdapter);
mListView.setOnItemClickListener(new OnItemClickListener()
{
public void onItemClick(AdapterView<?> parent, View view, int position, long duration)
{
// if the user clicks a number, it will open the dialer to call the number
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setData(Uri.parse("tel:"+view.getTag(R.string.phone_number_action_call).toString()));
intent.setFlags(Intent.FLAG_ACTIVITY_BROUGHT_TO_FRONT);
startActivity(intent);
}
});
}
@Override
public void onResume()
{
super.onResume();
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater)
{
mOptionsMenu = menu;
menu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
menu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
private void changeOptionsMenu(int position)
{
mOptionsMenu.clear();
if(position == 0)
{
mOptionsMenu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
else if(position == 1)
{
mOptionsMenu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Days")
.setIcon(R.drawable.ic_menu_days)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
else
{
mOptionsMenu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
}
public class ContentAdapter extends ArrayAdapter<PhoneNumber>
{
Context mContext;
List<PhoneNumber> mPhoneNumbersList;
private LayoutInflater mInflater;
public ContentAdapter(Context context, int textViewResourceId, List<PhoneNumber> phoneNumbers)
{
super(context, textViewResourceId);
mContext = context;
this.mPhoneNumbersList = phoneNumbers;
mInflater = mActivity.getLayoutInflater();
}
// An alternative for the ArrayAdapter.addAll() method, since the native
// is not supported by API level lesser then 11.
public void addAll(ArrayList<PhoneNumber> list)
{
for(PhoneNumber phoneNumber : list)
{
this.add(phoneNumber);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
ViewHolder holder = new ViewHolder();
if(row == null)
{
row = mInflater.inflate(R.layout.contact_info_list_item, parent, false);
holder.phoneNumber = (TextView) row.findViewById(R.id.textViewContactNumber);
holder.phoneNumberType = (TextView) row.findViewById(R.id.textViewNumberType);
row.setTag(holder);
}
else
{
holder = (ViewHolder) row.getTag();
}
// TODO: make a join to get the contact and phone number instead of calling method by method
PhoneNumber phoneNumber = mPhoneNumbersList.get(position);
if(phoneNumber != null)
{
holder.phoneNumber.setText(phoneNumber.getmPhoneNumberValue());
holder.phoneNumberType.setText(CallUtils.getPhoneNumberType(phoneNumber.getmPhoneNumberType()));
row.setTag(R.string.phone_number_action_call, phoneNumber.getmPhoneNumberValue());
}
return row;
}
}
public class ViewHolder
{
TextView phoneNumber;
TextView phoneNumberType;
}
}
ContactActivityFragment.java
public class ContactActivityFragment extends SherlockListFragment
{
private Activity mActivity;
private Menu mOptionsMenu;
private ListView mListView;
private final String CONTACT_ID = "contact_id";
private Long mContactId;
private List<Contact> mContactsData = new ArrayList<Contact>();
private List<Call> mCallsList = new ArrayList<Call>();
private final String[] mListHeaders = { "Calls" };
private Call mCall;
private Contact mContact;
private PhoneNumber mPhoneNumber;
private CallController mCallController;
private ContactController mContactController;
private PhoneNumberController mPhoneNumberController;
private SeparatedListAdapter mSeparatedListAdapter;
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
savedInstanceState = getArguments();
// set the contact id
mContactId = savedInstanceState.getLong(CONTACT_ID);
// initialize controllers
mCallController = new CallController(getActivity().getApplicationContext());
mContactController = new ContactController(getActivity().getApplicationContext());
mPhoneNumberController = new PhoneNumberController(getActivity().getApplicationContext());
// initialize models
mCall = new Call();
}
@Override
public void onSaveInstanceState(Bundle savedInstanceState)
{
super.onSaveInstanceState(savedInstanceState);
savedInstanceState.putString("class_name", "ContactInfoFragment");
}
@Override
public void onAttach(Activity activity)
{
super.onAttach(activity);
mActivity = activity;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
{
// inflate the layout
View view = inflater.inflate(R.layout.contact_info_fragment, container, false);
return view;
}
@Override
public void onStart()
{
super.onStart();
}
@Override
public void onActivityCreated(Bundle savedInstanceState)
{
super.onActivityCreated(savedInstanceState);
mCallsList = mCallController.getCallsByContact(mContactId);
mContact = mContactController.getContactById(mContactId);
mListView = (ListView) mActivity.findViewById(android.R.id.list);
ContentAdapter mAdapter = new ContentAdapter(getActivity().getApplicationContext(), android.R.id.list, mCallsList);
// add the items for the adapter
for (Call callList : mCallsList)
{
mAdapter.add(callList);
}
mSeparatedListAdapter = new SeparatedListAdapter(mActivity.getApplicationContext());
// Add Sections
mSeparatedListAdapter.addSection("Phone Numbers", mAdapter);
mAdapter.notifyDataSetChanged();
mSeparatedListAdapter.notifyDataSetChanged();
mListView.setAdapter(mSeparatedListAdapter);
}
@Override
public void onResume()
{
super.onResume();
setHasOptionsMenu(true);
}
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater)
{
mOptionsMenu = menu;
menu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
menu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
private void changeOptionsMenu(int position)
{
mOptionsMenu.clear();
if(position == 0)
{
mOptionsMenu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
else if(position == 1)
{
mOptionsMenu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Days")
.setIcon(R.drawable.ic_menu_days)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
else
{
mOptionsMenu.add("People")
.setIcon(R.drawable.ic_menu_contacts)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
mOptionsMenu.add("Share")
.setIcon(R.drawable.ic_menu_share)
.setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS | MenuItem.SHOW_AS_ACTION_WITH_TEXT);
}
}
public class ContentAdapter extends ArrayAdapter<Call>
{
Context mContext;
List<Call> mCallsList;
private LayoutInflater mInflater;
public ContentAdapter(Context context, int textViewResourceId, List<Call> calls)
{
super(context, textViewResourceId);
mContext = context;
this.mCallsList = calls;
mInflater = mActivity.getLayoutInflater();
}
// An alternative for the ArrayAdapter.addAll() method, since the native
// is not supported by API level lesser then 11.
public void addAll(ArrayList<Call> callsList)
{
for(Call call : callsList)
{
this.add(call);
}
}
@Override
public View getView(int position, View convertView, ViewGroup parent)
{
View row = convertView;
ViewHolder holder = new ViewHolder();
if(row == null)
{
row = mInflater.inflate(R.layout.contact_info_list_item, parent, false);
holder.phoneNumber = (TextView) row.findViewById(R.id.textViewContactNumber);
holder.phoneNumberType = (TextView) row.findViewById(R.id.textViewNumberType);
row.setTag(holder);
}
else
{
holder = (ViewHolder) row.getTag();
}
// TODO: make a join to get the contact and phone number instead of calling method by method
Call call = mCallsList.get(position);
if(call != null)
{
holder.phoneNumber.setText(call.getCallDuration().toString());
holder.phoneNumberType.setText(call.getCallDate());
}
return row;
}
}
public class ViewHolder
{
TextView phoneNumber;
TextView phoneNumberType;
}
}