ListView分栏--制作分栏音乐列表

之前我遇到过这样的需求,要求在ListView中按时间对数据分栏,当时的做法是在每个ListView的item中加入时间栏的布局,然后在代码中控制时间栏

的显示与隐藏。

但其实重写Adapter两个方法后就可以完成这个任务,当ListView中带有不同布局的时候,可以根据itemType来加载不同的布局。

int getItemViewType(int position) 返回指定position的itemView的viewType,用于加载不同布局。此方法必须返回0到getViewTypeCount()-1

的数字或者IGNORE_ITEM_VIEW_TYPE。

int getViewTypeCount() 返回你这个ListView有多少个不同的布局。

让我们先来看看两张分栏后的效果图:

     

这里按照歌曲名拼音的首字母分栏,把汉字转为拼音我用了Pinyin4j,例如"你好"可转为"NIHAO",由于这不是这篇文章的重点,不知道的可自行百度。

那么下面来看看怎么一步步地实现吧!

1.由图中列表可以看出,我们要显示歌曲名,歌手名,分栏需要用到歌曲名对应的汉字拼音,所以有了下面的MediaItem实体。

 1 public class MediaItem implements Serializable {
 2     private static final long serialVersionUID = 1L;
 3
 4     private int id; // ID
 5     private String songName; // 歌曲名
 6     private String singerName; // 歌手名
 7     private String sortKey; // 歌曲名的拼音(如"你好"-->"NIHAO")
 8
 9     public String getSongName() {
10         return songName;
11     }
12
13     public void setSongName(String songName) {
14         this.songName = songName;
15     }
16
17     public String getSingerName() {
18         return singerName;
19     }
20
21     public void setSingerName(String singerName) {
22         this.singerName = singerName;
23     }
24
25     public int getId() {
26         return id;
27     }
28
29     public void setId(int id) {
30         this.id = id;
31     }
32
33     public String getSortKey() {
34         return sortKey;
35     }
36
37     public void setSortKey(String sortKey) {
38         this.sortKey = sortKey;
39     }
40 }

2.接下来就是需要从数据库中查出歌曲信息,使用android提供的uri:MediaStore.Audio.Media.EXTERNAL_CONTENT_URI查询我们需要的字段,

然后封装成MediaItem实体,用于绑定ListView。如果查询时间超过500毫秒,则显示加载的progressBar。

布局比较简单,就是一个ListView和一个ProgressBar,我就不贴出来了。

 1 public class MediaActivity extends Activity {
 2     private static final String TAG = "MediaActivity";
 3
 4     private static final int MSG_SHOW_PROGRESS_BAR = 100;
 5
 6     private List<MediaItem> mList;
 7     private MediaAdapter mAdapter;
 8     private ListView mListView;
 9     private ProgressBar mProgressBar;
10
11     @Override
12     protected void onCreate(Bundle savedInstanceState) {
13         super.onCreate(savedInstanceState);
14         setContentView(R.layout.activity_media);
15
16         initViews();
17         // 查询音乐列表
18         getData();
19     }
20
21     private void initViews() {
22         mListView = (ListView) findViewById(R.id.media_list);
23         mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);
24         ViewCompat.setOverScrollMode(mListView, ViewCompat.OVER_SCROLL_NEVER);
25     }
26
27     private void getData() {
28         mList = new ArrayList<MediaItem>();
29         // 如果500毫秒内加载完成,则不显示ProgressBar
30         mHander.sendEmptyMessageDelayed(MSG_SHOW_PROGRESS_BAR, 500);
31         // 使用AsyncTask查询音乐列表,并构造实体列表MediaItem
32         new AsyncTask<Void, Void, List<MediaItem>>() {
33
34             @Override
35             protected List<MediaItem> doInBackground(Void... params) {
36                 Log.v(LogUtils.TAG, "AsyncTask doInBackground");
37                 Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
38                 String[] projection = { MediaStore.Audio.Media._ID, // ID
39                         MediaStore.Audio.Media.TITLE, // 显示的歌曲名
40                         MediaStore.Audio.Media.ARTIST // 艺术家
41                 };
42                 Cursor cursor = getContentResolver().query(uri, projection, null, null, MediaStore.Audio.Media.DEFAULT_SORT_ORDER);
43                 if (cursor != null) {
44                     try {
45                         while (cursor.moveToNext()) {
46                             MediaItem item = new MediaItem();
47                             String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
48                             item.setId(cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media._ID)));
49                             item.setSongName(title);
50                             item.setSingerName(cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST)));
51                             // 汉字转拼音
52                             item.setSortKey(PinYinUtils.getPinYin(title));
53                             mList.add(item);
54                         }
55                     } catch (Exception e) {
56                         Log.e(TAG, "get cursor data error!");
57                     } finally {
58                         cursor.close();
59                     }
60                 }
61                 return mList;
62             }
63
64             @Override
65             protected void onPostExecute(List<MediaItem> result) {
66                 Log.v(LogUtils.TAG, "AsyncTask onPostExecute result.size=" + result.size());
67                 mHander.removeMessages(MSG_SHOW_PROGRESS_BAR);
68                 mAdapter = new MediaAdapter(MediaActivity.this, mList);
69                 mListView.setAdapter(mAdapter);
70                 mListView.setVisibility(View.VISIBLE);
71                 if (mProgressBar.getVisibility() == View.VISIBLE) {
72                     mProgressBar.setVisibility(View.GONE);
73                 }
74             }
75         }.execute();
76     }
77
78     // 用于显示ProgressBar
79     Handler mHander = new Handler(new Callback() {
80         @Override
81         public boolean handleMessage(Message msg) {
82             switch (msg.what) {
83             case MSG_SHOW_PROGRESS_BAR:
84                 mProgressBar.setVisibility(View.VISIBLE);
85                 break;
86
87             default:
88                 break;
89             }
90             return false;
91         }
92     });
93
94 }

