Android教程網
  1. 首頁
  2. Android 技術
  3. Android 手機
  4. Android 系統教程
  5. Android 游戲
 Android教程網 >> Android技術 >> 關於Android編程 >> 仿微信通訊錄右側快速定位字母表控件的實現

仿微信通訊錄右側快速定位字母表控件的實現

編輯:關於Android編程

仿微信通訊錄右側快速定位字母表控件

先看效果圖:

\

 

界面比較單調,湊合看,主要看功能。這種控件在很多應用的通訊錄的界面,MIUI裡面的通訊錄都有這個功能,其實這是一個自定義View,相對來說,這個並不是一個多麼復雜的自定義View。

下面介紹一下這種控件的實現方法:

首先,自定義View,一般是對View的增強,因為系統提供的控件不能滿足需求,

一般情況下,都是繼承View,然後重寫裡面的onDraw等方法。

 

來看一下這個控件,A~#這27個符號需要繪制到view上去,這個當然需要在onDraw方法中實現。

另外,這個控件能夠響應用戶的Touch事件,在用戶觸碰到相應的字母的時候,左邊需要展示相應字母的對話框,

其實這裡可以用一個TextView來實現,在Touch事件裡面設置TextView的狀態是否可見就可以了,

處理需要展示對話框,還需要控制左邊ListView的位置,當手指碰到相應的字母時,ListView需要跳到相應的字母位置處。

下面我們來看下這個自定義View的源碼:

 

package com.hgao.view;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.graphics.drawable.ColorDrawable;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;

import com.hgao.main.R;
import com.hgao.utils.PixelUtil;

public class MyLetterView extends View {

	public static String[] letters = { "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", "#" };

	private Paint paint = new Paint();
	/**
	 * 用於標記哪個位置被選中
	 */
	private int choose = -1;
	//該TextView是左邊顯示的對話框
	private TextView mTextDialog;

	public void setTextDialog(TextView mTextDialog) {
		this.mTextDialog = mTextDialog;
	}

	private OnTouchingLetterChangedListener listener;

	public void setOnTouchingLetterChangedListener(OnTouchingLetterChangedListener listener) {
		this.listener = listener;
	}

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

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

	public MyLetterView(Context context, AttributeSet attrs, int defStyleAttr) {
		super(context, attrs, defStyleAttr);
	}

	@Override
	protected void onDraw(Canvas canvas) {
		super.onDraw(canvas);
		// 獲取該自定義View的寬度和高度
		int width = getWidth();
		int height = getHeight();

		// 單個字母的高度
		int singleHeight = height / letters.length;

		for (int i = 0; i < letters.length; i++) {
			paint.setColor(getResources().getColor(
					R.color.color_bottom_text_normal));
			paint.setTypeface(Typeface.DEFAULT_BOLD);
			paint.setAntiAlias(true);
			paint.setTextSize(PixelUtil.sp2px(12, getContext()));

			// 如果選中的話,改變樣式和顏色
			if (i == choose) {
				paint.setColor(Color.parseColor("#3399ff"));
				paint.setFakeBoldText(true);
			}

			// 首先確定每個字母的橫坐標的位置,橫坐標:該自定義View的一半 -(減去) 單個字母寬度的一半
			float xPos = width / 2 - paint.measureText(letters[i]) / 2;
			float yPos = singleHeight * (i + 1);

			canvas.drawText(letters[i], xPos, yPos, paint);

			// 重置畫筆
			paint.reset();
		}
	}

