編輯:關於Android編程
這樣的一個控件實現起來不難,需要對自定義view有一定的基礎,也要了解怎麼實現一個集合的排序。大體思路很簡單。
首先完成view的基本繪制以及相關的內部邏輯。 其次,就是要對聯系人數據進行排序,即姓名首字母按26個英文字母進行排序,說到排序不得不說的兩個接口Comparable和Comparator。利用這兩個接口中的一個,我們可以很方便的實現集合排序的功能。 接下來就可以使用ListView來展示聯系人數據了,關於ListView的使用相信大家都已經十分的熟練了,也就不多說了。 最後要實現點擊導航欄字母使ListView滾動到相應的位置,編寫一個相關回調接口即可。原理還是比較簡單的,有了思路,做事情就會事半功倍。接下來,首先考慮自定義導航欄的實現,其實就是需要繪制一個#號和26個英文字母豎直排列的View。接下來看這樣的View該如何實現。
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.TextView;
import cn.manchester.app.contactsnav.R;
/**
* 右側導航欄字母列表
*
* @author Administrator
*/
public class SideBar extends View {
/*
* 默認的導航欄中字母的大小
*/
private static final int DEFAULT_ALPHA_SIZE = 18;
/*
* 字母導航欄選擇時,在中部顯示所選擇字母的TextView
*/
private TextView mDialog;
/*
* 字母導航欄選擇事件回調接口
*/
private OnLetterSelectedListener mOnLetterSelectedListener;
/*
* 字母列表內容
*/
private static final String[] ALPHA_LIST = {"#", "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 int mChoiceIndex;
/*
* 畫筆
*/
private Paint mPaint;
/*
* 默認字母顏色
*/
private static final int DEFAULT_CHARACTER = Color.rgb(33, 66, 99);
/*
* 選中字母顏色
*/
private static final int CHIOCED_CHARACTER = Color.parseColor("#FF0000");
public SideBar(Context context, AttributeSet attrs) {
super(context, attrs);
mPaint = new Paint();
mPaint.setAntiAlias(true); // 抗鋸齒,字體圓滑
mPaint.setColor(DEFAULT_CHARACTER); // 設置字體顏色
mPaint.setTypeface(Typeface.DEFAULT_BOLD); // 設置字體為粗體
mPaint.setTextSize(sp2px(DEFAULT_ALPHA_SIZE));
}
/**
* 字母導航欄選中事件回調接口
*
* @author Administrator
*/
public interface OnLetterSelectedListener {
/**
* 字母被選中事件回調方法
*
* @param letter 被選中的字母
*/
void onLetterSelected(String letter);
}
/**
* 設置字母導航欄字母被選中時的回調接口
*
* @param listener 接口
*/
public void setOnLetterSelectedListener(OnLetterSelectedListener listener) {
mOnLetterSelectedListener = listener;
}
public int sp2px(float spValue) {
final float fontScale = getContext().getResources().getDisplayMetrics().scaledDensity;
return (int) (spValue * fontScale + 0.5f);
}
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
// 獲取整個SideBar的寬和高
int width = getWidth();
int height = getHeight();
// 平均每行所分配到的高度值
int singleHeight = height / ALPHA_LIST.length;
for (int i = 0; i < ALPHA_LIST.length; i++) {
// 計算每個字母的x坐標
float xPos = (width - mPaint.measureText(ALPHA_LIST[i])) / 2;
// 計算每個字母的y坐標(頂部預留一個singleHeight的高度)
float yPos = singleHeight * (i + 1);
// 如果當前字母是點擊的字母,則改變畫筆顏色
if (i == mChoiceIndex) {
mPaint.setColor(CHIOCED_CHARACTER);
}
// 繪制文本
canvas.drawText(ALPHA_LIST[i], xPos, yPos, mPaint);
// 繪制完成後,將畫筆顏色修改為默認的顏色
mPaint.setColor(DEFAULT_CHARACTER);
}
}
/**
* 觸摸事件
*/
@Override
public boolean dispatchTouchEvent(MotionEvent event) {
// 記錄當前的觸摸動作
int action = event.getAction();
// 獲取當前的y坐標
float yPos = event.getY();
int oldChoice = mChoiceIndex;
// 計算出當前的位置在哪個字母上面
int c = (int) (yPos / getHeight() * ALPHA_LIST.length);
switch (action) {
case MotionEvent.ACTION_UP:
setBackgroundColor(Color.TRANSPARENT);
// 手指彈起,沒有選中任何字母
mChoiceIndex = -1;
invalidate();
if (mDialog != null) {
// 將中部顯示字母的TextView隱藏
mDialog.setVisibility(View.INVISIBLE);
}
break;
default:
// 設置字母導航欄被觸摸時的背景
setBackgroundResource(R.drawable.sidebar_background);
// 如果當前的選擇不是之前的選擇,觸發字母選擇事件的回調接口方法,改變中部TextView中的字母
if (oldChoice != c) {
if (c >= 0 && c < ALPHA_LIST.length) {
// 將選中的字母傳遞給注冊事件的監聽者
if (mOnLetterSelectedListener != null) {
mOnLetterSelectedListener.onLetterSelected(ALPHA_LIST[c]);
}
if (mDialog != null) {
mDialog.setVisibility(View.VISIBLE);
mDialog.setText(ALPHA_LIST[c]);
}
}
mChoiceIndex = c;
invalidate();
}
break;
}
return true;
}
/**
* 綁定中部顯示字母的TextView控件
*
* @param tvDialog
*/
public void setDialog(TextView tvDialog) {
if (tvDialog == null) {
throw new NullPointerException("tvDialog can not be null...");
}
this.mDialog = tvDialog;
}
}
首先需要獲取屏幕的寬高,然後根據高度再決定每一行的高度並且計算每個字符的x,y坐標,就可以繪制出一個豎直排列的字母組合了。有了外觀接著就要實現view與用戶的交互,首先攔截用戶的觸摸事件,判斷事件類型,當用戶手指抬起的時候表示view要恢復原樣,其它情況下,表示用戶正在按下某個字母,再根據觸摸的位置從而可以計算出用戶具體按下的字母,然後實現自己的業務邏輯即可。
至於對集合排序的實現,則需要借助於Comparable和Comparator接口。這兩個接口都可以實現排序的功能,至於選擇哪個則看具體需求而定了。下面分別展示兩個接口具體的使用方法。
ListpersonList = new ArrayList<>(); Random random = new Random(); for (int i = 0; i < 5; i++) { Person p = new Person(); p.setName(String.valueOf(i)); p.setAge(random.nextInt(5)); personList.add(p); } // Collections.sort(personList); Collections.sort(personList, new AgeComparator()); System.out.println(personList);
打印的結果信息
[Person{name='1', age=0}, Person{name='3', age=0}, Person{name='0', age=1}, Person{name='2', age=1}, Person{name='4', age=2}]
首先看讓Object實現Comparable接口的方式
public class Person implements Comparable{ private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; } @Override public int compareTo(Person o) { if (age < o.getAge()) { return -1; } else if (age == o.getAge()) { return 0; } return 1; } }
這裡要關注的點就是compareTo方法的返回值,如果當前對象的age小於要比較的對象並且返回-1,相等時返回0,大於時返回1,則表示排序的規則為升序。接下來看實現Comparator接口的方式。
public class AgeComparator implements Comparator{ @Override public int compare(Person o1, Person o2) { if (o1.getAge() < o2.getAge()) { return -1; } else if (o1.getAge() == o2.getAge()) { return 0; } return 1; } }
這樣就可以完成相關數據排序的功能了。僅僅是這樣還是不夠的,對於ListView的Adapter還需要一些特殊的處理,具體代碼如下。
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Locale;
public class SortAdapter extends BaseAdapter {
private static final String TAG = SortAdapter.class.getSimpleName();
private List mContactList;
public SortAdapter() {
mContactList = new ArrayList<>();
}
public void addData(List list) {
mContactList.addAll(list);
Collections.sort(mContactList, new PinYinComparator());
notifyDataSetChanged();
}
@Override
public int getCount() {
return mContactList.size();
}
@Override
public Object getItem(int position) {
return mContactList.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder;
Contact conatct = mContactList.get(position);
if (convertView == null) {
convertView = LayoutInflater.from(parent.getContext()).inflate(R.layout.listview_contact, parent, false);
holder = new ViewHolder(convertView);
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
// 獲取當前聯系人的首字母
char headerChar = getHeaderCharByPosition(position);
// 獲取當前聯系人首字母第一次出現的位置
int index = getFirstPositionByHeaderChar(headerChar);
// 如果當前聯系人首字母第一次出現的位置等於當前的位置,則表示該聯系人是該首字母下出現的第一個聯系人
if (position == index) {
holder.mCharCategoryText.setVisibility(View.VISIBLE);
holder.mCharCategoryText.setText(conatct.getHeaderChar());
holder.mDivider.setVisibility(View.GONE);
} else {
// 默認設置字母欄不顯示
holder.mCharCategoryText.setVisibility(View.GONE);
holder.mDivider.setVisibility(View.VISIBLE);
}
holder.mNameText.setText(conatct.getName());
return convertView;
}
/**
* 通過聯系人的位置獲取該聯系人的名稱的首字母
*
* @param position
* @return 首字母
*/
private char getHeaderCharByPosition(int position) {
return mContactList.get(position).getHeaderChar().toUpperCase().charAt(0);
}
/**
* 通過首字母獲取顯示該首字母的第一個聯系人的位置:比如C,陳奕迅
*
* @param c
* @return 位置,如果返回-1表示未查找到該字母
*/
public int getFirstPositionByHeaderChar(char c) {
for (int i = 0; i < getCount(); i++) {
String headerChar = mContactList.get(i).getHeaderChar();
char firstChar = headerChar.toUpperCase(Locale.CHINA).charAt(0);
if (firstChar == c) {
return i;
}
}
return -1;
}
private class ViewHolder {
TextView mCharCategoryText;
TextView mNameText;
View mDivider;
ViewHolder(View view) {
mCharCategoryText = (TextView) view.findViewById(R.id.tv_header_char);
mNameText = (TextView) view.findViewById(R.id.tv_name);
mDivider = view.findViewById(R.id.divider);
}
}
}
ListView的item布局文件如下
基本的代碼已經完成,最後再MainActivity中創建一些測試的數據。
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.widget.ListView;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import cn.manchester.app.contactsnav.view.SideBar;
public class MainActivity extends AppCompatActivity implements SideBar.OnLetterSelectedListener {
private static final String TAG = MainActivity.class.getSimpleName();
ListView mListContact;
SideBar mSideBar;
TextView mTextChar;
String[] chinese = new String[]{"1234", "2345", "432143", "532", "6", "431", "98", "78", "89", "趙發", "錢去", "孫我", "李想", "一額", "發啊", "去的",
"額啊", "范圍", "是的", "干活", "干活", "干活", "干活", "干活", "干活", "干活",
"如圖", "後台", "熱突然", "未熱", "無法", "那就"};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mListContact = (ListView) findViewById(R.id.lv_contacts_list);
mSideBar = (SideBar) findViewById(R.id.side_bar);
mTextChar = (TextView) findViewById(R.id.tv_dialog);
mListContact.setAdapter(new SortAdapter());
mSideBar.setOnLetterSelectedListener(this);
Random random = new Random();
List contactList = new ArrayList<>();
for (int i = 0; i < chinese.length; i++) {
Contact contact = new Contact();
contact.setName(chinese[i]);
contactList.add(contact);
}
((SortAdapter) mListContact.getAdapter()).addData(contactList);
mSideBar.setDialog(mTextChar);
}
@Override
public void onLetterSelected(String letter) {
mTextChar.setText(letter);
SortAdapter adapter = (SortAdapter) mListContact.getAdapter();
int firstPosition = -1;
char c = letter.charAt(0);
do {
firstPosition = adapter.getFirstPositionByHeaderChar(c);
if (firstPosition == -1) {
c++;
}
} while (firstPosition == -1);
mListContact.setSelection(firstPosition);
Log.e(TAG, String.valueOf(c) + " pos=" + firstPosition);
}
}
MainActivity的布局文件
<framelayout android:layout_height="match_parent" android:layout_width="match_parent"> </framelayout>
最後看下實現的效果圖

Android漫游記(2)---ELF可執行文件格式
ELF是類Unix類系統,當然也包括Android系統上的可執行文件格式(也包括.so和.o類文件)。可以理解為Android系統上的exe或者dll文件&
Android實現圓角矩形和圓形ImageView的方式
Android中實現圓角矩形和圓形有很多種方式,其中最常見的方法有ImageLoader設置Option和自定義View。 1.ImageLoader加載圖片public
Android基礎入門教程——2.4.5 ListView之checkbox錯位問題解決
Android基礎入門教程——2.4.5 ListView之checkbox錯位問題解決標簽(空格分隔): Android基礎入門教程本節引言:
Java4Android開發教程(五)java的基本數據類型特征
java的數據類型分為基本數據類型和引用數據類型。 基本數據類型分為數值型、字符型(char)、布爾型(boolean) 數值型變量 1、整