一、ListView圓角:重寫ListView的onInterceptTouchEvent方法,通過pointToPosition(x,y)方法判斷當前點擊位置所對應的項,有三種情況:分別是第一項、最后一項、中間項;其實第一種情況又分為兩種情況:列表總共只有一項和列表不止一項;參照下邊的代碼即可理解;
@Override public boolean onInterceptTouchEvent(MotionEvent ev) { switch (ev.getAction()) { case MotionEvent.ACTION_DOWN: int x = (int) ev.getX(); int y = (int) ev.getY(); int itemnum = pointToPosition(x, y); if (itemnum == AdapterView.INVALID_POSITION) break; else{ if(itemnum==0){ // 選擇項為1 if(itemnum==(getAdapter().getCount()-1)){// 列表只有一項 setSelector(R.drawable.app_list_corner_round); }else{ // 列表不止一項 setSelector(R.drawable.app_list_corner_round_top); } }else if(itemnum==(getAdapter().getCount()-1)) // 選擇項為最后一項 setSelector(R.drawable.app_list_corner_round_bottom); else{ setSelector(R.drawable.app_list_corner_shape); } } break; case MotionEvent.ACTION_UP: break; }
二、選中項高亮顯示,實現ListView選中項高亮顯示有兩種方法;
1、查看ListView的方法setSelection(int index),看樣子好像是,但實際試了之后以現不行,既然不行,那就用一個比較笨的方法:自定義adapter,在adapter的getView方法里面設置背景;然后在ListView的onItemClick回調中向adapter傳入點擊的項,即可設置選中項高亮狀態;
public void setSelectedposition(int selectedposition) { this.selectedposition = selectedposition; } @Override public View getView(int position, View convertView, ViewGroup parent) { View v = inflater.inflate(R.layout.item, null); if (selectedposition == position) { v.setBackgroundColor(Color.parseColor("#36adcf")); } else { v.setBackgroundColor(Color.TRANSPARENT); } return v; }
2、有沒有更簡單的方法?第一種方法看着覺得有點怪怪的,有點歪門邪道的感覺;另一種的方法是通過selector方法實現;定義一個drawable文件,里面包含一個選擇狀態:state_activated,然后將這個drawable指定為ListView的item的背景;關於這個狀態具體含意不再多說,參見官方api;
<?xml version="1.0" encoding="utf-8"?> <selector xmlns:android="http://schemas.android.com/apk/res/android"> <item android:drawable="@drawable/adcf" android:state_activated="true"></item> <item android:drawable="@android:color/transparent"/> </selector>
下面是在代碼中的實現(部分代碼):
listview.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
@Override protected void onListItemClick(ListView l, View v, int position, long id) { getListView().setItemChecked(position, true); }
注:上面的方法是ListActivity的方法,此方法對應OnItemClickListener接口中的方法;
三、ListView列表項延遲加載
我們都知道當ListView上面滾動時,會執行adapter的getView方法,但問題出來了,有時候為了節約資源及解決效率問題,並不需要滾動到某一項就開始加載數據,而是希望滾動停止,或者說滾動速度低於某一個值時才獲取數據,下面討論的就是這種情況下的處理:
1、為ListView添加OnScrollListener滾動監聽器,此監聽器可以獲取當前滾動狀態及可視條目;
public int first = 0; // 第一條可視項目 public int visi = 0; // 可視條目總數 public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) { first = firstVisibleItem; visi = visibleItemCount; } public void onScrollStateChanged(AbsListView view, int scrollState) { switch (scrollState) { case OnScrollListener.SCROLL_STATE_IDLE: // 滾動完成 mBusy = false; int first = view.getFirstVisiblePosition(); for (int i = 0; i < visi; i++) { // 設置所有可視條目Tag為null,條目將會自動加載數據 TextView t = (TextView) view.getChildAt(i); if (t.getTag() != null) { t.setText(mStrings[first + i]); t.setTag(null); } }break; case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL: // 正在滾動(手指未離開屏幕) mBusy = true; break; case OnScrollListener.SCROLL_STATE_FLING: // 正在滾動(手指已離開屏幕) mBusy = true; break; } }
在Adapter中根據mBusy狀態,判斷是否立即加載數據,重寫getView方法;
public View getView(int position, View convertView, ViewGroup parent) { TextView text; if (convertView == null) { text = (TextView) mInflater.inflate(android.R.layout.simple_list_item_1, parent, false); } else { text = (TextView) convertView; } if (!mBusy) { text.setText(mStrings[position]); // Tag為空代表當前View的數據已經加載完成 text.setTag(null); } else { text.setText("Loading..."); // 非空的View代表當前View仍然需要加載更多的數據 text.setTag("cc"); } return text; } }
四、類似聊天記錄,自動將新添加的項從底部添加並顯示到可見區域,主要是兩個屬性的設置,如下:
<ListView android:id="@android:id/list" android:layout_width="match_parent" android:layout_height="0dip" android:layout_weight="1" android:stackFromBottom="true" 自動顯示到列表最底部 android:transcriptMode="normal" /> 設置當顯示大量的條目時,希望將最新的item顯示到可見區域
順便記錄一下輸入框點擊Enter事件,OnKeyListener(參照下面的代碼,可實現輸入新消息按下Enter並將新消息添加到列表底部);
public boolean onKey(View v, int keyCode, KeyEvent event) { if (event.getAction() == KeyEvent.ACTION_DOWN) { switch (keyCode) { case KeyEvent.KEYCODE_DPAD_CENTER: case KeyEvent.KEYCODE_ENTER: // TODO return true; } } return false; }
五、設置部分條目不可用:通常情況下,一個列表中的所有條目都可以使用,也即可以響應點擊事件和有點擊的效果,但有的情況下,並不希望所有的條目都這樣,就可以自定義Adapter,通過條目的相關屬性判斷是否可用,(disEnabled狀態下,默認的分割條不顯示)
@Override public boolean areAllItemsEnabled() { return false; } @Override public boolean isEnabled(int position) { // TODO 通過一定的判斷條件設置position條目可用 }
六、ListView點擊條目展開條目顯示更多信息:這種情況也是比較常見的,最多見的應該是評論,一個評論列表,不同的人,評論的內容長度不同,如果全部顯示完,就會顯示得參差不齊,但又不能只顯示了部分,此時就得添加點擊條目查看更多的功能了;
簡單說下思路:自定義一個View,繼承自FrameLayout,里面有兩個TextView,一個是單行的(singleLine),另外一個是多行的(wrap_content);公開一個方法設置多行的TextView的可見狀態為VISIBLE或者GONE,在ListView的ItemClick回調中設置當列表條目未展開時,點擊條目展開條目,也即顯示更多信息;當列表是展開的,點擊收起條目;並且通知數據源改變:notifyDataSetChanged;
七、在二中說了選中單項的高亮顯示,現在來看看多選的高亮顯示,同樣,多選我這里也知道兩種方法;
1、同二中第2種方法,定義一個selector,里面包含一個選擇狀態state_activited,將此drawable設置為ListView的selector或者是直接設置為條目的背景;此種模式類似於4.0之后部分手機短信列表的長按之后進行選擇條目然后進行操作刪除的效果;
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL); // 設置選擇模式 lv.setMultiChoiceModeListener(new ModeCallback());
private class ModeCallback implements ListView.MultiChoiceModeListener { public boolean onCreateActionMode(ActionMode mode, Menu menu) { // 在ActionBar上創建按鈕 MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.list_select_menu, menu); mode.setTitle("Select Items"); return true; } public boolean onPrepareActionMode(ActionMode mode, Menu menu) { return true; } public boolean onActionItemClicked(ActionMode mode, MenuItem item) { switch (item.getItemId()) { // TODO 點擊某一個按鈕的回調 } return true; } public void onDestroyActionMode(ActionMode mode) { } public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) { // 選擇項改變回調事件,通過此方法獲取選擇了哪些項 final int checkedCount = getListView().getCheckedItemCount(); } } }
2、第二種方法比較簡單些,適用范圍更加廣泛,上一種方法在2.x的版本上運行會出錯,因為MultiChoiceMode是高版本上的新的一個接口;對於低版本的手機,下面這種方法更加適用(Item的背景或者selector同上);
lv.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
如果需要獲取此種方法的選擇項,就需要在OnItemClickListener的回調方法中進行判斷,即每點擊一項,改變當前checked的值,最后再獲取結果;
未完待續。。。