	@SuppressWarnings("deprecation")
	@Override
	public boolean dispatchTouchEvent(MotionEvent event) {

		int action = event.getAction();
		float y = event.getY();

		int oldChoose = choose;
		// 根據y坐標確定當前哪個字母被選中
		int pos = (int) (y / getHeight() * letters.length);

		switch (action) {
		case MotionEvent.ACTION_UP:
			// 當手指抬起時,設置View的背景為白色
			setBackgroundDrawable(new ColorDrawable(0x00000000));
			// 重置為初始狀態
			choose = -1;
			// 讓View重繪
			invalidate();

			// 將對話框設置為不可見
			if (mTextDialog != null) {
				mTextDialog.setVisibility(View.INVISIBLE);
			}

			break;
		default:
			// 設置右邊字母View的背景色
			setBackgroundResource(R.drawable.v2_sortlistview_sidebar_background);
			if (pos != oldChoose) {
				// 如果之前選中的和當前的不一樣,需要重繪
				if (pos >= 0 && pos < letters.length) {
					if(listener != null) {
						//當前字母被選中,需要讓ListView去更新顯示的位置
						listener.onTouchingLetterChanged(letters[pos]);
					}
					//在左邊顯示選中的字母,該字母放在TextView上,相當於一個dialog
					if (mTextDialog != null) {
						mTextDialog.setText(letters[pos]); //讓對話框顯示響應的字母
						mTextDialog.setVisibility(View.VISIBLE);
					}
					choose = pos;  //當前位置為選中位置
				}
			}
			break;
		}

		return true;
	}

	/**
	 * 該回調接口用於通知ListView更新狀態
	 */
	public interface OnTouchingLetterChangedListener {
		public void onTouchingLetterChanged(String s);
	}

}

 

在onDraw方法中,將A~#這些字母繪制成功,這裡面的邏輯相對簡單,計算下高度初始化畫筆,設置畫筆相關的屬性和style。

該控件對Touch事件進行響應,顯然需要重寫Touch事件相關的方法,其實這裡重寫onTouchEvent事件和dispatchTouchEvent都是可以的。

 

我們這裡重寫的是dispatchTouchEvent這個方法,這個涉及到事件的分發機制,有興趣的同學可以去研究下。

其實View的事件遵循這樣的流程:dispatchTouchEvent------>onTouch------>onTouchEvent------->onClick。

在dispatchTouchEvent中重寫,返回true,這樣不會往下分發,其實重寫onTouchEvent是一樣的,都是響應用戶的Touch事件,

然後讓View作出相應的重繪。

 

 

/**
	 * 該回調接口用於通知ListView更新狀態
	 */
	public interface OnTouchingLetterChangedListener {
		public void onTouchingLetterChanged(String s);
	}
注意到定義了這個接口,這個接口是暴露給ListView,用於ListView能夠更新自己顯示的位置。

 

下面我們來看下主界面的代碼:

 

 

package com.hgao.main;

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

import android.app.Activity;
import android.os.Bundle;
import android.widget.ListView;
import android.widget.TextView;

import com.hgao.utils.CharacterParser;
import com.hgao.utils.PinyinComparator;
import com.hgao.view.MyLetterView;
import com.hgao.view.MyLetterView.OnTouchingLetterChangedListener;

public class MainActivity extends Activity {

	private ListView list_friends;
	private TextView dialog;
	private MyLetterView right_letter;

	private List friends;
	
	/**
	 * 根據拼音來排列ListView中的數據
	 */
	private PinyinComparator pinyinComparator;

	private CharacterParser characterParser;
	
	@Override
	protected void onCreate(Bundle savedInstanceState) {
		super.onCreate(savedInstanceState);
		setContentView(R.layout.activity_main);

		initData();
		initView();
	}

	private void initData() {
		characterParser = CharacterParser.getInstance();
		pinyinComparator = new PinyinComparator();
		String[] names = getResources().getStringArray(R.array.names);  //這裡ListView展示的數據不是從網絡加載的
		friends = new ArrayList();				//數據寫在string.xml文件中
		for (int i = 0; i < names.length; i++) {
			Friend f = new Friend();
			f.setName(names[i]);
			String pinyin = characterParser.getSelling(names[i]);
			String sortString = pinyin.substring(0, 1).toUpperCase();
			// 正則表達式,判斷首字母是否是英文字母
			if (sortString.matches("[A-Z]")) {
				f.setSortLetters(sortString.toUpperCase());
			} else {
				f.setSortLetters("#");
			}
			friends.add(f);
		}
		Collections.sort(friends, pinyinComparator);  //將數據進行,pinyinComparator是一個比較器
	}

