ExpandableListView是ListView控件的延伸,它能够对数据进行分组显示和隐藏,并统计总数量。可进行滚动,对某一内容高亮显示。

<1>编写xml布局文件,用于获取ExpandableListView对象

sf_activity_contact_list.xml

<span style="font-family:Microsoft YaHei;"><LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"android:id="@+id/contacts_list_holder"style="@style/SF_ActivityBackground"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@color/sf_activity_background_color"android:overScrollMode="always"android:scrollbarStyle="outsideInset"android:scrollbars="vertical"><!-- style="@style/SF_ActivityBackground" --><ExpandableListViewandroid:id="@+id/contacts_list"android:layout_width="match_parent"android:layout_height="wrap_content"android:fastScrollEnabled="true" ></ExpandableListView></LinearLayout></span>

<2>编写Activity界面,获取ExpandableListView对象。并设置适配器和对应的属性、事件等等

package com.snapfish.ui;import java.util.ArrayList;
import java.util.List;import org.json.JSONException;
import org.json.JSONObject;import android.app.AlertDialog;
import android.app.SearchManager;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.os.Bundle;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.SearchView;
import android.support.v7.widget.SearchView.OnQueryTextListener;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.view.WindowManager.LayoutParams;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.EditText;
import android.widget.ExpandableListView;
import android.widget.ExpandableListView.OnChildClickListener;
import android.widget.ImageView;
import android.widget.TextView;import com.snapfish.R;
import com.snapfish.android.generated.bean.AddressType;
import com.snapfish.android.generated.bean.Entry;
import com.snapfish.android.generated.bean.MrchShippingOption;
import com.snapfish.android.generated.bean.OpenSocialResponseType;
import com.snapfish.android.generated.bean.Person;
import com.snapfish.android.generated.bean.PublisherOrder;
import com.snapfish.android.generated.bean.PublisherOrderAddress;
import com.snapfish.checkout.IUserData;
import com.snapfish.internal.core.SFConstants;
import com.snapfish.internal.database.SFContactsHolderDB;
import com.snapfish.internal.datamodel.SFContact;
import com.snapfish.internal.datamodel.SFContactDBManager;
import com.snapfish.internal.event.SFEventManager;
import com.snapfish.internal.event.SFIEventListener;
import com.snapfish.internal.event.SFLocaleContactsEvent;
import com.snapfish.internal.event.SFRemoteContactsEvent;
import com.snapfish.util.CShippingOptionsAdapter;
import com.snapfish.util.SFActivityUtils;
import com.snapfish.util.SFContactExpandableListAdapter;
import com.snapfish.util.SFLogger;public class SFContactsListActivity extends ABaseActivity { // implements// LoaderManager.LoaderCallbacks<Cursor>private static final SFLogger sLogger = SFLogger.getInstance(SFContactsListActivity.class.getName());private Context m_ctx;private TextView m_contactNameOverlay;private boolean m_visible;// private static final int CONTACT_DATA_LOADER = 111;OnScrollListener mItemsListScrollListener = new OnScrollListener() {@Overridepublic void onScrollStateChanged(AbsListView view, int scrollState) {m_visible = true;// when stop scrollingif (scrollState == OnScrollListener.SCROLL_STATE_IDLE) {m_contactNameOverlay.setVisibility(View.INVISIBLE);m_visible = false;}// when scrolling and the finger is on the screenelse if (scrollState == OnScrollListener.SCROLL_STATE_TOUCH_SCROLL) {}// when scrolling and the finger perform an action aboveelse if (scrollState == OnScrollListener.SCROLL_STATE_FLING) {}}@Overridepublic void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {if (visibleItemCount > 0 && m_visible) {SFContact contact = mPhoneContacts.get(firstVisibleItem);m_contactNameOverlay.setText(String.valueOf(contact.getName().charAt(0)));m_contactNameOverlay.setVisibility(View.VISIBLE);}}};/*************************************************** The event of getting locale contacts*************************************************/private SFIEventListener<SFLocaleContactsEvent> m_localeContactListEvent = new SFIEventListener<SFLocaleContactsEvent>() {@Overridepublic void onEvent(SFLocaleContactsEvent event) {// TODO The event of getting locale contactssLogger.debug("****************The event of getting locale contacts*****************");hideProgressDialog();if (null != event && event.getContactsList() != null) {mPhoneContacts = event.getContactsList();showProgressDialog(getString(R.string.sf_please_wait),getString(R.string.sf_progress_remote_contact_list));// Get all remote contactsSFContactDBManager.asyncGetAllRemoteContacts(getSession());}}};/*************************************************** The event of getting remote contacts*************************************************/private SFIEventListener<SFRemoteContactsEvent> m_remoteContactListEvent = new SFIEventListener<SFRemoteContactsEvent>() {@Overridepublic void onEvent(SFRemoteContactsEvent event) {// TODO The event of getting remote contactssLogger.debug("****************The event of getting remote contacts*****************");hideProgressDialog();if (null != event && event.getOpenSocialResponseType() != null) {OpenSocialResponseType osrt = event.getOpenSocialResponseType();if (null != osrt) {List<Entry> entries = osrt.getEntryList();if (null != entries && !entries.isEmpty()) {int entrySize = entries.size();for (int i = 0; i < entrySize; i++) {try {Person person = Person.newFromJSON(entries.get(i).toJSON());if (null != person) {AddressType address = person.getAddress();String displayName = person.getDisplayName();if (isValidContact(address)&& !TextUtils.isEmpty(displayName)) {SFContact contact = new SFContact(IUserData.EUserDataType.SNAPFISH,person.getId(),displayName,address.getStreet1(),address.getCity(),address.getProvince(),address.getPostalCode(),SFContact.ContactAddressType.TYPE_OTHER);contact.setGuessedPrecision(SFContact.GuessedPrecision.ABSOLUTE);contact.setCountry(address.getCountry() == null ? getResources().getConfiguration().locale.getCountry() : address.getCountry());contact.setAddressGuessed(false);contact.setIsConfirmed(true);contact.setPhone(address.getPhone());mPhoneContacts.add(contact);}}} catch (JSONException e) {sLogger.error(e.getMessage());}}}}}onContactsListCreated();}};/** carry over from order review activity in case if contact has to be edited* or order update must happen on this screen*/private PublisherOrder m_publisherOrder;private int m_orderQuantity;private long m_mrchId;private SFContact mSelectedAddress;private List<SFContact> mPhoneContacts;private ExpandableListView mContactsListView;private SFContactExpandableListAdapter mContactsListAdapter;private List<MrchShippingOption> m_mrchShippingOptions = new ArrayList<MrchShippingOption>();private String m_shippingOptionDesc;private String m_shippingOptionType;/* search */private MenuItem searchItem;private SearchView mSearchView;private ImageView closeBtn;private EditText searchEditText;public static boolean isSearchQuery;private String mSearchString;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.sf_activity_contact_list);m_ctx = this;// getSupportLoaderManager().initLoader(CONTACT_DATA_LOADER, null,// this);// Display home and finish current activitySFActivityUtils.displayHomeAsUp(this);getDataFromIntent();mContactsListView = (ExpandableListView) findViewById(R.id.contacts_list);/** on child click the action that happens will depend whether contact* selected is confirmed or not, meaning that the end-user has validated* address and order can be safely sent to selected address*/mContactsListView.setOnChildClickListener(new OnChildClickListener() {@Overridepublic boolean onChildClick(ExpandableListView parent, View v,int groupPosition, int childPosition, long id) {mSelectedAddress = (SFContact) mContactsListAdapter.getChild(groupPosition, childPosition);/** in the event when address precision is below 100% it must be* validated by the end-user so the CShippingAddressActivity is* launched first*/Intent exitIntent = null;if (mSelectedAddress.getGuessedPrecision() != SFContact.GuessedPrecision.ABSOLUTE) {exitIntent = new Intent(SFContactsListActivity.this,SFShippingAddressActivity.class);/* pass contact _id */exitIntent.putExtra(SFContactsHolderDB.SFContactColumns._ID,mSelectedAddress.getId());/* selected shipping address for edit */exitIntent.putExtra(SFConstants.SF_SHIPPING_ADDRESS,handleLocalization(mSelectedAddress));/* shipping options */exitIntent.putExtra(SFConstants.MRCH_SHIP_OPTIONS,getIntent().getStringArrayExtra(SFConstants.MRCH_SHIP_OPTIONS));/* order ID ? */exitIntent.putExtra(SFConstants.ORDERID,m_publisherOrder.getOrderId());/* quantity ? */exitIntent.putExtra(SFConstants.ORDER_QUANTITY, getIntent().getIntExtra(SFConstants.ORDER_QUANTITY, -1));/* mrchId */exitIntent.putExtra(SFConstants.MRCH_ID, getIntent().getLongExtra(SFConstants.MRCH_ID, -1));/* and finally order */exitIntent.putExtra(SFConstants.SF_ORDER, orderAsString());startActivityForResult(exitIntent,SFConstants.SF_REQUEST_CODE_SHIPPING_ADDRESS);} else {/* adding selected address to order */m_publisherOrder.setShippingAddress(mSelectedAddress);resetShippingOptions();if (m_shippingOptionDesc != null&& m_shippingOptionType != null) {exitIntent = new Intent();exitIntent.putExtra(SFConstants.SF_ORDER,orderAsString());exitIntent.putExtra(SFConstants.SF_SHIPPING_OPTION_DESCRIPTION,m_shippingOptionDesc);exitIntent.putExtra(SFConstants.SF_SHIPPING_OPTION_SHIPPING_TYPE,m_shippingOptionType);setResult(RESULT_OK, exitIntent);finish();}}return false;}});}@Overrideprotected void onResume() {// register the event of getting locale contactsSFEventManager.subscribe(m_ctx, SFLocaleContactsEvent.class,m_localeContactListEvent);// register the event of getting remote contactsSFEventManager.subscribe(m_ctx, SFRemoteContactsEvent.class,m_remoteContactListEvent);m_contactNameOverlay = (TextView) LayoutInflater.from(m_ctx).inflate(R.layout.sf_contact_pop_overlay, null);m_contactNameOverlay.setVisibility(View.INVISIBLE);WindowManager.LayoutParams lp = new WindowManager.LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT,WindowManager.LayoutParams.TYPE_APPLICATION,WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE| WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE,PixelFormat.TRANSLUCENT);WindowManager windowManager = (WindowManager) getSystemService(Context.WINDOW_SERVICE);windowManager.addView(m_contactNameOverlay, lp);showProgressDialog(getString(R.string.sf_please_wait),getString(R.string.sf_progress_locale_contact_list));// Get all local contactsSFContactDBManager.asyncGetAllLocalContacts(getSession());// initializeData();// ctx = this;// mContactsListView.setAdapter(new ContactExpandListAdapter(group,// child, ctx));super.onResume();}@Overrideprotected void onPause() {// uninstall the event of getting locale contactsSFEventManager.unsubscribe(m_ctx, SFLocaleContactsEvent.class,m_localeContactListEvent);// uninstall the event of getting remote contactsSFEventManager.unsubscribe(m_ctx, SFRemoteContactsEvent.class,m_remoteContactListEvent);super.onPause();}/*** simply copies street1 into street2 whenever street2 is a mandatory field* in the address entry* * @param contact* @return*/private String handleLocalization(PublisherOrderAddress contact) {if (getResources().getBoolean(R.bool.sf_address_street2_required)) {sLogger.debug("not a localized address: " + contact.toString());if (contact.getStreet2() == null) {contact.setStreet2(contact.getStreet1());contact.setStreet1(null);}}String convertedAddr = "";try {convertedAddr = contact.toJSON().toString();sLogger.debug("localized address: " + convertedAddr);} catch (JSONException e) {sLogger.error("cannot convert selected address: " + e);}return convertedAddr;}@Overrideprotected void onActivityResult(int requestCode, int resultCode, Intent data) {super.onActivityResult(requestCode, resultCode, data);if (requestCode == SFConstants.SF_REQUEST_CODE_SHIPPING_ADDRESS) {if (resultCode == RESULT_OK) {/** after a contact had been verified by end-user it's precision* level is moved to ABSOLUTE*/mSelectedAddress.setGuessedPrecision(SFContact.GuessedPrecision.ABSOLUTE);setResult(RESULT_OK, data);finish();}}}/*** carry over the data required by CShippingAddressActivity*/private void getDataFromIntent() {Intent intent = getIntent();String order = intent.getStringExtra(SFConstants.SF_ORDER);String[] mrchShippingOptions = getIntent().getStringArrayExtra(SFConstants.MRCH_SHIP_OPTIONS);if (order != null) {try {m_publisherOrder = PublisherOrder.newFromJSON(new JSONObject(order));if (null != mrchShippingOptions) {for (String mrchShippingOption : mrchShippingOptions) {m_mrchShippingOptions.add(MrchShippingOption.newFromJSON(new JSONObject(mrchShippingOption)));}}} catch (JSONException e) {sLogger.error("cannot parse order: " + order);sLogger.error("cannot parse order: " + e);}} else {showWarningDialog("Order failed", "order not found in intent");}}/*** initializes the contacts list adapter*/private void onContactsListCreated() {/* set adapter */mContactsListAdapter = new SFContactExpandableListAdapter(this,mPhoneContacts);mContactsListView.setAdapter(mContactsListAdapter);/* hide the divider */mContactsListView.setDivider(null);/** hide indicator, by design it's the first letter of a contact that* takes place of the indicator*/mContactsListView.setGroupIndicator(null);/** make list state expanded - the first time it should display expanded* list of contacts*/for (int i = 0; i < mContactsListAdapter.getGroupCount(); i++) {mContactsListView.expandGroup(i);}/** onScrollListener*/mContactsListView.setOnScrollListener(mItemsListScrollListener);}// private List<String> group;// 组列表// private List<List<String>> child;// 子列表// private Context ctx;//// private void initializeData() {// group = new ArrayList<String>();// child = new ArrayList<List<String>>();//// addInfo("Andy", new String[] { "male", "138123***", "GuangZhou" });// addInfo("Fairy", new String[] { "female", "138123***", "GuangZhou" });// addInfo("Jerry", new String[] { "male", "138123***", "ShenZhen" });// addInfo("Tom", new String[] { "female", "138123***", "ShangHai" });// addInfo("Bill", new String[] { "male", "138231***", "ZhanJiang" });// }//// private void addInfo(String group, String[] child) {// this.group.add(group);// List<String> childItem = new ArrayList<String>();// for (int i = 0; i < child.length; i++) {// childItem.add(child[i]);// }//// this.child.add(childItem);// }/*** converts order to string* * @return*/private String orderAsString() {String orderAsString = "";try {orderAsString = m_publisherOrder.toJSON().toString();} catch (JSONException e) {e.printStackTrace();}return orderAsString;}private void resetShippingOptions() {PublisherOrder order = m_publisherOrder;PublisherOrderAddress cnt = null;try {cnt = PublisherOrderAddress.newFromJSON(new JSONObject(handleLocalization(order.getShippingAddress())));sLogger.debug("order address: " + cnt.toJSON());} catch (JSONException e) {e.printStackTrace();}if (m_mrchShippingOptions.size() > 0) {CShippingOptionsAdapter soa = new CShippingOptionsAdapter(getApplicationContext(), getSession().getAppCredentials().getLocale(), m_mrchShippingOptions, m_mrchId,m_orderQuantity, cnt);String shipping = getApplicationContext().getResources().getString(R.string.sf_order_shipping_opt_no_colon);sLogger.debug("getSession().getAppCredentials().getLocale(): "+ getSession().getAppCredentials().getLocale());sLogger.debug("soa.getAvailableShipOptions().size(): "+ soa.getAvailableShipOptions().size());if (soa.getAvailableShipOptions().size() > 0) {MrchShippingOption item = soa.getAvailableShipOptions().get(0);String shippingDescription = item.getDescription();String shippingType = Character.toString(shippingDescription.charAt(0)).toUpperCase()+ shippingDescription.substring(1);order.setShippingOption(item.getShippingCode());order.setShipping(0.0f);m_shippingOptionDesc = soa.estimatedDeliveryDays(item.getShippingCode());m_shippingOptionType = shippingType + shipping;showProgressDialog(getResources().getString(R.string.sf_please_wait),getResources().getString(R.string.sf_progress_update_order));} else {showErrorAlertDialog();}}}private void showErrorAlertDialog() {AlertDialog.Builder alertDialog = new AlertDialog.Builder(SFContactsListActivity.this);alertDialog.setTitle(R.string.sf_warning_dialog_title);alertDialog.setMessage(R.string.sf_error_no_shipping_options).setPositiveButton("OK", new DialogInterface.OnClickListener() {@Overridepublic void onClick(DialogInterface dialog, int which) {dialog.dismiss();}}).show();}@Overridepublic boolean onCreateOptionsMenu(Menu menu) {getMenuInflater().inflate(R.menu.sf_menu_p2r, menu);searchItem = menu.findItem(R.id.p2r_action_search);mSearchView = (SearchView) MenuItemCompat.getActionView(searchItem);searchEditText = (EditText) mSearchView.findViewById(R.id.search_src_text);searchEditText.setHintTextColor(getResources().getColor(R.color.sf_button_normal));searchEditText.setHint(R.string.sf_contacts_search_hint);SearchManager searchManager = (SearchManager) getSystemService(Context.SEARCH_SERVICE);ComponentName searchComponent = new ComponentName(this,SFContactsListActivity.class);mSearchView.setSearchableInfo(searchManager.getSearchableInfo(searchComponent));mSearchView.setIconifiedByDefault(true);mSearchView.setSubmitButtonEnabled(false);closeBtn = (ImageView) mSearchView.findViewById(R.id.search_close_btn);MenuItemCompat.setOnActionExpandListener(searchItem,new MenuItemCompat.OnActionExpandListener() {@Overridepublic boolean onMenuItemActionCollapse(MenuItem arg0) {sLogger.debug("search field collapsed");return true;}@Overridepublic boolean onMenuItemActionExpand(MenuItem arg0) {sLogger.debug("search field expanded");return true;}});closeBtn.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {searchEditText.setText("");}});mSearchView.setOnQueryTextListener(new OnQueryTextListener() {@Overridepublic boolean onQueryTextSubmit(String arg0) {return false;}@Overridepublic boolean onQueryTextChange(String searchText) {if (searchText.length() > 0) {mSearchString = searchText;sLogger.debug("search text is: " + searchText);} else {mSearchString = null;sLogger.debug("reset list");}return true;}});return super.onCreateOptionsMenu(menu);}/*** Invoking this method when the item option of menu selected*/@Overridepublic boolean onOptionsItemSelected(MenuItem item) {// when the top|left icon selected,finish current activityif (item.getItemId() == android.R.id.home) {finish();overridePendingTransition(R.anim.fade_in, R.anim.fade_out);return true;}return super.onOptionsItemSelected(item);}private boolean isValidContact(AddressType addressType) {if (addressType == null)return false;if (!TextUtils.isEmpty(addressType.getStreet1())&& !TextUtils.isEmpty(addressType.getCity())&& !TextUtils.isEmpty(addressType.getProvince())&& !TextUtils.isEmpty(addressType.getPhone()))return true;return false;}}

<3>编写布局文件sf_view_contact_sort_header.xml。用于展示租名和记录数量。

<?

xml version="1.0" encoding="utf-8"?

> <RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android" android:paddingTop="5dp" android:paddingBottom="5dp" android:paddingLeft="@dimen/sf_standard_layout_padding" android:paddingRight="@dimen/sf_standard_layout_padding" android:layout_width="match_parent" android:layout_height="wrap_content" > <TextView android:id="@+id/sf_contact_sort_by_name" style="@style/SF_MediumBlueBoldTxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_marginBottom="5dp" android:layout_marginLeft="5dp" android:text="@string/sf_contacts_sorting_txt" /> <TextView android:id="@+id/sf_contact_sort_counter" style="@style/SF_SmallLightGreyTxt" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_alignParentRight="true" android:layout_alignParentTop="true" android:gravity="right" android:text="@string/sf_contacts_counter_txt" android:visibility="gone" /> </RelativeLayout>

<4>编写布局文件sf_view_contact_item.xml,用于展示数据的内容:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"style="@style/SF_SelectableSectionBackground"android:layout_width="match_parent"android:layout_height="wrap_content"android:background="@color/sf_activity_background_color"android:paddingBottom="5dp"android:paddingLeft="@dimen/sf_contact_item_layout_padding"android:paddingRight="@dimen/sf_contact_item_layout_padding" ><LinearLayoutandroid:id="@+id/sf_ll_contact_item_divider"style="@style/SF_HorizontalDivider"android:layout_width="match_parent"android:layout_marginBottom="5dp"android:orientation="horizontal" /><includeandroid:id="@+id/sf_include_contact_item_photo"android:layout_width="65dp"android:layout_height="65dp"android:layout_alignLeft="@+id/sf_ll_contact_item_divider"android:layout_alignTop="@+id/sf_ll_contact_item_divider"android:layout_centerHorizontal="true"android:layout_centerInParent="true"android:layout_centerVertical="true"android:layout_marginTop="5dp"layout="@layout/sf_view_contact_graphics"android:visibility="visible" /><LinearLayoutandroid:id="@+id/sf_ll_contact_item_details"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginBottom="5dp"android:layout_marginLeft="5dp"android:layout_marginRight="5dp"android:layout_marginTop="5dp"android:layout_toRightOf="@id/sf_include_contact_item_photo"android:gravity="left|center_horizontal"android:orientation="vertical" ><!-- name --><TextViewandroid:id="@+id/sf_tv_contact_item_name"style="@style/SF_MediumBlackTxt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="left"android:ellipsize="end"android:singleLine="true"android:text="@string/sf_name_placeholder" /><!-- name end --><!-- address --><TextViewandroid:id="@+id/sf_tv_contact_item_address"style="@style/SF_SmallGreyTxt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_gravity="left"android:ellipsize="end"android:singleLine="false"android:text="@string/sf_address_placeholder"android:textAlignment="center" /><!-- address end --><TextViewandroid:id="@+id/sf_tv_contact_item_phone"style="@style/SF_MediumLightGreyTxt"android:layout_width="wrap_content"android:layout_height="wrap_content"android:ellipsize="end"android:minLines="1"android:scrollHorizontally="false"android:singleLine="true"android:text="@string/sf_adress_type_txt"android:visibility="gone" /></LinearLayout></RelativeLayout>

<5>编写SFContactExpandableListAdapter.java。继承BaseExpandableListAdapter.java

package com.snapfish.util;import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;import android.annotation.SuppressLint;
import android.content.ContentUris;
import android.content.Context;
import android.content.res.Resources;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.net.Uri;
import android.provider.ContactsContract.Contacts;
import android.text.TextUtils;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseExpandableListAdapter;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;import com.snapfish.R;
import com.snapfish.checkout.IUserData;
import com.snapfish.internal.datamodel.SFContact;
import com.snapfish.ui.SFContactsListActivity;public class SFContactExpandableListAdapter extends BaseExpandableListAdapter { // implements// LoaderManager.LoaderCallbacks<Cursor>private static final SFLogger sLogger = SFLogger.getInstance(SFContactsListActivity.class.getName());private Context m_ctx;private ViewHolder m_viewHolder;private List<SFContact> mCombinedContacts;private Map<String, List<SFContact>> mSortedContacts;public SFContactExpandableListAdapter(Context mContext,List<SFContact> mCombinedContacts) {this.m_ctx = mContext;this.mCombinedContacts = mCombinedContacts;this.mSortedContacts = getSortedContacts();m_viewHolder = new ViewHolder();}@Overridepublic int getGroupCount() {return mSortedContacts.size();}@Overridepublic int getChildrenCount(int groupPosition) {List<SFContact> children = mSortedContacts.get(getKeyByPosition(groupPosition));return children.size();}@Overridepublic Object getGroup(int groupPosition) {List<SFContact> group = mSortedContacts.get(getKeyByPosition(groupPosition));return group;}@Overridepublic Object getChild(int groupPosition, int childPosition) {@SuppressWarnings("unchecked")List<SFContact> selectedGroup = (List<SFContact>) getGroup(groupPosition);if (selectedGroup.size() < childPosition)return null;return selectedGroup.get(childPosition);}@Overridepublic long getGroupId(int groupPosition) {return groupPosition;}@SuppressWarnings("unchecked")@Overridepublic long getChildId(int groupPosition, int childPosition) {List<SFContact> childGroup = (List<SFContact>) getGroup(groupPosition);if (null != childGroup && childGroup.get(childPosition) != null)return childPosition;return 0;}@Overridepublic boolean hasStableIds() {return false;}@Overridepublic View getGroupView(int groupPosition, boolean isExpanded,View convertView, ViewGroup parent) {View row = LayoutInflater.from(m_ctx).inflate(R.layout.sf_view_contact_sort_header, parent, false);TextView sortTextView = (TextView) row.findViewById(R.id.sf_contact_sort_by_name);TextView countTextView = (TextView) row.findViewById(R.id.sf_contact_sort_counter);String letter = getKeyByPosition(groupPosition);if (groupPosition == 0) {countTextView.setVisibility(View.VISIBLE);countTextView.setText("" + mCombinedContacts.size() + " "+ m_ctx.getString(R.string.sf_contacts_counter_txt));}sortTextView.setText("  " + letter);return row;}@Overridepublic View getChildView(int groupPosition, int childPosition,boolean isLastChild, View convertView, ViewGroup parent) {SFContact contact = (SFContact) getChild(groupPosition, childPosition);if (contact != null) {// SFContact.ContactAddressType displayType = contact// .getAddressType();String displayName = contact.getName();String displayAddress = getContactAddress(contact);View row = LayoutInflater.from(m_ctx).inflate(R.layout.sf_view_contact_item, parent, false);// contact namem_viewHolder.m_name = (TextView) row.findViewById(R.id.sf_tv_contact_item_name);m_viewHolder.m_name.setText(displayName);// contact addressm_viewHolder.m_address = (TextView) row.findViewById(R.id.sf_tv_contact_item_address);m_viewHolder.m_address.setText(displayAddress);/* contact address type */m_viewHolder.m_phone = (TextView) row.findViewById(R.id.sf_tv_contact_item_phone);// contact photosetContactIcon(row, contact, getKeyByPosition(groupPosition));// if (isMultipleAddress(groupPosition, childPosition)// || contact.getGuessedPrecision() ==// SFContact.GuessedPrecision.LOW) {// showAddressType(m_viewHolder.m_phone,// contact.getGuessedPrecision(), displayType);// }row.setTag(m_viewHolder);return row;}return convertView;}/*** to avoid displaying address type when we do not have matching contacts* * @param groupPosition* @param childPosition* @return*/private boolean isMultipleAddress(int groupPosition, int childPosition) {@SuppressWarnings("unchecked")List<SFContact> group = (List<SFContact>) getGroup(groupPosition);SFContact reference = (SFContact) getChild(groupPosition, childPosition);String referenceName = reference.getName();int i = group.size();for (int a = 0; a < i; a++) {if (a != childPosition)if (referenceName.equalsIgnoreCase(group.get(a).getName()))return true;}return false;}@Overridepublic boolean isChildSelectable(int groupPosition, int childPosition) {return true;}/*** contact icons are of three kinds: image, icon, or warning message* * @param aContact* @param letter* @param addrPositiveMatch*/private void setContactIcon(View row, SFContact aContact, String letter) {// TODO guessed precision was not set in the DB - set it upSFContact.GuessedPrecision addrPositiveMatch = aContact.getGuessedPrecision();ViewGroup contactImageView = (ViewGroup) row.findViewById(R.id.sf_include_contact_item_photo);if (contactImageView != null) {ImageView unverifiedAddress = (ImageView) contactImageView.findViewById(R.id.sf_iv_contact_need_confirmation);LinearLayout noCntPhoto = (LinearLayout) contactImageView.findViewById(R.id.sf_ll_contact_no_photo);TextView cntFirstLetter = (TextView) noCntPhoto.findViewById(R.id.sf_tv_contact_name_letter);ImageView defaultCntIcon = (ImageView) contactImageView.findViewById(R.id.sf_iv_contact_default_photo);defaultCntIcon.setVisibility(View.GONE);cntFirstLetter.setVisibility(View.GONE);noCntPhoto.setVisibility(View.GONE);/* get profile photo bitmap to load into ImageView */Bitmap bmp = getProfilePhoto(aContact);switch (addrPositiveMatch) {case ABSOLUTE:if (bmp != null) {defaultCntIcon.setVisibility(View.VISIBLE);defaultCntIcon.setImageBitmap(bmp);} else {noCntPhoto.setVisibility(View.VISIBLE);noCntPhoto.setBackgroundColor(Color.parseColor(getRandomColor()));cntFirstLetter.setVisibility(View.VISIBLE);cntFirstLetter.setText(letter);}break;case HIGH:unverifiedAddress.setVisibility(View.VISIBLE);break;case MEDIUM:unverifiedAddress.setVisibility(View.VISIBLE);break;case LOW:break;default:unverifiedAddress.setVisibility(View.VISIBLE);break;}}}private void showAddressType(TextView typeView,SFContact.GuessedPrecision precisionLevel,SFContact.ContactAddressType displayType) {Resources r = m_ctx.getResources();// typeView.setVisibility(View.VISIBLE);typeView.setVisibility(View.GONE);if (precisionLevel != SFContact.GuessedPrecision.ABSOLUTE) {// not// verified// (guessed)// addresstypeView.setTextColor(r.getColor(R.color.sf_error_message_txt_color));typeView.setText(r.getString(R.string.sf_verify_address_label));} else if (displayType != null) { // verified addresstypeView.setText(getAddressType(displayType));}}private int colorsUsed;/*** using preset swatches to set background colors in the layout* * @return*/private String getRandomColor() {String[] colorChoises = m_ctx.getResources().getStringArray(R.array.sf_swatches_sp_set);if (colorsUsed == colorChoises.length)colorsUsed = 0;String colorOut = colorChoises[colorsUsed];colorsUsed++;return colorOut;}private Bitmap getProfilePhoto(SFContact aContact) {if (aContact.getContactSource().equals(IUserData.EUserDataType.PHONEBOOK)) {InputStream photoStream = getPhotoAsInputSteam(aContact);if (photoStream != null)return BitmapFactory.decodeStream(photoStream);}return null;}private String getKeyByPosition(int groupPosition) {int keyPosition = 0;Iterator<String> iter = mSortedContacts.keySet().iterator();while (iter.hasNext()) {String key = iter.next();if (keyPosition == groupPosition) {return key;}keyPosition++;}return null;}/*** extracting first letter of display name* * @return*/private Collection<String> extractFirstLetter() {Collection<String> sortByLetter = new HashSet<String>();int index = mCombinedContacts.size();Collections.sort(mCombinedContacts, new Comparator<SFContact>() {@Overridepublic int compare(SFContact lhs, SFContact rhs) {return lhs.getName().compareTo(rhs.getName());}});for (int i = 0; i < index; i++) {char letter = mCombinedContacts.get(i).getName().charAt(0);sortByLetter.add(String.valueOf(letter).toUpperCase());}return sortByLetter;}private Map<String, List<SFContact>> getSortedContacts() {List<SFContact> selectedContact = null;mSortedContacts = new TreeMap<String, List<SFContact>>();Iterator<String> firstLetter = extractFirstLetter().iterator();while (firstLetter.hasNext()) {String s = firstLetter.next();int index = mCombinedContacts.size();selectedContact = new ArrayList<SFContact>();for (int i = 0; i < index; i++) {String initChar = String.valueOf(mCombinedContacts.get(i).getName().charAt(0));if (s.equalsIgnoreCase(initChar)) {selectedContact.add(mCombinedContacts.get(i));}}mSortedContacts.put(s, selectedContact);}return mSortedContacts;}/*** retrieves address type string from arrays.xml* * @param addressType* @return*/private String getAddressType(SFContact.ContactAddressType addressType) {String addrTypeOut = "";int arrSize = m_ctx.getResources().getStringArray(R.array.sf_address_type).length;if (arrSize != 0 && arrSize == 3) {switch (addressType) {case TYPE_HOME:addrTypeOut = m_ctx.getResources().getStringArray(R.array.sf_address_type)[0];break;case TYPE_WORK:addrTypeOut = m_ctx.getResources().getStringArray(R.array.sf_address_type)[1];break;case TYPE_OTHER:default:addrTypeOut = m_ctx.getResources().getStringArray(R.array.sf_address_type)[2];break;}} elseaddrTypeOut = "UNKNOWN";return addrTypeOut;}private InputStream getPhotoAsInputSteam(SFContact contact) {byte[] data = contact.getContactPhotoData();if (data != null) {return new ByteArrayInputStream(contact.getContactPhotoData());}return null;}@SuppressLint("InlinedApi")private InputStream openPhoto(long contactId) {Uri contactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI,contactId);Uri photoUri = Uri.withAppendedPath(contactUri,Contacts.Photo.CONTENT_DIRECTORY);Cursor cursor = m_ctx.getContentResolver().query(photoUri,new String[] { Contacts.Photo.PHOTO }, null, null, null);if (cursor == null) {return null;}try {if (cursor.moveToFirst()) {byte[] data = cursor.getBlob(0);if (data != null) {return new ByteArrayInputStream(data);}}} finally {cursor.close();}return null;}private String getContactAddress(SFContact contact) {StringBuilder sb = new StringBuilder();sb.append(contact.getCountry() + " ");sb.append(contact.getProvince() + " ");sb.append(contact.getCity() + " ");sb.append(TextUtils.isEmpty(contact.getStreet1()) ? (TextUtils.isEmpty(contact.getStreet2()) ? (TextUtils.isEmpty(contact.getStreet3()) ? "" : contact.getStreet3()) : contact.getStreet2()) : contact.getStreet1() + "  ");sb.append(contact.getPostalCode() + "\n");sb.append(contact.getPhone());return sb.toString();}private static class ViewHolder {TextView m_name;TextView m_address;TextView m_phone;}}

注意

继承BaseExpandableListAdapter需实现对应的方法:

<1>getGroupId():获取组号

<2>getChildId():获取子元素编号(位于组中)

<3>getGroup():获取指定组的组内容

<4>getChild():依据组和子元素的位置获取子元素内容

<5>getGroupCount():获取组的数目

<6>getChildrenCount():获取相应组的子元素的数目

<7>getGroupView():获取View对象用于显示组内容

<8>getChildView():依据组和子元素的位置获取View对象用于显示子元素的内容

<9>hasStableIds():组和子元素是否持有稳定的ID,也就是底层数据的改变不会影响到它们

<10>isChildSelectable():是否可选择指定位置的子元素

ExpandableListView的使用以及信息的高亮显示相关推荐

  1. ArcGIS API for JS4.7加载FeatureLayer,点击弹出信息并高亮显示

    我加载的是ArcGIS Server本地发布的FeatureService,ArcGIS API for JS4.7记载FeatureLayer时,在二维需要通过代码启用WebGL渲染,在三维模式下, ...

  2. 用户不见了_03 | 为什么你设计的信息用户总是看不见

    点击上方蓝字关注 + 点击右上方"..."设为星标 大家好,这是十万个为什么设计的第3篇,还剩99997篇. 很多设计师在进行信息设计的时候,通常靠着感觉和曾经尝试过的经验进行信息 ...

  3. Linux基础 常用命令学习

    2019独角兽企业重金招聘Python工程师标准>>> touch FILE 更新/创建文件 -a 访问时间 -m 修改时间 -c 不创建 -t 指定时间 mv FILE/DIR D ...

  4. Web应用扫描工具Wapiti

    Web应用扫描工具Wapiti Wapiti是Kali Linux预置的一款Web应用扫描工具.该工具执行黑盒扫描,用户只需要输入要扫描的网址即可.该工具可以探测文件包含.数据库注入.XSS.CRLF ...

  5. 一款好用且免费的语句分析工具Plan Explorer

    在调优过程中的查询语句优化阶段,分析语句的执行计划是必经之路,一款好的执行计划分析工具确实可以帮助我们事半功倍 小贴士:Plan Explorer是将Plan Explorer 专业版与免费版整合在一 ...

  6. linux卸载htop,linux下 htop 工具简介

    今天我们介绍一下 htop 工具的使用,这是个很实用的工具,可以查看系统进程.cpu占用率,内存使用等情况. 对于我们运维人员来说使用它可以快速定位是哪一个进程造成了系统堵塞:哪些进程长期霸占了CPU ...

  7. Android 4.3 新特性

    欢迎到Android 4.3版糖豆更甜! Android 4.3的包括性能优化和强大的新功能,为用户和开发人员.本文档提供了什么新开发的一瞥. 见的Android 4.3的API文档,在新开发的API ...

  8. 【转载】GitHub详细教程

    1 Git详细教程 1.1 Git简介 1.1.1 Git是何方神圣? Git是用C语言开发的分布版本控制系统.版本控制系统可以保留一个文件集合的历史记录,并能回滚文件集合到另一个状态(历史记录状态) ...

  9. Android WIFI密码查看器实例(在获取Root权限下查看系统文件)

    Android WIFI密码查看器实例 实现原理:使用shell命令查看保存WIFI密码的系统文件 涉及的知识 界面展示 基本的Shell命令 shell查看WIFI密码 ShellUtil的使用 正 ...

最新文章

  1. JAVA求是否为闰年,for-while循环,输出你好
  2. CVPR 2020丨基于点云的3D物体检测新框架
  3. 这可能是十年来最酷的神经科学发现
  4. 原生js实现ajax的文件异步提交功能、图片预览功能.实例
  5. golang map嵌套struct 结构体字段 不能直接修改 解决方法
  6. 5月22日云栖精选夜读:PHP学习路线图
  7. 程序人生:摆脱情绪低潮的10种方法
  8. mysql workbench查询快捷_mysql workbench快捷键
  9. 华为最新人事调整:余承东任智能汽车解决方案 BU CEO;美团悄悄更换抽佣规则,佣金不降反升;Scala 3 正式发布|极客头条...
  10. opencv-python 霍夫变换
  11. 中文文字校对和文档对比合并开源工具调研
  12. 高级电工实验室成套设备(带功率表、功率因数表)
  13. 【计算机网络实验】DHCP报文捕获和分析
  14. 营业执照编码验证规则(15位和18位)
  15. python爬取拉勾网_使用requests爬取拉勾网python职位数据
  16. 记一次失败的git截图工具使用经历——shareX
  17. 2019年安徽大学ACM/ICPC实验室新生赛题解
  18. 30道你不得不会的Elasticsearch面试题【附答案解析】
  19. c语言编程设计实验课件,c语言程序设计实验课件.ppt
  20. cad展点kszd小程序_CAD坐标展点脚本文件-CAD坐标展点程序下载v2 官方版-西西软件下载...

热门文章

  1. Android之四大组件(AIDL Service的使用)
  2. 人工智能实践:TensorFlow笔记学习(三)——TensorFlow框架
  3. iOS架构-cocoapods之公共库的发布与集成(16)
  4. php实现中英文网站插件,多语言网站方案
  5. python访问序列元素的编号用什么括起来_Python 序列通用操作介绍
  6. [Dijstra] 洛谷 P2939 改造路
  7. labview初始学习过程中遇到串口读取框红蓝色交替闪烁的处理
  8. 遍历list、set、map和array
  9. backboneJs 导图
  10. 浅谈ASP.NET 缓存技术