类 微信 通讯录,实时搜索,首字母分类,滚动header push,以及右侧字母列表

第一次写博客。。写的太渣请见谅。可以直接下代码~~

这段时间的项目中用到了这么一个类似的功能,主要用来选择城市。

刚好现在有空,所以提取整理出了代码。

当时在网上找了大概3-4个类似demo,但都功能不完整。所以决定参照那些demo自己写一个。


效果图

类 微信 通讯录,实时搜索,首字母分类,滚动header push,以及右侧字母列表_第1张图片 类 微信 通讯录,实时搜索,首字母分类,滚动header push,以及右侧字母列表_第2张图片 类 微信 通讯录,实时搜索,首字母分类,滚动header push,以及右侧字母列表_第3张图片


代码结构

类 微信 通讯录,实时搜索,首字母分类,滚动header push,以及右侧字母列表_第4张图片

先把思路说下。在每一个item上我都有一个head 和一个content,在adapter里判断当前item 的首字母是否首次出现,如果是首次出现,那么就显示head。
而那个可以被往上推动的headerView布局和  item 中 head的布局是一模一样的。在listView 滚动的时候, 不断判断下一个首字母的position 是否是当前item首字母的position+1,如果是的话,那么就要往上推动了。不是的话悬浮在顶部不动。
那么是如何判断这两个position的呢,就要用到 AlphaChar这个对象了,里面有两个字段一个是首字母char ,一个是index 首字母的position。通过遍历获得下一个首字母的位置
Iterator iterator=alphaChars.iterator();
		while (iterator.hasNext()) {
			AlphaChar ac= iterator.next();
			if (ac.c==section &&iterator.hasNext()) {
				ac=iterator.next();
				return ac.index;
			}
		}
		return -1;

先把PinnedHeaderListView介绍一下。这个View 继承了ListView ,并且实现了OnScrollListener接口。
public class PinnedHeaderListView extends ListView implements OnScrollListener{
	private static final String TAG = "";

	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;
	
	private static final int MAX_ALPHA = 255;

	private CityAdapter mAdapter;
	private View mHeaderView;
	private boolean mHeaderViewVisible;
	private int mHeaderViewWidth;
	private int mHeaderViewHeight;

	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);
		if (isInEditMode()) {
			return;
		}
		
	}
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        if (mHeaderView != null) {
            mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
            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();
		this.setOnScrollListener(this);
	}
    public void setAdapter(CityAdapter adapter) {
        super.setAdapter(adapter);
        
			mAdapter=adapter;
		
    }
    /**
     * 根据state状态 修改headView 的动作
     * @param position
     */
	public void configureHeaderView(int position) {
		if (mHeaderView == null) {
			return;
		}
		int state = getPinnedHeaderState(position);
		
		switch (state) {
		case PINNED_HEADER_GONE: {
			mHeaderViewVisible = false;
			break;
		}

		case PINNED_HEADER_VISIBLE: {
			configurePinnedHeader(position);
			if (mHeaderView.getTop() != 0) {
				mHeaderView.layout(0, 0, mHeaderViewWidth, mHeaderViewHeight);
			}
			mHeaderViewVisible = true;
			break;
		}

		case PINNED_HEADER_PUSHED_UP: {
			View firstView = getChildAt(0);
			if(firstView != null){
				
				int bottom = firstView.getBottom();
				int headerHeight = mHeaderView.getHeight();
				int y;
				int alpha;
				if (bottom < headerHeight) {
					y = (bottom - headerHeight);
					alpha = MAX_ALPHA * (headerHeight + y) / headerHeight;
				} else {
					y = 0;
					alpha = MAX_ALPHA;
				}
				configurePinnedHeader(position);
				if (mHeaderView.getTop() != y) {
					
					mHeaderView.layout(0, y, mHeaderViewWidth, mHeaderViewHeight
							+ y);
				}
				mHeaderViewVisible = true;
			}else{
				Log.w(TAG, "firstView ==null");
			}
			break;
		}
		}
	}
	/**
	 * 判断当前是该隐藏还是悬浮 还是被推动上移
	 * @param position
	 * @return
	 */
	public int getPinnedHeaderState(int position) {
		int realPosition = position;
		if (realPosition < 0) {
			return PINNED_HEADER_GONE;
		}

		int section = mAdapter.getSectionForPosition(realPosition);
		int nextSectionPosition = mAdapter.getNextPositionForSection(section);
		if (nextSectionPosition != -1
				&& realPosition == nextSectionPosition - 1) {
			return PINNED_HEADER_PUSHED_UP;
		}
		return PINNED_HEADER_VISIBLE;
	}
	/**
	 * 设置head的文字
	 * @param position
	 */
	private void configurePinnedHeader(int position){
		
		char title=(char) mAdapter.getSectionForPosition(position);
		String text=String.valueOf(title);

		((TextView)mHeaderView.findViewById(R.id.query_ticket_header_text)).setText(text);
	}
    protected void dispatchDraw(Canvas canvas) {
        super.dispatchDraw(canvas);
        if (mHeaderViewVisible) {
            drawChild(canvas, mHeaderView, getDrawingTime());
        }
    }

	@Override
	public void onScrollStateChanged(AbsListView view, int scrollState) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void onScroll(AbsListView view, int firstVisibleItem,
			int visibleItemCount, int totalItemCount) {
		// TODO Auto-generated method stub
		configureHeaderView(firstVisibleItem);
	}
}
CityAdapter 实现了SectionIndexer接口。代码里注释还好,就不多说其他的了,直接贴代码
public class CityAdapter extends BaseAdapter implements SectionIndexer{