	private void initView() {
		list_friends = (ListView) findViewById(R.id.list_friends);
		dialog = (TextView) findViewById(R.id.dialog);
		right_letter = (MyLetterView) findViewById(R.id.right_letter);
		right_letter.setTextDialog(dialog);
		final FriendsAdapter adapter = new FriendsAdapter(this,friends);
		right_letter
				.setOnTouchingLetterChangedListener(new OnTouchingLetterChangedListener() {
					@Override
					public void onTouchingLetterChanged(String s) {
						// 該字母首次出現的位置
						int position = adapter.getPositionForSection(s.charAt(0));
						if (position != -1) {
							list_friends.setSelection(position);
						}
					}
				});
	
		list_friends.setAdapter(adapter);
	}
}

主界面的邏輯其實很簡單,初始化數據,並初始化相關控件,並為控件設置響應的事件監聽,這樣,這個控件的實現簡單吧。

這裡面用到了一些工具類,用於漢字和拼音的轉換,後面有源碼,還是看源碼吧。

 

接下來看下數據適配器的代碼:

 

package com.hgao.main;

import java.util.List;

import android.content.Context;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

public class FriendsAdapter extends BaseAdapter {

	List friends;
	Context context;
	public FriendsAdapter(Context context, List friends) {
		this.friends = friends;
		this.context = context;
	}

	@Override
	public int getCount() {
		return friends.size();
	}

	@Override
	public Object getItem(int position) {
		return friends.get(position);
	}

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

	@Override
	public View getView(int position, View convertView, ViewGroup parent) {
		ViewHolder viewHolder = null;
		if(convertView == null) {
			convertView = LayoutInflater.from(context).inflate(R.layout.item_user_friend, null);
			viewHolder = new ViewHolder();
			viewHolder.name = (TextView) convertView.findViewById(R.id.tv_friend_name);
			viewHolder.alpha = (TextView) convertView.findViewById(R.id.alpha);
			convertView.setTag(viewHolder);
		} else {
			viewHolder = (ViewHolder) convertView.getTag();
		}
		
		Friend friend = friends.get(position);
		viewHolder.name.setText(friend.getName());
		
		// 根據position獲取分類的首字母的Char ascii值
		int section = getSectionForPosition(position);
		// 如果當前位置等於該分類首字母的Char的位置 ,則認為是第一次出現
		if (position == getPositionForSection(section)) {
			viewHolder.alpha.setVisibility(View.VISIBLE);
			viewHolder.alpha.setText(friend.getSortLetters());
		} else {
			viewHolder.alpha.setVisibility(View.GONE);
		}
		return convertView;
	}
	
	static class ViewHolder {
		TextView name;
		TextView alpha;;
	}
	//判斷當前位置是否是該位置對應的字母第一次出現
	public int getPositionForSection(int section) {
		for (int i = 0; i < getCount(); i++) {
			String sortStr = friends.get(i).getSortLetters();
			char firstChar = sortStr.toUpperCase().charAt(0);
			if (section == firstChar) {
				return i;
			}
		}
		return -1;
	}

	/**
	 * 根據ListView的當前位置獲取分類的首字母的Char ascii值
	 */
	private int getSectionForPosition(int position) {
		return friends.get(position).getSortLetters().charAt(0);
	}
}

adapter裡面的邏輯,注釋都寫了,也沒什麼好講的,理解理解應該就懂了。

最後把這個項目的代碼分享下,還是菜鳥,感覺還有點小bug,見笑了!

權當是記錄自己的學習筆記,O(∩_∩)O~!

源碼地址:QuickSearchView

  1. 上一頁:
  2. 下一頁:
熱門文章
閱讀排行版
Copyright © Android教程網 All Rights Reserved