使用ExpandableListView以及如何优化view的显示减少内存占用
上篇博客讲到如何获取手机中所有歌曲的信息。本文就把上篇获取到的歌曲按照歌手名字分类。用一个ExpandableListView显示出来。
MainActivity .java
-
public class MainActivity extends AppCompatActivity {
-
private static List<MusicLoader.MusicInfo> musicList = new ArrayList<MusicLoader.MusicInfo>();
-
private ExpandableListView groupLvSongs;
-
-
-
protected void onCreate(Bundle savedInstanceState) {
-
super.onCreate(savedInstanceState);
-
setContentView(R.layout.activity_main);
-
initView();
-
initEvent();
-
}
-
-
private void initEvent() {
-
// 这是获取musicList,与本篇博客主题无关,大家只需要知道musicList代表所有歌曲,它的每一项都包含一首歌的所有信息
-
musicList = MusicLoader.instance(getContentResolver()).getMusicList();
-
// 设置适配器,给listview提供数据
-
groupLvSongs.setAdapter(new myExadapter(MainActivity.this, musicList));
-
}
-
-
private void initView() {
-
groupLvSongs = (ExpandableListView) findViewById(R.id.groupLvSongs);
-
}
-
-
/**
-
* 按歌手分类的listview 对应的Adapter,自定义ExpandableListView的适配器
-
* getGroupId()getChildId()hasStableIds()isChildSelectable暂时都默认自动生成的,
-
* 最主要是getGroupView(),getChildView()方法
-
*/
-
class myExadapter extends BaseExpandableListAdapter {
-
-
//在获取view的时候需要context
-
private Context context;
-
//所有歌曲
-
private List<MusicInfo> musicList = new ArrayList<MusicInfo>();
-
//记录各个歌手名字
-
private List<String> groupName = new ArrayList<String>();
-
//按歌手名字分类后的所有歌曲
-
private List<List<MusicInfo>> musicGroupBySinger = new ArrayList<List<MusicInfo>>();
-
-
myExadapter(Context context, List<MusicInfo> group) {
-
this.context = context;
-
musicList = group;
-
-
sortByArtistName();
-
}
-
-
// 根据歌手分类最终获得 musicGroupBySinger
-
private void sortByArtistName() {
-
// 第一个特殊
-
groupName.add(musicList.get(0).getArtist());
-
List<MusicInfo> musicListWithSameSinger = new ArrayList<MusicInfo>();
-
musicListWithSameSinger.add(musicList.get(0));
-
musicGroupBySinger.add(musicListWithSameSinger);
-
for (int i = 1; i < musicList.size(); i++) {
-
boolean flag = false;
-
for (int j = 0; j < groupName.size(); j++) {
-
// if该歌手名字已经存在
-
if (musicList.get(i).getArtist().equals(groupName.get(j))) {
-
flag = true;
-
musicGroupBySinger.get(j).add(musicList.get(i));
-
break;
-
}
-
}
-
if (!flag) {
-
groupName.add(musicList.get(i).getArtist());
-
List<MusicInfo> musicListWithSameSinger2 = new ArrayList<MusicInfo>();
-
musicListWithSameSinger2.add(musicList.get(i));
-
musicGroupBySinger.add(musicListWithSameSinger2);
-
}
-
}
-
}
-
-
-
public int getGroupCount() {
-
return musicGroupBySinger.size();
-
}
-
-
-
public int getChildrenCount(int groupPosition) {
-
return musicGroupBySinger.get(groupPosition).size();
-
}
-
-
-
public Object getGroup(int groupPosition) {
-
return musicGroupBySinger.get(groupPosition);
-
}
-
-
-
public Object getChild(int groupPosition, int childPosition) {
-
return musicGroupBySinger.get(groupPosition).get(childPosition);
-
}
-
-
-
public long getGroupId(int groupPosition) {
-
return 0;
-
}
-
-
-
public long getChildId(int groupPosition, int childPosition) {
-
return 0;
-
}
-
-
//true还是false感觉没什么区别
-
-
public boolean hasStableIds() {
-
return false;
-
}
-
-
//获取Group的视图
-
-
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
-
-
if (convertView == null) {
-
LayoutInflater inflater = LayoutInflater.from(context);
-
// R.layout.groups这个参数是group的视图
-
convertView = inflater.inflate(R.layout.groups, null);
-
}
-
-
TextView title = (TextView) convertView.findViewById(R.id.tvSinger);
-
title.setText(groupName.get(groupPosition));// 设置大组成员名称
-
-
return convertView;
-
}
-
//获取展开的子视图
-
-
/**
-
* 在这里我有必要提一下listview加载视图的优化问题
-
* <p/>
-
* 一、复用convertView
-
* 首先讲下ListView的原理:ListView中的每一个Item显示都需要Adapter调用一次getView的方法,这个方法会传入一个convertView的参数,
-
* 返回的View就是这个Item显示的View。如果当Item的数量足够大,再为每一个Item都创建一个View对象,必将占用很多内存,
-
* 创建View对象(mInflater.inflate(R.layout.lv_item, null);从xml中生成View,这是属于IO操作)也是耗时操作,所以必将影响性能。
-
* Android提供了一个叫做Recycler(反复循环器)的构件,就是当ListView的Item从上方滚出屏幕视角之外,对应Item的View会被缓存到Recycler中,
-
* 相应的会从下方生成一个Item,而此时调用的getView中的convertView参数就是滚出屏幕的Item的View,所以说如果能重用这个convertView,
-
* 就会大大改善性能。
-
* 所以getChildView 一开始会有一个判断语句
-
* if (convertView == null) 如果不为空就直接使用之前那个。
-
* <p/>
-
* <p/>
-
* 二、使用viewHolder类
-
* 我们都知道在getView方法中的操作是这样的:
-
* 先从xml中创建view对象(inflate操作,我们采用了重用convertView方法优化),然后在这个view去findViewById,
-
* 找到每一个子View,如:一个TextView等。这里的findViewById操作是一个树查找过程,也是一个耗时的操作,所以这里也需要优化,
-
* 就是使用viewHolder,把每一个子View都放在Holder中,当第一次创建convertView对象时,把这些子view找出来。
-
* 然后用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。
-
* 当第二次重用convertView时,只需从convertView中getTag取出来就可以。
-
*/
-
-
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
-
int position = musicList.indexOf(getChild(groupPosition,
-
childPosition));
-
// 优化listView
-
ViewHolder viewHolder;
-
if (convertView == null) {
-
// R.layout.music_item是每一项的视图xml文件
-
convertView = LayoutInflater.from(MainActivity.this).inflate(
-
R.layout.music_item, null);
-
-
TextView pTitle = (TextView) convertView
-
.findViewById(R.id.title);
-
viewHolder = new ViewHolder(pTitle);
-
// 用convertView的setTag将viewHolder设置到Tag中,以便系统第二次绘制ListView时从Tag中取出。
-
convertView.setTag(viewHolder);
-
} else {
-
// 当第二次重用convertView时,只需从convertView中getTag取出来就可以。
-
viewHolder = (ViewHolder) convertView.getTag();
-
}
-
viewHolder.title.setText(musicList.get(position).getTitle());
-
return convertView;
-
}
-
-
-
public boolean isChildSelectable(int groupPosition, int childPosition) {
-
return false;
-
}
-
}
-
-
class ViewHolder {
-
TextView title;
-
-
public ViewHolder(TextView pTitle) {
-
title = pTitle;
-
}
-
}
-
}
-
-
成果展示: