Android学习之联系人PinnedHeaderListView(通讯录)
2015-07-29 10:11
447 查看
今天学习的是通讯录~~根据自己的理解~~添加了部分注释。
1. BladView,主要实现了触摸时返回对应字母
2. PinnedHeaderListView
3. FriendsAdapter
4. MainActivity 测试
第一次发文~比较随意~学习所用
有需要的可以去http://download.csdn.net/detail/u010247214/8942201下载~
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下载~
相关文章推荐
- android动画介绍之 自定义Animation动画实现qq抖一抖效果
- android动画介绍之 自定义Animation动画实现qq抖一抖效果
- Android控件ToggleButton的用法
- Android控件ToggleButton的用法 分类: Android 2015-07-29 10:07 155人阅读 评论(1) 收藏
- Android碎片第四期
- Android DEX自动拆包及动态加载简介
- android 进制转换方法
- 比较文件MD5
- 【Android】Paint的效果研究
- Mac电脑svn使用经验,Mac电脑使用android studio使用经验
- 【转】Android中定时器的3种实现方法
- Android多线程及异步处理问题详细探讨
- Java,XML,Android 删除各种注释内容
- 通过浏览器调用Android要么iOS应用
- Android自定义状态栏通知(Status Notification)的正确实现
- android js调试
- Android Material Design之Toolbar与Palette实践
- Android使用UncaughtExceptionHandler捕获全局异常
- Android SharedPreferences保存和读取对象
- Android Studio-Gradle项目中添加JNI生成文件(.so文件)