首先效果圖:
兩個文件(可以直接使用):
第一個: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);
}
}
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>