IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 移动开发 -> Android入门系列(十三):在线歌词显示、通知栏显示歌曲、歌曲收藏和本地视频播放等 -> 正文阅读

[移动开发]Android入门系列(十三):在线歌词显示、通知栏显示歌曲、歌曲收藏和本地视频播放等

音乐播放器2.0

这次的需求和之前多了很多内容,且需要按照客户的既定设计来编写布局

之前的音乐播放器为

Android入门系列(十二):在线本地音乐播放、专辑图片、ID3信息、进度控制、AudioFocus处理、频谱显示

一、需求

  1. 支持本地音乐文件播放

  2. 支持播放、暂停、上一首、下一首等功能

  3. 支持随机/单曲循环/列表循环播放

  4. 支持后台播放

  5. 支持ID3信息显示

  6. 记忆播放文件

  7. 界面显示正常

  8. 支持音乐的后台播放和切换

  9. 支持侧滑菜单显示

  10. 实现音乐波形图

  11. 显示专辑图片

  12. 音乐列表界面

  13. 支持显示播放进度

  14. 支持进度拖动

  15. 支持按键控制

  16. 支持播放进度恢复

  17. 实现AudioFocus处理

  18. 支持在线音频播放

  19. 支持频谱显示

  20. 支持歌词根据播放进度显示

  21. 支持播放本地视频和在线视频

  22. 实现播放音乐时,专辑图片转动显示

  23. 实现收藏歌单的添加和播放

二、分析

和上一次的比较,相较于多并且比较有挑战性的功能包括,通知栏控制歌曲,歌词根据播放进度显示,收藏。其余的大部分之前都做过,这里简单说一下就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; //声明SQLiteDatabase对象

    //构造函数
    DBHelper(Context c){
        super(c, DB_NAME, null, 1);
    }

    @Override
    public void onCreate(SQLiteDatabase db){
        //获取SQLiteDatabase对象
        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;
    }
//    查询所有like
public Cursor queryL(){
    SQLiteDatabase db = getWritableDatabase();
    Cursor c = db.query(TBL_NAME_Like, null, null, null, null, null, null);
    return c;
}

//    添加like
    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 getP(int id){
//        String p = "";
//        db =getWritableDatabase();
//        Cursor c = db.query(TBL_NAME_P, 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 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)});
    }
    //删除音乐by id
    public void deleteMusic(int id){
        if(db == null){
            db = getWritableDatabase();
        }
        db.delete(TBL_NAME_MUSIC, "_id=?", new String[]{String.valueOf(id)});
    }
    //删除音乐by name
    public void deleteMusic(String musicname){
        if(db == null){
            db = getWritableDatabase();
        }
        db.delete(TBL_NAME_MUSIC, "musicname=?", new String[]{String.valueOf(musicname)});
    }
    //删除记录by name
    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;
    }

//    查询一条by id
    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;
    }
//    查询一个by name
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;
}
//    查询一个by name
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();
//                先获取改name下的pwd,再判断pwd是否相等
                String pwd = dbHelper.getPwd(usernameS);
                if(!passwordS.equals(pwd)){
//                    登录失败
                    Toast.makeText(LoginActivity.this, "用户名或密码错误!", Toast.LENGTH_SHORT).show();
                    return;
                }
//                这里log看看数据库信息
                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 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">
<!--            <intent-filter>-->
<!--                <action android:name="android.intent.action.MyAction"/>-->
<!--                <category android:name="android.intent.category.DEFAULT"/>-->
<!--            </intent-filter>-->
        </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[] PERMISSIONS1 = {"android.permission.READ_EXTERNAL_STORAGE"};
//        String[] PERMISSIONS2 = {"android.permission.WRITE_EXTERNAL_STORAGE"};
//        String[] PERMISSIONS3 = {"android.permission.MODIFY_AUDIO_SETTINGS"};
//        String[] PERMISSIONS4 = {"android.permission.RECORD_AUDIO"};
//        String[] PERMISSIONS5 = {"android.permission.READ_CONTACTS"};
//
//        int permission1 = ContextCompat.checkSelfPermission(MainActivity.this, "android.permission.READ_EXTERNAL_STORAGE");
//        int permission2 = ContextCompat.checkSelfPermission(MainActivity.this, "android.permission.WRITE_EXTERNAL_STORAGE");
//        int permission3 = ContextCompat.checkSelfPermission(MainActivity.this, "android.permission.MODIFY_AUDIO_SETTINGS");
//        int permission4 = ContextCompat.checkSelfPermission(MainActivity.this, "android.permission.RECORD_AUDIO");
//        int permission5 = ContextCompat.checkSelfPermission(MainActivity.this, "android.permission.READ_CONTACTS");
        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);
        }
