滑动显示首字母的ListView

以前在网上找了一个例子挺好好的,一直没有有时间和大家分享一下,现在刚好有时间,本来一直想每天写点东西,但是本人意志力不是很好,所以。。。好了进入正题。

要用到下面几个类

效果图


不想看类文件的可以下载0分源代码:http://download.csdn.net/detail/mark_dev/4022027

项目名:ComplexListDemo

类文件

MainActivity.java

package com.mark;

import java.lang.reflect.Field;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.graphics.PixelFormat;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.ListView;
import android.widget.TextView;


public class MainActivity extends Activity implements ListView.OnScrollListener, OnItemClickListener,
		android.view.View.OnClickListener {
	private Handler handler;
	private DisapearThread disapearThread;
	/** 标识List的滚动状态。 */
	private int scrollState;
	private ListAdapter listAdapter;
	private ListView listMain;
	private TextView txtOverlay;
	private WindowManager windowManager;

	/** Called when the activity is first created. */
	public void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.main);
		handler = new Handler();
		// 初始化首字母悬浮提示框
		txtOverlay = (TextView) LayoutInflater.from(this).inflate(R.layout.popup_char_hint, null);
		txtOverlay.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) getSystemService(Context.WINDOW_SERVICE);
		windowManager.addView(txtOverlay, lp);
		// 初始化ListAdapter
		listAdapter = new ListAdapter(this, stringArr, this);
		listMain = (ListView) findViewById(R.id.listInfo);
		listMain.setOnItemClickListener(this);
		listMain.setOnScrollListener(this);
		listMain.setAdapter(listAdapter);
		changeFastScrollerDrawable(listMain);
		disapearThread = new DisapearThread();
	}

	/** 更改指定ListView的快速滚动条图标。 */
	private void changeFastScrollerDrawable(ListView list) {
		try {
			Field f = AbsListView.class.getDeclaredField("mFastScroller");
			f.setAccessible(true);
			Object obj = f.get(list);
			f = f.getType().getDeclaredField("mThumbDrawable");
			f.setAccessible(true);
			Drawable drawable = (Drawable) f.get(obj);
			drawable = getResources().getDrawable(R.drawable.fast_scroller_img);
			f.set(obj, drawable);
		} catch (Exception e) {
			throw new RuntimeException(e);
		}
	}

	/** ListView.OnScrollListener */
	public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount,
			int totalItemCount) {
		// 以中间的ListItem为标准项。
		txtOverlay.setText(String.valueOf(stringArr[firstVisibleItem + (visibleItemCount >> 1)]
				.charAt(0)));
	}

	/** ListView.OnScrollListener */
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		this.scrollState = scrollState;
		if (scrollState == ListView.OnScrollListener.SCROLL_STATE_IDLE) {
			handler.removeCallbacks(disapearThread);
			// 提示延迟1.5s再消失
			boolean bool = handler.postDelayed(disapearThread, 1500);
			Log.d("ANDROID_INFO", "postDelayed=" + bool);
		} else {
			txtOverlay.setVisibility(View.VISIBLE);
		}
	}

	/** OnItemClickListener */
	public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
		String personalName = stringArr[position];
		String url = ListAdapter.URL_PREFIX + personalName.replace(" ", "%20");
		showInfo(personalName, url);
	}

	/**
	 * View.OnClickListener <br/>
	 * 点击“咧牙”图片。<br/>
	 */
	public void onClick(View view) {
		if (view instanceof ImageView) {
			// "咧牙"图标
			int position = ((Integer) view.getTag()).intValue();
			ActionItem actionAdd = new ActionItem(getResources().getDrawable(R.drawable.icon_info),
					"Info", this);
			ActionItem actionWeb = new ActionItem(getResources().getDrawable(R.drawable.icon_web),
					"Web", this);
			ActionItem actionEMail = new ActionItem(getResources().getDrawable(
					R.drawable.icon_email), "Email", this);
			QuickActionBar qaBar = new QuickActionBar(view, position);
			qaBar.setEnableActionsLayoutAnim(true);
			qaBar.addActionItem(actionAdd);
			qaBar.addActionItem(actionWeb);
			qaBar.addActionItem(actionEMail);
			qaBar.show();
		} else if (view instanceof LinearLayout) {
			// ActionItem组件
			LinearLayout actionsLayout = (LinearLayout) view;
			QuickActionBar bar = (QuickActionBar) actionsLayout.getTag();
			bar.dismissQuickActionBar();
			int listItemIdx = bar.getListItemIndex();
			TextView txtView = (TextView) actionsLayout.findViewById(R.id.qa_actionItem_name);
			String actionName = txtView.getText().toString();
			String personalName = stringArr[listItemIdx];
			String url = ListAdapter.URL_PREFIX + personalName.replace(" ", "%20");
			if (actionName.equals("Info")) {
				showInfo(personalName, url);
			} else if (actionName.equals("Web")) {
				go2Web(url);
			} else if (actionName.equals("Email")) {
				sendEMail(personalName, url);
			}
		}
	}

	public void showInfo(String name, String url) {
		String content = "Name:" + name + "\n"//
				+ "URL:" + url//
		;
		Dialog dialog = new Dialog(this);
		dialog.setContentView(R.layout.info_dialog);
		dialog.setTitle("Personal Information");
		TextView text = (TextView) dialog.findViewById(R.id.text);
		text.setText(content);
		ImageView image = (ImageView) dialog.findViewById(R.id.image);
		image.setImageResource(R.drawable.icon_info);
		dialog.show();
	}

	private void go2Web(String url) {
		Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
		startActivity(intent);
	}

	private void sendEMail(String name, String url) {
		Intent mailIntent = new Intent(Intent.ACTION_SEND);
		// 设置邮件类型为纯文本,即为所有可见字符与一些简单的控制符的组合。
		mailIntent.setType("plain/text");
		// 收件人地址
		String[] arrReceiver = { "sodinoopen@hotmail.com" };
		// 抄送地址
		String[] arrCc = { "sodinoopen@hotmail.com" };
		// 密送地址
		String[] arrBcc = { "sodinoopen@hotmail.com", "sodinoopen@hotmail.com" };
		String mailSubject = "This is a mail send by list demo";
		String mailBody = "Hello everyone, I think the my demo is very good, haha. thanks.";
		// 指定要添加的附件的完整路径
		String attachPath = "file:///sdcard/Test.apk";
		mailIntent.putExtra(Intent.EXTRA_EMAIL, arrReceiver);
		mailIntent.putExtra(Intent.EXTRA_CC, arrCc);
		mailIntent.putExtra(Intent.EXTRA_BCC, arrBcc);
		mailIntent.putExtra(Intent.EXTRA_SUBJECT, mailSubject);
		mailIntent.putExtra(Intent.EXTRA_TEXT, mailBody);
		mailIntent.putExtra(Intent.EXTRA_STREAM, Uri.parse(attachPath));
		// 过滤Intent并启动GMail界面,之后点击Send即可将邮件发送出去。
		Intent finalIntent = Intent.createChooser(mailIntent, "Mail Sending...");
		startActivity(finalIntent);
	}

	private class DisapearThread implements Runnable {
		public void run() {
			// 避免在1.5s内,用户再次拖动时提示框又执行隐藏命令。
			if (scrollState == ListView.OnScrollListener.SCROLL_STATE_IDLE) {
				txtOverlay.setVisibility(View.INVISIBLE);
			}
		}
	}

	public void onDestroy() {
		super.onDestroy();
		// 将txtOverlay删除。
		txtOverlay.setVisibility(View.INVISIBLE);
		windowManager.removeView(txtOverlay);
	}

	private String[] stringArr = { "Abbaye de Belloc", "Abbaye du Mont des Cats", "Abertam",
			"Abondance", "Ackawi", "Acorn", "Adelost", "Affidelice au Chablis", "Afuega'l Pitu",
			"Airag", "Airedale", "Aisy Cendre", "Allgauer Emmentaler", "Alverca", "Ambert",
			"American Cheese", "Ami du Chambertin", "Anejo Enchilado", "Anneau du Vic-Bilh",
			"Anthoriro", "Appenzell", "Aragon", "Ardi Gasna", "Ardrahan", "Armenian String",
			"Aromes au Gene de Marc", "Asadero", "Asiago", "Aubisque Pyrenees", "Autun",
			"Avaxtskyr", "Baby Swiss", "Babybel", "Baguette Laonnaise", "Bakers", "Baladi",
			"Balaton", "Bandal", "Banon", "Barry's Bay Cheddar", "Basing", "Basket Cheese",
			"Bath Cheese", "Bavarian Bergkase", "Baylough", "Beaufort", "Beauvoorde",
			"Beenleigh Blue", "Beer Cheese", "Bel Paese", "Bergader", "Bergere Bleue", "Berkswell",
			"Beyaz Peynir", "Bierkase", "Bishop Kennedy", "Blarney", "Bleu d'Auvergne",
			"Bleu de Gex", "Bleu de Laqueuille", "Bleu de Septmoncel", "Bleu Des Causses", "Blue",
			"Blue Castello", "Blue Rathgore", "Blue Vein (Australian)", "Blue Vein Cheeses",
			"Bocconcini", "Bocconcini (Australian)", "Boeren Leidenkaas", "Bonchester", "Bosworth",
			"Bougon", "Boule Du Roves", "Boulette d'Avesnes", "Boursault", "Boursin", "Bouyssou",
			"Bra", "Braudostur", "Breakfast Cheese", "Brebis du Lavort", "Brebis du Lochois",
			"Brebis du Puyfaucon", "Bresse Bleu", "Brick", "Brie", "Brie de Meaux",
			"Brie de Melun", "Brillat-Savarin", "Brin", "Brin d' Amour", "Brin d'Amour",
			"Brinza (Burduf Brinza)", "Briquette de Brebis", "Briquette du Forez", "Broccio",
			"Broccio Demi-Affine", "Brousse du Rove", "Bruder Basil",
			"Brusselae Kaas (Fromage de Bruxelles)", "Bryndza", "Buchette d'Anjou", "Buffalo",
			"Burgos", "Butte", "Butterkase", "Button (Innes)", "Buxton Blue", "Cabecou", "Caboc",
			"Cabrales", "Cachaille", "Caciocavallo", "Caciotta", "Caerphilly", "Cairnsmore",
			"Calenzana", "Cambazola", "Camembert de Normandie", "Canadian Cheddar", "Canestrato",
			"Cantal", "Caprice des Dieux", "Capricorn Goat", "Capriole Banon", "Carre de l'Est",
			"Casciotta di Urbino", "Cashel Blue", "Castellano", "Castelleno", "Castelmagno",
			"Castelo Branco", "Castigliano", "Cathelain", "Celtic Promise", "Cendre d'Olivet",
			"Cerney", "Chabichou", "Chabichou du Poitou", "Chabis de Gatine", "Chaource",
			"Charolais", "Chaumes", "Cheddar", "Cheddar Clothbound", "Cheshire", "Chevres",
			"Chevrotin des Aravis", "Chontaleno", "Civray", "Coeur de Camembert au Calvados",
			"Coeur de Chevre", "Colby", "Cold Pack", "Comte", "Coolea", "Cooleney", "Coquetdale",
			"Corleggy", "Cornish Pepper", "Cotherstone", "Cotija", "Cottage Cheese",
			"Cottage Cheese (Australian)", "Cougar Gold", "Coulommiers", "Coverdale",
			"Crayeux de Roncq", "Cream Cheese", "Cream Havarti", "Crema Agria", "Crema Mexicana",
			"Creme Fraiche", "Crescenza", "Croghan", "Crottin de Chavignol",
			"Crottin du Chavignol", "Crowdie", "Crowley", "Cuajada", "Curd", "Cure Nantais",
			"Curworthy", "Cwmtawe Pecorino", "Cypress Grove Chevre", "Danablu (Danish Blue)",
			"Danbo", "Danish Fontina", "Daralagjazsky", "Dauphin", "Delice des Fiouves",
			"Denhany Dorset Drum", "Derby", "Dessertnyj Belyj", "Devon Blue", "Devon Garland",
			"Dolcelatte", "Doolin", "Doppelrhamstufel", "Dorset Blue Vinney", "Double Gloucester",
			"Double Worcester", "Dreux a la Feuille", "Dry Jack", "Duddleswell", "Dunbarra",
			"Dunlop", "Dunsyre Blue", "Duroblando", "Durrus", "Dutch Mimolette (Commissiekaas)",
			"Edam", "Edelpilz", "Emental Grand Cru", "Emlett", "Emmental", "Epoisses de Bourgogne",
			"Esbareich", "Esrom", "Etorki", "Evansdale Farmhouse Brie", "Evora De L'Alentejo",
			"Exmoor Blue", "Explorateur", "Feta", "Feta (Australian)", "Figue", "Filetta",
			"Fin-de-Siecle", "Finlandia Swiss", "Finn", "Fiore Sardo", "Fleur du Maquis",
			"Flor de Guia", "Flower Marie", "Folded", "Folded cheese with mint",
			"Fondant de Brebis", "Fontainebleau", "Fontal", "Fontina Val d'Aosta",
			"Formaggio di capra", "Fougerus", "Four Herb Gouda", "Fourme d' Ambert",
			"Fourme de Haute Loire", "Fourme de Montbrison", "Fresh Jack", "Fresh Mozzarella",
			"Fresh Ricotta", "Fresh Truffles", "Fribourgeois", "Friesekaas", "Friesian", "Friesla",
			"Frinault", "Fromage a Raclette", "Fromage Corse", "Fromage de Montagne de Savoie",
			"Fromage Frais", "Fruit Cream Cheese", "Frying Cheese", "Fynbo", "Gabriel",
			"Galette du Paludier", "Galette Lyonnaise", "Galloway Goat's Milk Gems", "Gammelost",
			"Gaperon a l'Ail", "Garrotxa", "Gastanberra", "Geitost", "Gippsland Blue", "Gjetost",
			"Gloucester", "Golden Cross", "Gorgonzola", "Gornyaltajski", "Gospel Green", "Gouda",
			"Goutu", "Gowrie", "Grabetto", "Graddost", "Grafton Village Cheddar", "Grana",
			"Grana Padano", "Grand Vatel", "Grataron d' Areches", "Gratte-Paille", "Graviera",
			"Greuilh", "Greve", "Gris de Lille", "Gruyere", "Gubbeen", "Guerbigny", "Halloumi",
			"Halloumy (Australian)", "Haloumi-Style Cheese", "Harbourne Blue", "Havarti",
			"Heidi Gruyere", "Hereford Hop", "Herrgardsost", "Herriot Farmhouse", "Herve",
			"Hipi Iti", "Hubbardston Blue Cow", "Hushallsost", "Iberico", "Idaho Goatster",
			"Idiazabal", "Il Boschetto al Tartufo", "Ile d'Yeu", "Isle of Mull", "Jarlsberg",
			"Jermi Tortes", "Jibneh Arabieh", "Jindi Brie", "Jubilee Blue", "Juustoleipa",
			"Kadchgall", "Kaseri", "Kashta", "Kefalotyri", "Kenafa", "Kernhem", "Kervella Affine",
			"Kikorangi", "King Island Cape Wickham Brie", "King River Gold", "Klosterkaese",
			"Knockalara", "Kugelkase", "L'Aveyronnais", "L'Ecir de l'Aubrac", "La Taupiniere",
			"La Vache Qui Rit", "Laguiole", "Lairobell", "Lajta", "Lanark Blue", "Lancashire",
			"Langres", "Lappi", "Laruns", "Lavistown", "Le Brin", "Le Fium Orbo", "Le Lacandou",
			"Le Roule", "Leafield", "Lebbene", "Leerdammer", "Leicester", "Leyden", "Limburger",
			"Lincolnshire Poacher", "Lingot Saint Bousquet d'Orb", "Liptauer", "Little Rydings",
			"Livarot", "Llanboidy", "Llanglofan Farmhouse", "Loch Arthur Farmhouse",
			"Loddiswell Avondale", "Longhorn", "Lou Palou", "Lou Pevre", "Lyonnais", "Maasdam",
			"Macconais", "Mahoe Aged Gouda", "Mahon", "Malvern", "Mamirolle", "Manchego",
			"Manouri", "Manur", "Marble Cheddar", "Marbled Cheeses", "Maredsous", "Margotin",
			"Maribo", "Maroilles", "Mascares", "Mascarpone", "Mascarpone (Australian)",
			"Mascarpone Torta", "Matocq", "Maytag Blue", "Meira", "Menallack Farmhouse",
			"Menonita", "Meredith Blue", "Mesost", "Metton (Cancoillotte)", "Meyer Vintage Gouda",
			"Mihalic Peynir", "Milleens", "Mimolette", "Mine-Gabhar", "Mini Baby Bells", "Mixte",
			"Molbo", "Monastery Cheeses", "Mondseer", "Mont D'or Lyonnais", "Montasio",
			"Monterey Jack", "Monterey Jack Dry", "Morbier", "Morbier Cru de Montagne",
			"Mothais a la Feuille", "Mozzarella", "Mozzarella (Australian)",
			"Mozzarella di Bufala", "Mozzarella Fresh, in water", "Mozzarella Rolls", "Munster",
			"Murol", "Mycella", "Myzithra", "Naboulsi", "Nantais", "Neufchatel",
			"Neufchatel (Australian)", "Niolo", "Nokkelost", "Northumberland", "Oaxaca",
			"Olde York", "Olivet au Foin", "Olivet Bleu", "Olivet Cendre",
			"Orkney Extra Mature Cheddar", "Orla", "Oschtjepka", "Ossau Fermier", "Ossau-Iraty",
			"Oszczypek", "Oxford Blue", "P'tit Berrichon", "Palet de Babligny", "Paneer", "Panela",
			"Pannerone", "Pant ys Gawn", "Parmesan (Parmigiano)", "Parmigiano Reggiano",
			"Pas de l'Escalette", "Passendale", "Pasteurized Processed", "Pate de Fromage",
			"Patefine Fort", "Pave d'Affinois", "Pave d'Auge", "Pave de Chirac", "Pave du Berry",
			"Pecorino", "Pecorino in Walnut Leaves", "Pecorino Romano", "Peekskill Pyramid",
			"Pelardon des Cevennes", "Pelardon des Corbieres", "Penamellera", "Penbryn",
			"Pencarreg", "Perail de Brebis", "Petit Morin", "Petit Pardou", "Petit-Suisse",
			"Picodon de Chevre", "Picos de Europa", "Piora", "Pithtviers au Foin",
			"Plateau de Herve", "Plymouth Cheese", "Podhalanski", "Poivre d'Ane", "Polkolbin",
			"Pont l'Eveque", "Port Nicholson", "Port-Salut", "Postel", "Pouligny-Saint-Pierre",
			"Pourly", "Prastost", "Pressato", "Prince-Jean", "Processed Cheddar", "Provolone",
			"Provolone (Australian)", "Pyengana Cheddar", "Pyramide", "Quark",
			"Quark (Australian)", "Quartirolo Lombardo", "Quatre-Vents", "Quercy Petit",
			"Queso Blanco", "Queso Blanco con Frutas --Pina y Mango", "Queso de Murcia",
			"Queso del Montsec", "Queso del Tietar", "Queso Fresco", "Queso Fresco (Adobera)",
			"Queso Iberico", "Queso Jalapeno", "Queso Majorero", "Queso Media Luna",
			"Queso Para Frier", "Queso Quesadilla", "Rabacal", "Raclette", "Ragusano", "Raschera",
			"Reblochon", "Red Leicester", "Regal de la Dombes", "Reggianito", "Remedou",
			"Requeson", "Richelieu", "Ricotta", "Ricotta (Australian)", "Ricotta Salata", "Ridder",
			"Rigotte", "Rocamadour", "Rollot", "Romano", "Romans Part Dieu", "Roncal", "Roquefort",
			"Roule", "Rouleau De Beaulieu", "Royalp Tilsit", "Rubens", "Rustinu", "Saaland Pfarr",
			"Saanenkaese", "Saga", "Sage Derby", "Sainte Maure", "Saint-Marcellin",
			"Saint-Nectaire", "Saint-Paulin", "Salers", "Samso", "San Simon", "Sancerre",
			"Sap Sago", "Sardo", "Sardo Egyptian", "Sbrinz", "Scamorza", "Schabzieger", "Schloss",
			"Selles sur Cher", "Selva", "Serat", "Seriously Strong Cheddar", "Serra da Estrela",
			"Sharpam", "Shelburne Cheddar", "Shropshire Blue", "Siraz", "Sirene", "Smoked Gouda",
			"Somerset Brie", "Sonoma Jack", "Sottocenare al Tartufo", "Soumaintrain",
			"Sourire Lozerien", "Spenwood", "Sraffordshire Organic", "St. Agur Blue Cheese",
			"Stilton", "Stinking Bishop", "String", "Sussex Slipcote", "Sveciaost", "Swaledale",
			"Sweet Style Swiss", "Swiss", "Syrian (Armenian String)", "Tala", "Taleggio", "Tamie",
			"Tasmania Highland Chevre Log", "Taupiniere", "Teifi", "Telemea", "Testouri",
			"Tete de Moine", "Tetilla", "Texas Goat Cheese", "Tibet", "Tillamook Cheddar",
			"Tilsit", "Timboon Brie", "Toma", "Tomme Brulee", "Tomme d'Abondance",
			"Tomme de Chevre", "Tomme de Romans", "Tomme de Savoie", "Tomme des Chouans", "Tommes",
			"Torta del Casar", "Toscanello", "Touree de L'Aubier", "Tourmalet",
			"Trappe (Veritable)", "Trois Cornes De Vendee", "Tronchon", "Trou du Cru", "Truffe",
			"Tupi", "Turunmaa", "Tymsboro", "Tyn Grug", "Tyning", "Ubriaco", "Ulloa",
			"Vacherin-Fribourgeois", "Valencay", "Vasterbottenost", "Venaco", "Vendomois",
			"Vieux Corse", "Vignotte", "Vulscombe", "Waimata Farmhouse Blue",
			"Washed Rind Cheese (Australian)", "Waterloo", "Weichkaese", "Wellington",
			"Wensleydale", "White Stilton", "Whitestone Farmhouse", "Wigmore", "Woodside Cabecou",
			"Xanadu", "Xynotyro", "Yarg Cornish", "Yarra Valley Pyramid", "Yorkshire Blue",
			"Zamorano", "Zanetti Grana Padano", "Zanetti Parmigiano Reggiano" };

}

QuickActionBar.java

package com.mark;

import java.util.Vector;

import android.content.Context;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnTouchListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.Interpolator;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

public class QuickActionBar {
	// 设置了四种动画方式。
	public static final int ANIM_GROW_FROM_LEFT = 1;
	public static final int ANIM_GROW_FROM_RIGHT = 2;
	public static final int ANIM_GROW_FROM_CENTER = 3;
	/** 根据位置自动匹配动画的出现位置。 */
	public static final int ANIM_AUTO = 4;
	public static final Interpolator interpolator;
	/** 即响应点击后显示操作框的组件,本例中为“咧牙”图标。 */
	private View anchor;
	private Vector<ActionItem> vecActions;
	/** 快捷操作框附加在PopupWindow上。 */
	private PopupWindow popupWindow;
	/** 整个快捷操作框布局。 */
	private View qaBarRoot;
	/** 手机屏幕的宽度。 */
	private int phoneScreenWidth;
	/** 手机屏幕的高度。 */
	private int phoneScreenHeight;
	private int animType;
	/** 各种组件的填充器。 */
	private LayoutInflater inflater;
	/** 默认PopupWindow有自己不透明的灰色的背景,可能会与整体风格不符,所以需额外设置背景。 */
	private Drawable popupWindowBackground;
	/** 快捷操作框的向上箭头,可能不显示。 */
	private ImageView arrowUp;
	/** 快捷操作框的向下箭头,可能不显示。 */
	private ImageView arrowDown;
	/** 是否允许ActionsLayout以动画的形式出现。 */
	private boolean enableActionsLayout;
	private Animation actionsLayoutAnim;
	/** 引发此QuickActionBar的ListItem的序号。 */
	private int listItemIndex;

	static {
		interpolator = new CustomInterpolator();
	}

	/**
	 * @param anchor
	 *            快捷操作框将要指示的视图组件。
	 * @param idx
	 *            引发此QuickActionBar的ListItem的序号。
	 */
	public QuickActionBar(View anchor, int idx) {
		if (vecActions == null) {
			vecActions = new Vector<ActionItem>();
		}
		this.anchor = anchor;
		this.listItemIndex = idx;
		Context context = this.anchor.getContext();
		this.popupWindow = new PopupWindow(context);
		// 当显示快捷操作框时,如果用户点击了框外的地方,则框要消失
		popupWindow.setTouchInterceptor(new OnTouchListener() {
			public boolean onTouch(View v, MotionEvent event) {
				if (event.getAction() == MotionEvent.ACTION_OUTSIDE) {
					QuickActionBar.this.popupWindow.dismiss();
					// 该事件已处理完返回true。
					return true;
				}
				return false;
			}
		});
		WindowManager windowManager = (WindowManager) context
				.getSystemService(Context.WINDOW_SERVICE);
		phoneScreenWidth = windowManager.getDefaultDisplay().getWidth();
		phoneScreenHeight = windowManager.getDefaultDisplay().getHeight();
		// 填充QuickActionBar界面
		inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
		qaBarRoot = (ViewGroup) inflater.inflate(R.layout.qa_bar, null);
		arrowUp = (ImageView) qaBarRoot.findViewById(R.id.qa_arrow_up);
		arrowDown = (ImageView) qaBarRoot.findViewById(R.id.qa_arrow_down);
		animType = ANIM_AUTO;

		actionsLayoutAnim = AnimationUtils.loadAnimation(anchor.getContext(),
				R.anim.anim_actionslayout);
		actionsLayoutAnim.setInterpolator(interpolator);
	}

