这次复习一下ListView的优化,大概分三种方法:
1.view item重用机制(如果Adapter的getView(int position, View convertView, ViewGroup parent),当convertView==null时才会通过findViewById()或者Inflate在xml中实例化,否则直接拿来用)
2.viewHolder缓存item(不必每次都通过findViewById()或者Inflate在xml中查找或实例化,而是每次实例化后都存在viewHolder这个内部类中(放在内存中),大大加快了加载的速度。但不足很明显,数据量过大时会造成内存溢出,应用崩溃。)
3.分批加载item,甚至有更好的分页式加载(分页查询在学校做java后台时也有过使用,通过当前页数index,计算出当前要加载的数据list,最终显示在jsp页面上。而分页的好处也很明显:每次只需要获取和加载一页个数的数据,流量小,客户体验好!)。
下面通过代码来进行解释:
public class VideoActivity extends Activity implements AbsListView.OnScrollListener{
...
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
initView();
initData();
Log.i("XX", "从心开始了");
}
/**
* 初始化界面
**/
private void initView() {
setContentView(R.layout.activity_video);
videoList = (ListView) findViewById(R.id.video_list);
}
private void initData() {
adapter = new MyAdapter(this.getApplicationContext());
videoList.setAdapter(adapter);
videoList.setOnScrollListener(this);
path = new ArrayList<>();
path.add("视频地址");//可以是网络地址
path.add("视频地址");
path.add("视频地址");
path.add("视频地址");
path.add("视频地址");
path.add("视频地址");
path.add("视频地址");
path.add("视频地址");
path.add("视频地址");
adapter.setData(path);
}
/**
* Callback method to be invoked while the list view or grid view is being scrolled.
* If the view is being scrolled, this method will be called before the next frame of the scroll is rendered.
* In particular, it will be called before any calls to Adapter.getView(int, View, ViewGroup).
*/
@Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
@Override
/**
* Callback method to be invoked when the list or grid has been scrolled.
* This will be called after the scroll has completed
*/
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, int totalItemCount) {
Log.i("onScroll", "当前" + currentPosition);
Log.i("onScroll", "可见的第一个" + videoList.getFirstVisiblePosition());
if ((currentPosition < videoList.getFirstVisiblePosition() || currentPosition > videoList
.getLastVisiblePosition()) && isPlaying && getResources().getConfiguration().orientation
== Configuration.ORIENTATION_PORTRAIT) {
closeVideo();
}
}
private class MyAdapter extends BaseAdapter {
private Context mContext;
private List<String> videoPaths;
public MyAdapter(Context mContext) {
this.mContext = mContext;
}
public void setData(List<String> videoPaths) {
this.videoPaths = videoPaths;
notifyDataSetChanged();
}
@Override
public int getCount() {
return videoPaths != null && videoPaths.size() > 0 ? videoPaths.size() : 0;
}
@Override
public Object getItem(int position) {
return videoPaths != null && videoPaths.size() > 0 ? videoPaths.get(position) : null;
}
@Override
public long getItemId(int position) {
return position;
}
@Override
public View getView(final int position, View convertView, ViewGroup parent) {
//使用holder这一做法,便于当itemView中的布局复杂时容易阅读理解和管理
ViewHolder viewHolder = null;
//重用itemView,而不是重新Inflate①
if (convertView == null) {
convertView = LayoutInflater.from(mContext).inflate(R.layout.video_play_items, parent, false);
viewHolder = new ViewHolder();
viewHolder.play_btn = (ImageView) convertView.findViewById(R.id.play_btn);
viewHolder.show_layout = (FrameLayout) convertView.findViewById(R.id.show_layout);
viewHolder.play_view = (VideoPlayView) convertView.findViewById(R.id.video_play_view);
//将生成的holder与对应的item“绑定”,省去了重新Inflate到加载进内存的过程。效率很高。
convertView.setTag(viewHolder);
} else {
//重用itemView,而不是重新Inflate②,
viewHolder = (ViewHolder) convertView.getTag();
}
viewHolder.play_btn.setOnClickListener(new MyClick(position,
viewHolder.play_view, viewHolder.show_layout, convertView));
if (currentPosition == position) {
viewHolder.play_view.setVisibility(View.VISIBLE);
} else {
viewHolder.play_view.setVisibility(View.GONE);
viewHolder.show_layout.setVisibility(View.VISIBLE);
viewHolder.play_view.stop();
}
return convertView;
}
//Holder用于存放当前的ItemView布局,方便管理和阅读
private class ViewHolder {
private ImageView play_btn;//播放按钮
private FrameLayout show_layout;
private VideoPlayView play_view;
}
class MyClick implements View.OnClickListener {
private int position;
private VideoPlayView playView;
private FrameLayout show_layout;
private View convertView;
public MyClick(int position, VideoPlayView playView, FrameLayout show_layout, View convertView) {
this.position = position;
this.show_layout = show_layout;
this.playView = playView;
this.convertView = convertView;
}
@Override
public void onClick(View v) {
isPlaying = true;
currentPosition = position;
show_layout.setVisibility(View.GONE);
playView.setUrl(videoPaths.get(position));
currentItemView = convertView;
setPlayView(playView);
playView.openVideo();
notifyDataSetChanged();
}
}
}
...
}
根据以上代码,其实可以看到关于ListView优化的前两种方法,而第三种方法的代码正在利用闲时间编写,之后再会写一篇关于完整的ListView优化的代码和解释。
最后,这里只提一下第三种方法的实现思路,添加一个当前页的全局变量,在Adapter类中加载当前页要显示的data,onScroll实现中设置好一页显示的item的最大个数(要根据屏幕高度与每个item的高度进行计算,向上取整),当滑动到最后一个后仍向下滑动,那么获取和加载下一页的数据到Adapter中。
而分页式的数据加载适合于大数据的在线传输和显示,既可以降低服务器的压力,也使得客户端能够非常流畅。
下图是实现好的效果: