您的位置:首页 > 移动开发 > Android开发

Android学习之联系人PinnedHeaderListView(通讯录)

2015-07-29 10:11 447 查看
今天学习的是通讯录~~根据自己的理解~~添加了部分注释。





1. BladView,主要实现了触摸时返回对应字母

package com.way.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Handler;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.MotionEvent;
import android.view.View;
import android.widget.LinearLayout;
import android.widget.PopupWindow;
import android.widget.TextView;

public class BladeView extends View {
private OnItemClickListener mOnItemClickListener;
String[] b = { "#", "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" };
int choose = -1;
Paint paint = new Paint();
boolean showBkg = false;
private PopupWindow mPopupWindow;
private TextView mPopupText;
private Handler handler = new Handler();

public BladeView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}

public BladeView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public BladeView(Context context) {
super(context);
}

@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (showBkg) {
canvas.drawColor(Color.parseColor("#00000000"));
}

int height = getHeight(); // 画布的高
if (height > 1000) {
/*
* 这里打算写个逻辑判断~因为在机器测试过程中发现字体很小
*/
paint.setTextSize(dpToPx(getContext(), 10));
}
int width = getWidth(); // 画布的宽
// Log.i("123", "height = " + height + ",  width = " + width);
int singleHeight = height / b.length; // 计算出平均每一个字所占的高度
for (int i = 0; i < b.length; i++) { // 遍历字符集合,画出所有的字
paint.setColor(Color.BLACK); // 画笔黑色
paint.setTypeface(Typeface.DEFAULT_BOLD); // 设置字体,默认+加粗
paint.setFakeBoldText(true); // 设定标记
paint.setAntiAlias(true); // 抗锯齿
if (i == choose) { // 被选中项
paint.setColor(Color.parseColor("#3399ff")); // 画笔蓝色
}
// 计算 字符串的x坐标
// x = 画布中点 - 文字大小的一半
float xPos = width / 2 - paint.measureText(b[i]) / 2;
// y = 平均高度 * (i + 1 )
float yPos = singleHeight * i + singleHeight;
// 画出文字
canvas.drawText(b[i], xPos, yPos, paint);
// 回复默认的颜色
paint.reset();
}

}

@Override
public boolean dispatchTouchEvent(MotionEvent event) {
final int action = event.getAction();
final float y = event.getY(); // 触摸点
final int oldChoose = choose; // 记录下悬着点
int h = getHeight(); // 画布高度

// Log.i("123", "y = " + y + " , h = " + h);
/*
* 获得被选中的position position = 触摸点/ 画布高度 * 字符串集合总长度 ;
* 因为强转成int部分,没有计算小数部分~故不用 - 1;
*/
final int c = (int) (y / h * b.length);
switch (action) { // 对触摸进行判断
case MotionEvent.ACTION_DOWN: // 按下的时候
showBkg = true; // 触摸状态
if (oldChoose != c) { // 如果选择点发生改变
if (c > 0 && c < b.length) { // 选择点在 0 到 b.length之间
performItemClicked(c); // 传递当前选择项
choose = c; // 记录下选择点
invalidate(); // 通知重新画图
}
}

break;
case MotionEvent.ACTION_MOVE: // 移动的话
if (oldChoose != c) {
if (c > 0 && c < b.length) {
performItemClicked(c);
choose = c;
invalidate();
}
}
break;
case MotionEvent.ACTION_UP: // 弹起
showBkg = false; // 隐藏状态
choose = -1; // 选择项 初始化
dismissPopup(); //
invalidate();
break;
}
return true;
}

private void showPopup(int item) {
if (mPopupWindow == null) { // 如果浮动窗口不为空
handler.removeCallbacks(dismissRunnable); // 消息机制移除 dismissRunnable
mPopupText = new TextView(getContext()); // 初始化浮动窗口
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT);

mPopupText.setLayoutParams(params);
mPopupText.setBackgroundColor(Color.GRAY); // 设置背景颜色
mPopupText.setTextColor(Color.CYAN); // 设置文字颜色
mPopupText.setTextSize(dpToPx(getContext(), 24)); // 设置为34dp
mPopupText.setGravity(Gravity.CENTER_HORIZONTAL
| Gravity.CENTER_VERTICAL);// 设置文字居中
mPopupWindow = new PopupWindow(mPopupText,
dpToPx(getContext(), 50), dpToPx(getContext(), 50)); // 设置浮动窗口大小
}

String text = ""; // 需要显示的文字
if (item == 0) {
text = "#";
} else {
text = Character.toString((char) ('A' + item - 1));
}
mPopupText.setText(text);
if (mPopupWindow.isShowing()) { // 显示状态?
mPopupWindow.update(); // 更新
} else {
/*
* 第一个参数:显示的view所在层~~getRootView() ---> 最顶层 第二个参数:Gravity 第三个参数: x坐标
* 第四个参数:y坐标
*/
mPopupWindow.showAtLocation(getRootView(), Gravity.CENTER, 0, 0);
}
}

