Android高级进阶学习笔记》第1个知识点:ListView使用技巧
目录
1、ListView常用优化技巧
1.1 使用ViewHolder模式提高效率
1.2 设置项目间分隔线
1.3 隐藏ListView的滚动条
1.4 取消ListView的Item点击效果
1.5 设置ListView需要显示在第几项
1.6 动态修改ListView
1.7 遍历ListView中的所有Item
1.8 处理空ListView
1.9 ListView的滑动监听
1.9.1 OnTouchListener
1.9.2 OnScrollListener
2、ListView常用拓展
2.1 具有弹性的ListView
2.2 自动显示、隐藏布局的ListView
2.3 聊天界面的实现
2.4动态改变ListView布局
1、ListView常用优化技巧
1.1 使用ViewHolder模式提高效率
ViewHolder模式是提高ListView效率的一个重要的方法。ViewHolder模式充分利用了ListView的视图缓存机制,避免了每次在调用getView()的时候都去通过findViewById()实例化控件,完整代码如下
public class NotifyAdapter extends BaseAdapter {
private List mData;
private LayoutInflater mInflater;
public NotifyAdapter(Context context, List data) {
this.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();
// 通过LayoutInflater实例化布局
convertView = mInflater.inflate(R.layout.notify_item, null);
holder.img = (ImageView) convertView.findViewById(R.id.imageView);
holder.title = (TextView) convertView.findViewById(R.id.textView);
convertView.setTag(holder);
} else {
// 通过tag找到缓存的布局
holder = (ViewHolder) convertView.getTag();
}
// 设置布局中控件要显示的视图
holder.img.setBackgroundResource(R.drawable.ic_launcher);
holder.title.setText(mData.get(position));
return convertView;
}
public final class ViewHolder {
public ImageView img;
public TextView title;
}
}
1.2 设置项目间分隔线
-
系统默认
自定义
android:dividerHeight="10dp"
android:divider="@android:color/darker_gray"
- 把分隔线设置透明
android:divider="@null"
1.3 隐藏ListView的滚动条
android:scrollbars="none"
1.4 取消ListView的Item点击效果
5.X以上是水波纹效果,5.X以下是一个改变背景颜色的效果,设置如下所示的listSelector的属性值可以去掉点击效果
android:listSelector="@android:color/transparent"
android:listSelector="#00000000"
1.5 设置ListView需要显示在第几项
ListView以Item为单位进行显示,默认显示在第一个Item,当需要指定具体显示的Item时,可以通过如下代码来实现
//瞬间完成的移动
mListView.setSelection(position);
//实现平滑移动
mListView.smoothScrollByOffset(offset);
mListView.smoothScrollBy(diatance,duration);
mListView.smoothScrollToPosition(index);
1.6 动态修改ListView
//对适配器的数据进行更新
mData.add("new");
mAdapter.notifyDataSetChanged();
注意的是,必须保证传进Adapter的数据List是同一个List而不能是其它对象,否则将无法实现该效果。
完整代码如下:
public class NotifyTest extends Activity {
private List mData;
private ListView mListView;
private NotifyAdapter mAdapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.notify);
mData = new ArrayList();
for (int i = 0; i < 20; i++) {
mData.add("" + i);
}
mListView = (ListView) findViewById(R.id.listView);
mAdapter = new NotifyAdapter(this, mData);
mListView.setAdapter(mAdapter);
}
public void btnAdd(View view) {
//对适配器的数据进行更新
mData.add("new");
mAdapter.notifyDataSetChanged();
mListView.setSelection(mData.size() - 1);
}
}
1.7 遍历ListView中的所有Item
for (int i = 0; i < mListView.getChildCount(); i++) {
View view = mListView.getChildAt(i);
}
1.8 处理空ListView
ListView用于展示列表数据,但当列表中无数据时,ListView不会显示任何数据或者提示,不过ListView提供了一个方法--setEmptyView(),可以通过这个方法我们给ListView设置一个在空数据下显示的默认提示,使用方法如下:
private ListView lvList;
private ImageView ivEmpty;
lvList = (ListView) findViewById(R.id.lv_list);
ivEmpty = (ImageView) findViewById(R.id.iv_empty);
lvList.setEmptyView(ivEmpty);
1.9 ListView的滑动监听
ListView的滑动监听主要包括:OnTouchListener以及OnScrollListener,此外开发者通常还需要使用GestureDetector手势识别、VelocityTracker滑动速度检测辅助类来完成更好的监听。
1.9.1 OnTouchListener
lvList.setOnTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
switch (event.getAction()){
//触摸时操作
case MotionEvent.ACTION_DOWN:
break;
//移动时操作
case MotionEvent.ACTION_HOVER_MOVE:
break;
//离开时操作
case MotionEvent.ACTION_UP:
break;
}
return false;
}
});
1.9.2 OnScrollListener
mListView.setOnScrollListener(new AbsListView.OnScrollListener() {
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
switch (scrollState){
//滑动停止时
case AbsListView.OnScrollListener.SCROLL_STATE_IDLE:
Log.d(TAG,"SCROLL_STATE_IDLE===========");
break;
//正在滑动时
case AbsListView.OnScrollListener.SCROLL_STATE_TOUCH_SCROLL:
Log.d(TAG,"SCROLL_STATE_TOUCH_SCROLL===========");
break;
//手指用力滑动,即是在离开后ListView由于惯性继续滑动
case AbsListView.OnScrollListener.SCROLL_STATE_FLING:
Log.d(TAG,"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总数
//滚动时一直调用
Log.d(TAG,"onScroll=============");
Log.d(TAG,"firstVisibleItem============="+firstVisibleItem);
Log.d(TAG,"visibleItemCount============="+visibleItemCount);
Log.d(TAG,"totalItemCount============="+totalItemCount);
//重要的使用场景1:判断是否滚动到了最后一行
if (firstVisibleItem + visibleItemCount == totalItemCount && totalItemCount > 0){
Log.d(TAG,"滚动到了最后一行============");
}
//重要的使用场景2:判断滚动方向
if (firstVisibleItem > lastVisibleItemPosition){
//上滑
}else if (firstVisibleItem < lastVisibleItemPosition){
//下滑
}
lastVisibleItemPosition = firstVisibleItem;
}
});
此外,ListView封装了一些方法来获取当前可视的Item的位置等消息
//获取可视区域内最后一个Item的id
mListView.getLastVisiblePosition();
//获取可视区域内第一个Item的id
mListView.getFirstVisiblePosition();
2、ListView常用拓展
2.1 具有弹性的ListView
Android默认的ListView在滚动到顶端或者低端的时候,没有很好的提示,下面通过修改源码的方法来实现其中的一种实现方式。
源码中的方法如下:
代码实现过程:
/**
* 具有弹性滑动的ListView
*/
public class CustomListview extends ListView {
private int mMaxOverDistance = 30;
public CustomListview(Context context) {
super(context);
}
public CustomListview(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CustomListview(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
//初始化操作
private void initView(Context context){
//让不同分辨率都显示一样的距离
DisplayMetrics metrics = context.getResources().getDisplayMetrics();
float density = metrics.density;
mMaxOverDistance = (int)(density * mMaxOverDistance);
}
@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);
}
}
2.2 自动显示、隐藏布局的ListView
public class ScrollHideListView extends Activity {
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;// 上拉
} else if (mFirstY - mCurrentY > mTouchSlop) {
direction = 1;// 下拉
}
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.scroll_hide);
//获取系统认为的最低滑动距离
// mTouchSlop = TOUCH_SLOP;
// private static final int TOUCH_SLOP = 8;
mTouchSlop = ViewConfiguration.get(this).getScaledTouchSlop();
mToolbar = (Toolbar) findViewById(R.id.toolbar);
mListView = (ListView) findViewById(R.id.listview);
for (int i = 0; i < mStr.length; i++) {
mStr[i] = "Item " + i;
}
//给ListView添加一个头部:是为了避免第一个Item被toolbar遮挡
View header = new View(this);
header.setLayoutParams(new AbsListView.LayoutParams(
AbsListView.LayoutParams.MATCH_PARENT,
//abc_action_bar_default_height_material : 获取系统ActonBar的高度
(int) getResources().getDimension(
R.dimen.abc_action_bar_default_height_material)));
mListView.addHeaderView(header);
mListView.setAdapter(new ArrayAdapter(
ScrollHideListView.this,
android.R.layout.simple_expandable_list_item_1,
mStr));
mListView.setOnTouchListener(myTouchListener);
}
private void toolbarAnim(int flag) {
if (mAnimator != null && mAnimator.isRunning()) {
mAnimator.cancel();
}
if (flag == 0) { //隐藏ToolBar
mAnimator = ObjectAnimator.ofFloat(mToolbar,
"translationY", mToolbar.getTranslationY(), 0);
} else { //显示ToolBar
mAnimator = ObjectAnimator.ofFloat(mToolbar,
"translationY", mToolbar.getTranslationY(),
-mToolbar.getHeight());
}
mAnimator.start();
}
}
2.3 聊天界面的实现
实现思路:通过Adapter的getItemViewType()和getViewTypeCount()方法来加载不同的布局
左边消息的布局:
右边消息的布局:
信息实体Bean
public class ChatItemListViewBean {
private int type;
private String text;
private Bitmap icon;
public ChatItemListViewBean() {
}
public int getType() {
return type;
}
public void setType(int type) {
this.type = type;
}
public String getText() {
return text;
}
public void setText(String text) {
this.text = text;
}
public Bitmap getIcon() {
return icon;
}
public void setIcon(Bitmap icon) {
this.icon = icon;
}
}
适配器的实现:
public class ChatItemListViewAdapter extends BaseAdapter {
private List mData;
private LayoutInflater mInflater;
public ChatItemListViewAdapter(Context context,
List data) {
this.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 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;
if (convertView == null) {
if (getItemViewType(position) == 0) {
holder = new ViewHolder();
convertView = mInflater.inflate(
R.layout.chat_item_itemin, null);
holder.icon = (ImageView) convertView.findViewById(
R.id.icon_in);
holder.text = (TextView) convertView.findViewById(
R.id.text_in);
} else {
holder = new ViewHolder();
convertView = mInflater.inflate(
R.layout.chat_item_itemout, null);
holder.icon = (ImageView) convertView.findViewById(
R.id.icon_out);
holder.text = (TextView) convertView.findViewById(
R.id.text_out);
}
convertView.setTag(holder);
} else {
holder = (ViewHolder) convertView.getTag();
}
holder.icon.setImageBitmap(mData.get(position).getIcon());
holder.text.setText(mData.get(position).getText());
return convertView;
}
public final class ViewHolder {
public ImageView icon;
public TextView text;
}
}
主界面逻辑实现:
public class ChatItemListViewTest extends Activity {
private ListView mListView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.chat_item_main);
mListView = (ListView) findViewById(R.id.listView_chat);
ChatItemListViewBean bean1 = new ChatItemListViewBean();
bean1.setType(0);
bean1.setIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.in_icon));
bean1.setText("Hello how are you?");
ChatItemListViewBean bean2 = new ChatItemListViewBean();
bean2.setType(1);
bean2.setIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher));
bean2.setText("Fine thank you, and you?");
ChatItemListViewBean bean3 = new ChatItemListViewBean();
bean3.setType(0);
bean3.setIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.in_icon));
bean3.setText("I am fine too");
ChatItemListViewBean bean4 = new ChatItemListViewBean();
bean4.setType(1);
bean4.setIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.ic_launcher));
bean4.setText("Bye bye");
ChatItemListViewBean bean5 = new ChatItemListViewBean();
bean5.setType(0);
bean5.setIcon(BitmapFactory.decodeResource(getResources(),
R.drawable.in_icon));
bean5.setText("See you");
List data = new ArrayList();
data.add(bean1);
data.add(bean2);
data.add(bean3);
data.add(bean4);
data.add(bean5);
mListView.setAdapter(new ChatItemListViewAdapter(this, data));
}
}
2.4动态改变ListView布局
实现思路:在getView()的时候,通过判断来选择加载不同的布局
适配器代码实现:
public class FocusListViewAdapter extends BaseAdapter {
private List mData;
private Context mContext;
private int mCurrentItem = 0;
public FocusListViewAdapter(Context context, List data) {
this.mContext = context;
this.mData = data;
}
public int getCount() {
return mData.size();
}
public Object getItem(int position) {
return mData.get(position);
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.VERTICAL);
if (mCurrentItem == position) {
layout.addView(addFocusView(position));
} else {
layout.addView(addNormalView(position));
}
return layout;
}
public void setCurrentItem(int currentItem) {
this.mCurrentItem = currentItem;
}
private View addFocusView(int i) {
ImageView iv = new ImageView(mContext);
iv.setImageResource(R.drawable.ic_launcher);
return iv;
}
private View addNormalView(int i) {
LinearLayout layout = new LinearLayout(mContext);
layout.setOrientation(LinearLayout.HORIZONTAL);
ImageView iv = new ImageView(mContext);
iv.setImageResource(R.drawable.in_icon);
layout.addView(iv, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
TextView tv = new TextView(mContext);
tv.setText(mData.get(i));
layout.addView(tv, new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.WRAP_CONTENT,
LinearLayout.LayoutParams.WRAP_CONTENT));
layout.setGravity(Gravity.CENTER);
return layout;
}
主界面逻辑实现:
public class FocusListViewTest extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.focus);
ListView listView = (ListView) findViewById(R.id.focus_listView);
List data = new ArrayList();
data.add("I am item 1");
data.add("I am item 2");
data.add("I am item 3");
data.add("I am item 4");
data.add("I am item 5");
final FocusListViewAdapter adapter = new FocusListViewAdapter(this, data);
listView.setAdapter(adapter);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView parent, View view,
int position, long id) {
adapter.setCurrentItem(position);
adapter.notifyDataSetChanged();
}
});
}
}
《Android高级进阶学习笔记》目录链接地址:
https://www.jianshu.com/c/abfff0ab5dbb