//        if (permission1 != PackageManager.PERMISSION_GRANTED) {
//            ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS1,1);
//        }
//        if (permission2 != PackageManager.PERMISSION_GRANTED) {
    //            ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS2,2);
//        }
//        if (permission3 != PackageManager.PERMISSION_GRANTED) {
//            ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS3,3);
//        }
//        if (permission4 != PackageManager.PERMISSION_GRANTED) {
//            ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS4,4);
//        }
//        if (permission5 != PackageManager.PERMISSION_GRANTED) {
//            ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS5,5);
//        }




        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);
//                titleView.setText(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) {
//                Intent intent = new Intent(MainActivity.this, MusicActivity.class);
//                startActivity(intent);
//                overridePendingTransition(R.anim.activity_open, R.anim.activity_open);
            }
        });


//        侧边栏
        left = new LeftFragment();
//        left = (LeftFragment) fragmentManager.findFragmentById(R.id.left_menu);
        initDrawer();

//        获取登录的用户名和邮箱
        Intent intent = getIntent();
        username = intent.getStringExtra("username");
        email = dbHelper.getEmail(username);

        //        获得了username后查询username和id
        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 displayMetrics = new DisplayMetrics();
        //将当前窗口的一些信息放在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);
        //将滚动条的初始位置设置成与左边界间隔一个offset
        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:
                    /**
                     * TranslateAnimation的四个属性分别为
                     * float fromXDelta 动画开始的点离当前View X坐标上的差值
                     * float toXDelta 动画结束的点离当前View X坐标上的差值
                     * float fromYDelta 动画开始的点离当前View Y坐标上的差值
                     * float toYDelta 动画开始的点离当前View Y坐标上的差值
                     **/
                    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;
            }
            //arg0为切换到的页的编码
            currIndex = arg0;
            // 将此属性设置为true可以使得图片停在动画结束时的位置
            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);
    }

    /**
     * 设置其是否能滑动换页
     * @param isCanScroll false 不能换页, true 可以滑动换页
     */
    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};
//    存放图片id
    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);
//                点击歌曲发送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();
//                这里返回为true才会不触发点击事件
                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();
        }
        //判断instantiateItem函数所返回来的Key与一个页面视图是否是代表的同一个视图
        @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();
        //每隔2s时间,固定执行轮播任务。
        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(“image/*”);//选择图片
        intent.setType("audio/*"); //选择音频
        //intent.setType(“video/*”); //选择视频 (mp4 3gp 是android支持的视频格式)
        //intent.setType(“video/*;image/*”);//同时选择视频和图片
        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);
//                  创建数据库工具类DBHelper
                helper = new DBHelper(getActivity().getApplicationContext());

                //调用insert()方法插入数据
//                    这里写一个判断是否有重复的查询判断,如果有就不添加
                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]);
//                    对查询结果进行遍历,如果其中有一条数据符合要求则return
                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();
//                  这里log看看数据库信息
                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);

//            Cursor cursor = helper.queryAllUser();
            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 HelloWebViewClient());//将每个新开的activity填入web视图中
        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延迟发送更改状态信息
            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;


//        supportRequestWindowFeature(Window.FEATURE_NO_TITLE);

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);
            }
        });
//        webView.setWebViewClient(new HelloWebViewClient());//将每个新开的activity填入web视图中

    }

    @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);
    }
    //public void choseFile(){
    ///    Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
    //    intent.setType("*/*");
    //    intent.addCategory(Intent.CATEGORY_OPENABLE);
    //    try {
    //        startActivityForResult(Intent.createChooser(intent, "选择文件"), FILE_SELECT_CODE);
    //    } catch (android.content.ActivityNotFoundException ex) {
    //        Toast.makeText(this, "亲,木有文件管理器啊-_-!!", Toast.LENGTH_SHORT).show();
    //    }

    // }
}

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());

        //注册广播机制,接收传来的广播(意图:接收来自mainAdapter传来的广播以及值,用于为TextView设置内容)
        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);
//                点击歌曲发送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();
//                这里返回为true才会不触发点击事件
                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){
//                如果用户相等找这首歌的path
                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);