	private static final String TAG = "TicketCityAdapter";

	private List list;
	private LayoutInflater inflater;

	private List alphaChars;
	
	public CityAdapter(Context context, List list,List alphaChars) {
		// TODO Auto-generated constructor stub
		inflater = LayoutInflater.from(context);
		this.list = list;
		this.alphaChars=alphaChars;

	}

	public void updateListView(List list,List alphaChars) {
		this.list = list;
		this.alphaChars=alphaChars;
		
		notifyDataSetChanged();
	}

	@Override
	public int getCount() {
		// TODO Auto-generated method stub
		return list.size();
	}

	@Override
	public TicketCity getItem(int position) {
		// TODO Auto-generated method stub
		return list.get(position);
	}

	@Override
	public long getItemId(int position) {
		// TODO Auto-generated method stub
		return position;
	}

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		// TODO Auto-generated method stub
		int section = getSectionForPosition(position);
		Holder holder = null;
		if (convertView == null) {
			convertView = inflater.inflate(R.layout.query_ticket_item, null);
			holder = new Holder();
			holder.mHeaderParent=(LinearLayout)convertView.findViewById(R.id.query_ticket_item_parent);
			holder.mHeaderText=(TextView)convertView.findViewById(R.id.query_ticket_item_header_text);
			holder.textView=(TextView)convertView.findViewById(R.id.query_ticket_item_text);
			convertView.setTag(holder);
		} else {
			holder = (Holder) convertView.getTag();
		}

		
		if (getPositionForSection(section) == position) {
			holder.mHeaderParent.setVisibility(View.VISIBLE);
			String text=list.get(position).cityShortPinyin.toUpperCase()
					.substring(0, 1);
			holder.mHeaderText.setText(text);
		} else {
			holder.mHeaderParent.setVisibility(View.GONE);
		}

		holder.textView.setText(list.get(position).cityName);
		return convertView;
	}

	/**
	 * 根据分类的首字母的Char ascii值获取其第一次出现该首字母的位置 section=65 在总list中查开头是65的
	 */
	@Override
	public int getPositionForSection(int section) {
		if (section < 0) {
			return -1;
		}
		for (AlphaChar ac : alphaChars) {
			if (ac.c==section) {
				return ac.index;
			}
		}
		return -1;
	}
	/**
	 * 获得下一个首字母的位置
	 * @param section
	 * @return
	 */
	public int getNextPositionForSection(int section) {
		if (section < 0 ) {
			return -1;
		}
		Iterator iterator=alphaChars.iterator();
		while (iterator.hasNext()) {
			AlphaChar ac= iterator.next();
			if (ac.c==section &&iterator.hasNext()) {
				ac=iterator.next();
				return ac.index;
			}
		}
		return -1;
	}
	/**
	 * 根据ListView的当前位置获取分类的首字母的Char ascii值 
	 * 例如cityShortPinyin=ags char= A return 65
	 */
	@Override
	public int getSectionForPosition(int position) {
		// TODO Auto-generated method stub
		if (position >= list.size()) {
			return -1;
		}
		return list.get(position).cityShortPinyin.toUpperCase().charAt(0);
	}

	@Override
	public Object[] getSections() {
		// TODO Auto-generated method stub
		return null;
	}

	class Holder {
		
		LinearLayout mHeaderParent;
		
		TextView mHeaderText;
		
		TextView textView;

	}


}

然后是 MainActivity
public class MainActivity extends Activity {
	private static final String TAG = MainActivity.class.getSimpleName();

	private static final int HANDLER_MSG_SHOW = 1;
	private PinnedHeaderListView mListView;

	private CityAdapter mAdapter;

	private BladeView mLetter;

	private ClearEditText mClearEditText;

	private List tcs;

	private List alphaChars;
	String[] datas;

	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);
		mListView = (PinnedHeaderListView) findViewById(R.id.query_ticket_activity_list);
		mLetter = (BladeView) findViewById(R.id.query_ticket_activity_sidrbar);
		mClearEditText = (ClearEditText) findViewById(R.id.query_ticket_activity_edit);
		init();
	}

	private void init() {
		Thread thread = new Thread(new Runnable() {

			@Override
			public void run() {
				// TODO Auto-generated method stub
				// http获取数据等,这里直接读本地的文件了
				datas = getResources().getStringArray(R.array.countries);
				if (tcs==null) {
					tcs=new ArrayList();
				}
				tcs.clear();
				TicketCity tc;
				for (int i = 0; i < datas.length; i++) {
					tc = new TicketCity();
					tc.cityShortPinyin = datas[i];
					tc.cityName = datas[i] + "名字" + i;
					tc.cityFullPinyin = datas[i] + "全拼" + i;
					tcs.add(tc);
				}
				Collections.sort(tcs);
				initSAlphaChar(tcs);
				mHandler.sendEmptyMessage(HANDLER_MSG_SHOW);
			}
		});
		thread.start();

	}

	private Handler mHandler = new Handler() {

		@Override
		public void handleMessage(Message msg) {
			// TODO Auto-generated method stub
			super.handleMessage(msg);
			switch (msg.what) {
			case HANDLER_MSG_SHOW:
				showView();
				break;

			default:
				break;
			}
		}

	};

	protected void showView() {
		// TODO Auto-generated method stub
		mLetter.setOnItemClickListener(new BladeView.OnItemClickListener() {

			@Override
			public void onItemClick(String s) {
				mListView.setSelection(mAdapter.getPositionForSection(s
						.toUpperCase().charAt(0)));
			}
		});

		mClearEditText.addTextChangedListener(new TextWatcher() {

			@Override
			public void onTextChanged(CharSequence s, int start, int before,
					int count) {
				try {
					filterData(s.toString());
				} catch (Exception e) {
				}
			}

			@Override
			public void beforeTextChanged(CharSequence s, int start, int count,
					int after) {
				// TODO Auto-generated method stub

			}

			@Override
			public void afterTextChanged(Editable s) {
				// TODO Auto-generated method stub

			}
		});

		mAdapter = new CityAdapter(this, tcs, alphaChars);

		mListView.setAdapter(mAdapter);
		mListView.setPinnedHeaderView(LayoutInflater.from(this).inflate(
				R.layout.query_ticket_header, mListView, false));
		mListView.setOnItemClickListener(new OnItemClickListener() {

			@Override
			public void onItemClick(AdapterView parent, View view,
					int position, long id) {
				// TODO Auto-generated method stub
				String py= mAdapter.getItem(position).cityShortPinyin;
				Toast.makeText(MainActivity.this, py, Toast.LENGTH_LONG).show();;
			}
			
		});
	}

	/**
	 * 根据输入框中的值来过滤数据并更新ListView
	 * 
	 * @param filterStr
	 */
	private void filterData(String filterStr) {

		List filterDateList = new ArrayList();

		if (TextUtils.isEmpty(filterStr)) {
			filterDateList = tcs;
		} else {
			filterDateList.clear();
			for (TicketCity tc : tcs) {
				// 简拼 全屏 汉字都匹配
				if (tc.cityShortPinyin.toUpperCase().indexOf(
						filterStr.toUpperCase()) >= 0
						|| tc.cityName.indexOf(filterStr) >= 0
						|| tc.cityFullPinyin.toUpperCase().indexOf(
								filterStr.toUpperCase()) >= 0) {
					filterDateList.add(tc);
				}

			}
		}

		// 根据a-z进行排序
		Collections.sort(filterDateList);
		initSAlphaChar(filterDateList);
		mAdapter.updateListView(filterDateList, alphaChars);

	}

	/**
	 * 初始化起始城市首字母列表
	 * 
	 * @param list
	 */
	private void initSAlphaChar(List list) {

		if (alphaChars == null) {
			alphaChars = new ArrayList();
		}
		alphaChars.clear();

		char c;
		char oldc = 0;

		for (int i = 0; i < list.size(); i++) {

			c = list.get(i).cityShortPinyin.toUpperCase().charAt(0);

			if (c != oldc) {
				AlphaChar ac = new AlphaChar();
				ac.c = c;
				ac.index = i;
				alphaChars.add(ac);
			}
			oldc = c;
		}

	}

}

还有两个数据类就没啥好说的了
TicketCity 加了一个排序
public class TicketCity  implements Comparable{
	/**
	 * 城市名称
	 */
	public String cityName;
	/**
	 * 城市拼音全拼
	 */
	public String cityFullPinyin;
	/**
	 * 城市拼音简拼
	 */
	public String cityShortPinyin;
	
	
	Comparator cmp = Collator.getInstance(java.util.Locale.CHINA);
	@Override
	public int compareTo(TicketCity another) {
		// TODO Auto-generated method stub
		
		
		return cmp.compare(this.cityShortPinyin, another.cityShortPinyin);
	}
	
}




xml的布局文件:



    

        

    

    


主界面布局:


    

        
    

    

    





资源下载链接点击打开链接




你可能感兴趣的:(Android)