1. 说明

源码已同步到Gitee仓库,GitHub仓库,觉得还不错的话帮忙点个“star”吧,非常感谢。

以往的文章

  • 服务端:Android音乐播放器开发–服务端

  • 登录:Android音乐播放器开发–登录

  • 注册:Android音乐播放器开发–注册

  • 修改密码:Android音乐播放器开发–修改密码

  • 播放:Android音乐播放器开发–播放

适用于平时做个小课设的小伙伴们

本部分内容实现效果如下:

2. 界面设计

界面主体是一个ListView,它以列表的形式展示内容,当数据量足够多时会出现滚动条,并且能够根据数据量自适应屏幕;而ListView的元素都是歌曲对应的基本信息。

  1. 标题栏

标题栏仿照登录时介绍的main_title_bar.xml新建了一个表头文件,命名为title_bar.xml

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="56dp"android:background="@color/colorPrimary"><ImageButtonandroid:id="@+id/ib_title_back"android:src="@drawable/go_back_selector"android:background="@null"android:layout_marginLeft="10dp"android:layout_width="50dp"android:layout_height="50dp"android:layout_alignParentLeft="true"android:layout_centerVertical="true"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:text="播放列表"android:layout_centerVertical="true"android:layout_centerHorizontal="true"android:textColor="#fff"android:textSize="20sp"android:id="@+id/tv_title"/>
</RelativeLayout>

  1. ListView

这一部分只加了一个ListView组件(activity_music_list.xml)

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="cn.sjcup.musicplayer.activity.MusicListActivity"><include layout="@layout/title_bar"></include><FrameLayoutandroid:layout_width="match_parent"android:layout_height="match_parent"><ListViewandroid:layout_width="match_parent"android:layout_height="match_parent"android:id="@+id/lv_music" /></FrameLayout></LinearLayout>

  1. Item布局

这部分是ListView显示的元素布局。(activity_item.xml)

Item显示了歌曲的基本信息,包括歌曲封面、歌名和演唱信息,另外加了一个标记,用于标识当前所播的歌曲。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="65dp"><com.loopj.android.image.SmartImageViewandroid:id="@+id/siv_img"android:layout_width="80dp"android:layout_height="60dp"android:layout_alignParentLeft="true"android:layout_marginBottom="5dp"android:layout_marginLeft="5dp"android:layout_marginTop="5dp"android:scaleType="centerCrop"android:src="@mipmap/ic_launcher"></com.loopj.android.image.SmartImageView><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_name"android:layout_marginLeft="5dp"android:layout_marginTop="10dp"android:layout_toRightOf="@id/siv_img"android:ellipsize="end"android:maxLength="20"android:singleLine="true"android:text="歌曲"android:textColor="#000000"android:textSize="18sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_author"android:layout_below="@id/tv_name"android:layout_marginLeft="5dp"android:layout_marginTop="5dp"android:layout_toRightOf="@id/siv_img"android:ellipsize="end"android:maxLength="16"android:singleLine="true"android:text="作者"android:textColor="#99000000"android:textSize="14sp"/><TextViewandroid:layout_width="wrap_content"android:layout_height="wrap_content"android:id="@+id/tv_type"android:layout_alignParentBottom="true"android:layout_alignParentRight="true"android:layout_marginBottom="5dp"android:layout_marginRight="10dp"android:text="播放中"android:textColor="#99000000"android:textSize="12sp"/></RelativeLayout>

3. 功能设计

3.1定义变量

//控件
private ImageButton mBack;
private ListView mMusicList;
private TextView mState;//获取到的数据
private JSONArray musicList;
private JSONObject musicInfo;//获取工具类实例化对象(这个工具类很重要,在播放界面一文的第五部分提到)
private MusicPlayUtil musicPlayUtil = MusicPlayUtil.getInstance();

3.2 初始化界面

在初始化时,分为了界面初始化和事件初始化

@Override
protected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_music_list);//初始化界面initView();//设置相关事件initEvent();
}

初始化界面,绑定界面控件,将歌曲信息填充到ListView中

//初始化界面
private void initView() {mBack = findViewById(R.id.ib_title_back);mMusicList = findViewById(R.id.lv_music);fillData();  //填充数据
}

数据填充使用setAdapter方法,这是ListView本身自带的方法,通过名称可以看出,Adapter意为适配器,我们将适配器填充到ListView中。参数是实例化后的MusicAdapter对象。MusicAdapter对象继承了BaseAdapter,并重写了四个方法。

四个方法分别为:

  • int getCount() 填充的item个数
  • Object getItem(int position) 指定索引对应的item数据项
  • long getItemId(int position) 指定索引对应item的id值
  • View getView(final int position, View convertView, ViewGroup parent) 填充每个item的可视内容并返回

我们可以简单的理解为,适配器先从getCount里确定填充的数量,然后循环执行getView方法将条目一个一个绘制出来,所以必须重写的是getCountgetView方法。而getItemgetItemId是调用某些函数才会触发的方法,如果不需要使用时可以暂时不修改。

具体的介绍可以参考—>博客

下面是填充数据的这一部分整体的程序,稍后会拆开介绍。

//填充数据
private void fillData() {mMusicList.setAdapter(new MusicAdapter());
}private class MusicAdapter extends BaseAdapter {@Overridepublic int getCount() {return musicPlayUtil.getMusicNum();}@Overridepublic View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;   //ViewHolder是定义的局部定义类,作为信息传递的载体musicList = musicPlayUtil.getMusicList();try{musicInfo = musicList.getJSONObject(position);}catch (Exception e){e.printStackTrace();}if (convertView == null){convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.activity_item,parent,false);holder = new ViewHolder();holder.siv = convertView.findViewById(R.id.siv_img);holder.tv_name = convertView.findViewById(R.id.tv_name);holder.tv_author = convertView.findViewById(R.id.tv_author);holder.tv_type = convertView.findViewById(R.id.tv_type);convertView.setTag(holder);}else {holder = (ViewHolder) convertView.getTag();}holder.siv.setImageUrl(IMG+musicInfo.optString("img"), R.mipmap.ic_launcher,R.mipmap.ic_launcher);holder.tv_name.setText(musicInfo.optString("name"));holder.tv_author.setText(musicInfo.optString("author"));holder.tv_type.setText("");if (musicPlayUtil.getMusicId()==position){holder.tv_type.setText("播放中");holder.tv_type.setTextColor(Color.RED);mState=holder.tv_type;}return convertView;}@Overridepublic Object getItem(int position) {return null;}@Overridepublic long getItemId(int position) {return 0;}class ViewHolder{TextView tv_name;TextView tv_author;TextView tv_type;SmartImageView siv;}
}

分别讲解一下。

因为需要频繁的传递信息,这里定义了一个局部内部类,分别对应了item布局的4个控件(拓展一下:内部类分为静态内部类、成员内部类、局部内部类、匿名内部类。感兴趣的小伙伴可以去复习一下)

class ViewHolder{TextView tv_name;TextView tv_author;TextView tv_type;SmartImageView siv;
}

getCount方法,返回的是填充的item的数量,在本项目中,就是歌曲的数量。歌曲数量已经在MainActivity中获取,这里可以通过工具类(重申一遍,这个工具类在播放界面这篇文章的第五部分有讲到)直接获取到这个值。

@Override
public int getCount() {return musicPlayUtil.getMusicNum();
}

getView方法,返回的是填充的view信息,这一部分是本文最难理解的。参数position相当于一个标识,用来表示进行操作的是哪一个item,从0开始计数;convertView就是待返回的view信息,初始化时为null,我们要加工的也就是这个对象。

首先,定义了一个类holder,使用时需要实例化一个ViewHolder(上面定义的局部内部类)作为信息传递的载体,也就是填充得item数量决定了ViewHolder的实例化对象的数量。所有歌曲信息可以通过上面定义的工具类获取。然后通过position获取到对应的歌曲信息(JSONArray里的object也是从0开始计数的,我们可以认为是一一对应的)。

下面我们做个测试,检测一下什么时候调用getView方法。

------------------------------------------------------------Test---------------------------------------------------------

我们在getView方法中输出position,也就是每次调用getView方法时都会输出当前的position

这时我们打开音乐列表,可以看到显示了11首歌曲

输出显示调用了11次getView方法,position对应了0–>10

然后向上滑,加载其它的歌曲,上滑的同时,其它内容通过getView方法被加载,position对应11->16

现在我们想一个问题,此时下滑,也就是希望显示之前的歌曲信息,getView会被重新调用吗?(1. 第一种想法当然是认为之前的歌曲信息已经加载出来了,当然不会再调用了;2. 第二种想法就是会调用吧)

事实是加载之前的信息会重新调用getView

经过简单的测试,我们可以得出一个小结论,界面中可以显示出多少个item,就会调用多少次getView方法加载信息,也就是哪个item进入可是范围,就会调用getView方法信息。前面介绍道,理论上有多少条信息就应该有对应相应数量的item,但是考虑到手机的内存是有限的,如果数量过多,占用内存就会过大,实际上的操作只会初始化特定数量的item(依屏幕长度而定),在滑动过程中,item重复使用,使用getView方法不断赋予新的信息。

------------------------------------------------------------ENDTest---------------------------------------------------------

