Android ListView
專門(mén)用于處理那種內(nèi)容元素很多,手機(jī)屏幕無(wú)法展示出所有內(nèi)容的情況剃氧。ListView可以使用列表的形式來(lái)展示內(nèi)容,超出屏幕部分的內(nèi)容只需要通過(guò)手指滑動(dòng)就可以移動(dòng)到屏幕內(nèi)了阻星。
ListView屬性
設(shè)置分割線
- android:divider朋鞍,設(shè)置分割線風(fēng)格,可以是顏色妥箕,也可以是圖片滥酥。當(dāng)不需要分割線時(shí),賦值@null即可畦幢。
- android:dividerHeight坎吻,設(shè)置分割線高度。
滾動(dòng)條設(shè)置
android:scrollbars,通過(guò)該屬性可以設(shè)置滾動(dòng)條狀態(tài)宇葱。不需要滾動(dòng)條時(shí)瘦真,賦值none刊头。
取消Item點(diǎn)擊效果
android:listSelector,賦值為#00000000(color ARGB),或者@android:color/transparent诸尽。
設(shè)置ListView Item顯示位置
默認(rèn)顯示第一個(gè)原杂,調(diào)用
ListView.setSelection(pos)從指定item開(kāi)始顯示。
但是此方法是瞬間完成滾動(dòng)操作弦讽,可以使用以下三種方法實(shí)現(xiàn)平滑滾動(dòng):
- smoothScrollBy(distance, duration),指定滾動(dòng)距離污尉,distance的正,負(fù)決定滾動(dòng)的方向(正值向上往产,負(fù)值向下)被碗。滾動(dòng)速度有duration決定,即滾動(dòng)時(shí)間仿村。
- smoothScrollByOffset(offset),方法參數(shù)是指現(xiàn)在顯示的第一個(gè)item視圖的偏移量,負(fù)號(hào)代表向上移動(dòng),正號(hào)代表向下移動(dòng).
- smoothScrollToPosition(pos),平滑移動(dòng)到指定的position item.
ListView基礎(chǔ)用法
動(dòng)態(tài)修改ListView
在某些情況下锐朴,ListView的數(shù)據(jù)更新了,那么就需要?jiǎng)討B(tài)的修改ListView item的顯示蔼囊》僦荆可以調(diào)用Adapter.notifyDataSetChanged()方法。代碼如下:
mList.add("new");
mAdapter.notifyDataSetChanged();
mList是BaseAdapter的數(shù)據(jù)畏鼓。更新它之后調(diào)用notifyDataSetChanged()更新視圖酱酬。注意調(diào)用notifyDataSetChanged()之后是更新了整個(gè)ListView的所有item視圖,也就是重新繪制了ListView云矫,所以效率有點(diǎn)差膳沽,可以試試單獨(dú)更新某個(gè)item。
遍歷item
當(dāng)需要遍歷ListView的item時(shí)让禀,可以使用getFirstVisiblePosition(),getChildAt(),getChildCount()等方法挑社。不過(guò)需要注意:
- getFirstVisiblePosition(),返回的是當(dāng)前屏幕上顯示的第一個(gè)item(包括不完整的)在所有item中的位置index巡揍。
- getChildCount()痛阻,返回當(dāng)前屏幕顯示的item數(shù)量。
- getChildAt()腮敌,返回指定位置的item視圖阱当。指定位置的范圍不能超過(guò)當(dāng)前屏幕顯示的數(shù)量,因?yàn)榉祷氐囊晥D是在當(dāng)前顯示的item中的糜工。
通過(guò)以上三種方法斗这,更進(jìn)一步了解了視圖緩存機(jī)制。ListView并沒(méi)有給所有數(shù)據(jù)項(xiàng)創(chuàng)建item視圖啤斗,只給需要顯示的創(chuàng)建表箭。
item.getTop()
當(dāng)通過(guò)item視圖調(diào)用getTop()時(shí),返回的坐標(biāo)是以item左上角點(diǎn)在以ListView左上角為原點(diǎn)構(gòu)建的坐標(biāo)系的Y坐標(biāo)彼水。
空ListView
當(dāng)列表沒(méi)有數(shù)據(jù)時(shí)凤覆,可以設(shè)置一個(gè)提示用戶的View盯桦。通過(guò)ListView.setEmptyView()來(lái)設(shè)置拥峦。
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_empty_list_view);
initView();
}
private void initView() {
mListView = (ListView) findViewById(R.id.id_list_view_empty);
mEmptyImg = (ImageView) findViewById(R.id.img_empty_view);
mListView.setEmptyView(mEmptyImg);
mList = new ArrayList<>();
mAdapter = new ViewHolderAdapter(mList, this);
mListView.setAdapter(mAdapter);
}
public void btnAddItem(View view) {
mList.add("new");
mAdapter.notifyDataSetChanged();
mListView.smoothScrollToPosition(mList.size() - 1);
}
布局代碼
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ListView
android:id="@+id/id_list_view_empty"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"/>
<ImageView
android:id="@+id/img_empty_view"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:src="@mipmap/ic_launcher"/>
<Button
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:onClick="btnAddItem"
android:text="@string/add_item"/>
</LinearLayout>
效果:
滑動(dòng)監(jiān)聽(tīng)
OnTouchListener
mListView.setOnTouchListener(new View.OnTouchListener() {
int position = 0;
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
//觸摸時(shí)操作
break;
case MotionEvent.ACTION_MOVE:
//移動(dòng)時(shí)操作
break;
case MotionEvent.ACTION_UP:
//手指離開(kāi)時(shí)操作
//用getTop()方法獲取到的坐標(biāo)是相對(duì)于父控件坐標(biāo)系的坐標(biāo).
position = mListView.getChildAt(0).getTop();
mView.setText("顯示的第一個(gè)Item在ListView中的坐標(biāo):" + position);
break;
}
return false;
}
});
OnScrollListener
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
int pos = 0;
int lastVisibleItemPos = 0;
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch(scrollState) {
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
//滑動(dòng)停止時(shí)
pos = mListView.getChildAt(0).getTop();
mTextView.setText("顯示的第一個(gè)Item在ListView中的坐標(biāo):" + pos);
break;
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
//正在滾動(dòng)時(shí)
mTextView.setText("正在滑動(dòng)...");
break;
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
//手指拋動(dòng)時(shí),手指用力滑動(dòng)后,手指離開(kāi)屏幕ListView由于慣性繼續(xù)滑動(dòng)
mTextView.setText("漂移中...");
break;
default:
break;
}
}
//滾動(dòng)時(shí)一直調(diào)用
//firstVisibleItem當(dāng)前顯示的第一個(gè)item在所有item中的index
//visibleItemCount當(dāng)前顯示的item數(shù)量
//totalItemCount所有item 數(shù)量
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//判斷滑動(dòng)方向
if(firstVisibleItem > lastVisibleItemPos) {
//判斷是否滾動(dòng)到最后一項(xiàng)
if(firstVisibleItem + visibleItemCount == totalItemCount
&& totalItemCount > 0) {
Log.d(TAG, "滾動(dòng)到了最后一個(gè)item");
}
Log.d(TAG, "上滑");
}else if(firstVisibleItem < lastVisibleItemPos) {
Log.d(TAG, "下滑");
}
lastVisibleItemPos = firstVisibleItem;
}
});
ListView點(diǎn)擊事件
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
// lastScrollY = mListView.getScrollY();
// Log.d(TAG, lastScrollY + "");
lastScrollY = getScrollY();
Log.d(TAG, lastScrollY + "");
mTextView.setText(String.valueOf(lastScrollY));
Intent intent = new Intent(RestoreListView.this, JumpActivity.class);
startActivity(intent);
mListView.setSelection(0);
}
});
ListView基礎(chǔ)優(yōu)化(ViewHolder)
優(yōu)化的原理是基于ListView的RecycleBin機(jī)制
ViewHolder的優(yōu)化就是利用了ListView 的視圖緩沖機(jī)制诫舅,一般情況下代碼都差不多羽利,關(guān)鍵在于BaseAdapter。所以可以參照以下模版來(lái)寫(xiě)刊懈。
BaseAdapter代碼
public class ViewHolderAdapter extends BaseAdapter {
private List<String> mData;
private LayoutInflater mInflater;
public ViewHolderAdapter(List<String> data, Context context) {
mData = data;
mInflater = LayoutInflater.from(context);
}
@Override
public int getCount() {
return mData.size();
}
@Override
public Object getItem(int position) {
return mData.get(position);
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
//判斷是否緩存
if (convertView == null) {
holder = new ViewHolder();
//通過(guò)LayoutInflater實(shí)例化布局
convertView = mInflater.inflate(R.layout.viewholder_item, null);
holder.img = (ImageView) convertView.findViewById(R.id.img_view_holder);
holder.title = (TextView) convertView.findViewById(R.id.tv_view_holder);
convertView.setTag(holder);
} else {
//通過(guò)Tag找到緩存布局
holder = (ViewHolder) convertView.getTag();
}
//設(shè)置布局中空間要顯示的視圖
holder.img.setBackgroundResource(R.mipmap.ic_launcher);
holder.title.setText(mData.get(position));
return convertView;
}
public class ViewHolder {
public ImageView img;
public TextView title;
}
}
代碼分析,在上面的模版中看到覆寫(xiě)了幾個(gè)方法:
-
getCount()
,返回item數(shù)量 -
getItem(int position)
,返回指定位置的item -
getItemId(int position)
,返回指定位置item的行號(hào) -
getView()
这弧,返回顯示的item view
整個(gè)優(yōu)化的關(guān)鍵在于ViewHolder模式充分利用了ListView的視圖緩存機(jī)制,避免每一次調(diào)用getView()時(shí)都去實(shí)例化item布局俏讹,并且調(diào)用findViewById()實(shí)例化控件。
ListView進(jìn)階用法
ListView位置恢復(fù)
有兩種方法畜吊,第一種是在監(jiān)聽(tīng)ListView時(shí),通過(guò)ListView.getScrollY()方法獲取最終滾動(dòng)的Y坐標(biāo)殉疼,然后調(diào)用smoothScrollBy()方法恢復(fù)礼预;第二種是通過(guò)記錄當(dāng)前ListView顯示的第一個(gè)Item的index柒巫,調(diào)用smoothToPosition()來(lái)恢復(fù)城豁。第一中方法完全不可行漩绵,因?yàn)榈玫降挠肋h(yuǎn)都是0(通過(guò)ListView調(diào)用getScrollY()方法,得到的坐標(biāo)是ListView左上角在以ListView父視圖左上角為原點(diǎn)構(gòu)建的坐標(biāo)系中的Y軸坐標(biāo)瘩燥,所以一直是0)。第二種方法不精確。
下面采用以下方法:
public int getScrollY() {
View child = mListView.getChildAt(0);
if(child == null) {
return 0;
}
int top = -child.getTop();
int firstVisibleItemPos = mListView.getFirstVisiblePosition();
return top + firstVisibleItemPos * child.getHeight();
}
當(dāng)然這是一種理想狀態(tài)企软,默認(rèn)為所有item的高度都相等。
恢復(fù)ListView
mListView.post(new Runnable() {
@Override
public void run() {
mListView.smoothScrollBy(scrolledY, 0);
}
});
動(dòng)態(tài)改變ListView布局
有兩種方案:一種是兩種布局都寫(xiě)苇倡,通過(guò)控制布局的顯示隱藏來(lái)達(dá)到切換布局的效果胜嗓;另一種是在getView()時(shí)通過(guò)判斷來(lái)選擇加載不同的布局寥粹。以下主要以第二種方案來(lái)實(shí)現(xiàn)媚狰。
關(guān)鍵思想,要獲取不同的布局肯定要覆寫(xiě)getView()方法。而ListView提供了兩種方法,封裝好了布局的判斷:
- getItemViewType()赋兵,獲取指定位置item的布局類型拯田。
- getViewTypeCount()帕膜,獲取不同布局的總數(shù)张弛。
關(guān)鍵代碼
@Override
public int getItemViewType(int position) {
ChatItemListViewBean bean = mData.get(position);
return bean.getType();
}
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder holder = null;
if(convertView == null) {
holder = new ViewHolder();
if(getItemViewType(position) == 0) {
convertView = mInflater.inflate(R.layout.chat_item_in, null);
holder.mIcon = (ImageView) convertView.findViewById(R.id.id_img_chat_item_in);
holder.mTv = (TextView) convertView.findViewById(R.id.id_tv_chat_item_in);
}else {
convertView = mInflater.inflate(R.layout.chat_item_out, null);
holder.mIcon = (ImageView) convertView.findViewById(R.id.id_img_chat_item_out);
holder.mTv = (TextView) convertView.findViewById(R.id.id_tv_chat_item_out);
}
convertView.setTag(holder);
}else {
holder = (ViewHolder) convertView.getTag();
}
holder.mIcon.setImageBitmap(mData.get(position).getIcon());
holder.mTv.setText(mData.get(position).getText());
return convertView;
}
public class ViewHolder{
public ImageView mIcon;
public TextView mTv;
}
效果:
雜技
獲取ActionBar高度
getResources().getDimensionPixelOffset(
android.support.v7.appcompat.R.dimen.abc_action_bar_default_height_material
)
android.support.v7.appcompat.R.dimen.abc_action_bar_default_height_material是V7包內(nèi)的actionBar height 的資源id。
獲取最小滑動(dòng)距離
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
問(wèn)題
- 如何實(shí)現(xiàn)單獨(dú)更新某個(gè)指定的item數(shù)據(jù)
- View.post()
- 如何實(shí)現(xiàn)彈性ListView
- 如何自動(dòng)隱藏和顯示Toolbar
- View.getTranslationY()獲取到的是什么坐標(biāo)
- ListView的適配器御吞,BaseAdapter揍诽,ArrayAdapter...