	/** 设置整个QuickActionBar的出现动画效果。 */
	public void setAnimType(int type) {
		this.animType = type;
	}

	public void setEnableActionsLayoutAnim(boolean bool) {
		enableActionsLayout = bool;
	}

	public void addActionItem(ActionItem actionWeb) {
		vecActions.add(actionWeb);
	}

	public void show() {
		// 确定popupWindow的显示位置
		int[] location = new int[2];
		anchor.getLocationOnScreen(location);
		Rect anchorRect = new Rect(location[0], location[1], location[0] + anchor.getWidth(),
				location[1] + anchor.getHeight());
		// Log.d("ANDROID_LAB", "anchorRect:" + anchorRect.toString());
		qaBarRoot.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
				LayoutParams.WRAP_CONTENT));
		qaBarRoot.measure(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		int rootWidth = qaBarRoot.getMeasuredWidth();
		int rootHeight = qaBarRoot.getMeasuredHeight();
		// 标识快捷操作框箭头尖角的X坐标
		int xPos = (phoneScreenWidth - rootWidth) >> 1;
		// 标识快捷操作框箭头尖角的Y坐标
		int yPos = anchorRect.bottom;
		// 判断指示箭头的位置,并设定快捷操作框的整体位移值
		int diff = -10;
		boolean arrowOnTop = true;
		if (anchorRect.bottom + diff + rootHeight > phoneScreenHeight) {
			arrowOnTop = false;
			yPos = anchorRect.top - rootHeight;
			diff = -diff;
		}
		// 显示/隐藏指定的箭头
		int arrowId = arrowOnTop ? R.id.qa_arrow_up : R.id.qa_arrow_down;
		int marginLeft = anchorRect.centerX() - (arrowUp.getMeasuredWidth() >> 1);
		handleArrow(arrowId, marginLeft);
		// 填充ActionItem组件并添加到actionsLayout中
		// 包括ActionItem头尾的边框及中间的各个ActionItem。
		LinearLayout actionsLayout = (LinearLayout) qaBarRoot.findViewById(R.id.actionsLayout);
		appendActionsItemUI(actionsLayout, vecActions);

		// 设置PopupWindow
		// 设置PopupWindow的出现动画与消失动画
		setAnimationStyle(phoneScreenWidth, anchorRect.centerX(), arrowOnTop);
		// 如果对操作框设置默认背景的话,就显示上去。如果没有的话,则生成一个全空透明的BitmapDrawable替代原有的不符合风格的默认背景。
		if (popupWindowBackground == null) {
			popupWindow.setBackgroundDrawable(new BitmapDrawable());
		} else {
			popupWindow.setBackgroundDrawable(popupWindowBackground);
		}
		popupWindow.setWidth(WindowManager.LayoutParams.WRAP_CONTENT);
		popupWindow.setHeight(WindowManager.LayoutParams.WRAP_CONTENT);
		popupWindow.setTouchable(true);
		popupWindow.setFocusable(true);
		// 对框外的区域可响应,用于点击框外区域时让框消失
		popupWindow.setOutsideTouchable(true);
		// 将显示界面与PopupWindow关联
		popupWindow.setContentView(qaBarRoot);
		popupWindow.showAtLocation(this.anchor, Gravity.NO_GRAVITY, xPos, yPos + diff);

		if (enableActionsLayout) {
			actionsLayout.startAnimation(actionsLayoutAnim);
		}
	}

	/** 填充ActionItem组件并添加到actionsLayout中 */
	private void appendActionsItemUI(ViewGroup actionsLayout, Vector<ActionItem> vec) {
		if (vec == null || vec.size() == 0) {
			return;
		}

		View view = null;
		int idx = 1;
		int size = vec.size();
		for (int i = 0; i < size; i++, idx++) {
			view = getActionItemUI(vecActions.get(i));
			// 将本QuickActionBar设为Tag供处理点击事件时隐藏QuickActionBar
			view.setTag(this);
			actionsLayout.addView(view, idx);
		}
	}

	private View getActionItemUI(ActionItem item) {
		LinearLayout actionItemLayout = (LinearLayout) inflater.inflate(R.layout.action_item, null);
		ImageView icon = (ImageView) actionItemLayout.findViewById(R.id.qa_actionItem_icon);
		TextView txtName = (TextView) actionItemLayout.findViewById(R.id.qa_actionItem_name);
		Drawable drawable = item.getIcon();
		if (drawable == null) {
			icon.setVisibility(View.GONE);
		} else {
			icon.setImageDrawable(drawable);
		}
		String name = item.getActionName();
		if (name == null) {
			txtName.setVisibility(View.GONE);
		} else {
			txtName.setText(name);
		}
		actionItemLayout.setOnClickListener(item.getOnClickListener());
		return actionItemLayout;
	}

	/**
	 * 显示/隐藏相应的箭头,并将箭头尖角的位移至相应的X坐标。
	 * 
	 * @param arrowShowId
	 *            最终显示在屏幕上的箭头ID。
	 * @param marginLeft
	 *            为将箭头尖角设置在anchor中心点的X值处所需的位置值。
	 */
	private void handleArrow(int arrowShowId, int marginLeft) {
		View showArrow = (arrowShowId == R.id.qa_arrow_up) ? arrowUp : arrowDown;
		View hideArrow = (arrowShowId == R.id.qa_arrow_up) ? arrowDown : arrowUp;
		showArrow.setVisibility(View.VISIBLE);
		hideArrow.setVisibility(View.INVISIBLE);
		// 设置箭头的X最终显示的位置,通过margin调整位移量
		ViewGroup.MarginLayoutParams param = (ViewGroup.MarginLayoutParams) showArrow
				.getLayoutParams();
		param.leftMargin = marginLeft;
	}

	/**
	 * 设置PopupWindow的Style选择PopupWindow的显示/隐藏动画。
	 * 
	 * @param screenWidth
	 *            手机屏幕的宽度。
	 * @param coordinateX
	 *            响应图标中心点的X坐标值。
	 * @param arrowOnTop
	 *            标识指示箭头是否在上。
	 */
	private void setAnimationStyle(int screenWidth, int coordinateX, boolean arrowOnTop) {
		int arrowPos = coordinateX - (arrowUp.getMeasuredWidth() >> 1);
		switch (animType) {
		case ANIM_GROW_FROM_LEFT:
			popupWindow.setAnimationStyle((arrowOnTop) ? R.style.QuickActionBar_PopDown_Left
					: R.style.QuickActionBar_PopUp_Left);
			break;
		case ANIM_GROW_FROM_RIGHT:
			popupWindow.setAnimationStyle((arrowOnTop) ? R.style.QuickActionBar_PopDown_Right
					: R.style.QuickActionBar_PopUp_Right);
			break;
		case ANIM_GROW_FROM_CENTER:
			popupWindow.setAnimationStyle((arrowOnTop) ? R.style.QuickActionBar_PopDown_Center
					: R.style.QuickActionBar_PopUp_Center);
			break;
		case ANIM_AUTO:
			if (arrowPos <= screenWidth / 4) {
				popupWindow.setAnimationStyle((arrowOnTop) ? R.style.QuickActionBar_PopDown_Left
						: R.style.QuickActionBar_PopUp_Left);
			} else if (arrowPos > screenWidth / 4 && arrowPos < 3 * (screenWidth / 4)) {
				popupWindow.setAnimationStyle((arrowOnTop) ? R.style.QuickActionBar_PopDown_Center
						: R.style.QuickActionBar_PopUp_Center);
			} else {
				popupWindow.setAnimationStyle((arrowOnTop) ? R.style.QuickActionBar_PopDown_Right
						: R.style.QuickActionBar_PopUp_Right);
			}
			break;
		}
	}

	public void dismissQuickActionBar() {
		popupWindow.dismiss();
	}

	public int getListItemIndex() {
		return listItemIndex;
	}
}