关于setTaggetTag的使用。在setTag之前,convertView需要找到xml中定义的layout,这里是activity_item,相当于绑定了一个目标item,然后使用findViewById绑定item中的组件,再对每个组件赋予不同的值。前面介绍道,上滑又下滑后,前面加载的信息会再次使用getView方法“绘制”一遍,但已经绑定的组件没有必要再绑定一次,因为这个过程比较消耗资源,因此在一次使用findViewById绑定后,调用setTag保存起来,再次加载时,同于同样的信息,直接使用getTag获取到已绑定组件的ViewHolder,而无需重复绑定组件。

if (convertView == null){convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.activity_item,parent,false);holder = new ViewHolder();holder.siv = convertView.findViewById(R.id.siv_img);holder.tv_name = convertView.findViewById(R.id.tv_name);holder.tv_author = convertView.findViewById(R.id.tv_author);holder.tv_type = convertView.findViewById(R.id.tv_type);convertView.setTag(holder);
}else {holder = (ViewHolder) convertView.getTag();
}

对于初学者来说,这个比较难理解,建议多看几篇文章。以上很多内容都是本人自己的理解,如果有不对的地方敬请指出。

@Override
public View getView(int position, View convertView, ViewGroup parent) {ViewHolder holder;   //ViewHolder是定义的局部内部类,作为信息传递的载体musicList = musicPlayUtil.getMusicList();try{musicInfo = musicList.getJSONObject(position);}catch (Exception e){e.printStackTrace();}if (convertView == null){convertView = LayoutInflater.from(getApplicationContext()).inflate(R.layout.activity_item,parent,false);holder = new ViewHolder();holder.siv = convertView.findViewById(R.id.siv_img);holder.tv_name = convertView.findViewById(R.id.tv_name);holder.tv_author = convertView.findViewById(R.id.tv_author);holder.tv_type = convertView.findViewById(R.id.tv_type);convertView.setTag(holder);}else {holder = (ViewHolder) convertView.getTag();}holder.siv.setImageUrl(IMG+musicInfo.optString("img"), R.mipmap.ic_launcher,R.mipmap.ic_launcher);holder.tv_name.setText(musicInfo.optString("name"));holder.tv_author.setText(musicInfo.optString("author"));holder.tv_type.setText("");if (musicPlayUtil.getMusicId()==position){holder.tv_type.setText("播放中");holder.tv_type.setTextColor(Color.RED);mState=holder.tv_type;}return convertView;
}

3.3定义事件

在这个界面,我们希望实现两个点击事件,1. 点击“返回”按钮,可以返回播放界面;2. 点击一个歌曲信息,可以播放对应的歌曲。

//初始化事件
private void initEvent() {mMusicList.setOnItemClickListener(new AdapterView.OnItemClickListener() {@Overridepublic void onItemClick(AdapterView<?> parent, View view, int position, long id) {if (mState == null) {mState = view.findViewById(R.id.tv_type);mState.setText("播放中");mState.setTextColor(Color.RED);musicPlayUtil.setMusicId(position);musicPlayUtil.setMusicView(MainActivity.IsPlay.play);finish();} else {mState.setText("");mState = view.findViewById(R.id.tv_type);mState.setText("播放中");mState.setTextColor(Color.RED);musicPlayUtil.setMusicId(position);musicPlayUtil.setMusicView(MainActivity.IsPlay.play);finish();}}});mBack.setOnClickListener(new View.OnClickListener() {@Overridepublic void onClick(View v) {MusicListActivity.this.finish();}});
}

第一个事件,返回播放界面比较简单,这里不再赘述。

关于第二个事件,定义的变量mState表示的是歌曲的播放状态,如果在播放状态,就显示“播放中”(如下图),否则不显示内容。

第一次点击时,mState是null,并没有被赋值,view就是被点击的item,第一步就是绑定被点击的item的组件,更改它的内容,响应点击事件。然后调用工具类中的方法修改歌曲id,这里的position和前面介绍的可以认为是一样的,由于position从0开始,和我定义的歌曲id是一样的,这里直接把它当作歌曲id了,如果不一致,这里需要再加一些处理,找到对应的歌曲id,再调用setMusicView播放对应的歌曲。至于finish方法,是做出点击事件后关闭播放列表界面,返回播放界面,如果你认为没有必要可以不加。

mState如果不是null,那么之前是已经绑定过别的item了,修改之前绑定的播放状态为空字符串,表示没有播放,再重新绑定新的item,后面内容就与上面相似了,不再赘述。

4. 测试

测试成功,放一个测试的动图

