編輯:關於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 ListmContactList; 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(); ListcontactList = 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>
最後看下實現的效果圖
ELF是類Unix類系統,當然也包括Android系統上的可執行文件格式(也包括.so和.o類文件)。可以理解為Android系統上的exe或者dll文件&
Android中實現圓角矩形和圓形有很多種方式,其中最常見的方法有ImageLoader設置Option和自定義View。 1.ImageLoader加載圖片public
Android基礎入門教程——2.4.5 ListView之checkbox錯位問題解決標簽(空格分隔): Android基礎入門教程本節引言:
java的數據類型分為基本數據類型和引用數據類型。 基本數據類型分為數值型、字符型(char)、布爾型(boolean) 數值型變量 1、整