ListAdapter.java

package com.mark;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.TextView;


public class ListAdapter extends BaseAdapter {
	public static final String URL_PREFIX = "http://www.google.com/#sclient=psy&hl=en&newwindow=1&q=";
	private LayoutInflater layoutInflater;
	private OnClickListener onClickListener;
	private String[] stringArr;

	public ListAdapter(Context context, String[] arr, OnClickListener listener) {
		layoutInflater = LayoutInflater.from(context);
		this.onClickListener = listener;
		stringArr = arr;
	}

	public int getCount() {
		return stringArr == null ? 0 : stringArr.length;
	}

	public Object getItem(int position) {
		if (stringArr != null) {
			return stringArr[position];
		}
		return null;
	}

	public long getItemId(int position) {
		return position;
	}

	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder holder = null;
		if (convertView == null) {
			convertView = layoutInflater.inflate(R.layout.list_item, null);
			holder = new ViewHolder();
			holder.firstCharHintTextView = (TextView) convertView
					.findViewById(R.id.text_first_char_hint);
			holder.orderTextView = (TextView) convertView.findViewById(R.id.list_order_number);
			holder.nameTextView = (TextView) convertView.findViewById(R.id.text_website_name);
			holder.urlTextView = (TextView) convertView.findViewById(R.id.text_website_url);
			holder.imgView = (ImageView) convertView.findViewById(R.id.list_item_img_view);
			convertView.setTag(holder);
		} else {
			holder = (ViewHolder) convertView.getTag();
		}
		holder.orderTextView.setText(String.valueOf(position + 1) + ".");
		holder.nameTextView.setText(stringArr[position]);
		holder.urlTextView.setText(URL_PREFIX + stringArr[position]);
		holder.urlTextView.setTextColor(0xFFFFFF00);
		holder.imgView.setOnClickListener(onClickListener);
		holder.imgView.setTag(position);
		int idx = position - 1;
		char previewChar = idx >= 0 ? stringArr[idx].charAt(0) : ' ';
		char currentChar = stringArr[position].charAt(0);
		if (currentChar != previewChar) {
			holder.firstCharHintTextView.setVisibility(View.VISIBLE);
			holder.firstCharHintTextView.setText(String.valueOf(currentChar));
		} else {
			// 此段代码不可缺:实例化一个CurrentView后,会被多次赋值并且只有最后一次赋值的position是正确
			holder.firstCharHintTextView.setVisibility(View.GONE);
		}
		return convertView;
	}

	public final class ViewHolder {
		public TextView firstCharHintTextView;
		public TextView orderTextView;
		public TextView nameTextView;
		public TextView urlTextView;
		public ImageView imgView;
	}
}