Android音乐播放器开发(6)—ListView组件创建歌曲播放列表(内含原理分析)相关推荐

  1. Android音乐播放器开发(2)—登录

    1. 说明 本音乐播放器基于Android开发,原为我和另外两个小伙伴在上学期间一起做的一个小项目,近来有时间整理一下.之前我有文章已经介绍了播放界面的功能实现(Android音乐播放器开发),但介绍 ...

  2. Android音乐播放器开发(3)—注册

    1. 说明 本音乐播放器基于Android开发,原为我和另外两个小伙伴在上学期间一起做的一个小项目,近来有时间整理一下.之前我有文章已经介绍了播放界面的功能实现(Android音乐播放器开发),但介绍 ...

  3. Android音乐播放器开发(4)—修改密码

    1. 说明 本音乐播放器基于Android开发,原为我和另外两个小伙伴在上学期间一起做的一个小项目,近来有时间整理一下.之前我有文章已经介绍了播放界面的功能实现(Android音乐播放器开发),但介绍 ...

  4. Android音乐播放器开发(5)—播放界面(播放、暂停、上一首、下一首,顺序播放、随机播放、拖拽进度条…)

    1. 说明 源码已同步到Gitee仓库,Github仓库,觉得还不错的话帮忙点个"star"吧,非常感谢. Android播放器专栏其它文章: 服务端:Android音乐播放器开发 ...

  5. android 播放音乐 自定义控件,Android音乐播放器开发小记——项目简介

    项目源码: 前言 之前做的App多半是巡检,点检类的,一直想开发不同类型的APP来练习.所以选择做一款音乐播放器,基本可以涵盖android所有的基础知识,比如四大组件,自定义控件,网络请求,跨进程通 ...

  6. android音乐播放器底部,android音乐播放器开发中所遇到的bug及解决方法

    这几天在写一个android版的音乐播放器,在开发过程中遇到了一些小问题 小bug,最后经过一番调试与查找 终于发现问题所在.现在将开发中的问题与解决方法分享给大家... 一,音乐播放器的界面我采用的 ...

  7. android音乐播放器开发 SweetMusicPlayer 播放本地音乐

    代码地址:https://github.com/huweigoodboy/SweetMusicPlayer 上一篇写了加载歌曲列表,http://blog.csdn.net/huweigoodboy/ ...

  8. android音乐播放器开发 SweetMusicPlayer 实现思路

    代码地址:https://github.com/huweigoodboy/SweetMusicPlayer 一,实现效果 目前还不是特别完善,主要有以下几个功能, 1,加载歌曲列表(实现a-z字母检索 ...

  9. android音乐播放器开发 SweetMusicPlayer 载入歌曲列表

    上一篇写了播放器的总体实现思路,http://blog.csdn.net/huweigoodboy/article/details/39855653,如今来总结下载入歌曲列表. 代码地址:https: ...

最新文章

  1. Android系统原理与源码分析(1):利用Java反射技术阻止通过按钮关闭对话框
  2. php开发ftp服务器搭建教程,在Linux中搭建一个FTP服务器
  3. boost::lexical_cast模块测试 Source 是否不可复制
  4. 使用imbalanced-learn处理数据不均衡问题
  5. JPA实体锁定模式的差异
  6. HTML DOM方法
  7. oldboy_09_03day
  8. MySQL小问题:导入employee测试数据
  9. 配置MAC地址表实现绑定和过滤
  10. TCP/IP,三次握手四次挥手,TCP/UDP , HTTP/HTTPS
  11. 如何获取集合里面的下标_怎样获取list集合中的最后一个对象中的值
  12. 阅卷系统java语言_利用Python开发智能阅卷系统
  13. 助教日志_沈航1.2班第一二周作业
  14. tumblr_使用Google Analytics(分析)获取有关您的Tumblr博客的详细统计信息
  15. 伽卡他卡终极毁灭版---如何卸载!!!
  16. UEBA在信息安全领域的使用
  17. GDPR(欧盟通用数据保护条例)基础知识
  18. 单片机实验三(1):中断处理,定时器
  19. bonjour 概述
  20. 雪碧图HTML人物,animateSprite-可控制雪碧图(sprites)动画的jQuery插件

热门文章

  1. 国家何时整治程序员的高薪现象
  2. php对电话号码生成图片,帝国CMS插件之手机号码生成图片格式插件
  3. 《Photoshop Lightroom4 经典教程》—第2课2.1节入门
  4. oracle update语句提交,Oracle UPDATE语句使用示例
  5. Tomcat启动后出现乱码
  6. AMT49105:高度集成的 ASIL BLDC MOSFET驱动器IC
  7. mysql 鼠标右键无法使用_笔记本电脑鼠标右键无反应怎么办
  8. ASP.NET DataBinder.Eval()
  9. 二、Vue2.0项目结构内容及配置解析
  10. linebreak_vue-cli构建的项目,eslint一直报CRLF/LF的linebreak错误