private void dismissPopup() {
/*
* 第一个参数:线程 第二个参数:多少毫秒后执行传递过来的线程
*/
handler.postDelayed(dismissRunnable, 800);
}

Runnable dismissRunnable = new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
if (mPopupWindow != null) {
mPopupWindow.dismiss(); // 弹窗影藏
}
}
};

public boolean onTouchEvent(MotionEvent event) {
return super.onTouchEvent(event);
}

public void setOnItemClickListener(OnItemClickListener listener) {
mOnItemClickListener = listener;
}

/**
* 显示当前选择的字符串
*
* @param item
*/
private void performItemClicked(int item) {
if (mOnItemClickListener != null) {
mOnItemClickListener.onItemClick(b[item]);
showPopup(item);
}
}

public interface OnItemClickListener {
void onItemClick(String s);
}

/**
* dp 转 像素
* @param context
* @param dp
* @return
*/
public int dpToPx(Context context, int dp) {
// 密度比dpi
float scale = context.getResources().getDisplayMetrics().density;
// 140dp转成px动态加载
int px = (int) (scale * dp + 0.5f);

return px;
}

}


2. PinnedHeaderListView

package com.way.view;

import android.content.Context;
import android.graphics.Canvas;
import android.util.AttributeSet;
import android.view.View;
import android.widget.ListAdapter;
import android.widget.ListView;

public class PinnedHeaderListView extends ListView {

public interface PinnedHeaderAdapter {
public static final int PINNED_HEADER_GONE = 0; // 移除
public static final int PINNED_HEADER_VISIBLE = 1; // 显示
public static final int PINNED_HEADER_PUSHED_UP = 2; // 上移

int getPinnedHeaderState(int position); // 获取头部view的状态

/*
* 头部view + 索引 + 透明度
*/
void configurePinnedHeader(View header, int position, int alpha);
}

private static final int MAX_ALPHA = 255; // 最大的透明值

private PinnedHeaderAdapter mAdapter;
private View mHeaderView; // 头部View
private boolean mHeaderViewVisible; // 头部view状态
private int mHeaderViewWidth; // 头部view宽
private int mHeaderViewHeight; // 头部view高

public PinnedHeaderListView(Context context) {
super(context);
}

public PinnedHeaderListView(Context context, AttributeSet attrs) {
super(context, attrs);
}

public PinnedHeaderListView(Context context, AttributeSet attrs,
int defStyle) {
super(context, attrs, defStyle);
}

/*
* 重写onLayout方法
*/
@Override
protected void onLayout(boolean changed, int left, int top, int right,
int bottom) {
super.onLayout(changed, left, top, right, bottom);
if (mHeaderView != null) { // 头部view不为null
/*
* left, top, right, bottom 设置view的大小
*/
mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
/*
* 返回适配器第一个显示在屏幕上的item的索引
*/
configureHeaderView(getFirstVisiblePosition());
}
}

protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if (mHeaderView != null) {
measureChild(mHeaderView, widthMeasureSpec, heightMeasureSpec);
mHeaderViewWidth = mHeaderView.getMeasuredWidth();
mHeaderViewHeight = mHeaderView.getMeasuredHeight();
}
}

public void setPinnedHeaderView(View view) {
mHeaderView = view;
if (mHeaderView != null) {
setFadingEdgeLength(0);
}
requestLayout();
}

/**
* @param 传递一个实现了PinnedHeaderAdapter接口的adapter
*/
public void setAdapter(ListAdapter adapter) {
super.setAdapter(adapter);
mAdapter = (PinnedHeaderAdapter) adapter; // FriendsAdapter实现了接口
}