3.接下来就是比较重要的Adapter,分栏的任务在这里完成。首先注意adapter绑定的数据源是一个List<TypeItem>,TypeItem对数据做了一层

封装,它包含itemType,就是在getItemViewType()需要返回的参数。而通过generateItems()方法把传入的List<MediaItem>构造成

List<TypeItem>,再加入itemType的同时如果发现MediaItem中歌曲名的拼音首字母不一样,就插入一个分组的头部。然后在getView()中就可以

根据itemType来区分不同的布局,由于具有两个不同的布局,所以定义ViewHolder基类,分栏布局HeaderViewHolder和歌曲列表MediaViewHolder

都继承自ViewHolder,用于缓存视图,然后就可以根据不同的ViewHolder实例来绑定数据(在ListView中有多个布局的时候都可以使用此方法)。

  1 package com.yangy.test.adapter;
  2
  3 import java.util.ArrayList;
  4 import java.util.List;
  5
  6 import android.content.Context;
  7 import android.view.LayoutInflater;
  8 import android.view.View;
  9 import android.view.ViewGroup;
 10 import android.widget.BaseAdapter;
 11 import android.widget.TextView;
 12
 13 import com.yangy.test.model.MediaItem;
 14 import com.yy.gallerytest.activity.R;
 15
 16 public class MediaAdapter extends BaseAdapter {
 17
 18     private static final int VIEW_TYPE_COUNT = 2; // 有几种不同的布局
 19     private static final int VIEW_TYPE_HEADER = 0; // 分组的头部
 20     private static final int VIEW_TYPE_ITEM = 1; // 音乐列表item
 21
 22     private LayoutInflater mInflater;
 23     private List<TypeItem> items;
 24
 25     public MediaAdapter(Context context, List<MediaItem> items) {
 26         mInflater = LayoutInflater.from(context);
 27         this.items = generateItems(items);
 28     }
 29
 30     /**
 31      * 实体基类,包含itemType,以便区分不同的布局,子类需要的其他数据可自行指定
 32      */
 33     class TypeItem {
 34         int itemType;
 35
 36         public TypeItem(int itemType) {
 37             this.itemType = itemType;
 38         }
 39     }
 40
 41     /**
 42      * 音乐列表实体,指定itemType为VIEW_TYPE_ITEM 包含MediaItem实体
 43      */
 44     class MediaTypeItem extends TypeItem {
 45         MediaItem mediaItem;
 46
 47         public MediaTypeItem(MediaItem mediaItem) {
 48             super(VIEW_TYPE_ITEM);
 49             this.mediaItem = mediaItem;
 50         }
 51     }
 52
 53     /**
 54      * 头布局实体,指定itemType为VIEW_TYPE_HEADER 包含分组中头部的字母
 55      */
 56     class HeaderTypeItem extends TypeItem {
 57         char header;
 58
 59         public HeaderTypeItem(char header) {
 60             super(VIEW_TYPE_HEADER);
 61             this.header = header;
 62         }
 63     }
 64
 65     /**
 66      * 根据传入的mediaItem list,构造带有header的TypeItem
 67      *
 68      * @param mediaItems
 69      *            音乐列表实体
 70      * @return 包含itemType的实体
 71      */
 72     private List<TypeItem> generateItems(List<MediaItem> mediaItems) {
 73         List<TypeItem> items = new ArrayList<TypeItem>();
 74         int size = mediaItems == null ? 0 : mediaItems.size();
 75         char currIndex;
 76         char preIndex = ‘{‘;
 77         for (int i = 0; i < size; i++) {
 78             currIndex = mediaItems.get(i).getSortKey().charAt(0);
 79             // 是第一个item或者两个数据的拼音首字母不相等则插入头部
 80             if (i == 0 || currIndex != preIndex) {
 81                 items.add(new HeaderTypeItem(currIndex));
 82             }
 83             items.add(new MediaTypeItem(mediaItems.get(i)));
 84             preIndex = currIndex;
 85         }
 86         return items;
 87     }
 88
 89     /**
 90      * ViewHolder基类,itemView用于查找子view
 91      */
 92     class ViewHolder {
 93         View itemView;
 94
 95         public ViewHolder(View itemView) {
 96             if (itemView == null) {
 97                 throw new IllegalArgumentException("itemView can not be null!");
 98             }
 99             this.itemView = itemView;
100         }
101     }
102
103     /**
104      * 音乐列表ViewHolder
105      */
106     class MediaViewHolder extends ViewHolder {
107         TextView songName;
108         TextView singerName;
109
110         public MediaViewHolder(View view) {
111             super(view);
112             songName = (TextView) view.findViewById(R.id.song_name);
113             singerName = (TextView) view.findViewById(R.id.singer_name);
114         }
115     }
116
117     /**
118      * 头部ViewHolder
119      */
120     class HeaderViewHolder extends ViewHolder {
121         TextView header;
122
123         public HeaderViewHolder(View view) {
124             super(view);
125             header = (TextView) view.findViewById(R.id.header);
126         }
127     }
128
129     @Override
130     public View getView(int postion, View convertView, ViewGroup parent) {
131         TypeItem item = items.get(postion);
132         ViewHolder viewHolder;
133         if (convertView == null) {
134             // 根据不同的viewType,初始化不同的布局
135             switch (getItemViewType(postion)) {
136             case VIEW_TYPE_HEADER:
137                 viewHolder = new HeaderViewHolder(mInflater.inflate(R.layout.media_header_item, null));
138                 break;
139             case VIEW_TYPE_ITEM:
140                 viewHolder = new MediaViewHolder(mInflater.inflate(R.layout.media_item, null));
141                 break;
142
143             default:
144                 throw new IllegalArgumentException("invalid view type : " + getItemViewType(postion));
145             }
146
147             // 缓存header与item视图
148             convertView = viewHolder.itemView;
149             convertView.setTag(viewHolder);
150         } else {
151             viewHolder = (ViewHolder) convertView.getTag();
152         }
153
154         // 根据初始化的不同布局,绑定数据
155         if (viewHolder instanceof HeaderViewHolder) {
156             ((HeaderViewHolder) viewHolder).header.setText(String.valueOf(((HeaderTypeItem) item).header));
157         } else if (viewHolder instanceof MediaViewHolder) {
158             onBindMediaItem((MediaViewHolder) viewHolder, ((MediaTypeItem) item).mediaItem);
159         }
160         return convertView;
161     }
162
163     private void onBindMediaItem(MediaViewHolder viewHolder, MediaItem mediaItem) {
164         viewHolder.songName.setText(mediaItem.getSongName());
165         viewHolder.singerName.setText(mediaItem.getSingerName());
166     }
167
168     @Override
169     public int getItemViewType(int position) {
170         if (items != null) {
171             return items.get(position).itemType;
172         }
173         return super.getItemViewType(position);
174     }
175
176     @Override
177     public int getViewTypeCount() {
178         return VIEW_TYPE_COUNT;
179     }
180
181     @Override
182     public int getCount() {
183         return items != null ? items.size() : 0;
184     }
185
186     @Override
187     public Object getItem(int position) {
188         if (items != null && position > 0 && position < items.size()) {
189             return items.get(position);
190         }
191         return null;
192     }
193
194     @Override
195     public long getItemId(int postion) {
196         return postion;
197     }
198 }

至此,一个带有分栏的音乐列表制作完成。

存在的问题:

查询音乐列表是使用的排序方式为DEFAULT_SORT_ORDER,歌曲名为英文或特殊字符的歌曲会排在中文歌曲之后,而以上的分栏依赖歌曲的

排序,所以会出现先对中文分组,再对英文分组的情况。我的想法是可以通过建立一张数据库表(包含使用pinYin4j生成的sortKey字段),将歌曲

信息读入,然后查询时根据sortKey排序,这样中文和英文就能正确分组了(有时间再去实现一下^_^)。

时间: 2024-10-07 06:13:30

ListView分栏--制作分栏音乐列表的相关文章

WORD里怎样能做到局部“分栏”就是一页里有的分有的不分

选中你要分的部分再分栏如果不想分的部分也被分了,那就可以选中不想分的那部分,选择“分栏”->“一栏” 转自:http://zhidao.baidu.com/question/9873268.html?qbl=relate_question_0&word=word%20%B7%D6%C0%B8%20%D6%D0%B6%CF&optimi=4

水平导航栏制作

在网页中水平导航栏是每一张网页都有的. 现在做一个简单的导航栏: <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd"><html><head> <meta content="text/html" charset="utf-8">

学习码匠社区-spring boot 使用bootstrap + thymeleaf 制作导航栏

spring boot 使用 bootstrap + thymeleaf 制作导航栏 添加 thymeleaf 依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> 配置 application.properties,使得 sprin

用CSS制作三栏液态布局

下面是CSS代码: body {     margin: 0px;     padding: 0px;}div#header {     clear: both;     height: 50px;     background-color: aqua;     padding: 1px;}div#left {     float: left;     width: 150px;     background-color: red;}div#right {     float: right;  

手把手教你做音乐播放器(五)音乐列表的存储(上)

第5节 播放列表的存取 关于播放列表的存取需要三个组件的协同配合, MusicListActivity:让用户选择多首或一首音乐,将用户的选择项,传递给MusicService: MusicService:接收到添加列表的请求后,把数据交给PlayListContentProvider,进行存储: PlayListContentProvider:将播放列表存储到SQLite数据库中: 5.1 PlayListContentProvider的实现 自定义的ContentProvider与系统自带的

【REACT NATIVE 系列教程之十三】利用LISTVIEW与TEXTINPUT制作聊天/对话框&&获取组件实例常用的两种方式

本站文章均为 李华明Himi 原创,转载务必在明显处注明: 转载自[黑米GameDev街区] 原文链接: http://www.himigame.com/react-native/2346.html 本篇Himi来利用ListView和TextInput这两种组件实现对话.聊天框. 首先需要准备的有几点:(组件的学习就不赘述了,简单且官方有文档) 1. 学习下 ListView: 官方示例:http://reactnative.cn/docs/0.27/tutorial.html#content

读&ldquo;40 分,60 分,90 分&rdquo;

原文链接: http://mp.weixin.qq.com/s?__biz=MzA5MjYyNzY1OQ==&mid=2650901947&idx=1&sn=89af64d3b0dec01587f14aab15e62b9a#rd   40 分,60 分,90 分 原创 2016-05-27 汪海 汪海的实验室 背景 最近在微博上看到有人说,找工作太难了,面试官要求还高,才大三就问会不会这个框架那个框架的,并且表示平时项目机会少, jQuery Bootstrap 这种基础框架基本够

Android开发本地及网络Mp3音乐播放器(十二)创建NetMusicListAdapter、SearchResult显示网络音乐列表

实现功能: 实现NetMusicListAdapter(网络音乐列表适配器) 实现SearchResult(搜索音乐对象) 使用Jsoup组件请求网络,并解析音乐数据,并,音乐数据加载到列表中 实现FooterView 截止到目前的源码下载: http://download.csdn.net/detail/iwanghang/9507635 Jsoup组件导入: AndroidStudio简单快速导入GitHub中的第三方组件 : http://blog.csdn.net/iwanghang/a

安卓MP3播放器开发实例(1)之音乐列表界面

学习安卓开发有一年了,想想这一年的努力,确实也收获了不少.也找到了比較如意的工作. 今天准备分享一个以前在初学阶段练习的一个项目.通过这个项目我真正的找到了开发安卓软件的感觉,从此逐渐步入安卓开发的正规.这个项目是当时借鉴Mars老师的初学视频做的安州手机的MP3播放器.自己又进行了改进,特别在歌词的优化和加入进度条方面的.因为是8个月前做的,水平非常0基础,bug应该非常多,如今自己又懒得再一次改进,仅仅希望可以给初学的朋友们提供一些帮助.或者起到抛砖引玉的效果.那我就心惬意足了. 先整体介绍