书中的示例代码:github
本章主要讲了一些关于ListView的优化
- 关于
ViewHolder
的使用:略(这个太简单了)。不过要注意使用ViewHolder
做缓存以后,在getView
的方法中无论这项的每个视图是否需要设置属性(比如TextView
设置的属性可能为null
,item
的某一个按钮的背景为透明、某一项的颜色为透明等),都需要为每一项的所有视图设置属性(textview
的属性为空也需要设置setText("")
,背景透明也需要设置),否则在滑动的过程中会出现内容的显示错乱。
2.设置项目间的分割线:
android:divider="@android:color/darker_gray" <!--分割线背景-->
android:dividerHeight="5dp" <!--分割线高度-->
如果不需要分割线可以如此:
android:divider="@null"
3.滚动条的隐藏:
android:scrollbars="none"
4.取消item
的点击效果:
android:listSelector="#00000000" <!--设置为透明-->
5.设置ListView
显示第几项:
listView.setSelection(n);//这个方法和scollTo类似,是瞬间完成移动
listView.smoothScrollBy(distance,duration);//下面三个为平滑移动
listView.smoothScrollByOffset(offset);
listView.smoothScrollToPosition(index);
6.遍历ListView
中所有item
:
for(int i=0,i<listView.getChildCount();i++){
View view = listView.getChildAt(i);
}
7.处理空ListView
:
先是layout
的布局设置:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent" >
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" >
</ListView>
<ImageView
android:id="@+id/empty_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:src="@drawable/ic_launcher" />
</FrameLayout>
然后是代码中:
listView.setEmptyView(findViewById(R.id.empty_view));
//设置以后ListView会根据是否有数据来判断是否显示该EmptyView。
8.ListView
滑动监听:
-
OnTouchListener
:
listView.setOnTouchListener(new OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// 触摸时操作
break;
case MotionEvent.ACTION_MOVE:
// 移动时操作
break;
case MotionEvent.ACTION_UP:
// 离开时操作
break;
}
return false;
}
});
-
OnScrollListener
:
listView.setOnScrollListener(new OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState) {
case OnScrollListener.SCROLL_STATE_IDLE:
//滑动停止时
break;
case OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
//正在滚动时
break;
case OnScrollListener.SCROLL_STATE_FLING:
//手指快速滑动,手指离开后会因惯性继续滑动时
break;
}
}
@Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
//firstVisibleItem 当前能看到的第一个item的id,从0开始,包括显示不完整的
//visibleItemCount 当前能看见的item总数,包括显示不完整的
//totalItemCount listview的item总数
//滚动时一直调用
if(firstVisibleItem+visibleItemCount==totalItemCount&&totalItemCount>0){
//滚动至最后一行
}
if(firstVisibleItem> lastVisibleItem){
//上滑
}else if(firstVisibleItem< lastVisibleItem){
//下滑
}
lastVisibleItem=firstVisibleItem;//自行定义一个lastVisibleItem的成员变量
listView.getFirstVisiblePosition();
listView.getLastVisiblePosition();//当然也可以通过ListView提供的方法直接获取
}
});
9.具有弹性的ListView
:
private int mMaxOverDistance=20;
/*
* 继承ListView重写overScrollBy,然后根据自己需求将maxOverScrollY设置为某个值即可,也可利用代码获取屏幕密度与该值相乘来适配屏幕
*/
@Override
protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY,
int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) {
return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX,
mMaxOverDistance, isTouchEvent);
}
10.自动显示和隐藏布局的ListView
:
public class MainActivity extends AppCompatActivity {
private Toolbar mToolbar;
private ListView mListView;
private String[] mStr = new String[20];
private int mTouchSlop;
private float mFirstY;
private float mCurrentY;
private int direction;
private ObjectAnimator mAnimator;
private boolean mShow = true;
View.OnTouchListener myTouchListener = new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
mFirstY = event.getY();
break;
case MotionEvent.ACTION_MOVE:
mCurrentY = event.getY();
if (mCurrentY - mFirstY > mTouchSlop) {
direction = 0;// down
} else if (mFirstY - mCurrentY > mTouchSlop) {
direction = 1;// up
}
if (direction == 1) {
if (mShow) {
toolbarAnim(1);//show
mShow = !mShow;
}
} else if (direction == 0) {
if (!mShow) {
toolbarAnim(0);//hide
mShow = !mShow;
}
}
break;
case MotionEvent.ACTION_UP:
break;
}
return false;
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
mToolbar = (Toolbar) findViewById(R.id.tool_bar);
mListView = (ListView) findViewById(R.id.lv);
for (int i = 0; i < mStr.length; i++) {
mStr[i] = "Item " + i;
}
View header = new View(this);
header.setLayoutParams(new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
(int) getResources().getDimension(R.dimen.abc_action_bar_default_height_material)));
mListView.addHeaderView(header);
mListView.setAdapter(new ArrayAdapter<String>(
this,
android.R.layout.simple_list_item_1,
mStr));
mListView.setOnTouchListener(myTouchListener);
}
private void toolbarAnim(int flag) {
if (mAnimator != null && mAnimator.isRunning()) {
mAnimator.cancel();
}
if (flag == 0) {
mAnimator = ObjectAnimator.ofFloat(mToolbar, "translationY", mToolbar.getTranslationY(), 0);
} else {
mAnimator = ObjectAnimator.ofFloat(mToolbar, "translationY", mToolbar.getTranslationY(), -mToolbar.getHeight());
}
mAnimator.start();
}
}
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MainActivity" >
<android.widget.Toolbar
android:id="@+id/tool_bar"
android:layout_width="match_parent"
android:layout_height="@dimen/abc_action_bar_default_height_material"
android:background="#fff332" />
<ListView
android:id="@+id/lv"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</FrameLayout>
11.聊天ListView
,这里直接贴关键代码:
这里主要重写BaseAdapter
中的几个方法
@Override
public int getViewTypeCount() {
return 2;
}
@Override
public int getItemViewType(int position) {
return mChatBeanList.get(position).getType();
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder viewHolder;
ChatBean chatBean = mChatBeanList.get(position);
if (convertView == null) {
viewHolder = new ViewHolder();
if (getItemViewType(position) == 0) {// 不重写方法直接用ChatBean里的type比较也一样
convertView = View.inflate(getActivity(), R.layout.chat_item_in, null);
viewHolder.tv = (TextView) convertView.findViewById(R.id.tv_in_text);
} else {
convertView = View.inflate(getActivity(), R.layout.chat_item_out, null);
viewHolder.tv = (TextView) convertView.findViewById(R.id.tv_out_text);
}
convertView.setTag(viewHolder);
} else {
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.tv.setText(chatBean.getText());
return convertView;
}
12动态改变ListView
布局:
//在BaseAdapter里增加一个currentItem属性,通过它与position比较来判断哪个被选中
@Override
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout layout = new LinearLayout(getActivity());
layout.setOrientation(LinearLayout.VERTICAL);
if(currentItem==position){
layout.addView(addFocusView());
}else{
layout.addView(addNormalView(position));
}
return layout;
}
//在ListView的item点击事件设置点击中的item并且刷新ListView数据
mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
mAdapter.setCurrentItem(position);
mAdapter.notifyDataSetChanged();
}
});