欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

项目示例] - 音乐播放器 - Android 前端实现 - Java 后端实现 - ???? 歌曲详情页面

最编程 2024-10-15 10:22:19
...

???? 后端查询列表功能

???? 数据库sql语句
/*
Navicat MySQL Data Transfer

Source Server         : hui
Source Server Version : 50525
Source Host           : localhost:3306
Source Database       : music

Target Server Type    : MYSQL
Target Server Version : 50525
File Encoding         : 65001

Date: 2023-09-07 00:34:12
*/

SET FOREIGN_KEY_CHECKS=0;

-- ----------------------------
-- Table structure for `music`
-- ----------------------------
DROP TABLE IF EXISTS `music`;
CREATE TABLE `music` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `pic` varchar(255) DEFAULT NULL,
  `author` varchar(255) DEFAULT NULL,
  `location` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8;

-- ----------------------------
-- Records of music
-- ----------------------------
INSERT INTO `music` VALUES ('1', '听说爱情回来过', null, '蔡依林', 'music/听说爱情回来过 - 蔡依林.mp3');
INSERT INTO `music` VALUES ('2', '天外来物', null, '薛之谦', 'music/天外来物 - 薛之谦.mp3');
INSERT INTO `music` VALUES ('3', '药水歌', null, '药水哥', 'music/药水歌 - 药水哥.mp3');
???? 添加数据库驱动依赖

访问Mysql数据库,需要用到Mysql的驱动,我们在Pom.xml中添加数据库的依赖,代码如下:

<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
  <version>5.1.46</version>
</dependency>
???? 数据库操作工具类

在项目的util包中添加DBUtil类,该类用来访问mysql数据,具体代码如下:

package com.softeem.webService.util;

import java.lang.reflect.Field;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;

/**
 * 操作数据库的工具类  -》  Mybatis 的原理
 */
public class DBUtils {
    private static final String DRIVER_CLASS = "com.mysql.jdbc.Driver";
    /** url��ַ  192.168.32.125 **/
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/music?useUnicode=true&characterEncoding=utf8";
    private static final String USER = "root";
    private static final String PASSWORD = "123456";