CustomInterpolator.java

package com.mark;

import android.util.Log;
import android.view.animation.Interpolator;


public class CustomInterpolator implements Interpolator {

	/**
	 * @param input
	 *            A value between 0 and 1.0 indicating our current point in the
	 *            animation where 0 represents the start and 1.0 represents the
	 *            end
	 * @return Returns The interpolation value. This value can be more than 1.0
	 *         for Interpolators which overshoot their targets, or less than 0
	 *         for Interpolators that undershoot their targets.
	 */
	public float getInterpolation(float input) {
		Log.d("ANDROID_LAB", "input=" + input);
		// 设定动画的加速度变化值。此例的效果是使用actionsLayout超过目标旋转区后再反弹回来。
		// 插值计算公式: 1.2-((x*1.55f)-1.1)^2
		// 画出函数图的话即可观察出动画执行过程中越过目标区再反弹的详细过程。
		// x :0 <= v <= 1.0
		// (x*1.55f) :0 <= v <= 1.55
		// ((x*1.55f)-1.1) :-1.1 <= v <= 0.45
		// ((x*1.55f)-1.1)^2 :0<= v <= 1.21
		// 1.2-((x*1.55f)-1.1)^2 :-0.1 <= v <= 1.2
		final float inner = (input * 1.55f) - 1.1f;
		// 如果返回值为常量1的话,则相当于没有动画效果。
		return 1.2f - inner * inner;
	}
}

ActionItem.java

package com.mark;

import android.graphics.drawable.Drawable;
import android.view.View.OnClickListener;

public class ActionItem {
	private Drawable icon;
	private String actionName;
	private OnClickListener onClickListener;

	public ActionItem(Drawable img, String name, OnClickListener listener) {
		icon = img;
		actionName = name;
		this.onClickListener = listener;
	}

	public Drawable getIcon() {
		return icon;
	}

	public String getActionName() {
		return actionName;
	}

	public OnClickListener getOnClickListener() {
		return this.onClickListener;
	}

}



其他文件就不发了,可以看源代码。






  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值