//            Cursor cursor = helper.queryAllUser();
            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";
    //    动态数组当然是list了
    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);
        //为prev控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_previous, prevPendingIntent);

        //通知栏控制器播放暂停按钮广播操作  //用于接收广播时过滤意图信息
        Intent intentPlay = new Intent(PLAY);
        PendingIntent playPendingIntent = PendingIntent.getBroadcast(this, 0, intentPlay, 0);
        //为play控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_play, playPendingIntent);

        //通知栏控制器下一首按钮广播操作
        Intent intentNext = new Intent(NEXT);
        PendingIntent nextPendingIntent = PendingIntent.getBroadcast(this, 0, intentNext, 0);
        //为next控件注册事件
        remoteViews.setOnClickPendingIntent(R.id.btn_notification_next, nextPendingIntent);

        //通知栏控制器关闭按钮广播操作
        Intent intentClose = new Intent(CLOSE);
        PendingIntent closePendingIntent = PendingIntent.getBroadcast(this, 0, intentClose, 0);
        //为close控件注册事件
        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) {
            //UI控制
            UIControl(intent.getAction(), TAG);
        }
    }
//    5.27
    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();
//                pauseOrContinueMusic();
                Log.i(TAG, PLAY + " or " + PAUSE);
                sendUpdateMsg(true);
                break;
            case PREV:

                preMusic();
//                previousMusic();
                Log.i(TAG, PREV);
                sendUpdateMsg(true);

                break;
            case NEXT:

                nextMusic();
//                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);

//        activityLiveData.postValue(CLOSE);
    }
    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){
//                                    有时间再看吧
//                                    MusicActivity.show();
                                }
                                if (isRecovery){
                                    if(mp != null){
//                                        MusicActivity.show();

                                    }
                                    mp.seekTo(Integer.parseInt(p));
                                }
                                musicIndex = getMusicIndex(url);
                                updateNotificationShow(musicIndex);
                            } catch (IOException e) {
                                e.printStackTrace();
                            }
                        }
//                        第一次播放,mp是空的,在内部实例化,并且设置好歌曲和监听器,但是第二次mp就不为mull了
                        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);
        //歌手名
//        remoteViews.setTextViewText(R.id.tv_notification_singer,"xxxx");
        //发送通知
        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;
    }

    //    服务init
    public final IBinder binder = new MyBinder();

    private String path;
    private int musicIndex = 1;

    @Nullable
    @Override
    public IBinder onBind(Intent intent) {
//        path = intent.getStringExtra("path");
//        init();
        return binder;
    }

    public class MyBinder extends Binder{
        MusicService getService(){
            return MusicService.this;
        }
    }

    //    音乐init


//    public MusicService() {
//        try {
            默认歌曲设置
//            mp.setDataSource(path);
//            //mp.setDataSource(Environment.getDataDirectory().getAbsolutePath()+"/You.mp3");
//            mp.prepare();
//        } catch (Exception e) {
//            e.printStackTrace();
//        }
//    }

//    public void play(String string) throws IOException {
//        if (requestFocus()){
//            mp.reset();
//            mp.setDataSource((new FileInputStream(new File(string))).getFD());
                musicService.mp.setDataSource(path);
//            mp.prepare();
//        }
//        if (!mp.isPlaying()){
//            mp.start();
//        }
//    }

    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) {
//        123
        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;
        }

//        InputStream is = MusicApplication.getApp().getResources().openRawResource(R.drawable.music);
//        Bitmap bitmap0 = BitmapFactory.decodeStream(is);
        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());
                    在获取name的地方发送广播
//                    String name3 = tv_m_name.getText().toString().substring(0,tv_m_name.getText().toString().length()-4);   //截掉
//                    bcIntent.putExtra("name",name3);
//                    LocalBroadcastManager.getInstance(MusicActivity.this).sendBroadcast(bcIntent);
//                    Log.i(TAG, "run: 里发送的广播"+bcIntent.getStringExtra("name"));

                    //将file传入给id3helper分析出id3结果
                    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());

//                MusicFrequencyView.visualizer.setEnabled(false);
                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);
//        lrcView = (LrcView) super.findViewById(R.id.lyric_show);

        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();
//                                    ListsActivity.listsActivity.finish();
                                }
                                finish();
                            }
                        }).setNeutralButton("退出并不保存进度", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        if (musicService.mp != null){
                            musicService.mp.stop();
                        }
                        finish();
//                        exit();
                    }
                }).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();
//            后续再写吧
//        showLy();
    }
    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();
    }
//    5.27
    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);
//                    在获取name的地方发送广播
                    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());