/**
* 根据传递过来的值~~设置头部View的是否显示,
* @param position
*/
public void configureHeaderView(int position) {
if (mHeaderView == null) { // 头部view为null
return;
}

int state = mAdapter.getPinnedHeaderState(position); // 传递第一个item的值过去
switch (state) {
case PinnedHeaderAdapter.PINNED_HEADER_GONE: { // 移除状态
mHeaderViewVisible = false; // 不显示
break;
}

case PinnedHeaderAdapter.PINNED_HEADER_VISIBLE: { // 显示状态
mAdapter.configurePinnedHeader(mHeaderView, position, MAX_ALPHA);
if (mHeaderView.getTop() != 0) { // 头部view不在0位置~~
mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);// 显示出来~设置大小
}
mHeaderViewVisible = true; // 显示状态
break;
}

case PinnedHeaderAdapter.PINNED_HEADER_PUSHED_UP: { // 向上移动状态
View firstView = getChildAt(0); // 获取当前第一个view
int bottom = firstView.getBottom(); // 得到view的底步位置
int headerHeight = mHeaderView.getHeight(); // 得到头部view的高度
int y; //
int alpha; //
if (bottom < headerHeight) { // 如果view的总高度 小于 头部view的高度
y = (bottom - headerHeight); // 求出 差值
alpha = MAX_ALPHA * (headerHeight + y) / headerHeight; // 计算
// 透明度值
} else {
y = 0; //
alpha = MAX_ALPHA; // 不透明
}
mAdapter.configurePinnedHeader(mHeaderView, position, alpha);
if (mHeaderView.getTop() != y) { // 如果头部view的顶部值 != y值
mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight
+ y); // 设置头部view的大小
}
mHeaderViewVisible = true; // 显示状态
break;
}
}
}

protected void dispatchDraw(Canvas canvas) {
super.dispatchDraw(canvas);
if (mHeaderViewVisible) { // 显示状态~就把头部view话出来
drawChild(canvas, mHeaderView, getDrawingTime());
}
}
}


3. FriendsAdapter

package com.way.pinnedheaderlistview;

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

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AbsListView;
import android.widget.AbsListView.OnScrollListener;
import android.widget.BaseAdapter;
import android.widget.LinearLayout;
import android.widget.SectionIndexer;
import android.widget.TextView;

import com.way.view.PinnedHeaderListView;
import com.way.view.PinnedHeaderListView.PinnedHeaderAdapter;

public class FriendsAdapter extends BaseAdapter implements SectionIndexer,
PinnedHeaderAdapter, OnScrollListener {
private int mLocationPosition = -1; // 定位点
private String[] mDatas; // 数据源
// 首字母集
private List<String> mFriendsSections;
// 首字母位置集合
private List<Integer> mFriendsPositions;
// 布局构造器
private LayoutInflater inflater;

public FriendsAdapter(Context context, String[] datas,
List<String> friendsSections, List<Integer> friendsPositions) {
// TODO Auto-generated constructor stub
inflater = LayoutInflater.from(context);
mDatas = datas;
mFriendsSections = friendsSections;
mFriendsPositions = friendsPositions;
}

@Override
public int getCount() {
return mDatas != null && mDatas.length != 0 ? mDatas.length : 0;
}

@Override
public Object getItem(int position) {
return mDatas[position];
}

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

@Override
public View getView(int position, View convertView, ViewGroup parent) {
int section = getSectionForPosition(position);
ViewHorlder vh = null;
if (convertView == null) {
convertView = inflater.inflate(R.layout.listview_item, null);
vh = new ViewHorlder();
vh.mHeaderParent = (LinearLayout) convertView
.findViewById(R.id.friends_item_header_parent);
vh.mHeaderText = (TextView) convertView
.findViewById(R.id.friends_item_header_text);
vh.mTextView_name = (TextView) convertView
.findViewById(R.id.friends_item);
convertView.setTag(vh);
} else {
vh = (ViewHorlder) convertView.getTag();
}
// LinearLayout mHeaderParent = (LinearLayout) convertView
// .findViewById(R.id.friends_item_header_parent);
// TextView mHeaderText = (TextView) convertView
// .findViewById(R.id.friends_item_header_text);
if (getPositionForSection(section) == position) { // 判断头部view的状态~~是否显示
vh.mHeaderParent.setVisibility(View.VISIBLE);
vh.mHeaderText.setText(mFriendsSections.get(section));
} else {
vh.mHeaderParent.setVisibility(View.GONE);
}
// TextView textView = (TextView) convertView
// .findViewById(R.id.friends_item);
vh.mTextView_name.setText(mDatas[position]);
return convertView;
}

private class ViewHorlder {
TextView mHeaderText;
LinearLayout mHeaderParent;
TextView mTextView_name;;
}

@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {

}

@Override
public void onScroll(AbsListView view, int firstVisibleItem,
int visibleItemCount, int totalItemCount) {
// 滑动的时候判断view是否是PinnedHeaderListView
if (view instanceof PinnedHeaderListView) {
// 传递当前第一个显示的item索引
((PinnedHeaderListView) view).configureHeaderView(firstVisibleItem);
}
}

@Override
public int getPinnedHeaderState(int position) {
int realPosition = position; // 当前首item
if (realPosition < 0
|| (mLocationPosition != -1 && mLocationPosition == realPosition)) {
// 小于0 或 定位点不等于 -1 且 定位点不等于 当前首item
return PINNED_HEADER_GONE; // 移除状态
}
mLocationPosition = -1; // 初始化定位点
int section = getSectionForPosition(realPosition); // 获得首字母索引
int nextSectionPosition = getPositionForSection(section + 1); // 下一个首字母索引
if (nextSectionPosition != -1 // 不等于 -1
&& realPosition == nextSectionPosition - 1) { // 当前值 = 下一个值 -1
return PINNED_HEADER_PUSHED_UP; // 上移状态
}
return PINNED_HEADER_VISIBLE; // 显示状态
}

@Override
public void configurePinnedHeader(View header, int position, int alpha) {
int realPosition = position; // 当前首项
int section = getSectionForPosition(realPosition); // 得到首字母索引
String title = (String) getSections()[section]; // 首字母
((TextView) header.findViewById(R.id.friends_list_header_text))
.setText(title); // 找到头部view显示首字母
}

@Override
public Object[] getSections() {
return mFriendsSections.toArray();
}

@Override
public int getPositionForSection(int section) {
if (section < 0 || section >= mFriendsSections.size()) {
return -1;
}
return mFriendsPositions.get(section);
}

/**
* 得到首字母索引
*/
@Override
public int getSectionForPosition(int position) {
if (position < 0 || position >= getCount()) {
return -1;
}
/*
* 找到指定的首字母索引
*/
int index = Arrays.binarySearch(mFriendsPositions.toArray(), position);
return index >= 0 ? index : -index - 2; // -index -2 没理解
}

}


