實現帶快速導航的ListView(自定義View和自定義ViewGroup的結合),可直接使用和修改使用

首先效果圖:




兩個文件(可以直接使用):

第一個:SlideView.java,這個類實現了快速導航的側邊欄

package com.lym.view;

import java.util.Arrays;
import java.util.List;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Paint.FontMetrics;
import android.os.Handler;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup.LayoutParams;

public class SlideView extends View {
	private static final String TAG = "SlideView";

	/** 最大字體 */
	private final static int maxTxtSize = 50;
	private Context context;
	private int txtSize = maxTxtSize;
	private int txtColor = Color.BLACK;

	/** 每個字符的高度 */
	private float txtHeight;
	private float txtWidth;
	/** 每個字符的bottomPadding */
	private int padding = 5;
	private float lineHeight;
	private Paint txtPaint;

	private Paint bgPaint;

	private int height = -1;
	private int width = -1;

	private static List<String> charSet = Arrays.asList("A", "B", "C", "D",
			"E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", "Q",
			"R", "S", "T", "U", "V", "W", "X", "Y", "Z");

	public SlideView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
		this.context = context;
		txtPaint = new Paint();
		// txtPaint.setTextAlign(Paint.Align.CENTER);
		txtPaint.setAntiAlias(true);
		txtPaint.setColor(txtColor);
		txtPaint.setTextSize(txtSize);

		bgPaint = new Paint();
		bgPaint.setColor(Color.parseColor("#55ffaaaa"));
	}

	public SlideView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public SlideView(Context context) {
		this(context, null);
	}

	@Override
	protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
		super.onMeasure(widthMeasureSpec, heightMeasureSpec);
		setMeasuredDimension((int) txtWidth + padding,
				MeasureSpec.getSize(heightMeasureSpec));

		Log.i(TAG, "onMeasure");
	}

	/**
	 * 是否需要重新測量View的大小
	 */
	private boolean needRemeasure = true;

	/**
	 * 重新測量
	 */
	private void reMeasure() {
		if (charSet.size() == 0) {
			return;
		}
		height = getHeight();
		FontMetrics fm = txtPaint.getFontMetrics();
		// 字體高度
		txtHeight = fm.bottom - fm.top;
		while ((height / txtHeight) < charSet.size()) {
			txtPaint.setTextSize(txtSize--);
			fm = txtPaint.getFontMetrics();
			txtHeight = fm.leading - fm.top;
			// fm.leading
		}

		// 測量得到最寬的字符(使用最寬的字符來作爲View的寬度)
		for (String str : charSet) {
			if (str == null || str.length() == 0) {
				str = "";
			} else {
				str = str.substring(0, 1);
			}
			float tWidth = txtPaint.measureText(str);
			txtWidth = txtWidth < tWidth ? tWidth : txtWidth;
		}

		lineHeight = height / charSet.size();

		LayoutParams layoutParams = getLayoutParams();
		layoutParams.width = Math.round(txtWidth) + padding;
		setLayoutParams(layoutParams);

		needRemeasure = false;
	}

	public void setCharSet(List<String> charSet) {
		SlideView.charSet = charSet;
		needRemeasure = true;
		invalidate();
	}

	@Override
	protected void onLayout(boolean changed, int left, int top, int right,
			int bottom) {
		super.onLayout(changed, left, top, right, bottom);
		Log.i(TAG, "onLayout");
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		Log.i(TAG, "onDraw");

		if (needRemeasure) {
			reMeasure();
		} else {
			width = getWidth();
			height = getHeight();
		}

		// 開始繪製
		for (int i = 0; i < charSet.size(); i++) {
			String str;
			if (charSet.get(i) == null || charSet.get(i).length() == 0) {
				str = "";
			} else {
				str = charSet.get(i).substring(0, 1);
			}

			canvas.drawText(str, width / 2 - (txtPaint.measureText(str) / 2), i
					* lineHeight + txtHeight, txtPaint);
		}

		if (isTouch || isSelected) {
			// 選擇背景
			canvas.drawRect(0, indexInTouch * lineHeight + 5, width,
					indexInTouch * lineHeight + txtHeight + 5, bgPaint);
		}
	}

	private int indexInTouch = 0;

	private SelectListener selectListener;

	private boolean isTouch = false;
	private boolean isSelected = false;

	@Override
	public boolean onTouchEvent(MotionEvent event) {
		float y;
		switch (event.getAction()) {
		case MotionEvent.ACTION_DOWN:
			y = event.getY();
			getSelected(y);
			isTouch = true;
			break;
		case MotionEvent.ACTION_UP:
			isTouch = false;
			invalidate();
			break;
		case MotionEvent.ACTION_MOVE:
			y = event.getY();
			getSelected(y);
			break;
		default:
			break;
		}
		return true;
	}

	public void getSelected(float y) {
		y = y < 0 ? 0 : y;
		int index = (int) (y / lineHeight);
		index = index >= charSet.size() ? (charSet.size() - 1) : index;
		index = index < 0 ? 0 : index;
		if (indexInTouch != index) {
			Log.i(TAG, "選擇的字符:" + charSet.get(index));
			indexInTouch = index;
			if (selectListener != null) {
				selectListener.selected(indexInTouch, charSet.get(index));
			}
		}

		invalidate();
	}

	Handler handler = new Handler();

	public void select(int indexOfChatSet) {
		if (!isTouch) {
			indexInTouch = indexOfChatSet;
			isSelected = true;
			invalidate();
			handler.postDelayed(new Runnable() {
				@Override
				public void run() {
					isSelected = false;
					invalidate();
				}
			}, 1000);
		}
	}

	public void select(String indexStr) {
		select(charSet.indexOf(indexStr));
		invalidate();
	}

	public void setSelectListener(SelectListener listener) {
		this.selectListener = listener;
	}

	/**
	 * 選擇監聽器
	 * 
	 * @author xuyao
	 * 
	 */
	interface SelectListener {
		/**
		 * 
		 * 
		 * @param index
		 *            選擇的索引
		 * @param selectedStr
		 *            選擇的字符
		 */
		public void selected(int index, String selectedStr);
	}
}



第二個文件:SlideListView.java

package com.lym.view;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.List;

import android.content.Context;
import android.graphics.Color;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.BaseAdapter;
import android.widget.ListView;
import android.widget.RelativeLayout;
import android.widget.TextView;
import android.widget.Toast;

import com.lym.view.SlideView.SelectListener;

public class SlideListView extends RelativeLayout {
	private Context context;
	private SlideView sv;
	private ListView lv;
	/**
	 * 是否顯示懸浮框
	 */
	private boolean showFloatWindow = true;

	/** 數字 */
	public final static String SlideChat_NUMBER = "#";
	/** 特殊字符 */
	public final static String SlideChat_OTHER = "@";

	private SlideListViewAdapter adapter;
	private OnScrollListener scrollListener;
	private OnItemClickListener itemclickListener;

	private Toast toast;

	public SlideListView(Context context, AttributeSet attrs, int defStyle) {
		super(context, attrs, defStyle);
		this.context = context;

		init();
	}

	public SlideListView(Context context, AttributeSet attrs) {
		this(context, attrs, 0);
	}

	public SlideListView(Context context) {
		this(context, null);
	}

	/** 是否是slideView滾動 */
	private boolean isSlideViewScroll = true;

	void init() {
		// 左面的滑塊
		sv = new SlideView(context);
		int svId = sv.hashCode();
		sv.setBackgroundColor(Color.parseColor("#66666666"));
		sv.setId(svId);
		RelativeLayout.LayoutParams layoutParams2 = new RelativeLayout.LayoutParams(
				LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
		layoutParams2.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
		addView(sv, layoutParams2);

		sv.setSelectListener(new SelectListener() {
			@Override
			public void selected(int index, String selectedStr) {
				isSlideViewScroll = true;
				int n = adapter.getPositionForIndex(selectedStr);
				lv.setSelection(n);
				showFloaWindow(selectedStr);

			}
		});

		// ListView
		RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
				LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
		lv = new ListView(context);
		lv.setLayoutParams(layoutParams);
		lv.setVerticalScrollBarEnabled(false);
		layoutParams.addRule(RelativeLayout.LEFT_OF, svId);
		addView(lv, layoutParams);

		lv.setOnItemClickListener(new OnItemClickListener() {
			@Override
			public void onItemClick(AdapterView<?> parent, View view,
					int position, long id) {
				if (itemclickListener != null) {
					itemclickListener.onItemClick(parent, view, position, id);
				}
				isSlideViewScroll = true;
				//showFloaWindow(adapter.getIndexForPosition(position));
			}

		});
		lv.setOnScrollListener(new OnScrollListener() {
			@Override
			public void onScrollStateChanged(AbsListView view, int scrollState) {
				System.out.println("onScrollStateChanged");
				isSlideViewScroll = false;
				if (scrollListener != null) {
					scrollListener.onScrollStateChanged(view, scrollState);
				}
			}

			@Override
			public void onScroll(AbsListView view, int firstVisibleItem,
					int visibleItemCount, int totalItemCount) {
				System.out.println("onScroll");

				if (scrollListener != null) {
					scrollListener.onScroll(view, firstVisibleItem,
							visibleItemCount, totalItemCount);
				}

				if (adapter != null) {
					if (!isSlideViewScroll) {
						String indexForPosition = adapter
								.getIndexForPosition(firstVisibleItem);
						sv.select(indexForPosition);
						// 顯示懸浮框
						showFloaWindow(indexForPosition);
					}
				}
			}
		});
	}

	private void showFloaWindow(String str) {
		if (!showFloatWindow) {
			return;
		}
		if (toast == null) {
			toast = Toast.makeText(context, str, Toast.LENGTH_SHORT);
			int[] location = new int[2];
			getLocationInWindow(location);
			toast.setGravity(Gravity.NO_GRAVITY, location[0] / 2,
					location[1] / 2);
			View toastView = toast.getView();
			TextView tv = (TextView) toastView
					.findViewById(android.R.id.message);
			tv.setTextSize(50);
		}
		if (str != null && !"".equals(str)) {
			toast.setText(str);
			toast.show();
		}
	}

	public void setAdapter(SlideListViewAdapter adapter) {
		// 相互設置引用
		this.adapter = adapter;
		adapter.setSlv(this);

		List<String> indexs = adapter.getIndexs();
		setSlidViewChatSet(indexs);
		lv.setAdapter(adapter);
	}

	/**
	 * 是否顯示懸浮框
	 * 
	 * @param showFloatWindow
	 */
	public void setShowFloatWindow(boolean showFloatWindow) {
		this.showFloatWindow = showFloatWindow;
	}

	/**
	 * 設置左邊索引的索引值
	 * 
	 * @param indexs
	 */
	void setSlidViewChatSet(List<String> indexs) {
		if (indexs != null) {
			// 獲取索引值
			HashSet<String> hs = new HashSet<String>(indexs);
			List<String> charSetList = new ArrayList<String>(hs);
			Collections.sort(charSetList, new Comparator<String>() {
				@Override
				public int compare(String lhs, String rhs) {
					return lhs.compareTo(rhs);
				}
			});
			int indexOf = charSetList.indexOf(SlideChat_OTHER);
			if (indexOf != -1) {
				String remove = charSetList.remove(indexOf);
				charSetList.add(remove);
			}
			sv.setCharSet(charSetList);
		}
	}

	public void setOnScrollListener(final OnScrollListener listener) {
		this.scrollListener = listener;
	}

	public void onItemClickListener(OnItemClickListener listener) {
		this.itemclickListener = listener;
	}

}

/**
 * 適配器
 * 
 * @author xuyao
 * 
 */
abstract class SlideListViewAdapter extends BaseAdapter {
	private SlideListView slv;

	public SlideListView getSlv() {
		return slv;
	}

	public void setSlv(SlideListView slv) {
		this.slv = slv;
	}

	/**
	 * 得到索引(即每個條目對應的特定符號的集合),例如是每個條目中顯示的文本的第一個字符(首字母)
	 * 
	 * @return
	 */
	public abstract List<String> getIndexs();

	/**
	 * 通過索引獲取該索引在ListView中的位置
	 * 
	 * @param index
	 * @return 索引index所在的位置,如果不存在則返回-1
	 */
	public abstract int getPositionForIndex(String index);

	/**
	 * 獲取position位置對應的索引值
	 * 
	 * @param position
	 * @return position對應的索引值,如果position>indexs.size,則返回null
	 */
	public abstract String getIndexForPosition(int position);

	@Override
	public final void notifyDataSetChanged() {
		List<String> indexs = getIndexs();
		slv.setSlidViewChatSet(indexs);
		super.notifyDataSetChanged();
	}
}
</pre><pre name="code" class="java">最後就是使用:在佈局文件中像普通控件一樣
<pre name="code" class="html"><com.lym.test.SlideListView
   android:id="@+id/slv"
    android:layout_width="fill_parent"
     android:layout_height="wrap_content" />







完整使用項目:<a target=_blank href="http://download.csdn.net/detail/linyimu000/9301761" target="_blank">下載</a>





發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章