//        MyFragment myFragment = new MyFragment();
//        myFragment.initMusic();
//        更新fragment
        Intent intent = new Intent("com.broadcast.updateList");
//        intent.putExtra("isUpdateList",isUpdate);
        LocalBroadcastManager.getInstance(MusicActivity.this).sendBroadcast(intent);
//        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);
//                musicService.play(path);
            }
            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;
                }
//            musicFrequencyView.setMediaPlayer(MusicService.mp);
            }
            if (!(path == null||"".equals(path))){
                String name1 = path.substring(path.lastIndexOf("/") + 1, path.length());
                tv_m_name.setText("当前播放:"+name1);
                //                    在获取name的地方发送广播

                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();
    }
    //启动模式为singletask会在onPause之前执行onNewIntent
    @Override
    protected void onNewIntent(Intent intent) {
        super.onNewIntent(intent);
        setIntent(intent);
    }
    @Override
    protected void onDestroy() {
        unbindService(sc);
        super.onDestroy();
    }
    public static void show(){
//            为了不setCaptureSize() called in wrong state: 2报错才这样写
        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 = musicService.getCurrentName();
        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);
//                    URL url = new URL(encodeUrl(urlStr));  //字符串进行URL编码
//                    URL url = new URL(URLEncoder.encode(urlStr));  //字符串进行URL编码
                    UriUtils uriUtils = new UriUtils();

                    URL url = new URL(urlStr);
                    HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                    conn.connect();

                    //读取流 -- JSON歌曲列表
                    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);  //字符串读取为JSON
                    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);
//                    url = new URL(URLEncoder.encode(urlStr));
                    conn = (HttpURLConnection) url.openConnection();
                    conn.connect();

                    //读取流 -- 歌词
                    input = conn.getInputStream();
                    res = FileUtil.streamToString(input);
                    JSONObject json4 = new JSONObject(res);

                    //获取歌词base64,并进行解码
                    String base64 = json4.getString("content");


                    String lyric = new String(Base64.decode(base64,Base64.DEFAULT));

//                    final String lyric = Base64.getFromBASE64(base64);
//                    Log.i("lyric", lyric);
//                              找到歌词后显示
                    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));
//                         Log.i(TAG, "获取的歌词有几行"+ contentList.size());
                     }
                 });
                } 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);
            // 一次读入一行,直到读入null为文件结束
            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();//电脑的开始计时时间是从1970年1月1日早上8点钟开始
                            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); // 填充整个Canvas
        paint.setColor(color);

        // 以下有两种方法画圆,drawRounRect和drawCircle
        // canvas.drawRoundRect(rectF, roundPx, roundPx, paint);// 画圆角矩形,第一个参数为图形显示区域,第二个参数和第三个参数分别是水平圆角半径和垂直圆角半径。
        canvas.drawCircle(roundPx, roundPx, roundPx, paint);

        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));// 设置两张图片相交时的模式,参考http://trylovecatch.iteye.com/blog/1189452
        canvas.drawBitmap(bitmap, src, dst, paint); //以Mode.SRC_IN模式合并bitmap和已经draw了的Circle

        return output;
    }

    private static final String SEP1 = "\n";
    private static final String SEP2 = "|";
    private static final String SEP3 = "=";
    /**
     * List转换String
     *
     * @param list
     *            :需要转换的List
     * @return String转换后的字符串
     */
    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;
                }
                // 如果值是list类型则调用自己
                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();
    }

    /**
     * Map转换String
     *
     * @param map
     *            :需要转换的Map
     * @return String转换后的字符串
     */
    public static String MapToString(Map<?, ?> map) {
        StringBuffer sb = new StringBuffer();
        // 遍历map
        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); // 转换key与value的数组
            if (keyText.length < 1) {
                continue;
            }
            String key = keyText[0]; // key
            String value = keyText[1]; // value
            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;
    }

    /**
     * String转换List
     *
     * @param listText
     *            :需要转换的文本
     * @return List<?>
     */
    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));
        //new一个StringBuffer用于字符串拼接
        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

在这里插入图片描述

  移动开发 最新文章
Vue3装载axios和element-ui
android adb cmd
【xcode】Xcode常用快捷键与技巧
Android开发中的线程池使用
Java 和 Android 的 Base64
Android 测试文字编码格式
微信小程序支付
安卓权限记录
知乎之自动养号
【Android Jetpack】DataStore
上一篇文章      下一篇文章      查看所有文章
加:2022-06-14 22:45:08  更:2022-06-14 22:49:12 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/18 9:03:34-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码