4. MainActivity 测试

package com.way.pinnedheaderlistview;

import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;

import com.way.view.BladeView;
import com.way.view.BladeView.OnItemClickListener;
import com.way.view.PinnedHeaderListView;

public class MainActivity extends Activity {
/*
*
*/
private static final String FORMAT = "^[a-z,A-Z].*$";
private PinnedHeaderListView mListView;
private BladeView mLetter;
private FriendsAdapter mAdapter;
// 数据源
private String[] datas;
// 首字母集
private List<String> mSections;
// 根据首字母存放数据
private Map<String, List<String>> mMap;
// 首字母位置集
private List<Integer> mPositions;
// 首字母对应的位置
private Map<String, Integer> mIndexer;

@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initData();
initView();
}

/**
* 初始化数据源
*/
private void initData() {
datas = getResources().getStringArray(R.array.countries); // 字符串集合
mSections = new ArrayList<String>();
mMap = new HashMap<String, List<String>>();
mPositions = new ArrayList<Integer>();
mIndexer = new HashMap<String, Integer>();

for (int i = 0; i < datas.length; i++) {
String firstName = datas[i].substring(0, 1);
Log.i("123", "FORMAT--->" + FORMAT);
if (firstName.matches(FORMAT)) { // 匹配首字母
if (mSections.contains(firstName)) { // 如果首字母集包含改首字母
mMap.get(firstName).add(datas[i]); // 该字符串放入对应字母的 集合中
} else {
mSections.add(firstName); // 加入到首字母集合
List<String> list = new ArrayList<String>(); // 初始化新的字母集合
list.add(datas[i]); // 加入集合中
mMap.put(firstName, list); // 该字符串放入对应字母的 集合中
}
} else {
if (mSections.contains("#")) { // 如果包含#号
mMap.get("#").add(datas[i]); // 添加对应的字符串map中
} else {
mSections.add("#"); // 加入#号
List<String> list = new ArrayList<String>(); // 初始化新的字母集合
list.add(datas[i]); // 加入集合中
mMap.put("#", list);// 该字符串放入对应字母的 集合中
}
}
}

Collections.sort(mSections); // 排序~~升序
int position = 0;
for (int i = 0; i < mSections.size(); i++) {
mIndexer.put(mSections.get(i), position);// 存入map中,key为首字母字符串,value为首字母在listview中位置
mPositions.add(position);// 首字母在listview中位置,存入list中
position += mMap.get(mSections.get(i)).size();// 计算下一个首字母在listview的位置
}
}

private void initView() {
// TODO Auto-generated method stub
mListView = (PinnedHeaderListView) findViewById(R.id.friends_display);
mLetter = (BladeView) findViewById(R.id.friends_myletterlistview);
mLetter.setOnItemClickListener(new OnItemClickListener() {

@Override
public void onItemClick(String s) {
if (mIndexer.get(s) != null) {
/*
* 选中适合的项
*/
mListView.setSelection(mIndexer.get(s));
}
}
});
mAdapter = new FriendsAdapter(this, datas, mSections, mPositions);
mListView.setAdapter(mAdapter);
mListView.setOnScrollListener(mAdapter);
/*
* 设置视图
*/
mListView.setPinnedHeaderView(LayoutInflater.from(this).inflate(
R.layout.listview_head, mListView, false));
}

}


第一次发文~比较随意~学习所用

有需要的可以去http://download.csdn.net/detail/u010247214/8942201下载~
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: