音乐播放器2.0
这次的需求和之前多了很多内容,且需要按照客户的既定设计来编写布局
之前的音乐播放器为
Android入门系列(十二):在线本地音乐播放、专辑图片、ID3信息、进度控制、AudioFocus处理、频谱显示
一、需求
-
支持本地音乐文件播放 -
支持播放、暂停、上一首、下一首等功能 -
支持随机/单曲循环/列表循环播放 -
支持后台播放 -
支持ID3信息显示 -
记忆播放文件 -
界面显示正常 -
支持音乐的后台播放和切换 -
支持侧滑菜单显示 -
实现音乐波形图 -
显示专辑图片 -
音乐列表界面 -
支持显示播放进度 -
支持进度拖动 -
支持按键控制 -
支持播放进度恢复 -
实现AudioFocus处理 -
支持在线音频播放 -
支持频谱显示 -
支持歌词根据播放进度显示 -
支持播放本地视频和在线视频 -
实现播放音乐时,专辑图片转动显示 -
实现收藏歌单的添加和播放
二、分析
和上一次的比较,相较于多并且比较有挑战性的功能包括,通知栏控制歌曲,歌词根据播放进度显示,收藏。其余的大部分之前都做过,这里简单说一下就ok
整体做下来还是有很深的感触,不过相较于上一次,多了很多对异步和线程的理解,对服务的理解,以及对安卓的其它组件等更加熟练了,这也算是个进步吧我想
三、代码模块分析
1. 项目结构代码
还是先看结构图
各类的作用如下
CustomViewPager:关于主菜单左右滑动的页面管理的布局类
DBHelper:数据库不用多说了
FileUtil:工具类,实现了inputsteam转string的功能
HomeFragment:主页fragment页面,用来显示歌曲列表和轮播图等
ID3Helper:没有改动,id3信息解析工具类,和之前的一样
InternetMusicActivity:在线音乐播放页面,一个intent过去的单独页面
InternetVideoActivity:同上
LeftFragment:主页侧边栏的fragment,显示登陆后的信息
LocalVideoActivity:本地视频播放页面
LoginActivity:登录页面,也是启动软件后第一个页面
LrcView:显示歌词的view,研究了两天,这个view还是和本地lrc歌词文件在一起比较好,我是通过在线获取歌词的所以效果体现不出来
MainActivity:主页,包含了三个fragment,left,home和my
MusicActivity:音乐详情页,和之前项目的localactivity差不多
MusicApplication:全局application,用来给服务获取全局对象的
MusicFrequencyView:也没有改动,显示频谱的view
MusicList:歌曲列表的实体类
MusicService:最重要的类,音乐播放服务
MyFragment:主页右边的fragment,用来显示收藏歌曲
NotificationClickReceiver:通知栏的广播接收器,控制通知栏按钮点击
RecordActivity:记忆播放文件的acitivy,用来显示之前的记录
RegisterActivity:有登录就有注册,先注册在登陆
UriUtils:uri工具类,通过uri获取path,改进后兼容android10
Utils:这个是bitmap变成圆bitmap工具类,切割作用
layout结构如下所示
这个就不说了,一看也应该知道是什么,view_list和1这两个分别是homefragment每首歌显示的list和my的每首歌显示的list
2.数据库设计
讲项目肯定是要先说数据库,上代码
DBHelper.java
package com.thundersoft.mediaplayer2;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
public class DBHelper extends SQLiteOpenHelper{
private static final String DB_NAME = "music2.db";
private static final String TBL_NAME_USER = "UserTbl";
private static final String TBL_NAME_MUSIC = "MusicTbl";
private static final String TBL_NAME_Like = "LikeTbl";
private static final String TBL_NAME_RECORD = "RecordTbl";
private SQLiteDatabase db;
DBHelper(Context c){
super(c, DB_NAME, null, 1);
}
@Override
public void onCreate(SQLiteDatabase db){
this.db = db;
String CREATE_TBL_USER = "create table UserTbl(_id integer primary key autoincrement, username text unique, password varchar, email varchar)";
String CREATE_TBL_MUSIC = "create table MusicTbl(_id integer primary key autoincrement, musicname varchar, artist varchar, path varchar)";
String CREATE_TBL_LIKE = "create table LikeTbl(_id integer primary key autoincrement, _userid integer, musicname varchar)";
String CREATE_TBL_RECORD = "create table RecordTbl(_id integer primary key autoincrement, time varchar, p varchar, path varchar)";
db.execSQL(CREATE_TBL_USER);
db.execSQL(CREATE_TBL_MUSIC);
db.execSQL(CREATE_TBL_LIKE);
db.execSQL(CREATE_TBL_RECORD);
}
public int findUserByName(String name){
int id;
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_USER, null, null, null, null, null, null);
while (c.moveToNext()){
int usernameIndex = c.getColumnIndex("username");
String usernameQ = c.getString(usernameIndex);
if (usernameQ.equals(name)){
int idIndex = c.getColumnIndex("_id");
id = c.getInt(idIndex);
return id;
}
}
return 0;
}
public Cursor queryL(){
SQLiteDatabase db = getWritableDatabase();
Cursor c = db.query(TBL_NAME_Like, null, null, null, null, null, null);
return c;
}
public void like(ContentValues values){
SQLiteDatabase db = getWritableDatabase();
db.insert(TBL_NAME_Like, null, values);
db.close();
}
public Cursor queryP(){
SQLiteDatabase db = getWritableDatabase();
Cursor c = db.query(TBL_NAME_RECORD, null, null, null, null, null, null);
return c;
}
public String getP(int id){
String p = "";
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_RECORD, null, null, null, null, null, null);
while (c.moveToNext()){
int idIndex = c.getColumnIndex("_id");
int idQ = c.getInt(idIndex);
if (idQ == id){
int pathIndex = c.getColumnIndex("p");
p = c.getString(pathIndex);
return p;
}
}
return p;
}
public String getPathP(int id){
String path = "";
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_RECORD, null, null, null, null, null, null);
while (c.moveToNext()){
int idIndex = c.getColumnIndex("_id");
int idQ = c.getInt(idIndex);
if (idQ == id){
int pathIndex = c.getColumnIndex("path");
path = c.getString(pathIndex);
return path;
}
}
return path;
}
public void delP(int id){
if(db == null){
db = getWritableDatabase();
}
db.delete(TBL_NAME_RECORD, "_id=?", new String[]{String.valueOf(id)});
}
public void insertP(ContentValues values){
SQLiteDatabase db = getWritableDatabase();
db.insert(TBL_NAME_RECORD, null, values);
db.close();
}
public void insertUser(ContentValues values){
SQLiteDatabase db = getWritableDatabase();
db.insert(TBL_NAME_USER, null, values);
db.close();
}
public void insertMusic(ContentValues values){
SQLiteDatabase db = getWritableDatabase();
db.insert(TBL_NAME_MUSIC, null, values);
db.close();
}
public Cursor queryAllUser(){
SQLiteDatabase db = getWritableDatabase();
Cursor c = db.query(TBL_NAME_USER, null, null, null, null, null, null);
return c;
}
public Cursor queryAllMusic(){
SQLiteDatabase db = getWritableDatabase();
Cursor c = db.query(TBL_NAME_MUSIC, null, null, null, null, null, null);
return c;
}
public String getPwd(String username){
String pwd = "";
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_USER, null, null, null, null, null, null);
while (c.moveToNext()){
int usernameIndex = c.getColumnIndex("username");
String usernameQ = c.getString(usernameIndex);
if (usernameQ.equals(username)){
int pwdIndex = c.getColumnIndex("password");
pwd = c.getString(pwdIndex);
return pwd;
}
}
return pwd;
}
public String getEmail(String username){
String email = "";
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_USER, null, null, null, null, null, null);
while (c.moveToNext()){
int usernameIndex = c.getColumnIndex("username");
String usernameQ = c.getString(usernameIndex);
if (usernameQ.equals(username)){
int emailIndex = c.getColumnIndex("email");
email = c.getString(emailIndex);
return email;
}
}
return email;
}
public void deleteUser(int id){
if(db == null){
db = getWritableDatabase();
}
db.delete(TBL_NAME_USER, "_id=?", new String[]{String.valueOf(id)});
}
public void deleteMusic(int id){
if(db == null){
db = getWritableDatabase();
}
db.delete(TBL_NAME_MUSIC, "_id=?", new String[]{String.valueOf(id)});
}
public void deleteMusic(String musicname){
if(db == null){
db = getWritableDatabase();
}
db.delete(TBL_NAME_MUSIC, "musicname=?", new String[]{String.valueOf(musicname)});
}
public void deleteLike(int id,String musicname){
if(db == null){
db = getWritableDatabase();
}
db.delete(TBL_NAME_Like, "_userid=? and musicname=?", new String[]{String.valueOf(id),String.valueOf(musicname)});
}
public Cursor query(){
SQLiteDatabase db = getWritableDatabase();
Cursor c = db.query(TBL_NAME_MUSIC, null, null, null, null, null, null);
return c;
}
public String getPath(int id){
String path = "";
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_MUSIC, null, null, null, null, null, null);
while (c.moveToNext()){
int idIndex = c.getColumnIndex("_id");
int idQ = c.getInt(idIndex);
if (idQ == id){
int pathIndex = c.getColumnIndex("path");
path = c.getString(pathIndex);
return path;
}
}
return path;
}
public String getPath(String musicname){
String path = "";
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_MUSIC, null, null, null, null, null, null);
while (c.moveToNext()){
int musicnameIndex = c.getColumnIndex("musicname");
String musicnameQ = c.getString(musicnameIndex);
if (musicname.contains(musicnameQ)){
int pathIndex = c.getColumnIndex("path");
path = c.getString(pathIndex);
return path;
}
}
return path;
}
public String getArtist(String musicname){
String artist = "";
db =getWritableDatabase();
Cursor c = db.query(TBL_NAME_MUSIC, null, null, null, null, null, null);
while (c.moveToNext()){
int musicnameIndex = c.getColumnIndex("musicname");
String musicnameQ = c.getString(musicnameIndex);
if (musicnameQ.equals(musicname)){
int artistIndex = c.getColumnIndex("artist");
artist = c.getString(artistIndex);
return artist;
}
}
return artist;
}
public void close(){
if(db != null){
db.close();
}
}
@Override
public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
}
}
这里面包含了四张表,和之前相同的是音乐列表和记录回复表,多出了用户表和用户收藏表
值得一提的是,多设计一张用户收藏表是实现收藏最简单的方式,只有id,用户id和music名字三个字段,收藏了就增,取消收藏了就删,默认显示刷新就查。
多说一句,我最开始想的是把音乐收藏放在音乐列表里,我在音乐表里加一个字段叫likeid,当我id为1的用户收藏了这首歌,这首歌的字段就是1,当我id为2的收藏了,就是1,2,以此类推,但是感觉查询的时候不是很容易,数据库的方便的工作比较繁杂
3.登录注册模块
很传统的登陆注册
先放布局
activity_login.xml
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/back"
android:gravity="center"
android:stretchColumns="0,3">
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="用户名:"
android:textSize="30dp"/>
<EditText
android:id="@+id/et_username_log"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
android:hint="请输入你的用户名"
android:textSize="20dp"/>
<TextView/>
</TableRow>
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="密码:"
android:textSize="30dp"/>
<EditText
android:id="@+id/et_password_log"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入你的密码"
android:inputType="textPassword"
android:textSize="20dp"/>
<TextView/>
</TableRow>
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<CheckBox
android:id="@+id/cb_remember"
android:text="记住用户名和密码"
android:textSize="15dp"/>
<TextView/>
</TableRow>
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<Button
android:id="@+id/bt_login"
android:layout_height="wrap_content"
android:layout_width="200px"
android:text="登录"
android:textSize="25dp"
android:backgroundTint="@color/c2"
/>
<TextView/>
</TableRow>
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<Button
android:id="@+id/bt_register_log"
android:layout_height="wrap_content"
android:layout_width="200px"
android:text="注册"
android:textSize="25dp"
android:backgroundTint="@color/c2"
/>
<TextView/>
</TableRow>
</TableLayout>
activity_register.xml
<?xml version="1.0" encoding="utf-8"?>
<TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@drawable/back2"
android:gravity="center"
android:stretchColumns="0,3">
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="用户名:"
android:textSize="30dp"/>
<EditText
android:id="@+id/et_username_reg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:inputType="text"
android:hint="请输入你的用户名"
android:textSize="20dp"/>
<TextView/>
</TableRow>
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="密码:"
android:textSize="30dp"/>
<EditText
android:id="@+id/et_password_reg"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入你的密码"
android:inputType="textPassword"
android:textSize="20dp"/>
<TextView/>
</TableRow>
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<TextView
android:layout_height="wrap_content"
android:layout_width="wrap_content"
android:text="邮箱:"
android:textSize="30dp"/>
<EditText
android:id="@+id/et_email"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:hint="请输入你的邮箱"
android:inputType="textEmailAddress"
android:textSize="20dp"/>
<TextView/>
</TableRow>
<TableRow
android:layout_height="wrap_content"
android:layout_width="wrap_content"
>
<TextView/>
<Button
android:id="@+id/bt_register_reg"
android:layout_height="wrap_content"
android:backgroundTint="@color/c2"
android:layout_width="200px"
android:text="注册"
android:textSize="25dp"
/>
<TextView/>
</TableRow>
</TableLayout>
LoginActivity.java
package com.thundersoft.mediaplayer2;
import android.content.ContentValues;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class LoginActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_login);
SharedPreferences sp = null;
EditText username = findViewById(R.id.et_username_log);
EditText password = findViewById(R.id.et_password_log);
Button login = findViewById(R.id.bt_login);
Button register = findViewById(R.id.bt_register_log);
CheckBox remember = findViewById(R.id.cb_remember);
String TAG = "LoginActivityTAG";
final DBHelper dbHelper = new DBHelper(this);
sp = this.getSharedPreferences("userinfo", Context.MODE_PRIVATE);
if (sp.getBoolean("checkboxBoolean", false))
{
username.setText(sp.getString("user", null));
password.setText(sp.getString("pwd", null));
remember.setChecked(true);
}
SharedPreferences finalSp = sp;
login.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String usernameS = username.getText().toString();
String passwordS = password.getText().toString();
if(usernameS.trim().equals("")){
Toast.makeText(LoginActivity.this, "请您输入用户名!", Toast.LENGTH_SHORT).show();
return;
}
if(passwordS.trim().equals("")){
Toast.makeText(LoginActivity.this, "请您输入密码!", Toast.LENGTH_SHORT).show();
return;
}
ContentValues values = new ContentValues();
String pwd = dbHelper.getPwd(usernameS);
if(!passwordS.equals(pwd)){
Toast.makeText(LoginActivity.this, "用户名或密码错误!", Toast.LENGTH_SHORT).show();
return;
}
Cursor cursor = dbHelper.queryAllUser();
while (cursor.moveToNext()){
int id = cursor.getColumnIndex("_id");
int username1 = cursor.getColumnIndex("username");
int password1 = cursor.getColumnIndex("password");
int email = cursor.getColumnIndex("email");
int anInt = cursor.getInt(id);
String string = cursor.getString(username1);
String string1 = cursor.getString(password1);
String string2 = cursor.getString(email);
Log.i(TAG, "用户"+anInt+"的用户名是:"+string+" 密码是:"+string1+" 邮箱是:"+string2);
}
SharedPreferences.Editor editor = finalSp.edit();
if (remember.isChecked())
{
editor.putString("user", usernameS);
editor.putString("pwd", passwordS);
editor.putBoolean("checkboxBoolean", true);
}
else
{
editor.putString("user", null);
editor.putString("pwd", null);
editor.putBoolean("checkboxBoolean", false);
}
editor.commit();
Toast.makeText(LoginActivity.this, "登录成功!", Toast.LENGTH_SHORT).show();
Intent intent=new Intent(LoginActivity.this,MainActivity.class);
intent.putExtra("username", usernameS);
startActivity(intent);
finish();
}
});
register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Intent intent=new Intent(LoginActivity.this,RegisterActivity.class);
startActivity(intent);
}
});
}
}
RegisterActivity.java
package com.thundersoft.mediaplayer2;
import android.content.ContentValues;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
public class RegisterActivity extends AppCompatActivity {
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_register);
EditText username = findViewById(R.id.et_username_reg);
EditText password = findViewById(R.id.et_password_reg);
EditText email = findViewById(R.id.et_email);
Button register = findViewById(R.id.bt_register_reg);
register.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String usernameS = username.getText().toString();
String passwordS = password.getText().toString();
String emailS = email.getText().toString();
if(usernameS.trim().equals("")){
Toast.makeText(RegisterActivity.this, "请您输入用户名!", Toast.LENGTH_SHORT).show();
return;
}
if(passwordS.trim().equals("")){
Toast.makeText(RegisterActivity.this, "请您输入密码!", Toast.LENGTH_SHORT).show();
return;
}
if(emailS.trim().equals("")){
Toast.makeText(RegisterActivity.this, "请您输入邮箱!", Toast.LENGTH_SHORT).show();
return;
}
ContentValues values = new ContentValues();
values.put("username",usernameS);
values.put("password",passwordS);
values.put("email",emailS);
DBHelper helper = new DBHelper(getApplicationContext());
helper.insertUser(values);
helper.close();
Toast.makeText(RegisterActivity.this, "注册成功!", Toast.LENGTH_SHORT).show();
Intent intent=new Intent(RegisterActivity.this,LoginActivity.class);
startActivity(intent);
finish();
}
});
}
}
先说注册吧,很传统的静态检测以后没有输入用户名密码信息什么的,然后登录,这里可以写一个是否有相同用户名判断操作,和歌曲重复添加是一个写法,后面说,我这里没有写
登录主要利用了 SharedPreferences共享,通过键值对存入登录密码的信息下次登录就不用再输入了,因为没有用户表查看数据库,这里写了个log显示所有用户方便查看,登录成功了就进到主页
4.全局对象
MusicApplication.java
package com.thundersoft.mediaplayer2;
import android.app.Application;
public class MusicApplication extends Application {
private static final String TAG = "MusicApplicationTAG";
private static MusicApplication mApp;
@Override
public void onCreate() {
super.onCreate();
mApp = this;
}
public static MusicApplication getApp() {
return mApp;
}
}
获得一个静态appliction对象,还要再清单里注册,这里先全部贴出来
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.thundersoft.mediaplayer2">
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.SET_WALLPAPER"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-permission android:name="android.permission.KILL_BACKGROUND_PROCESSES"/>
<uses-permission android:name="android.permission.RESTART_PACKAGES"/>
<uses-permission android:name="android.permission.READ_CONTACTS"/>
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.RECORD_AUDIO"/>
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE"/>
<application
android:allowBackup="true"
android:name=".MusicApplication"
android:icon="@mipmap/icon_app_logo"
android:label="@string/app_name"
android:usesCleartextTraffic="true"
android:roundIcon="@mipmap/icon_app_logo"
android:supportsRtl="true"
android:requestLegacyExternalStorage="true"
android:theme="@style/Theme.MediaPlayer2">
<activity
android:name=".LoginActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity android:name=".RegisterActivity"
android:launchMode="singleTask"/>
<activity android:name=".MainActivity"
android:launchMode="singleTask"
>
</activity>
<activity android:name=".MusicActivity"
android:launchMode="singleTask"
android:exported="false">
</activity>
<service android:name=".MusicService"
android:exported="true"/>
<activity android:name=".RecordActivity"/>
<receiver android:name=".NotificationClickReceiver"/>
<activity android:name=".LocalVideoActivity"/>
<activity android:name=".InternetMusicActivity"/>
<activity android:name=".InternetVideoActivity"/>
</application>
</manifest>
清单文件里第23行进行了注册,就可以直接用这个对象啦
5.侧边栏、两个页面滑动
MainActivity.java
package com.thundersoft.mediaplayer2;
import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentPagerAdapter;
import androidx.fragment.app.FragmentTransaction;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.viewpager.widget.ViewPager;
import android.app.Notification;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageManager;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Color;
import android.graphics.Matrix;
import android.os.Bundle;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.Gravity;
import android.view.View;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.RemoteViews;
import android.widget.TextView;
import java.util.ArrayList;
import java.util.List;
public class MainActivity extends AppCompatActivity {
private static final String TAG = "MainActivityTAG";
List<Fragment> fragments;
TextView tv_home;
TextView tv_my;
ImageView iv_home;
ImageView iv_my;
ImageView scrollbar;
CustomViewPager viewPager;
LinearLayout ll_home,ll_my;
public static NotificationManager manager;
FragmentManager fragmentManager;
int state = 0;
int currIndex = 0;
int one;
int offset = 0;
int bmpW;
LeftFragment left;
DrawerLayout drawer_layout;
View top;
ImageView iv_left;
String username = "";
String email = "";
final DBHelper dbHelper = new DBHelper(this);
ImageView iv_m_pic;
TextView tv_m_name;
ImageView iv_pre,iv_start,iv_next;
Notification notification;
int id;
RemoteViews remoteViews;
static MainActivity appCompatActivity;
public static MainActivity getInstance() {
return appCompatActivity;
}
public int getId() {
return id;
}
@Override
protected void onDestroy() {
super.onDestroy();
if(manager != null ){
manager.cancel(1);
}
}
@Override
protected void onResume() {
super.onResume();
if (MusicActivity.getInstance()!=null){
if (MusicActivity.getInstance().isPlay()){
iv_start.setImageResource(R.drawable.pause);
}else {
iv_start.setImageResource(R.drawable.play);
}
}
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_main);
String[] PERMISSIONS = {"android.permission.READ_EXTERNAL_STORAGE","android.permission.WRITE_EXTERNAL_STORAGE"
,"android.permission.MODIFY_AUDIO_SETTINGS","android.permission.RECORD_AUDIO","android.permission.READ_CONTACTS"};
int permission = ContextCompat.checkSelfPermission(MainActivity.this, "android.permission.READ_CONTACTS"+"android.permission.WRITE_EXTERNAL_STORAGE"+
"android.permission.MODIFY_AUDIO_SETTINGS"+"android.permission.RECORD_AUDIO"+"android.permission.READ_CONTACTS");
if (permission != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS,1);
}
iv_m_pic = findViewById(R.id.iv_m_pic);
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(MainActivity.this);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("song");
BroadcastReceiver mItemViewListClickReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String str = intent.getStringExtra("name");
Log.i(TAG, "onReceive: 收到的广播"+str);
tv_m_name.setText(" "+str);
String path = dbHelper.getPath(str);
Bitmap img = MusicService.getImg(path);
iv_m_pic.setImageBitmap(img);
}
};
broadcastManager.registerReceiver(mItemViewListClickReceiver,intentFilter);
tv_home = findViewById(R.id.tv_home);
tv_my = findViewById(R.id.tv_my);
iv_home = findViewById(R.id.iv_home);
iv_my = findViewById(R.id.iv_my);
scrollbar = findViewById(R.id.scrollbar);
viewPager = (CustomViewPager) findViewById(R.id.viewPager);
ll_home = findViewById(R.id.ll_home);
ll_my = findViewById(R.id.ll_my);
iv_pre = findViewById(R.id.iv_pre);
iv_pre.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
Log.i(TAG, "onClick: pre");
preMusic();
}
});
iv_start = findViewById(R.id.iv_play);
iv_start.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
playOrPause();
}
});
iv_next = findViewById(R.id.iv_next);
iv_next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
nextMusic();
}
});
tv_m_name = findViewById(R.id.tv_m_name);
tv_m_name.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
}
});
left = new LeftFragment();
initDrawer();
Intent intent = getIntent();
username = intent.getStringExtra("username");
email = dbHelper.getEmail(username);
id = dbHelper.findUserByName(username);
Log.i(TAG, "当前登录的用户id是: "+id);
Bundle bundle = new Bundle();
bundle.putString("t1",username);
bundle.putString("t2",email);
left.setArguments(bundle);
fragmentManager=getSupportFragmentManager();
FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction();
fragmentTransaction.add(R.id.left_menu,left);
fragmentTransaction.commit();
ll_home.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewPager.setCurrentItem(0);
}
});
ll_my.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
viewPager.setCurrentItem(1);
}
});
initPager();
}
private void preMusic(){
if (MusicActivity.getInstance()!=null){
MusicActivity.getInstance().preMusic();
if (MusicActivity.getInstance().isPlay()){
iv_start.setImageResource(R.drawable.pause);
}else {
iv_start.setImageResource(R.drawable.play);
}
}
}
private void nextMusic(){
if (MusicActivity.getInstance()!=null){
MusicActivity.getInstance().nextMusic();
if (MusicActivity.getInstance().isPlay()){
iv_start.setImageResource(R.drawable.pause);
}else {
iv_start.setImageResource(R.drawable.play);
}
}
}
private void playOrPause(){
if (MusicActivity.getInstance()!=null){
MusicActivity.getInstance().playOrPause();
if (MusicActivity.getInstance().isPlay()){
iv_start.setImageResource(R.drawable.pause);
}else {
iv_start.setImageResource(R.drawable.play);
}
}
}
private void initPager() {
fragments=new ArrayList<Fragment>();
fragments.add(new HomeFragment());
fragments.add(new MyFragment());
MyPagerAdapter myPagerAdapter=new MyPagerAdapter(fragmentManager);
viewPager.setAdapter(myPagerAdapter);
viewPager.addOnPageChangeListener(new MyOnPageChangeListener());
bmpW = BitmapFactory.decodeResource(getResources(), R.mipmap.scrollbar).getWidth();
DisplayMetrics displayMetrics = new DisplayMetrics();
getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
int screenW = displayMetrics.widthPixels;
offset = (screenW / 2 - bmpW) / 2;
one = offset * 2 + bmpW;
Matrix matrix = new Matrix();
matrix.postTranslate(offset, 0);
scrollbar.setImageMatrix(matrix);
viewPager.setScanScroll(true);
}
private void initDrawer() {
drawer_layout = (DrawerLayout) findViewById(R.id.drawer_layout);
top = findViewById(R.id.top);
iv_left= (ImageView) top.findViewById(R.id.iv_left);
iv_left.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
drawer_layout.openDrawer(Gravity.LEFT);
drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_UNLOCKED,
Gravity.LEFT);
}
});
drawer_layout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED,
Gravity.LEFT);
drawer_layout.setDrawerListener(new DrawerLayout.DrawerListener() {
@Override
public void onDrawerSlide(View view, float v) {
}
@Override
public void onDrawerOpened(View view) {
}
@Override
public void onDrawerClosed(View view) {
drawer_layout.setDrawerLockMode(
DrawerLayout.LOCK_MODE_LOCKED_CLOSED, Gravity.LEFT);
}
@Override
public void onDrawerStateChanged(int i) {
}
});
left.setDrawerLayout(drawer_layout);
}
public class MyPagerAdapter extends FragmentPagerAdapter
{
public MyPagerAdapter(@NonNull FragmentManager fm) {
super(fm);
}
@NonNull
@Override
public Fragment getItem(int position) {
return fragments.get(position);
}
@Override
public int getCount() {
return fragments.size();
}
}
public class MyOnPageChangeListener implements ViewPager.OnPageChangeListener {
@Override
public void onPageSelected(int arg0) {
Animation animation = null;
switch (arg0) {
case 0:
animation = new TranslateAnimation(one, 0, 0, 0);
tv_home.setTextColor(Color.rgb(0,0,0));
tv_my.setTextColor(Color.rgb(117,117,117));
iv_home.setImageResource(R.drawable.home2);
iv_my.setImageResource(R.drawable.my1);
break;
case 1:
animation = new TranslateAnimation(offset, one, 0, 0);
tv_home.setTextColor(Color.rgb(117,117,117));
tv_my.setTextColor(Color.rgb(0,0,0));
iv_home.setImageResource(R.drawable.home1);
iv_my.setImageResource(R.drawable.my2);
break;
}
currIndex = arg0;
animation.setFillAfter(true);
animation.setDuration(200);
scrollbar.startAnimation(animation);
}
@Override
public void onPageScrolled(int arg0, float arg1, int arg2) {
}
@Override
public void onPageScrollStateChanged(int arg0) {
}
}
}
这边有一个leftfragment和两个左右滑动的fragment是实现
侧边栏的实现主要在229-252,主要是通过intent拿到了主页的值,将用户名和邮箱拿过来显示出来
顶部的view
view_top.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="wrap_content"
android:background="@color/c2">
<ImageView
android:id="@+id/iv_left"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_alignParentLeft="true"
android:layout_alignParentTop="true"
android:layout_marginLeft="15dp"
android:layout_marginTop="8.5dp"
android:background="@drawable/index" />
</RelativeLayout>
在MainActivity的336设置侧边栏的点击显示
LeftFragment.java
package com.thundersoft.mediaplayer2;
import android.content.Context;
import android.os.Bundle;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.drawerlayout.widget.DrawerLayout;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager;
import org.w3c.dom.Text;
public class LeftFragment extends Fragment{
private DrawerLayout drawer_layout;
private FragmentManager fManager;
String username = "";
String email = "";
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_left, container, false);
TextView username = view.findViewById(R.id.tv_username);
TextView email = view.findViewById(R.id.tv_email);
fManager = getActivity().getSupportFragmentManager();
Bundle arguments = this.getArguments();
String t1 = arguments.getString("t1");
String t2 = arguments.getString("t2");
username.setText(t1);
email.setText(t2);
return view;
}
public void setDrawerLayout(DrawerLayout drawer_layout){
this.drawer_layout = drawer_layout;
}
}
fragment_left.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/c1"
android:orientation="vertical">
<ImageView
android:layout_gravity="center"
android:layout_width="100dp"
android:layout_height="wrap_content"
android:src="@drawable/head"/>
<TextView
android:textSize="30dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="20dp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="用户名: "/>
<TextView
android:id="@+id/tv_username"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="null"
android:textSize="20dp" />
</LinearLayout>
<TextView
android:textSize="30dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:textSize="20dp"
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="邮箱: "/>
<TextView
android:id="@+id/tv_email"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:gravity="center"
android:text="null"
android:textSize="20dp" />
</LinearLayout>
<TextView
android:textSize="30dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""/>
</LinearLayout>
两个左右滑动的fragment切换实现代码主要在254-267
通过一个ArrayList来吧左右滑动的fragment存进来,通过适配器操作
CustomViewPager
package com.thundersoft.mediaplayer2;
import android.content.Context;
import android.util.AttributeSet;
import android.view.MotionEvent;
import androidx.viewpager.widget.ViewPager;
public class CustomViewPager extends ViewPager {
private boolean isCanScroll = true;
public CustomViewPager(Context context) {
super(context);
}
public CustomViewPager(Context context, AttributeSet attrs) {
super(context, attrs);
}
public void setScanScroll(boolean isCanScroll) {
this.isCanScroll = isCanScroll;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
return isCanScroll && super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
return isCanScroll && super.onTouchEvent(ev);
}
}
375后面是适配器的内部类,用起来比较方便,不需要调来调去的
6.歌曲列表
这个和之前是一样的,通过文件选择,在选择结束方法里插入数据库并且显示
HomeFragment.java
package com.thundersoft.mediaplayer2;
import android.annotation.SuppressLint;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.net.Uri;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.viewpager.widget.PagerAdapter;
import androidx.viewpager.widget.ViewPager;
import android.os.Handler;
import android.os.Parcelable;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.SimpleCursorAdapter;
import android.widget.TextView;
import android.widget.Toast;
import java.io.File;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
public class HomeFragment extends Fragment {
String TAG = "HomeFragmentTAG";
MainActivity mainActivity;
ID3Helper id3Helper;
DBHelper helper;
private UriUtils uriUtils;
private List<MusicList> musicLists = new ArrayList<>();
ListView listView;
MusicListAdapter musicListAdapter;
int id1;
private ViewPager mViewPaper;
private List<ImageView> images;
private List<View> dots;
private int currentItem;
private int oldPosition = 0;
private int[] imageIds = new int[]{R.drawable.hb1, R.drawable.hb2, R.drawable.hb3};
private int[] imageid = new int[]{R.id.pager_image1,R.id.pager_image2,R.id.pager_image3};
private String[] titles = new String[]{"在线音乐", "本地视频", "在线视频"};
private TextView title;
private ViewPagerAdapter adapter;
private ScheduledExecutorService scheduledExecutorService;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_home, container, false);
mViewPaper = view.findViewById(R.id.viewpager);
images = new ArrayList<ImageView>();
for(int i = 0; i < imageIds.length; i++){
ImageView imageView = new ImageView(getActivity());
imageView.setBackgroundResource(imageIds[i]);
imageView.setId(imageid[i]);
imageView.setOnClickListener(new imageOnClick() );
images.add(imageView);
}
dots = new ArrayList<View>();
dots.add(view.findViewById(R.id.dot_0));
dots.add(view.findViewById(R.id.dot_1));
dots.add(view.findViewById(R.id.dot_2));
title = view.findViewById(R.id.title);
adapter = new ViewPagerAdapter();
mViewPaper.setAdapter(adapter);
mViewPaper.addOnPageChangeListener(new ViewPager.OnPageChangeListener() {
@Override
public void onPageSelected(int position) {
title.setText(titles[position]);
dots.get(position).setBackgroundResource(R.drawable.dy);
dots.get(oldPosition).setBackgroundResource(R.drawable.dn);
oldPosition = position;
currentItem = position;
Log.i(TAG, "onPageSelected: 轮播图的位置是"+position);
}
@Override
public void onPageScrollStateChanged(int state) {
}
@Override
public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
}
});
id3Helper = new ID3Helper();
uriUtils = new UriUtils();
helper = new DBHelper(getActivity());
ImageView iv_add = view.findViewById(R.id.iv_add);
iv_add.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
select();
}
});
initMusic();
musicListAdapter = new MusicListAdapter(getActivity(), R.layout.view_list, musicLists);
listView = view.findViewById(R.id.lv_m);
listView.setAdapter(musicListAdapter);
listView.setDivider(new ColorDrawable(Color.BLACK));
listView.setDividerHeight(1);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
TextView textView = view.findViewById(R.id.tv_i_musicname); String s = textView.getText().toString();
String s1 = textView.getText().toString();
String path = helper.getPath(s1);
Log.i(TAG, "发送的id是: "+id+"path是: "+path);
Intent intent = new Intent(getActivity(),MusicActivity.class);
intent.putExtra("path",path);
intent.putExtra("id",id1);
startActivity(intent);
}
});
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View arg1, int arg2, long arg3) {
builder.setMessage("真的要删除该记录吗?").setPositiveButton("是", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
TextView textView = arg1.findViewById(R.id.tv_i_musicname);
String s = textView.getText().toString();
Log.i(TAG, "onClick: "+s);
if (helper == null){
helper = new DBHelper(getActivity());
}
helper.deleteMusic(s);
initMusic();
musicListAdapter = new MusicListAdapter(getActivity(), R.layout.view_list, musicLists);
listView.setAdapter(musicListAdapter);
}
}).setNegativeButton("否", null);
AlertDialog ad = builder.create();
ad.show();
return true;
}
});
return view;
}
public class imageOnClick implements View.OnClickListener {
@Override
public void onClick(View v) {
switch (v.getId()) {
case R.id.pager_image1:
Toast.makeText(getContext(), "欢迎来到在线音乐", Toast.LENGTH_SHORT).show();
startActivity(new Intent(getContext(),InternetMusicActivity.class));
break;
case R.id.pager_image2:
Toast.makeText(getContext(), "欢迎来到本地视频", Toast.LENGTH_SHORT).show();
startActivity(new Intent(getContext(),LocalVideoActivity.class));
break;
case R.id.pager_image3:
Toast.makeText(getContext(), "欢迎来到在线视频", Toast.LENGTH_SHORT).show();
startActivity(new Intent(getContext(),InternetVideoActivity.class));
break;
}
}
}
public class ViewPagerAdapter extends PagerAdapter {
@Override
public int getCount() {
return images.size();
}
@Override
public boolean isViewFromObject(View arg0, Object arg1) {
return arg0 == arg1;
}
@Override
public void destroyItem(ViewGroup view, int position, Object object) {
view.removeView(images.get(position));
}
@Override
public Object instantiateItem(ViewGroup view, int position) {
view.addView(images.get(position));
return images.get(position);
}
}
@Override
public void onStart() {
super.onStart();
scheduledExecutorService = Executors.newSingleThreadScheduledExecutor();
scheduledExecutorService.scheduleWithFixedDelay(new ViewPageTask(), 2, 2, TimeUnit.SECONDS);
}
private class ViewPageTask implements Runnable{
@Override
public void run() {
currentItem = (currentItem + 1) % imageIds.length;
mHandler.sendEmptyMessage(0);
}
}
private Handler mHandler = new Handler(){
public void handleMessage(android.os.Message msg) {
mViewPaper.setCurrentItem(currentItem);
}
};
@Override
public void onStop() {
super.onStop();
if(scheduledExecutorService != null){
scheduledExecutorService.shutdown();
scheduledExecutorService = null;
}
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
id1 = ((MainActivity) context).getId();
Log.i(TAG, "onAttach: id是"+id1);
}
private void initMusic() {
Cursor cursor = helper.queryAllMusic();
List<MusicList> musicListsNew = new ArrayList<>();
while (cursor.moveToNext()){
int musicnameIndex = cursor.getColumnIndex("musicname");
int artistIndex = cursor.getColumnIndex("artist");
int pathIndex = cursor.getColumnIndex("path");
String musicname1 = cursor.getString(musicnameIndex);
String artist1 = cursor.getString(artistIndex);
String path1 = cursor.getString(pathIndex);
Bitmap bitmap = MusicService.getImg(path1);
SimpleDateFormat time = new SimpleDateFormat("m:ss");
long audioFileVoiceTime = MusicService.getAudioFileVoiceTime(path1);
MusicList musicList = new MusicList(musicname1,artist1,bitmap,time.format(audioFileVoiceTime)+"");
musicListsNew.add(musicList);
}
musicLists = musicListsNew;
}
private static final int FILE_SELECT_CODE = 0;
private void select() {
Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("audio/*");
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent, 1);
}
@SuppressLint("Range")
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
if (data != null) {
if (requestCode == 1) {
Uri uri = data.getData();
String path = UriUtils.getFileAbsolutePath(getContext(), uri);
File file = new File(path);
id3Helper.parseNoThrow(file);
String text = ("当前歌曲的ID3类型为:" + id3Helper.getId3Type() +
" 作者是:" + id3Helper.getArtist() +
" 歌名是:" + id3Helper.getTitle() +
" 专辑是:" + id3Helper.getFloder() +
" 年代是:" + id3Helper.getTyer() +
" 备注是:" + id3Helper.getComm());
Log.i(TAG, "该音乐信息为:" + text);
ContentValues values = new ContentValues();
values.put("musicname", id3Helper.getTitle());
values.put("artist", id3Helper.getArtist());
values.put("path", path);
helper = new DBHelper(getActivity().getApplicationContext());
ArrayList<String> names = new ArrayList<>();
Cursor c = helper.query();
while (c.moveToNext()) {
int nameIndex = c.getColumnIndex("musicname");
String name1 = c.getString(nameIndex);
names.add(name1);
}
String[] objects = names.toArray(new String[0]);
for (int i = 0; i < objects.length; i++) {
if (values.get("musicname").equals(objects[i])) {
Toast.makeText(getContext(), "请勿重复添加", Toast.LENGTH_SHORT).show();
return;
}
}
helper.insertMusic(values);
Toast.makeText(getContext(), "歌曲添加成功", Toast.LENGTH_SHORT).show();
Cursor cursor = helper.queryAllMusic();
while (cursor.moveToNext()) {
int id = cursor.getColumnIndex("_id");
int musicname1 = cursor.getColumnIndex("musicname");
int artist1 = cursor.getColumnIndex("artist");
int path1 = cursor.getColumnIndex("path");
int anInt = cursor.getInt(id);
String string = cursor.getString(musicname1);
String string1 = cursor.getString(artist1);
String string2 = cursor.getString(path1);
Log.i(TAG, "音乐" + anInt + "的音乐名是:" + string + " 作者是:" + string1 + " 路径是:" + string2);
initMusic();
musicListAdapter = new MusicListAdapter(getActivity(), R.layout.view_list, musicLists);
listView.setAdapter(musicListAdapter);
}
}
super.onActivityResult(requestCode, resultCode, data);
}
}
public class MusicListAdapter extends ArrayAdapter<MusicList> {
private int resourceId;
public MusicListAdapter(@NonNull Context context, int textViewResourceId, @NonNull List<MusicList> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
MusicList item = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
ImageView pic = view.findViewById(R.id.iv_l_pic);
TextView musicname = view.findViewById(R.id.tv_i_musicname);
TextView artist = view.findViewById(R.id.tv_i_artist);
TextView time = view.findViewById(R.id.tv_i_time);
pic.setImageBitmap(item.getPic());
musicname.setText(item.getMusicname());
artist.setText(item.getArtist());
time.setText(item.getTime()+" ");
return view;
}
}
}
代码主要在314-329的onActivityResult方法中,291的initMusic是为了方便歌曲变了界面不更新而设计的代码
356行这里有查询音乐数据库为了不重复添加歌曲,和歌曲name比较,数据库操作db那边有,这就是之前说的重复添加查询
398行的adapter就是显示歌曲每一条而写的适配器,这个适配器和myfragment里是差不多的
另外说一下listview横线很有意思的事情,155行中,设置颜色和高度必须要顺着设置,如果反过来就会看到横线是不显示的,我们点进去源码看一下
有一个getIntrinsicHeight的方法,而这方法进去看
高度为-1,也不懂android为啥要这样设置,如果先设置了颜色,高度就回为-1
fragment_home.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">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="250dp" >
<FrameLayout
android:layout_width="match_parent"
android:layout_height="230dip">
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewpager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="35dip"
android:layout_gravity="bottom"
android:background="#33000000"
android:gravity="center"
android:orientation="vertical">
<TextView
android:id="@+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text=""
android:textColor="@android:color/white" />
<LinearLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="3dip"
android:orientation="horizontal">
<View
android:id="@+id/dot_0"
android:layout_width="5dip"
android:layout_height="5dip"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:background="@drawable/dy" />
<View
android:id="@+id/dot_1"
android:layout_width="5dip"
android:layout_height="5dip"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:background="@drawable/dn" />
<View
android:id="@+id/dot_2"
android:layout_width="5dip"
android:layout_height="5dip"
android:layout_marginLeft="2dip"
android:layout_marginRight="2dip"
android:background="@drawable/dn" />
</LinearLayout>
</LinearLayout>
</FrameLayout>
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="50dp"
android:orientation="vertical">
<ImageView
android:id="@+id/iv_add"
android:layout_width="match_parent"
android:layout_height="49dp"
android:layout_gravity="center_vertical"
android:src="@drawable/add"/>
<View
android:layout_height="1px"
android:layout_width="match_parent"
android:background="#000"
/>
</LinearLayout>
<ListView
android:id="@+id/lv_m"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</ListView>
</LinearLayout>
view_list.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:layout_width="50dp"
android:layout_height="50dp"
android:src="@drawable/music"
android:id="@+id/iv_l_pic"/>
<View
android:layout_width="20dp"
android:layout_height="20dp"/>
<LinearLayout
android:layout_width="250dp"
android:layout_height="50dp"
android:orientation="vertical">
<TextView
android:id="@+id/tv_i_musicname"
android:gravity="left|center"
android:layout_width="match_parent"
android:layout_height="25dp"
android:textSize="40px"/>
<TextView
android:id="@+id/tv_i_artist"
android:gravity="left|center"
android:layout_width="match_parent"
android:layout_height="25dp"
android:textSize="40px"/>
</LinearLayout>
<TextView
android:gravity="right|center"
android:id="@+id/tv_i_time"
android:layout_width="fill_parent"
android:layout_height="50dp"
android:textSize="50px"/>
</LinearLayout>
这里值得一提的是,添加歌曲的uiview需要写在listview的上面,而且有listview的时候不要使用gridview,不然软件无法识别上滑下滑的焦点而导致不能上下移动
7.轮播图
轮播图三个图使用模板做的,网上有免费的网站直接搜索就行
HomeFragment的94到134
轮播图主要是要通过三个图片,三个标题,三个小点来组成数组,和轮播图组件配对
另外,轮播图的点击事件,需要在strings.xml里面定义三个id,如下
strings.xml
<resources>
<string name="app_name">MediaPlayer2</string>
<item name="pager_image1" type="id" />
<item name="pager_image2" type="id" />
<item name="pager_image3" type="id" />
</resources>
这里只是定义了id,和图片没有关系,接着再77行应用,再在100行set了id,这样才能通过204行点击每一个id来确定我点击的到底是哪一个轮播图
实体类
MusicList
package com.thundersoft.mediaplayer2;
import android.graphics.Bitmap;
import android.os.Parcel;
import android.os.Parcelable;
public class MusicList {
private String musicname;
private String artist;
private Bitmap pic;
private String time;
public String getMusicname() {
return musicname;
}
public String getArtist() {
return artist;
}
public Bitmap getPic() {
return pic;
}
public String getTime() {
return time;
}
public MusicList(String musicname, String artist, Bitmap pic, String time) {
this.musicname = musicname;
this.artist = artist;
this.pic = pic;
this.time = time;
}
}
8.在线音乐、视频
其实就是一个webview组件,进行了一些配置
InternetMusicActivity
package com.thundersoft.mediaplayer2;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class InternetMusicActivity extends AppCompatActivity {
private WebView webView;
String url="https://music.y444.cn/#/";
@SuppressLint({ "SetJavaScriptEnabled", "NewApi" })
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_music_internet);
webView=(WebView)findViewById(R.id.wabView);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setAllowContentAccess(true);
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
webView.loadUrl(url);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return super.shouldOverrideUrlLoading(view, url);
}
});
}
@Override
protected void onPause() {
webView.reload();
super.onPause();
}
private static int isExit=0;
Handler handler=new Handler(){
@Override
public void handleMessage(Message msg){
super.handleMessage(msg);
isExit--;
}
};
@Override
public boolean onKeyDown(int keyCode, KeyEvent event){
Toast.makeText(InternetMusicActivity.this,"滑两次退出在线音乐",Toast.LENGTH_SHORT).show();
if((keyCode== KeyEvent.KEYCODE_BACK) && webView.canGoBack())
{
webView.goBack();
return true;
}
if(keyCode==KeyEvent.KEYCODE_BACK){
isExit++;
exit();
return false;
}
return super.onKeyDown(keyCode,event);
}
private void exit(){
if(isExit<2){
handler.sendEmptyMessageDelayed(0,2000);
}else{
super.onBackPressed();
}
}
}
InternetVideoActivity
package com.thundersoft.mediaplayer2;
import android.annotation.SuppressLint;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.webkit.WebSettings;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
public class InternetVideoActivity extends AppCompatActivity {
private WebView webView;
String url="https://www.bilibili.com/";
@SuppressLint({ "SetJavaScriptEnabled", "NewApi" })
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_video_internet);
webView=(WebView)findViewById(R.id.wabView1);
webView.getSettings().setJavaScriptEnabled(true);
webView.getSettings().setAllowContentAccess(true);
webView.getSettings().setAllowFileAccess(true);
webView.getSettings().setCacheMode(WebSettings.LOAD_NO_CACHE);
webView.setLayerType(View.LAYER_TYPE_HARDWARE, null);
webView.getSettings().setMediaPlaybackRequiresUserGesture(false);
webView.loadUrl(url);
webView.setWebViewClient(new WebViewClient() {
@Override
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.loadUrl(url);
return super.shouldOverrideUrlLoading(view, url);
}
});
}
@Override
protected void onPause() {
webView.reload();
super.onPause();
}
private static int isExit=0;
}
哔哩哔哩的网页点过去之后会跳到系统自带的浏览器,这里只需要配置一个客户端就可以了见37行
两个页面的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:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<WebView
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="@+id/wabView"/>
</LinearLayout>
9.本地视频
本地视频播放使用videoview的组件,使用了一些比较简单的功能
LocalVideoActivity
package com.thundersoft.mediaplayer2;
import android.Manifest;
import android.app.Activity;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.net.Uri;
import android.os.Bundle;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.widget.Button;
import android.widget.Toast;
import android.widget.VideoView;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.app.ActivityCompat;
import androidx.core.content.ContextCompat;
import java.io.File;
public class LocalVideoActivity extends AppCompatActivity implements View.OnClickListener {
private VideoView videoView;
private static final int FILE_SELECT_CODE=1;
private static final String TAG="VideoActivity";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_video_local);
videoView=(VideoView)findViewById(R.id.video_view);
Button play=(Button)findViewById(R.id.play);
Button pause=(Button)findViewById(R.id.pause);
Button replay=(Button)findViewById(R.id.replay);
Button choice=(Button)findViewById(R.id.choice) ;
choice.setOnClickListener(this);
play.setOnClickListener(this);
pause.setOnClickListener(this);
replay.setOnClickListener(this);
if(ContextCompat.checkSelfPermission(LocalVideoActivity.this, Manifest.permission.WRITE_EXTERNAL_STORAGE)!= PackageManager.PERMISSION_GRANTED){
ActivityCompat.requestPermissions(LocalVideoActivity.this,new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},1);
}
}
public void onRequestPermissionsResult(int requestCode,String[] permissions,int[] grantResults) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
switch (requestCode) {
case 1:
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
} else {
Toast.makeText(this, "拒绝权限将无法访问程序", Toast.LENGTH_SHORT).show();
finish();
}
break;
default:
}
}
public void onClick(View v){
switch (v.getId()){
case R.id.play:
if(!videoView.isPlaying()){
videoView.start();
}
break;
case R.id.pause:
if(videoView.isPlaying()){
videoView.pause();
}
break;
case R.id.replay:
if(videoView.isPlaying()){
videoView.resume();
}
break;
case R.id.choice://选择文件
Intent intent=new Intent(Intent.ACTION_GET_CONTENT);
intent.setType("*/*");
intent.addCategory(Intent.CATEGORY_OPENABLE);
startActivityForResult(intent,1);
}
}
public void onDestroy(){
super.onDestroy();
if(videoView!=null){
videoView.suspend();
}
}
public void onActivityResult(int requestCode,int resultCode,Intent data){
if(resultCode== Activity.RESULT_OK){
Uri uri=data.getData();
videoView.setVideoURI(uri);
Toast.makeText(LocalVideoActivity.this,"视频选择成功!!!",Toast.LENGTH_SHORT).show();
super.onActivityResult(requestCode, resultCode, data);
return;
}
if (requestCode == FILE_SELECT_CODE) {
Uri uri = data.getData();
Log.i(TAG, "------->" + uri.getPath());
Toast.makeText(LocalVideoActivity.this,"视频选择成功!!!",Toast.LENGTH_SHORT).show();
}
super.onActivityResult(requestCode, resultCode, data);
}
}
activity_video_local.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
>
<VideoView
android:id="@+id/video_view"
android:layout_weight="12"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<LinearLayout
android:layout_weight="1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<Button
android:id="@+id/play"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@color/c2"
android:text="播放"/>
<Button
android:id="@+id/pause"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@color/c2"
android:text="暂停"/>
<Button
android:id="@+id/replay"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@color/c2"
android:text="重播"/>
<Button
android:id="@+id/choice"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:backgroundTint="@color/c2"
android:text="选择"/>
</LinearLayout>
</LinearLayout>
10.收藏
在音乐的详情界面有一个收藏按钮,点进去就会进行数据库操作,收藏歌曲列表如下
MyFragment
package com.thundersoft.mediaplayer2;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.os.Bundle;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.fragment.app.Fragment;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.List;
public class MyFragment extends Fragment {
String TAG = "HomeFragmentTAG";
MainActivity mainActivity;
ID3Helper id3Helper;
DBHelper helper;
private UriUtils uriUtils;
private List<MusicList> musicLists1 = new ArrayList<>();
ListView listView;
MusicListAdapter1 musicListAdapter;
int id1;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View view = inflater.inflate(R.layout.fragment_my, container, false);
id3Helper = new ID3Helper();
uriUtils = new UriUtils();
helper = new DBHelper(getActivity());
LocalBroadcastManager broadcastManager = LocalBroadcastManager.getInstance(getActivity());
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.broadcast.updateList");
BroadcastReceiver mItemViewListClickReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
initMusic();
musicListAdapter = new MusicListAdapter1(getActivity(), R.layout.view_list1, musicLists1);
listView.setAdapter(musicListAdapter);
Log.i(TAG, "onReceive: 收到收藏的广播了");
}
};
broadcastManager.registerReceiver(mItemViewListClickReceiver,intentFilter);
initMusic();
musicListAdapter = new MusicListAdapter1(getActivity(), R.layout.view_list1, musicLists1);
listView = view.findViewById(R.id.lv_m1);
listView.setAdapter(musicListAdapter);
listView.setDivider(new ColorDrawable(Color.BLACK));
listView.setDividerHeight(1);
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
TextView textView = view.findViewById(R.id.tv_i_musicname1);
String s = textView.getText().toString();
String s1 = textView.getText().toString();
String path = helper.getPath(s1);
Log.i(TAG, "发送的id是: "+id+"path是: "+path);
Intent intent = new Intent(getActivity(),MusicActivity.class);
intent.putExtra("path",path);
intent.putExtra("id",id1);
startActivity(intent);
}
});
final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
listView.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View arg1, int arg2, long arg3) {
builder.setMessage("真的要取消收藏吗?").setPositiveButton("是", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
TextView textView = arg1.findViewById(R.id.tv_i_musicname1);
String s = textView.getText().toString();
Log.i(TAG, "onClick: "+s);
helper.deleteLike(id1,s);
initMusic();
musicListAdapter = new MusicListAdapter1(getActivity(), R.layout.view_list1, musicLists1);
listView.setAdapter(musicListAdapter);
}
}).setNegativeButton("否", null);
AlertDialog ad = builder.create();
ad.show();
return true;
}
});
return view;
}
@Override
public void onActivityResult(int requestCode, int resultCode, @Nullable Intent data) {
initMusic();
musicListAdapter = new MusicListAdapter1(getActivity(), R.layout.view_list1, musicLists1);
listView.setAdapter(musicListAdapter);
Log.i(TAG, "onReceive: 收到收藏的广播了");
super.onActivityResult(requestCode, resultCode, data);
}
public void initMusic() {
Cursor cursor = helper.queryL();
List<MusicList> musicListsNew = new ArrayList<>();
while (cursor.moveToNext()){
int userIdIndex = cursor.getColumnIndex("_userid");
int musicnameIndex = cursor.getColumnIndex("musicname");
int userId = cursor.getInt(userIdIndex);
String musicname1 = cursor.getString(musicnameIndex);
String artist1 = null;
Bitmap bitmap = null;
SimpleDateFormat time = new SimpleDateFormat("m:ss");
long audioFileVoiceTime = 0;
if(id1 == userId){
String path0 = helper.getPath(musicname1);
artist1 = helper.getArtist(musicname1);
bitmap = MusicService.getImg(path0);
audioFileVoiceTime = MusicService.getAudioFileVoiceTime(path0);
}
MusicList musicList = new MusicList(musicname1,artist1,bitmap,time.format(audioFileVoiceTime)+"");
musicListsNew.add(musicList);
}
musicLists1 = musicListsNew;
}
@Override
public void onAttach(@NonNull Context context) {
super.onAttach(context);
id1 = ((MainActivity) context).getId();
Log.i(TAG, "onAttach: id是"+id1);
}
public class MusicListAdapter1 extends ArrayAdapter<MusicList> {
private int resourceId;
public MusicListAdapter1(@NonNull Context context, int textViewResourceId, @NonNull List<MusicList> objects) {
super(context, textViewResourceId, objects);
resourceId = textViewResourceId;
}
@NonNull
@Override
public View getView(int position, @Nullable View convertView, @NonNull ViewGroup parent) {
MusicList item = getItem(position);
View view = LayoutInflater.from(getContext()).inflate(resourceId, parent, false);
ImageView pic = view.findViewById(R.id.iv_l_pic1);
TextView musicname = view.findViewById(R.id.tv_i_musicname1);
TextView artist = view.findViewById(R.id.tv_i_artist1);
TextView time = view.findViewById(R.id.tv_i_time1);
pic.setImageBitmap(item.getPic());
musicname.setText(item.getMusicname());
artist.setText(item.getArtist());
time.setText(item.getTime()+" ");
return view;
}
}
}
同样的也是设置适配器和list相对应,代码差不多这里就不贴了
11.音乐播放
还是老样子,代码和之前的没有太多差别,优化了一部分
MusicService.java
package com.thundersoft.mediaplayer2;
import android.annotation.SuppressLint;
import android.annotation.TargetApi;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.media.AudioManager;
import android.media.MediaMetadataRetriever;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.Build;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.widget.RemoteViews;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Random;
public class MusicService extends Service {
private static String TAG = "MusicServiceTAG";
public ArrayList<String> songs = new ArrayList<>();
public String[] objects;
private AudioManager audioManager;
private boolean isPlaying,isRecovery;
private String url,p;
static int STATE,STATE2 = 0;
static MediaPlayer mp;
private int NOTIFICATION_ID = 1;
private static NotificationManager manager;
private static Notification notification;
public static final String PLAY = "play";
public static final String PAUSE = "pause";
public static final String PREV = "prev";
public static final String NEXT = "next";
public static final String CLOSE = "close";
public static final String PROGRESS = "progress";
private static RemoteViews remoteViews;
private MusicReceiver musicReceiver;
private void initRemoteViews() {
remoteViews = new RemoteViews(this.getPackageName(), R.layout.notification);
Intent intentPrev = new Intent(PREV);
PendingIntent prevPendingIntent = PendingIntent.getBroadcast(this, 0, intentPrev, 0);
remoteViews.setOnClickPendingIntent(R.id.btn_notification_previous, prevPendingIntent);
Intent intentPlay = new Intent(PLAY);
PendingIntent playPendingIntent = PendingIntent.getBroadcast(this, 0, intentPlay, 0);
remoteViews.setOnClickPendingIntent(R.id.btn_notification_play, playPendingIntent);
Intent intentNext = new Intent(NEXT);
PendingIntent nextPendingIntent = PendingIntent.getBroadcast(this, 0, intentNext, 0);
remoteViews.setOnClickPendingIntent(R.id.btn_notification_next, nextPendingIntent);
Intent intentClose = new Intent(CLOSE);
PendingIntent closePendingIntent = PendingIntent.getBroadcast(this, 0, intentClose, 0);
remoteViews.setOnClickPendingIntent(R.id.btn_notification_close, closePendingIntent);
}
public class MusicReceiver extends BroadcastReceiver {
public static final String TAG = "MusicReceiverTAG";
@Override
public void onReceive(Context context, Intent intent) {
UIControl(intent.getAction(), TAG);
}
}
private void sendUpdateMsg(boolean isUpdate){
Intent intent = new Intent("com.broadcast.update");
intent.putExtra("isUpdate",isUpdate);
this.sendBroadcast(intent);
}
private void UIControl(String state, String tag) {
switch (state) {
case PLAY:
playOrPause();
Log.i(TAG, PLAY + " or " + PAUSE);
sendUpdateMsg(true);
break;
case PREV:
preMusic();
Log.i(TAG, PREV);
sendUpdateMsg(true);
break;
case NEXT:
nextMusic();
Log.i(TAG, NEXT);
sendUpdateMsg(true);
break;
case CLOSE:
closeNotification();
Log.i(TAG, CLOSE);
break;
default:
break;
}
}
public void closeNotification() {
if (mp != null) {
if (mp.isPlaying()) {
mp.pause();
}
}
manager.cancel(NOTIFICATION_ID);
}
private void initNotification() {
String channelId = "play_control";
String channelName = "播放控制";
int importance = NotificationManager.IMPORTANCE_HIGH;
createNotificationChannel(channelId, channelName, importance);
Intent intent = new Intent(getApplicationContext(), NotificationClickReceiver.class);
PendingIntent pendingIntent = PendingIntent.getBroadcast(getApplicationContext(), 0,
intent, PendingIntent.FLAG_UPDATE_CURRENT);
notification = new NotificationCompat.Builder(this, "play_control")
.setContentIntent(pendingIntent)
.setWhen(System.currentTimeMillis())
.setSmallIcon(R.mipmap.icon_big_logo)
.setLargeIcon(BitmapFactory.decodeResource(getResources(), R.mipmap.icon_big_logo))
.setCustomContentView(remoteViews)
.setVisibility(NotificationCompat.VISIBILITY_PUBLIC)
.setAutoCancel(false)
.setOnlyAlertOnce(true)
.setOngoing(true)
.build();
}
@TargetApi(Build.VERSION_CODES.O)
private void createNotificationChannel(String channelId, String channelName, int importance) {
NotificationChannel channel = new NotificationChannel(channelId, channelName, importance);
channel.enableLights(false);
channel.enableVibration(false);
channel.setVibrationPattern(new long[]{0});
channel.setSound(null, null);
manager = (NotificationManager) getSystemService(
NOTIFICATION_SERVICE);
manager.createNotificationChannel(channel);
}
private void registerMusicReceiver() {
musicReceiver = new MusicReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(PLAY);
intentFilter.addAction(PREV);
intentFilter.addAction(NEXT);
intentFilter.addAction(CLOSE);
registerReceiver(musicReceiver, intentFilter);
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
MusicActivity musicActivity = new MusicActivity();
DBHelper helper = new DBHelper(getApplicationContext());
Cursor query = helper.query();
while (query.moveToNext()){
int pathIndex = query.getColumnIndex("path");
String path0 = query.getString(pathIndex);
songs.add(path0);
}
ArrayList<String> temp = this.songs;
objects = this.songs.toArray(new String[0]);
if (intent != null) {
Bundle bundle = intent.getExtras();
if (bundle != null) {
isPlaying = bundle.getBoolean("isPlay", true);
isRecovery = bundle.getBoolean("isRecovery",false);
url = bundle.getString("url");
p = bundle.getString("p");
Log.i(TAG, "onStartCommand: url"+url);
if (isPlaying){
if (requestFocus()){
if (STATE == 1){
try {
mp.reset();
mp.setDataSource(url);
mp.prepare();
if(mp != null){
}
if (isRecovery){
if(mp != null){
}
mp.seekTo(Integer.parseInt(p));
}
musicIndex = getMusicIndex(url);
updateNotificationShow(musicIndex);
} catch (IOException e) {
e.printStackTrace();
}
}
if (mp == null){
try {
mp = new MediaPlayer();
mp.setDataSource(url);
mp.prepare();
MusicActivity.show();
if (isRecovery){
mp.seekTo(Integer.parseInt(p));
}
musicIndex = getMusicIndex(url);
mp.setOnCompletionListener(completionListener);
STATE = 1;
updateNotificationShow(musicIndex);
} catch (IOException e) {
e.printStackTrace();
}
}
}
if (!mp.isPlaying()){
mp.start();
}
}
else
if (mp != null){
mp.stop();
}
}
}
return super.onStartCommand(intent, flags, startId);
}
@Override
public void onCreate() {
super.onCreate();
audioManager = (AudioManager) getSystemService(AUDIO_SERVICE);
initRemoteViews();
initNotification();
registerMusicReceiver();
}
public void updateNotificationShow(int position) {
if (mp.isPlaying()) {
remoteViews.setImageViewResource(R.id.btn_notification_play, R.drawable.pause_black);
} else {
remoteViews.setImageViewResource(R.id.btn_notification_play, R.drawable.play_black);
}
remoteViews.setImageViewBitmap(R.id.iv_album_cover, getImg(getCurrentPath()));
String currentName = getCurrentName();
String substring = currentName.substring(0, currentName.length() - 4);
remoteViews.setTextViewText(R.id.tv_notification_song_name, substring);
manager.notify(NOTIFICATION_ID, notification);
}
AudioManager.OnAudioFocusChangeListener changeListener = new AudioManager.OnAudioFocusChangeListener() {
@Override
public void onAudioFocusChange(int focusChange) {
if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT){
if (mp != null && mp.isPlaying()){
mp.pause();
}
}else if (focusChange == AudioManager.AUDIOFOCUS_GAIN){
if(mp != null){
if (STATE2 == 1){
mp.setVolume(0.5f,0.5f);
STATE2 = 0;
}
mp.start();
}
}else if (focusChange == AudioManager.AUDIOFOCUS_LOSS){
audioManager.abandonAudioFocus(changeListener);
if (mp != null){
mp.stop();
}
}else if (focusChange == AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK){
mp.setVolume(0.1f,0.1f);
STATE2 = 1;
}
}
};
MediaPlayer.OnCompletionListener completionListener = new MediaPlayer.OnCompletionListener() {
@Override
public void onCompletion(MediaPlayer player) {
if (player.isLooping()){
playMode2();
}
if (!player.isLooping()){
nextMusic();
audioManager.abandonAudioFocus(changeListener);
}
}
};
private boolean requestFocus(){
int result = audioManager.requestAudioFocus(changeListener,
AudioManager.STREAM_MUSIC,
AudioManager.AUDIOFOCUS_GAIN);
return result == AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
}
public final IBinder binder = new MyBinder();
private String path;
private int musicIndex = 1;
@Nullable
@Override
public IBinder onBind(Intent intent) {
return binder;
}
public class MyBinder extends Binder{
MusicService getService(){
return MusicService.this;
}
}
public int getMusicIndex(String str){
for (int i = 0; i < objects.length; i++) {
if (objects[i].equals(str)){
return i;
}
}
return 0;
}
@Override
public void onDestroy() {
if (mp != null){
mp.release();
}
if (musicReceiver != null) {
unregisterReceiver(musicReceiver);
}
super.onDestroy();
}
public void playOrPause(){
if(mp.isPlaying()){
mp.pause();
} else {
mp.start();
}
updateNotificationShow(musicIndex);
}
public void stop() throws IOException {
if (mp != null){
mp.stop();
mp.prepare();
mp.seekTo(0);
}
}
public String getCurrentName(){
String path4 = objects[musicIndex];
return path4.substring(path4.lastIndexOf("/") + 1, path4.length());
}
public String getCurrentPath(){
return objects[musicIndex];
}
public String getPath(int aInt){
return objects[aInt];
}
public String getId3(){
ID3Helper id3Helper = new ID3Helper();
File file = new File(objects[musicIndex]);
id3Helper.parseNoThrow(file);
return "当前歌曲的ID3类型为:"+id3Helper.getId3Type()+
" 作者是:"+id3Helper.getArtist()+
" 歌名是:"+id3Helper.getTitle()+
" 专辑是:"+id3Helper.getFloder()+
" 年代是:"+id3Helper.getTyer()+
" 备注是:"+id3Helper.getComm();
}
public void nextMusic() {
if(mp != null && musicIndex < objects.length) {
mp.stop();
try {
mp.reset();
mp.setDataSource(objects[musicIndex+1]);
musicIndex++;
mp.prepare();
mp.seekTo(0);
mp.start();
updateNotificationShow(musicIndex);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void playMode2(){
if(mp != null) {
mp.stop();
try {
mp.reset();
mp.setDataSource(objects[musicIndex]);
mp.prepare();
mp.seekTo(0);
mp.start();
updateNotificationShow(musicIndex);
} catch (Exception e) {
e.printStackTrace();
}
}
}
public void playMode3(){
if (mp != null){
mp.stop();
try {
Random random = new Random();
int randomNum;
do {
randomNum = random.nextInt(objects.length);
} while (getPath(randomNum).equals(getCurrentPath()));
musicIndex = randomNum;
mp.reset();
mp.setDataSource(objects[musicIndex]);
mp.prepare();
mp.start();
updateNotificationShow(musicIndex);
} catch (IOException e) {
e.printStackTrace();
}
}
}
public void preMusic() {
if(mp != null && musicIndex > 0) {
mp.stop();
try {
mp.reset();
mp.setDataSource(objects[musicIndex-1]);
musicIndex--;
mp.prepare();
mp.seekTo(0);
mp.start();
updateNotificationShow(musicIndex);
} catch (Exception e) {
e.printStackTrace();
}
}
}
static Bitmap getImg(String mediaUri) {
Bitmap bitmap = null;
MediaMetadataRetriever mediaMetadataRetriever=new MediaMetadataRetriever();
try {
mediaMetadataRetriever.setDataSource(mediaUri);
} catch (IllegalArgumentException e) {
e.printStackTrace();
}
if (mediaMetadataRetriever.getEmbeddedPicture() != null){
byte[] picture = mediaMetadataRetriever.getEmbeddedPicture();
bitmap= BitmapFactory.decodeByteArray(picture,0,picture.length);
return bitmap;
}
Bitmap bitmap0 = BitmapFactory.decodeResource(MusicApplication.getApp().getResources(), R.drawable.music);
return bitmap0;
}
public int getId(){
return mp.getAudioSessionId();
}
static long getAudioFileVoiceTime(String filePath) {
long mediaPlayerDuration = 0L;
if (filePath == null || filePath.isEmpty()) {
return 0;
}
MediaPlayer mediaPlayer = new MediaPlayer();
try {
mediaPlayer.setDataSource(filePath);
mediaPlayer.prepare();
mediaPlayerDuration = mediaPlayer.getDuration();
} catch (IOException ioException) {
Log.i(TAG, ioException.getMessage());
}
if (mediaPlayer != null) {
mediaPlayer.stop();
mediaPlayer.reset();
mediaPlayer.release();
}
return mediaPlayerDuration;
}
}
这里着重说一下广播,包括之前的代码里很多地方都有各种各样的广播接收器等,这是为了让我在一个页面控制了点击,不管是在fragment里面,还是activity里面,还是歌曲自动跳转,还是通知栏里面更改了音乐的播放状态,都要使得这些所有显示歌曲的页面进行更新操作,所以这些activity和fragment之间通信来通信去还是使用广播是最方便的
我在多说一句,问题就是这个问题都是慢慢发现的,也就是我的有些地方点击传值是通过intent,有些通过bundle,有些还是静态传值,就导致代码很乱,这里的信息更新不了,更新的时候空指针这种问题,也就说,广播需要先一步定义,因为他不会受到线程生命周期什么的诸多限制,我只要是注册了广播,不过它在哪里发出,我就能收到,别的地方发了几次,我这里就收到几次广播,优势显而易见
12.音乐通知
通知是写在服务里面的294-295
通知也需要写广播接收器
NotificationClickReceiver
package com.thundersoft.mediaplayer2;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
public class NotificationClickReceiver extends BroadcastReceiver {
public static final String TAG = "NotificationClickReceiver";
@Override
public void onReceive(Context context, Intent intent) {
Log.i(TAG,"通知栏点击");
}
}
且在清单文件里面注册
通知栏xml如下
notification.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<ImageView
android:id="@+id/iv_album_cover"
android:layout_width="64dp"
android:layout_height="64dp"
android:src="@mipmap/icon_notification_default" />
<LinearLayout
android:gravity="center_vertical"
android:paddingStart="@dimen/dp_12"
android:paddingEnd="@dimen/dp_6"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_64">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
<TextView
android:layout_weight="12"
android:id="@+id/tv_notification_song_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:focusable="true"
android:focusableInTouchMode="true"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:text="歌曲名"
android:textColor="@color/black"
android:textSize="14sp" />
<ImageButton
android:layout_weight="1"
android:id="@+id/btn_notification_close"
android:layout_width="@dimen/dp_20"
android:layout_height="@dimen/dp_20"
android:background="@color/transparent"
android:src="@drawable/close_gray" />
</LinearLayout>
<LinearLayout
android:layout_marginTop="@dimen/dp_4"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center">
<ImageButton
android:id="@+id/btn_notification_previous"
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_30"
android:background="@null"
android:scaleType="fitCenter"
android:src="@drawable/previous_black" />
<ImageButton
android:id="@+id/btn_notification_play"
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_30"
android:layout_marginStart="@dimen/dp_30"
android:layout_marginEnd="@dimen/dp_30"
android:background="@null"
android:scaleType="fitCenter"
android:src="@drawable/play_black" />
<ImageButton
android:id="@+id/btn_notification_next"
android:layout_width="@dimen/dp_30"
android:layout_height="@dimen/dp_30"
android:background="@null"
android:scaleType="fitCenter"
android:src="@drawable/next_black" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
其实通知栏的各种操作和fragment是一样的,只是以通知的形式显示,点击上一首下一首暂停接着更新内容实质上是差不多的
13.歌词显示
MusicActivity.java
package com.thundersoft.mediaplayer2;
import android.app.AlertDialog;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
import android.content.ContentValues;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.ServiceConnection;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.net.Uri;
import android.os.Bundle;
import android.os.IBinder;
import android.util.Log;
import android.view.View;
import android.view.Window;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.view.animation.LinearInterpolator;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import org.json.JSONArray;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.temporal.ValueRange;
import java.util.ArrayList;
import java.util.Arrays;
import android.util.Base64;
import java.util.Date;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class MusicActivity extends AppCompatActivity {
private static final String TAG = "MusicActivityTAG";
private ImageView iv_m_pic;
private TextView tv_m_name,tv_m_time,tv_m_id3,tv_m_area;
private SeekBar sb_m;
private Button bt_m_like,bt_m_type,bt_m_pre,bt_m_play,bt_m_next,bt_m_back,bt_exit,bt_record;
boolean isUpdate;
SimpleDateFormat time = new SimpleDateFormat("m:ss");
MusicService musicService;
MusicService.MyBinder binder;
ID3Helper id3Helper;
private int MODE = 1;
public int STATE = 1;
public int STATE2 = 1;
public int STATE0 = 0;
private String path;
int duration;
int id;
static MusicFrequencyView musicFrequencyView;
private LrcView lrcView = null;
private List<Long> timeList = new ArrayList<>();
private List<String> contentList = new ArrayList<>();
Intent bcIntent;
private ServiceConnection sc = new ServiceConnection() {
@Override
public void onServiceConnected(ComponentName componentName, IBinder iBinder) {
musicService = ((MusicService.MyBinder)iBinder).getService();
binder = (MusicService.MyBinder) iBinder;
}
@Override
public void onServiceDisconnected(ComponentName componentName) {
musicService = null;
}
};
public void initRound(){
Animation animation = AnimationUtils.loadAnimation(this, R.anim.img_round);
LinearInterpolator lin = new LinearInterpolator();
animation.setInterpolator(lin);
iv_m_pic.startAnimation(animation);
}
public android.os.Handler handler = new android.os.Handler();
public Runnable runnable = new Runnable(){
@Override
public void run() {
if (STATE == 2){
if (!(path == null||"".equals(path))){
iv_m_pic.setImageBitmap(Utils.toRoundBitmap(musicService.getImg(musicService.getCurrentPath())));
tv_m_name.setText("当前播放:"+musicService.getCurrentName());
tv_m_id3.setText(musicService.getId3());
if (STATE0 == 1){
searchLyric();
STATE0 = 0;
}
}
}
if (musicService.mp != null){
duration = musicService.mp.getDuration();
tv_m_time.setText(time.format(musicService.mp.getCurrentPosition()) + "/"
+ time.format(musicService.mp.getDuration()));
sb_m.setProgress(musicService.mp.getCurrentPosition());
sb_m.setMax(musicService.mp.getDuration());
if (musicService.mp.isPlaying()){
if (STATE2 == 1){
initRound();
STATE2 = 0;
}
}else {
iv_m_pic.clearAnimation();
}
}
sb_m.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
@Override
public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
if (fromUser) {
musicService.mp.seekTo(seekBar.getProgress());
}
}
@Override
public void onStartTrackingTouch(SeekBar seekBar) {
}
@Override
public void onStopTrackingTouch(SeekBar seekBar) {
}
});
handler.postDelayed(runnable, 100);
}
};
private void bindServiceConnection() {
Intent intent = new Intent(MusicActivity.this, MusicService.class);
startService(intent);
bindService(intent, sc, this.BIND_AUTO_CREATE);
}
@Override
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
setContentView(R.layout.activity_music);
musicActivity = this;
bcIntent = new Intent("song");
tv_m_id3 = findViewById(R.id.tv_m_id3);
iv_m_pic = findViewById(R.id.iv_m_pic_s);
tv_m_name = findViewById(R.id.tv_m_name_s);
tv_m_area = findViewById(R.id.tv_m_area);
tv_m_time =findViewById(R.id.tv_m_time);
sb_m = findViewById(R.id.sb_m);
bt_m_like = findViewById(R.id.bt_m_like);
bt_m_like.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
like();
}
});
bt_m_type = findViewById(R.id.btn_m_type);
bt_m_pre = findViewById(R.id.bt_m_pre);
bt_m_play = findViewById(R.id.bt_m_play);
bt_m_next = findViewById(R.id.bt_m_next);
bt_m_back = findViewById(R.id.bt_m_back);
musicFrequencyView = findViewById(R.id.m_mfv);
bt_exit = findViewById(R.id.bt_exit);
final AlertDialog.Builder builder = new AlertDialog.Builder(this);
bt_exit.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
builder.setMessage("退出并保存进度?")
.setPositiveButton("退出并保存进度", new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
if (musicService.mp != null){
save();
musicService.mp.stop();
}
finish();
}
}).setNeutralButton("退出并不保存进度", new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (musicService.mp != null){
musicService.mp.stop();
}
finish();
}
}).setNegativeButton("不退出", null);
AlertDialog ad = builder.create();
ad.show();
}
});
bt_record = findViewById(R.id.bt_record);
bt_record.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent().setClass(MusicActivity.this,RecordActivity.class));
}
});
iv_m_pic.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
iv_m_pic.setVisibility(View.GONE);
musicFrequencyView .setVisibility(View.VISIBLE);
}
});
musicFrequencyView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
musicFrequencyView.setVisibility(View.GONE);
iv_m_pic.setVisibility(View.VISIBLE);
}
});
bt_m_type.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
String s = bt_m_type.getText().toString();
if (s.equals("播放模式:顺序播放")){
bt_m_type.setText("播放模式:单曲循环");
MODE = 2;
return;
}
if (s.equals("播放模式:单曲循环")){
bt_m_type.setText("播放模式:随机播放");
MODE = 3;
return;
}
if (s.equals("播放模式:随机播放")){
bt_m_type.setText("播放模式:顺序播放");
MODE = 1;
return;
}
}
});
bt_m_next.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
STATE0 = 1;
if (MODE == 1){
STATE = 2;
musicService.nextMusic();
}
if (MODE == 2){
musicService.playMode2();
}
if (MODE == 3){
STATE = 2;
musicService.playMode3();
}
send();
}
});
bt_m_pre.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
STATE0 = 1;
if (MODE == 1){
STATE = 2;
musicService.preMusic();
}
if (MODE == 2){
musicService.playMode2();
}
if (MODE == 3){
STATE = 2;
musicService.playMode3();
}
send();
}
});
bt_m_play.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(musicService.mp != null){
musicService.playOrPause();
}
}
});
id3Helper = new ID3Helper();
musicService = new MusicService();
bindServiceConnection();
iv_m_pic.setImageResource(R.drawable.music);
bt_m_back.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
startActivity(new Intent(MusicActivity.this,MainActivity.class));
}
});
registerUpdateReceiver();
}
private static MusicActivity musicActivity;
public static MusicActivity getInstance(){
return musicActivity;
}
public boolean isPlay(){
if (MusicService.mp != null){
return MusicService.mp.isPlaying();
}
return false;
}
public void nextMusic(){
if (MODE == 1){
STATE = 2;
musicService.nextMusic();
}
if (MODE == 2){
musicService.playMode2();
}
if (MODE == 3){
STATE = 2;
musicService.playMode3();
}
send();
}
public void preMusic(){
if (MODE == 1){
STATE = 2;
musicService.preMusic();
}
if (MODE == 2){
musicService.playMode2();
}
if (MODE == 3){
STATE = 2;
musicService.playMode3();
}
send();
}
public void playOrPause(){
if(musicService.mp != null){
musicService.playOrPause();
}send();
}
public void registerUpdateReceiver(){
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction("com.broadcast.update");
registerReceiver(mReceiver,intentFilter);
}
private BroadcastReceiver mReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if(action.equals("com.broadcast.update")){
isUpdate = intent.getBooleanExtra("isUpdate",false);
update();
Log.i(TAG, "onReceive:收到通知栏的广播了+"+isUpdate);
}
}
};
private void update(){
if(isUpdate){
tv_m_name.setText(musicService.getCurrentName());
String pathu = musicService.getCurrentPath();
iv_m_pic.setImageBitmap(Utils.toRoundBitmap(MusicService.getImg(pathu)));
File fileu = new File(pathu);
id3Helper.parseNoThrow(fileu);
String text = ("当前歌曲的ID3类型为:"+id3Helper.getId3Type()+
" 作者是:"+id3Helper.getArtist()+
" 歌名是:"+id3Helper.getTitle()+
" 专辑是:"+id3Helper.getFloder()+
" 年代是:"+id3Helper.getTyer()+
" 备注是:"+id3Helper.getComm());
tv_m_id3.setText(text);
searchLyric();
send();
isUpdate = false;
}
}
private void send(){
new Thread(){
@Override
public void run() {
try {
Thread.sleep(1000);
String name3 = tv_m_name.getText().toString().substring(0, tv_m_name.getText().toString().length() - 4);
String name4 = name3.substring(5,name3.length());
bcIntent.putExtra("name", name4);
LocalBroadcastManager.getInstance(MusicActivity.this).sendBroadcast(bcIntent);
Log.i(TAG, "run: 里发送的广播" + bcIntent.getStringExtra("name"));
if (musicService.mp.isPlaying()){
STATE2 = 1;
}
} catch (InterruptedException e) {
e.printStackTrace();
}
super.run();
}
}.start();
}
private void like(){
String musicname2 = tv_m_name.getText().toString();
String musicname3 = path.substring(path.lastIndexOf("/") + 1, path.length());
String musicname4 = id3Helper.getTitle();
ContentValues values = new ContentValues();
DBHelper helper = new DBHelper(getApplicationContext());
Cursor cursor = helper.queryL();
while (cursor.moveToNext()){
int useridIndex = cursor.getColumnIndex("_userid");
int musicnameIndex = cursor.getColumnIndex("musicname");
int userid1 = cursor.getInt(useridIndex);
String musicname1 = cursor.getString(musicnameIndex);
if (id == userid1 & musicname4.equals(musicname1)){
Toast.makeText(this,"请勿重复收藏",Toast.LENGTH_SHORT).show();
return;
}
}
values.put("_userid",id);
values.put("musicname",musicname4);
helper.like(values);
Toast.makeText(this,"收藏成功",Toast.LENGTH_SHORT).show();
Log.i(TAG,"添加数据库like表内容是+"+values.toString());
Intent intent = new Intent("com.broadcast.updateList");
LocalBroadcastManager.getInstance(MusicActivity.this).sendBroadcast(intent);
}
private void showLy() {
runOnUiThread(new Runnable() {
@Override
public void run() {
Log.i(TAG, "activity里的run了");
lrcView.setList(timeList,contentList);
lrcView.init();
}
});
new Thread(lrcView).start();
}
static Bitmap pic = null;
protected void onResume() {
Intent intent = getIntent();
if (intent != null){
String p = intent.getStringExtra("p");
path = intent.getStringExtra("path");
id = intent.getIntExtra("id",0);
Log.i(TAG, "onResume: id是"+id);
if (!(path == null||"".equals(path))){
Intent intent1 = new Intent(MusicActivity.this, MusicService.class);
Bundle bundle = new Bundle();
bundle.putString("url",path);
bundle.putBoolean("isPlay",true);
if (!(p == null||"".equals(p))){
bundle.putString("p",p);
bundle.putBoolean("isRecovery",true);
Log.i(TAG, "onResume: p"+p);
Toast.makeText(this,"进度已恢复",Toast.LENGTH_SHORT).show();
}
intent1.putExtras(bundle);
startService(intent1);
}
Log.i(TAG, "onResume: path"+path);
handler.post(runnable);
if (MusicService.mp != null){
sb_m.setProgress(MusicService.mp.getCurrentPosition());
duration = MusicService.mp.getDuration();
sb_m.setMax(MusicService.mp.getDuration());
if (musicService.mp.isPlaying()){
STATE2 = 1;
}
}
if (!(path == null||"".equals(path))){
String name1 = path.substring(path.lastIndexOf("/") + 1, path.length());
tv_m_name.setText("当前播放:"+name1);
String name2 = name1.substring(0,name1.length()-4);
bcIntent.putExtra("name",name2);
LocalBroadcastManager.getInstance(MusicActivity.this).sendBroadcast(bcIntent);
Log.i(TAG, "onResume: 里发送的广播"+bcIntent.getStringExtra("name"));
File file = new File(path);
Log.i(TAG, "onResume: get img path: "+path);
pic = musicService.getImg(path);
iv_m_pic.setImageBitmap(Utils.toRoundBitmap(pic));
id3Helper.parseNoThrow(file);
String text = ("当前歌曲的ID3类型为:"+id3Helper.getId3Type()+
" 作者是:"+id3Helper.getArtist()+
" 歌名是:"+id3Helper.getTitle()+
" 专辑是:"+id3Helper.getFloder()+
" 年代是:"+id3Helper.getTyer()+
" 备注是:"+id3Helper.getComm());
tv_m_id3.setText(text);
searchLyric();
}
}
super.onResume();
}
@Override
protected void onNewIntent(Intent intent) {
super.onNewIntent(intent);
setIntent(intent);
}
@Override
protected void onDestroy() {
unbindService(sc);
super.onDestroy();
}
public static void show(){
if(MusicFrequencyView.visualizer != null){
MusicFrequencyView.visualizer = null;
}
if (MusicService.mp != null){
musicFrequencyView. setVis(MusicService.mp);
MusicFrequencyView.visualizer.setEnabled(false);
musicFrequencyView.setMediaPlayer(MusicService.mp);
}
}
private void save(){
ContentValues values = new ContentValues();
SimpleDateFormat formatter= new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date date = new Date(System.currentTimeMillis());
values.put("time",formatter.format(date));
values.put("p",musicService.mp.getCurrentPosition());
values.put("path",musicService.getCurrentPath());
DBHelper helper = new DBHelper(getApplicationContext());
helper.insertP(values);
Log.i(TAG,"insert success");
}
String res1;
public void searchLyric(){
String s = tv_m_name.getText().toString();
String substring = s.substring(5,s.length()-4);
final String name = substring;
long time = MusicService.getAudioFileVoiceTime(path);
new Thread(new Runnable() {
@Override
public void run() {
try {
String urlStr = "http://lyrics.kugou.com/search?ver=1&man=yes&client=pc&keyword=" + name + "&duration=" + time + "&hash=";
String encode = Uri.encode(urlStr);
UriUtils uriUtils = new UriUtils();
URL url = new URL(urlStr);
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.connect();
InputStream input = conn.getInputStream();
String res = FileUtil.streamToString(input);
res1 = res;
if(res.contains("Not Found") || res.contains("404")){
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_m_area.setLines(10);
tv_m_area.setText("歌词未找到");
}
});
return;
}
JSONObject json1 = new JSONObject(res);
JSONArray json2 = json1.getJSONArray("candidates");
JSONObject json3 = json2.getJSONObject(0);
urlStr = "http://lyrics.kugou.com/download?ver=1&client=pc&id=" + json3.get("id") + "&accesskey=" + json3.get("accesskey") + "&fmt=lrc&charset=utf8";
url = new URL(urlStr);
conn = (HttpURLConnection) url.openConnection();
conn.connect();
input = conn.getInputStream();
res = FileUtil.streamToString(input);
JSONObject json4 = new JSONObject(res);
String base64 = json4.getString("content");
String lyric = new String(Base64.decode(base64,Base64.DEFAULT));
InputStream inputStreamRoute = new ByteArrayInputStream(
new String(lyric).getBytes());
AnalyseLyricFile(inputStreamRoute);
runOnUiThread(new Runnable() {
@Override
public void run() {
tv_m_area.setLines(contentList.size());
tv_m_area.setText(Utils.ListToString(contentList));
}
});
} catch (Exception e) {
e.printStackTrace();
}
}
}).start();
}
public void AnalyseLyricFile(InputStream inputStream){
contentList.clear();
BufferedReader reader = null;
InputStreamReader inputFileReader = null;
String tempString = null;
Pattern timePattern = Pattern.compile("(.*?)]");
String time = "";
long timestamp = 0;
long timePadding = 0;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyyMMddHHmmss");
try {
inputFileReader = new InputStreamReader(inputStream, "utf-8");
reader = new BufferedReader(inputFileReader);
while ((tempString = reader.readLine()) != null) {
Matcher timeMatcher = timePattern.matcher(tempString);
if (timeMatcher.find()){
time = timeMatcher.group(1);
if((int)time.charAt(1) <= 57){
time = time.replace('.', ':');
time = time.substring(1, time.length());
String temp [] = time.split(":");
try {
timestamp = simpleDateFormat.parse("1970010108"+temp[0]+temp[1]).getTime();
timestamp+=10*Integer.parseInt(temp[2]);
timeList.add(timestamp-timePadding);
contentList.add(" "+tempString.substring(10, tempString.length()));
Log.i(TAG, "AnalyseLyricFile: "+ timeList.get(4));
timePadding = timestamp;
} catch (ParseException e) {
e.printStackTrace();
}
}
}
}
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (reader != null) {
try {
reader.close();
} catch (IOException e1) {
}
}
}
}
}
从上个项目co下来的进行了很多改动,实现音乐播放控制的主要activity,和service连接的activity
歌词显示在610行的searchLyric方法下,通过酷狗的api在线搜索歌词,首先得先对url进行编码,比如说https://,当中://这几个符号是会转换的,编码后,通过链接得到json,在进行解析,解码,再把歌词通过689行的AnalyseLyricFile方法进行解析,获得一个timeList和contentList,分别是歌词lrc文件每一行都时间短,和后面的歌词内容
值得一提的是,获取在线歌词需要在线程里操作,conn必须放在线程里不然连接不到,或者是连接超时等不能导致程序的错误,既然是线程,那就和程序是异步也,也就是说,我点击了某个按钮搜索歌词,这个时候歌曲在放,过了几秒钟或者是网慢十几秒钟,才能获取到歌词,这时候就要更新页面显示歌词。这也是为什么很多网站都会有一个“获取歌词中”这段话
最后,歌词在线程里获取到了,不能直接通过,控件.settextview来显示,这些组件必须要在主线程操作,有一个办法就是在675行的runOnUiThread中更新
14.圆形图片转动
工具类如下
Utils
package com.thundersoft.mediaplayer2;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
public class Utils {
public static Bitmap toRoundBitmap(Bitmap bitmap) {
int width = bitmap.getWidth();
int height = bitmap.getHeight();
float roundPx;
float left, top, right, bottom, dst_left, dst_top, dst_right, dst_bottom;
if (width <= height) {
roundPx = width / 2;
left = 0;
top = 0;
right = width;
bottom = width;
height = width;
dst_left = 0;
dst_top = 0;
dst_right = width;
dst_bottom = width;
} else {
roundPx = height / 2;
float clip = (width - height) / 2;
left = clip;
right = width - clip;
top = 0;
bottom = height;
width = height;
dst_left = 0;
dst_top = 0;
dst_right = height;
dst_bottom = height;
}
Bitmap output = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(output);
final int color = 0xff424242;
final Paint paint = new Paint();
final Rect src = new Rect((int) left, (int) top, (int) right, (int) bottom);
final Rect dst = new Rect((int) dst_left, (int) dst_top, (int) dst_right, (int) dst_bottom);
final RectF rectF = new RectF(dst);
paint.setAntiAlias(true);
canvas.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas.drawCircle(roundPx, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
canvas.drawBitmap(bitmap, src, dst, paint);
return output;
}
private static final String SEP1 = "\n";
private static final String SEP2 = "|";
private static final String SEP3 = "=";
public static String ListToString(List<?> list) {
StringBuffer sb = new StringBuffer();
if (list != null && list.size() > 0) {
for (int i = 0; i < list.size(); i++) {
if (list.get(i) == null || list.get(i) == "") {
continue;
}
if (list.get(i) instanceof List) {
sb.append(ListToString((List<?>) list.get(i)));
sb.append(SEP1);
} else if (list.get(i) instanceof Map) {
sb.append(MapToString((Map<?, ?>) list.get(i)));
sb.append(SEP1);
} else {
sb.append(list.get(i));
sb.append(SEP1);
}
}
}
return sb.toString();
}
public static String MapToString(Map<?, ?> map) {
StringBuffer sb = new StringBuffer();
for (Object obj : map.keySet()) {
if (obj == null) {
continue;
}
Object key = obj;
Object value = map.get(key);
if (value instanceof List<?>) {
sb.append(key.toString() + SEP1 + ListToString((List<?>) value));
sb.append(SEP2);
} else if (value instanceof Map<?, ?>) {
sb.append(key.toString() + SEP1
+ MapToString((Map<?, ?>) value));
sb.append(SEP2);
} else {
sb.append(key.toString() + SEP3 + value.toString());
sb.append(SEP2);
}
}
return "M" + sb.toString();
}
public static Map<String, Object> StringToMap(String mapText) {
if (mapText == null || mapText.equals("")) {
return null;
}
mapText = mapText.substring(1);
mapText = mapText;
Map<String, Object> map = new HashMap<String, Object>();
String[] text = mapText.split("\\" + SEP2);
for (String str : text) {
String[] keyText = str.split(SEP3);
if (keyText.length < 1) {
continue;
}
String key = keyText[0];
String value = keyText[1];
if (value.charAt(0) == 'M') {
Map<?, ?> map1 = StringToMap(value);
map.put(key, map1);
} else if (value.charAt(0) == 'L') {
List<?> list = StringToList(value);
map.put(key, list);
} else {
map.put(key, value);
}
}
return map;
}
public static List<Object> StringToList(String listText) {
if (listText == null || listText.equals("")) {
return null;
}
listText = listText.substring(1);
listText = listText;
List<Object> list = new ArrayList<Object>();
String[] text = listText.split(SEP1);
for (String str : text) {
if (str.charAt(0) == 'M') {
Map<?, ?> map = StringToMap(str);
list.add(map);
} else if (str.charAt(0) == 'L') {
List<?> lists = StringToList(str);
list.add(lists);
} else {
list.add(str);
}
}
return list;
}
}
传入一个bitmap,传出一个bitmap。后面还有list to string工具类方法,静态的方法可以直接通过类名.方法调用
如果要实现播放的时候图片转动,需要设置动画
在res下的动画xml文件img_round.xml
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android">
<rotate
android:duration="10000"
android:fromDegrees="0"
android:pivotX="50%"
android:pivotY="50%"
android:repeatCount="-1"
android:repeatMode="restart"
android:toDegrees="360" />
</set>
设置了图片转动的效果,只需要通过loadAnimation等方法就可以设置
见MusicActivity的103行
别忘记了给播放的时候设置监听,播放结束后就停止转动
15.streamToString
FileUtil
package com.thundersoft.mediaplayer2;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
public class FileUtil {
public static String streamToString(InputStream is) {
BufferedReader reader = new BufferedReader(new InputStreamReader(is));
StringBuffer sb = new StringBuffer();
String line = null;
try {
while ((line = reader.readLine()) != null) {
sb.append(line + "\n");
}
is.close();
reader.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
}
将inputstream转换为string,用于json
16.颜色设置
为了让软件更好看还学习了一些颜色方面的知识,一个软件的配色,主要由背景色、主题色、辅助色、点睛色四个组成
我这里没有搞得很复杂,就设计了三个颜色逐渐加深的配置,
<color name="c2">#FF8C00</color>
<color name="c1">#FF4500</color>
<color name="c3">#DEB887</color>
17.其他模块
其他的模块在之前的教程里大部分都有,如果又不太清楚的可以留言私信
四、效果演示
手机上显示效果如下,Honor10 Android10
|