編輯:關於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);
}
}
最後把這個項目的代碼分享下,還是菜鳥,感覺還有點小bug,見笑了!
權當是記錄自己的學習筆記,O(∩_∩)O~!
源碼地址:QuickSearchView
學會Retrofit+OkHttp+RxAndroid的使用
概括這篇博客裡面就來實踐下。在上一篇博客裡面說到了OkHttp類似HttpUrlConnection。按這樣說的話,我們在項目中肯定還是要封裝一層。如果嫌封裝麻煩的話,也
ExoPlayer備忘錄
1.簡述與應用范圍ExpPlayer是一個開源的,App等級的媒體API,它的開源項目包含了library和示例。ExoPlayer相較於MediaPlaye
Android布局之View.measure()動態量取高度並設置布局--(例:動態計算評論高度並顯示)
需求是這樣的:在應用程序的詳情介紹時,有評論的版塊,該頁評論最多顯示5條,而每條最大字數是140個字符,每條評論可能根據字數不同,所占據的高度也不一樣,如有的是1行,有的
Android模擬登錄評論CSDN實現代碼
有時候作為非官方開發的APP集成了官方的所有信息,但是現在需要實現另一個功能那就是登錄發表評論到官方的網站,而非官方的APP並不知道官方網站是怎麼實現登錄與評論的,而且越