    static {
        try {
            Class.forName(DRIVER_CLASS);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    /**
	 * 链接数据库的方法
	 */
    public static Connection getConn() {
        try {
            return DriverManager.getConnection(URL, USER, PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
	 * 数据库更新的工具方法
	 * @param sql   需要执行的sql语句
	 * @param params  执行sql语句需要的参数
	 * @return
	 * @throws SQLException
	 */
    public static boolean exeUpdate(String sql,Object ...params) throws SQLException {
        //获取数据库链接
        Connection conn = getConn();
        //预编译sql语句
        PreparedStatement ps = conn.prepareStatement(sql);
        //配置sql语句中的问号
        for(int i = 0;i<params.length;i++) {
            ps.setObject(i+1, params[i]);
        }
        //执行sql语句
        int i = ps.executeUpdate();
        //关闭数据库链接
        ps.close();
        conn.close();
        //返回执行结果
        return i > 0 ? true : false;
    }

    /**
	 * 查询多条数据的工具方法
	 * @param t
	 * @param sql
	 * @param params
	 * @param <T>
	 * @return
	 */
    public static <T> List<T> queryList(Class<T> t, String sql, Object... params) {
        List<T> list = new ArrayList<>();
        T obj = null;
        Connection conn = null;
        PreparedStatement ps = null;
        try {
            conn = getConn();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
                ps.setObject(i + 1, params[i]);
            }
            ResultSet rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            Map<String, Object> map = new HashMap<>();
            while (rs.next()) {
                map.clear();
                for (int i = 0; i < rsmd.getColumnCount(); i++) {
                    String cname = rsmd.getColumnLabel(i + 1);
                    Object value = rs.getObject(cname);
                    map.put(cname, value);
                }
                if (!map.isEmpty()) {
                    Set<String> columnNames = map.keySet();
                    obj = t.newInstance();
                    for (String column : columnNames) {
                        Object value = map.get(column);
                        if(Objects.nonNull(value)){	
                            Field f = t.getDeclaredField(column);
                            f.setAccessible(true);
                            f.set(obj, value);
                        }
                    }
                    list.add(obj);
                }
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
            return list;
        }

            /**
            * 查询单条数据的工具方法
            * @param t
            * @param sql
            * @param params
            * @param <T>
            * @return
            */
            public static <T> T queryOne(Class<T> t, String sql, Object... params) {
            T obj = null;
            Connection conn = null;
            PreparedStatement ps = null;
            try {
            conn = getConn();
            ps = conn.prepareStatement(sql);
            for (int i = 0; i < params.length; i++) {
            ps.setObject(i + 1, params[i]);
        }
            ResultSet rs = ps.executeQuery();
            ResultSetMetaData rsmd = rs.getMetaData();
            if (rs.next()) {
            obj = t.newInstance();
            for (int i = 0; i < rsmd.getColumnCount(); i++) {
            String cname = rsmd.getColumnLabel(i + 1);
            Object value = rs.getObject(cname);
            if(Objects.nonNull(value)){						
            Field field = t.getDeclaredField(cname);
            field.setAccessible(true);
            field.set(obj, value);
        }
        }
        }
        } catch (SQLException e) {
            e.printStackTrace();
        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (NoSuchFieldException e) {
            e.printStackTrace();
        } catch (SecurityException e) {
            e.printStackTrace();
        }
            return obj;
        }


        }
????添加实体类

在项目中添加Music类,其中的属性与数据库中的music表字段对应,用来暂存从数据库中查询出来的音乐数据。具体代码如下:

package com.softeem.webService.entity;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Music {

    private int id;
    private String name;
    private String pic;
    private String author;
    private String location ;
}
???? 编写查询音乐列表的接口

在Controller包中添加MusicListController并绑定music_list请求,当发送music_list请求时,访问service方法,去查询出所有的音乐列表,返回给客户端,具体代码如下:

package com.softeem.webService.controller;

import com.alibaba.fastjson2.JSONObject;
import com.softeem.webService.service.MusicService;
import com.softeem.webService.util.Result;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;

@WebServlet("/music_list")
public class MusicListController extends HttpServlet {

    @Override
    protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        // 设置请求编码
        req.setCharacterEncoding("utf-8");
        // 设置响应编码
        resp.setContentType("text/html;charset=utf-8");
        // 创建业务端
        MusicService service = new MusicService();
        // 调用服务端获取所有的音乐
        Result result = service.getAllMusics();
        // 将音乐列表转换成json对象
        JSONObject jsonObject = JSONObject.from(result);
        // 将结果返回给客户端
        PrintWriter out = resp.getWriter();
        out.println(jsonObject);
    }
}
???? 编写业务端,查询音乐列表

在项目中新建一个MusicService类,用来作为业务端口,负责处理有关于音乐的业务,具体代码如下:

package com.softeem.webService.service;

import com.softeem.webService.dao.MusicDao;
import com.softeem.webService.entity.Music;
import com.softeem.webService.util.Result;

import java.util.List;

public class MusicService {

    /**
     * 获取所有的音乐
     * @return
     */
    public Result getAllMusics() {
        // 创建Dao
        MusicDao dao = new MusicDao();
        // 操作数据库,查询所有的音乐列表
        List<Music> musicList = dao.findAllMusics();
        // 将查询出来的音乐列表进行封装
        return Result.success(musicList);
    }
}
???? 操作数据库,查询音乐

在项目中新建一个MusicDao类,该类负责操作数据库中的music表,目前我们先暂时只添加一个查询所有音乐的方法,具体代码如下:

package com.softeem.webService.dao;

import com.softeem.webService.entity.Music;
import com.softeem.webService.util.DBUtils;

import java.util.List;

public class MusicDao {
    public List<Music> findAllMusics() {
        String sql = "select * from music";
        return DBUtils.queryList(Music.class,sql);
    }
}
????测试

编写完毕之后,启动服务器,在浏览器中输入以下地址:

http://localhost:8080/music_list

显示结果如下:

???? 前端Android程序

???? 编写列表布局界面

在layout文件夹中新建music_list.xml,添加ListView代码如下:

<?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="vertical"
    android:background="#000000">
<ListView
    android:id="@+id/music_list"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:divider="@drawable/category_item_bg77"
    android:gravity="center"
    />
</LinearLayout>
???? 编写列表选项布局界面

在layout文件夹中新建music_item.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:gravity="center">

    <TextView
        android:id="@+id/num"
        android:layout_width="100px"
        android:layout_height="50dp"
        android:textSize="30px"
        android:gravity="center_vertical"
        android:textColor="#FFFFFF"
        android:singleLine="true"/>

    <TextView
        android:id="@+id/music_name"
        android:layout_width="194dp"
        android:layout_height="50dp"
        android:layout_marginLeft="30px"
        android:text="过火"
        android:textSize="30px"
        android:gravity="center_vertical"
        android:textColor="#FFFFFF"
        android:singleLine="true"
        ></TextView>

    <TextView
        android:id="@+id/music_author"
        android:layout_width="138dp"
        android:layout_height="50dp"
        android:layout_marginLeft="50px"
        android:text="张信哲"
        android:textSize="30px"
        android:gravity="center_vertical"
        android:textColor="#FFFFFF"
        android:singleLine="true"></TextView>
    <TextView
        android:id="@+id/music_id"
        android:layout_width="100px"
        android:layout_height="50dp"
        android:textSize="30px"
        android:gravity="center_vertical"
        android:textColor="#FFFFFF"
        android:singleLine="true"
        />
</LinearLayout>
????Music实体类:
package com.moxuan.mytest;


public class Music {
    private int id;
    private String name;
    private String pic;
    private String author;
    private String location ;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getPic() {
        return pic;
    }

    public void setPic(String pic) {
        this.pic = pic;
    }

    public String getAuthor() {
        return author;
    }

    public void setAuthor(String author) {
        this.author = author;
    }

    public String getLocation() {
        return location;
    }

    public void setLocation(String location) {
        this.location = location;
    }
}
???? 编写列表适配器

新建MusicListAdapter类,用来适配查询出来的音乐数据和列表布局界面,具体代码如下:

package com.moxuan.mytest;

import android.content.Context;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.TextView;

import androidx.recyclerview.widget.RecyclerView;

import java.util.List;

public class MusicListAdapter extends BaseAdapter {
    // 音乐列表数据源
    private List mData;
    // 声明布局服务
    private LayoutInflater mLayoutInflater;


    /**
     * 构造方法,创建适配器
     * @param context
     * @param data
     */
    public MusicListAdapter(Context context, List data){
        mData = data;
        mLayoutInflater = LayoutInflater.from(context);
    }

    /**
     * 获取列表的总条目数
     * @return
     */
    @Override
    public int getCount() {
        return mData.size();
    }

    /**
     * 获取列表中指定位置上的条目
     * @param position
     * @return
     */
    @Override
    public Object getItem(int position) {
        return mData.get(position);
    }

    /**
     * 获取条目在列表中的位置
     * @param position
     * @return
     */
    @Override
    public long getItemId(int position) {
        return position;
    }

    /**
     * 适配数据到布局文件中
     * @param position 位置
     * @param convertView 选项布局视图
     * @param parent
     * @return
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        ViewHolder viewHolder;
        if (convertView == null) {
            // 获取布局文件
            convertView = mLayoutInflater.inflate(R.layout.music_item, parent, false);
            viewHolder = new ViewHolder();
            //获取显示歌曲名称的视图
            viewHolder.nameView = convertView.findViewById(R.id.music_name);
            // 获取歌手的视图
            viewHolder.authorView = convertView.findViewById(R.id.music_author);
            // 获取歌曲编号的视图
            viewHolder.numView = convertView.findViewById(R.id.num);
            viewHolder.idView = convertView.findViewById(R.id.music_id);
            // 将视图设置到布局中
            convertView.setTag(viewHolder);
        } else {
            viewHolder = (ViewHolder) convertView.getTag();
        }
        // 将音乐数据,适配到对应的视图中
        Music music = (Music) mData.get(position);

        viewHolder.nameView.setText(music.getName());
        viewHolder.authorView.setText(music.getAuthor());
        viewHolder.numView.setText((position+1)+".");
        // 传入id,方便后续根据id查找歌曲
        Log.i("info",music.getId()+"");
        viewHolder.idView.setText(music.getId()+"");
        // 将id隐藏起来
        viewHolder.idView.setVisibility(View.GONE);

        return convertView;
    }

    /**
     * 自定义子选项布局视图
     * 对应music_item.xml中的组件
     */
    static class ViewHolder {
        TextView numView;
        TextView nameView;
        TextView authorView;
        TextView idView;
    }
}

???? 编写MusicListActivity

编写MusicListActivity发送请求,向服务器获取音乐列表,并通过适配器,显示出来,具体代码如下:

package com.moxuan.mytest;

import android.app.Activity;
import android.app.DownloadManager;
import android.os.Bundle;
import android.util.Log;
import android.widget.ListView;
import android.widget.Toast;

import androidx.annotation.Nullable;

import com.loopj.android.http.AsyncHttpClient;
import com.loopj.android.http.AsyncHttpResponseHandler;
import com.loopj.android.http.RequestParams;

import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;

import java.util.ArrayList;
import java.util.List;

import cz.msebera.android.httpclient.Header;

public class MusicListActivity extends Activity {
    // 显示音乐列表的列表视图
    ListView musicListView;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // 加载布局文件
        setContentView(R.layout.music_list);
        // 获取列表视图
        musicListView = findViewById(R.id.music_list);
        // 封装请求参数
        RequestParams requestParams = new RequestParams();
        // 发送异步请求 想服务端请求音乐列表数据
        // 注意:192.168.5.107 为我电脑上的IP,大家写的时候需要换成自己服务器端ip地址
        new AsyncHttpClient().post("http://192.168.5.107:8080/music_list", requestParams, new AsyncHttpResponseHandler() {
            /**
             * 当服务器成功响应时会执行的方法
             * @param status  服务器当前的状态, 值为200时,代表着服务成功处理请求,并返回了数据
             * @param headers
             * @param responseBody 服务器返回的数据
             */
            @Override
            public void onSuccess(int status, Header[] headers, byte[] responseBody) {
                // 如果服务器成功返回数据
                if(status==200){
                    // 将返回的数据转换成json对象
                    try {
                        JSONObject jsonObject = new JSONObject(new String(responseBody));
                        // 获取json数组,数组中存放的是音乐数据
                        JSONArray musics_array = jsonObject.getJSONArray("data");
                        List<Music> musicList = new ArrayList<>();
                        // 遍历音乐数据,将音乐数据封装到音乐列表中
                        for(int i=0;i<musics_array.length();i++){
                            Music music = new Music();
                            JSONObject jo = musics_array.getJSONObject(i);
                            music.setId(jo.getInt("id"));
                            music.setAuthor(jo.getString("author"));
                            music.setLocation(jo.getString("location"));
                            music.setName(jo.getString("name"));
                            musicList.add(music);
                        }
                        // 创建音乐列表适配器,将查询出来的音乐列表显示出来
                        MusicListAdapter adapter = new MusicListAdapter(MusicListActivity.this,musicList);
                        musicListView.setAdapter(adapter);

                    } catch (JSONException e) {
                        e.printStackTrace();
                    }
                }

            }

            /**
             * 如果服务器响应失败会执行的方法
             * @param i
             * @param headers
             * @param bytes
             * @param throwable
             */
            @Override
            public void onFailure(int i, Header[] headers, byte[] bytes, Throwable throwable) {
                Toast.makeText(MusicListActivity.this,"服务器繁忙",Toast.LENGTH_LONG).show();
            }
        });
    }
}
???? 注册MusicListActivity

在AndroidManifest.xml中注册MusicListActivity,并将其设置为启动页,具体代码如下:

<activity android:name=".MusicListActivity">
  <intent-filter>
    <action android:name="android.intent.action.MAIN" />

    <category android:name="android.intent.category.LAUNCHER" />
  </intent-filter>
</activity>

????测试效果

启动模拟器,运行程序,效果如下所示: