MultiAdapterSpinner.java revision 19acfbb21739e88784fae4844b5ea1e9a7f8974b
119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo/* 219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Copyright (C) 2011 Google Inc. 319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Licensed to The Android Open Source Project. 419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * 519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Licensed under the Apache License, Version 2.0 (the "License"); 619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * you may not use this file except in compliance with the License. 719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * You may obtain a copy of the License at 819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * 919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * http://www.apache.org/licenses/LICENSE-2.0 1019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * 1119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Unless required by applicable law or agreed to in writing, software 1219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * distributed under the License is distributed on an "AS IS" BASIS, 1319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * See the License for the specific language governing permissions and 1519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * limitations under the License. 1619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */ 1719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 1819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 1919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalopackage com.android.mailcommon; 2019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 2119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport com.android.mailcommon.MergedAdapter.ListSpinnerAdapter; 2219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport com.android.mailcommon.MergedAdapter.LocalAdapterPosition; 2319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 2419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.content.Context; 2519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.util.AttributeSet; 2619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.view.View; 2719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.view.ViewGroup; 2819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.AdapterView; 2919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.FrameLayout; 3019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.ListPopupWindow; 3119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komaloimport android.widget.ListView; 3219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 3319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 3419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo/** 3519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>A spinner-like widget that combines data and views from multiple adapters (via MergedAdapter) 3619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * and forwards certain events to those adapters. This widget also supports clickable but 3719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * unselectable dropdown items, useful when displaying extra items that should not affect spinner 3819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * selection state.</p> 3919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * 4019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>The framework's Spinner widget can't be extended for this task because it uses a private list 4119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * adapter (which prevents setting multiple item types) and hides access to its popup, which is 4219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * useful for clients to know about (like when it's been opened).</p> 4319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * 4419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>Clients must provide a set of adapters which the widget will use to load dropdown views, 4519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * receive callbacks, and load the selected item's view.</p> 4619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * 4719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * <p>Apps incorporating this widget must declare a custom attribute: "dropDownWidth" under the 4819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * "MultiAdapterSpinner" name as a "reference" format (see Gmail's attrs.xml file for an 4919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * example). This attribute controls the width of the dropdown, similar to the attribute in the 5019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * framework's Spinner widget.</p> 5119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * 5219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */ 5319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalopublic class MultiAdapterSpinner extends FrameLayout 5419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo implements AdapterView.OnItemClickListener, View.OnClickListener { 5519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 5619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo protected MergedSpinnerAdapter mAdapter; 5719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo protected ListPopupWindow mPopup; 5819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 5919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo private int mSelectedPosition; 6019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 6119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo /** 6219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * A basic adapter with some callbacks added so clients can be involved in spinner behavior. 6319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */ 6419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public interface FancySpinnerAdapter extends ListSpinnerAdapter { 6519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo /** 6619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Whether or not an item at position should become the new selected spinner item and change 6719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * the spinner item view. 6819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */ 6919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo boolean canSelect(int position); 7019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo /** 7119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Handle a click on an enabled item. 7219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */ 7319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo void onClick(int position); 7419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo /** 7519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * Fired when the popup window is about to be displayed. 7619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */ 7719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo void onShowPopup(); 7819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 7919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 8019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo private class MergedSpinnerAdapter extends MergedAdapter<FancySpinnerAdapter> { 8119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo /** 8219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo * ListPopupWindow uses getView() but spinners return dropdown views in getDropDownView(). 8319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo */ 8419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo @Override 8519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public View getView(int position, View convertView, ViewGroup parent) { 8619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo return super.getDropDownView(position, convertView, parent); 8719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 8819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 8919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 9019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public MultiAdapterSpinner(Context context) { 9119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo this(context, null); 9219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 9319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 9419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public MultiAdapterSpinner(Context context, AttributeSet attrs) { 9519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo super(context, attrs); 9619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 9719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mAdapter = new MergedSpinnerAdapter(); 9819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup = new ListPopupWindow(context, attrs); 9919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.setAnchorView(this); 10019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.setOnItemClickListener(this); 10119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.setModal(true); 10219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.setAdapter(mAdapter); 10319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 10419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 10519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public void setAdapters(FancySpinnerAdapter... adapters) { 10619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mAdapter.setAdapters(adapters); 10719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 10819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 10919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public void setSelectedItem(final FancySpinnerAdapter adapter, final int position) { 11019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo int globalPosition = 0; 11119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo boolean found = false; 11219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 11319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) { 11419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo ListSpinnerAdapter a = mAdapter.getSubAdapter(i); 11519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo if (a == adapter) { 11619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo globalPosition += position; 11719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo found = true; 11819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo break; 11919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 12019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo globalPosition += a.getCount(); 12119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 12219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo if (found) { 12319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo if (adapter.canSelect(position)) { 12419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo removeAllViews(); 12519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo View itemView = adapter.getView(position, null, this); 12619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo itemView.setClickable(true); 12719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo itemView.setOnClickListener(this); 12819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo addView(itemView); 12919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 13019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo if (position < adapter.getCount()) { 13119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mSelectedPosition = globalPosition; 13219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 13319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 13419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 13519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 13619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 13719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo @Override 13819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public void onClick(View v) { 13919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo if (!mPopup.isShowing()) { 14019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 14119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo for (int i = 0, count = mAdapter.getSubAdapterCount(); i < count; i++) { 14219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mAdapter.getSubAdapter(i).onShowPopup(); 14319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 14419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 14519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.show(); 14619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); 14719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.setSelection(mSelectedPosition); 14819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 14919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 15019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 15119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo @Override 15219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public void onItemClick(AdapterView<?> parent, View view, int position, long id) { 15319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 15419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo if (position != mSelectedPosition) { 15519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo final LocalAdapterPosition<FancySpinnerAdapter> result = 15619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mAdapter.getAdapterOffsetForItem(position); 15719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 15819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo if (result.mAdapter.canSelect(result.mLocalPosition)) { 15919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mSelectedPosition = position; 16019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } else { 16119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.clearListSelection(); 16219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 16319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 16419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo post(new Runnable() { 16519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo @Override 16619acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo public void run() { 16719acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo result.mAdapter.onClick(result.mLocalPosition); 16819acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 16919acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo }); 17019acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 17119acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 17219acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo mPopup.dismiss(); 17319acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo } 17419acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo 17519acfbb21739e88784fae4844b5ea1e9a7f8974bBen Komalo} 176