編輯:關於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 Listfriends; /** * 根據拼音來排列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 { Listfriends; 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
概括這篇博客裡面就來實踐下。在上一篇博客裡面說到了OkHttp類似HttpUrlConnection。按這樣說的話,我們在項目中肯定還是要封裝一層。如果嫌封裝麻煩的話,也
1.簡述與應用范圍ExpPlayer是一個開源的,App等級的媒體API,它的開源項目包含了library和示例。ExoPlayer相較於MediaPlaye
需求是這樣的:在應用程序的詳情介紹時,有評論的版塊,該頁評論最多顯示5條,而每條最大字數是140個字符,每條評論可能根據字數不同,所占據的高度也不一樣,如有的是1行,有的
有時候作為非官方開發的APP集成了官方的所有信息,但是現在需要實現另一個功能那就是登錄發表評論到官方的網站,而非官方的APP並不知道官方網站是怎麼實現登錄與評論的,而且越