作业三——contentProvide、http请求和json解析

转载请注明出处:http://blog.csdn.net/qq347198688/article/details/53070253
本文出自【何嘉龙的博客

前言

当年,有一本书刊特别流行,叫《故事会》。《故事会》后来销声匿迹了,在我眼里,取代《故事会》的是知乎。对,就是那个知乎。 我其实一直很反对一个论调:“上知乎,学知识”。醒醒吧。知乎明显就是现代版的故事会好吗?! 我不否认知乎存在有价值的知识,但,根本不值得你花那么多时间,去发现那少的可怜的知识。去读书吧。用最系统的逻辑,去学会一项技能,哪怕你去《硅谷百年史》里去读那些振奋人心的仙童半导体的故事,也比你不断的知乎看着水的一笔的帖子要强的多。现在,最可怕的是大家在知乎上消费着垃圾,却依然觉得自己在吃着奶酪,学习的过程都是伴随着思考和痛苦的,如果你边笑边学习,那你真的得醒醒了。

引用了代码家的这句话。我很同意这个观点,不要在知乎上刷垃圾了,这个跟在微博刷鸡汤是一个道理,虽然我们有时候需要这种鸡汤,但是绝大多数时刻都是不需要的。好了,回到正题,让我们一起来看到第三次作业吧。

作业要求

  1. 使用contentprovider获取本机音乐文件,列表展示,可参考例子工程MusicLoader
  2. 点击任意列表项,可用service播放,并可通过按钮控制(同作业二,本地文件播放参考例子工程MusicLoader)
  3. 点击列表项跳转至另一Activity,通过网络请求获取歌词下载链接,并用gson解析得到的json文件,将所有下载链接显示在界面上。可参考例子工程GetLyricByWebService,有关android studio中GSON的使用请参考博文(http://blog.csdn.net/qq_32583189/article/details/51440297)。获取歌词的请求地址为http://gecimi.com/api/lyric/,方式为get,参数为歌名(如http://gecimi.com/api/lyric/海阔天空),具体可参看API文档http://api.geci.me/en/latest/
  4. 本作业需要SD卡读取权限和internet访问权限,请注意配置AndroidManifest
  5. 调试并运行程序,并在作业平台提交该项目工程目录的压缩包和运行效果截图。

项目分析

首先,我们看到作业要求1 使用contentprovide获取本机音乐文件,列表展示,可参考例子工程MusicLoader ,那我们就把老师给的例子工程给下载下来参考参考。将下载下来的项目解压,然后进入java文件夹里面,可以看到两个java文件MainActivity.javaMusicLoader.java。这里给大家简单解释一下吧。

MusicLoader.java 通过ContentProvider从外部存储中获取音乐的信息,并将其封装在MusicInfo里面,然后再将MusicInfo封装在ArrayList中,并对外提供一个getMusicList()方法,返回所有本地音乐信息的集合。

MainActivity.java 继承自ListActivity,那什么是ListActivity呢?其实就是Activity 里面嵌套了一个ListView,并可以直接通过getListView()获得ListViewMainActivity 将音乐的消息(包括歌名、作者名、图片)展示在界面中。

当然,为了能让你的程序读你的存储器,需要加权限<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>。大概的思路就是这样了。如果大家把老师给的代码添加到自己的工程的话,是可以直接点击Item就听到歌声的。但是我们需要的不仅仅如此,我们还需要更加强大的功能。再看到作业要求3点击列表项跳转至另一Activity,通过网络请求获取歌词下载链接,并用gson解析得到的json文件,将所有下载链接显示在界面上,可参考例子工程GetLyricByWebService ,那我们参考参考例子工程GetLyricByWebService,打开工程目录,可以看到有四个java文件LoadDialogLyricActivityLyricListBeanMainActivity,给大家稍微解释一下。(千万不要着急,代码在后面马上就要贴了)

LoadDialog.java 是自定义的一个Dialog(对话框),这里用来展示网络请求状态,如果是正在请求,会有一个小球不停地旋转。并在里面提供了show()dismiss() 方法。

MainActivity.java 是将歌曲的信息传给LyricActivity.java 文件,并跳转至LyricActivity

LyricActivity.java 是获取从MainActivity 传过来的歌曲信息,并通过HttpURLConnection 网络请求得到数据显示在界面上。

LyricListBean 是歌曲信息的实体类,这个是Gson解析json数据的一种格式,使用Gson解析json数据,详情请戳 Android Studio配置与使用GSON框架解析json数据

当然,为了能进行操作,需要加入<uses-permission android:name="android.permission.INTERNET"/> 权限。这个时候我们如果运行这个例子的话,点击App中的Button,会跳转到LyricActivity界面,并且会显示歌词的所有下载链接。

那么所有要求里面只剩下作业要求2了,结合我们上次的作业,没看过的同学可以先看第二次作业——Service、Listview与ACtivity参数传递,然后将这三个项目整合到一起,就可以大功告成了。那让我们一起动手来做吧。

作业实战

我们需要建立一个新的项目job3。在写代码之前,我们先把那三个例子里面的drawable目录下的图片都放进自己的drawable目录下。可以添加的有music.png,default_img.png,然后还有作业二的三个按钮pause.png,stop.png,play.png。另外建议先将结构视图从Android换到Project。

这里写图片描述

点击红色箭头处的下拉菜单,将结构视图变为Project。为了让工程项目更加清晰,然后多建立几个包。

这里写图片描述

右键红色箭头处,然后选中New -> Package,新建五个Package(包),activity,entry,service,ui以及utils。其中activity用来存放Activity,service用来存放Service,entry用来存放Bean实体类,ui用来存放界面相关的类,utils用来存放工具类。这样代码是不是很清晰,当我们需要找Activity代码的时候,只需要点开activity包即可。在新建完Package之后,我们把MainActivity剪切到activity包内,这时AndroidStudio会自动地把AndroidManifest.xml的关于MainActivity的配置信息改变,而不需要我们手动去改。MainActivity.java 代码如下所示:

package edu.whut.xucheng.job3.activity;

import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.ServiceConnection;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.os.IBinder;
import android.support.v7.app.AppCompatActivity;
import android.view.View;
import android.widget.AdapterView;
import android.widget.ImageButton;
import android.widget.ListView;
import android.widget.SimpleAdapter;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import edu.whut.xucheng.job3.R;
import edu.whut.xucheng.job3.entry.MusicLoader;
import edu.whut.xucheng.job3.service.PlayerService;
import edu.whut.xucheng.job3.utils.TextUtils;

import static edu.whut.xucheng.job3.R.drawable.music;

public class MainActivity extends AppCompatActivity {

    /**
     * 播放按钮
     */
    private ImageButton play;

    /**
     * 暂停按钮
     */
    private ImageButton pause;

    /**
     * 停止按钮
     */
    private ImageButton stop;

    /**
     * ListView的实例对象
     */
    private ListView mListView = null;

    /**
     * PlayerService的实例对象
     */
    private PlayerService mPlayService;

    /**
     * MediaPlayer的实例对象
     */
    MediaPlayer mediaPlayer;

    /**
     * 存放歌曲信息
     */
    List<MusicLoader.MusicInfo> my_list;

    /**
     * 存取从MusicInfo获得的信息
     */
    ArrayList<Map<String, Object>> mData = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        initView();
        init();
        initData();
        initList();
    }

    /**
     * bindService将Activty跟Service建立连接,在Activity中可以调用Service的方法,用startService做不到
     * 如果要用startService做的话,只能传递数据过去告诉Service什么时候该做什么。
     */
    private void init() {
        Intent serviceIntent = new Intent(MainActivity.this, PlayerService.class);
        bindService(serviceIntent, connection, Context.BIND_AUTO_CREATE);
    }

    /**
     * 初始化数据
     */
    private void initData() {
        MusicLoader musicLoader = MusicLoader.instance(this.getContentResolver());
        my_list = musicLoader.getMusicList();
        int lengh = my_list.size();
        for (int i = 0; i < lengh; i++) {
            Map<String, Object> item = new HashMap<>();
            item.put("image", music);
            item.put("title", my_list.get(i).getTitle().substring(0, my_list.get(i).getTitle().lastIndexOf('.')));
            item.put("text", my_list.get(i).getArtist());
            item.put("url", my_list.get(i).getUrl());
            mData.add(item);
        }
    }

    /**
     * 初始化View
     */
    private void initView() {
        mListView = (ListView) this.findViewById(R.id.list_item);
        play = (ImageButton) this.findViewById(R.id.play);
        pause = (ImageButton) this.findViewById(R.id.pause);
        stop = (ImageButton) this.findViewById(R.id.stop);
        play.setOnClickListener(new MyClickLister());
        pause.setOnClickListener(new MyClickLister());
        stop.setOnClickListener(new MyClickLister());
    }

    /**
     * 初始化ListView
     */
    private void initList() {
        SimpleAdapter adapter = new SimpleAdapter(this, mData, R.layout.icon_list,
                new String[]{"image", "title", "text"}, new int[]{R.id.image, R.id.title, R.id.text});
        mListView.setAdapter(adapter);
        mListView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> adapterView, View view, int position,
                                    long id) {
//                ToastUtils.showToast(MainActivity.this, "您选择的歌曲路劲为:" + my_list.get(position).getUrl());
                mPlayService.play(my_list.get(position).getUrl());

                //在跳转到LyricActivity的时候,把歌曲的名字跟作者的名字传过去
                Intent intent = new Intent(MainActivity.this, LyricActivity.class);
                Bundle bundle = new Bundle();
                bundle.putString("title", TextUtils.getTitleString(my_list.get(position).getTitle().substring(0, my_list.get(position).getTitle().lastIndexOf('.'))));
                bundle.putString("text", my_list.get(position).getArtist());
                intent.putExtras(bundle);
                startActivity(intent);
            }
        });
    }

    /**
     * 跟服务Service建立连接。
     * 连接成功回调onServiceConnected方法,连接失败回调onServiceDisconnected方法。
     */
    private ServiceConnection connection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {
            mPlayService = ((PlayerService.MyBinder) service).getService();
        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mPlayService = null;
        }
    };

    /**
     * 在Activity退出的时候,释放掉MediaPlayer的资源
     */
    @Override
    protected void onStop() {
        if (mediaPlayer != null) {
            mediaPlayer.release();
        }
        super.onStop();
    }

    /**
     * 写一个自己的类实现View.OnClickListener。
     */
    class MyClickLister implements View.OnClickListener {

        @Override
        public void onClick(View view) {
            switch (view.getId()) {
                //播放
                case R.id.play:
                    mPlayService.replay();
                    break;
                //暂停
                case R.id.pause:
                    mPlayService.pause();
                    break;
                //停止
                case R.id.stop:
                    //说明:这里我本来想用stop函数的,但是我在真机上面调试的时候,发现stop之后,
                    //声音会断断续续的,我就用了pause()函数,stop跟pause的唯一区别就是stop之后要prepare()。
                    //如果你的手机可以用stop的话,将下面的pause()注释掉,将stop()取消注释。
                    mPlayService.pause();
//                    mPlayService.stop();
                    break;
            }
        }
    }

}

这里我们没有直接继承自ListActivity,为什么呢?看到作业要求2点击任意列表项,可用service播放,并可通过按钮控制(同作业二,本地文件播放参考例子工程MusicLoader) 因为我们还需要在底下加几个按钮。如果直接继承自ListActivity,这个时候Activity只有一个ListView布局,我们不能往里面添加按钮。大家观察MainActivity.java有没有发现这些代码都很熟悉,是的,大多数跟第二次作业——Service、Listview与ACtivity参数传递 是重合的。

那区别在哪里呢?

  • 在ListView的Item点击事件中,服务mPlayService的play()方法,传递的不是position,而是url。因为第二次作业的歌曲是自己写死的,定义在一个数组里面,故可以传position。例如:我们点开第二个Item,对应的position为1,又服务里面定义了三首歌的数组,假设a数组为歌曲的数组,那么a[postion = 1]对应的是第二首歌。而第三次作业的歌曲不是的,你并不知道你的Item对应哪首歌,你新下载几首歌,ListView就变了。然后我还需要简单地说一下startService()bindService()这两种启动服务的方法的区别,用startService()启动服务后,调用者(这里是MainActivity)跟服务没关系了,而用bindService()启动服务的话,调用者跟服务之间还存在联系,服务可以通过下面的方式给调用者传递自身。

    public class MyBinder extends Binder {
    
            public PlayerService getService() {
                return PlayerService.this;
            }
        }
    而调用者可以通过下面方式得到服务本身。
    
    private ServiceConnection connection = new ServiceConnection() {
    
            @Override
            public void onServiceConnected(ComponentName name, IBinder service) {
                mPlayService = ((PlayerService.MyBinder) service).getService();
            }
    
            @Override
            public void onServiceDisconnected(ComponentName name) {
                mPlayService = null;
            }
        };
    这样,就可以在调用者端直接使用服务的代码了。
    
  • MainActivityLyricActivity传递过去了参数,包括(歌名还有作者名)。

另外我们可以看到,在BundleputString() 方法中,用到了TextUtils.getTitleString() 方法,这是一个处理文本的工具类。在utils包下,新建一个TextUtils.java文件,代码如下:

package edu.whut.xucheng.job3.utils;

/**
 * 文本的工具类
 * Created by dragonhaw on 2016/11/6.
 */

public class TextUtils {

    /**
     * 如果前面有歌手名字的话,去掉前面的歌手名字。例如:Beyond - 海阔天空 变为海阔天空
     */
    public static String getTitleString(String oldTitle) {
        if(!oldTitle.contains("-")) {
            return oldTitle;
        } else {
            int index = oldTitle.indexOf("-");
            return oldTitle.substring(index + 2);
        }
    }
}

一首歌存入外部存储中,歌曲的名字会是“作者名字 - 歌名”,然而这样的歌曲名字,我们查Api会发现没有。比如说“http://gecimi.com/api/lyric/Beyond - 海阔天空”,我们通过浏览器进去,可得到如下结果:

这里写图片描述

可以看到,结果为空。而如果直接以歌名去查找“http://gecimi.com/api/lyric/海阔天空”,可以得到如下结果(json数据):

这里写图片描述

这样就有结果了。为了寻找到有json数据的API,所以我们自己写了这个方法将“作者名字 - 歌名”变为“歌名”。

MainActivity.java相对应的布局文件activity_main.xml代码如下:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:orientation="vertical">

    <ListView
        android:id="@+id/list_item"
        android:layout_width="match_parent"
        android:layout_weight="6"
        android:layout_height="0dp"
        android:divider="#000"
        android:dividerHeight="1dp" />

    <LinearLayout
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="1"
        android:layout_gravity="center_horizontal"
        android:layout_marginTop="10dp"
        android:gravity="center_horizontal"
        android:orientation='horizontal'>

        <ImageButton
            android:id="@+id/play"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:background="@drawable/btn_selector"
            android:src="@drawable/play" />

        <ImageButton
            android:id="@+id/pause"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:background="@drawable/btn_selector"
            android:src="@drawable/pause" />

        <ImageButton
            android:id="@+id/stop"
            android:layout_width="0dp"
            android:layout_weight="1"
            android:layout_height="wrap_content"
            android:background="@drawable/btn_selector"
            android:src="@drawable/stop" />
    </LinearLayout>
</LinearLayout>

可以看到,ImageButton的android:background="@drawable/btn_selector" 属性使用了btn_selector,故我们需要在drawable目录下,新建一个btn_selector.xml文件,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
    <item android:state_pressed="true" android:drawable="@color/colorAccent"/>
</selector>

ListView对应的Item的布局文件,在layout布局文件夹下新建一个icon_list.xml文件,其代码如下所示:

<?xml version="1.0" encoding="utf-8"?>

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="?android:attr/listPreferredItemHeight">

    <ImageView
        android:id="@+id/image"
        android:layout_width="wrap_content"
        android:layout_height="fill_parent"
        android:layout_alignParentBottom="true"
        android:layout_alignParentTop="true"
        android:adjustViewBounds="true"
        android:padding="2dip" />

    <TextView
        android:id="@+id/title"
        android:layout_width="wrap_content"
        android:maxLines="1"
        android:layout_height="wrap_content"
        android:layout_alignParentRight="true"
        android:layout_alignParentTop="true"
        android:layout_alignWithParentIfMissing="true"
        android:layout_toRightOf="@+id/image"
        android:gravity="center_vertical"
        android:textSize="15dip" />

    <TextView
        android:id="@+id/text"
        android:layout_width="fill_parent"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_toRightOf="@+id/image"
        android:ellipsize="marquee"
        android:singleLine="true"
        android:textSize="20dip" />

</RelativeLayout>

这几个布局文件就不讲了。为了能让歌曲播放,我们需要在service包下新建一个PlayerService.java 文件,代码如下:

package edu.whut.xucheng.job3.service;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.os.Binder;
import android.os.IBinder;

import java.io.IOException;

import edu.whut.xucheng.job3.utils.ToastUtils;

public class PlayerService extends Service {
    private MediaPlayer mediaPlayer;
    //播放文件url
    private String path;

    private final IBinder binder = new MyBinder();

    /**
     * 客户端的bindService对应的是服务Service的onBind()方法(startService -> onstartCommand)
     * @param intent 客户端即Activity的bindService()传入的Intent
     */
    @Override
    public IBinder onBind(Intent intent) {
        return binder;
    }

    public class MyBinder extends Binder {

        public PlayerService getService() {
            return PlayerService.this;
        }
    }

    /**
     * 播放音乐,有参
     * 给ListView提供,当点击不同的Item时,会播放不同的音乐。
     */
    public void play(String path) {
        if (mediaPlayer != null) {
            mediaPlayer.release();
        }
        mediaPlayer = new MediaPlayer();
        try {
            mediaPlayer.setDataSource(path);
            mediaPlayer.prepare();

        } catch (Exception e) {
            e.printStackTrace();
        }
        mediaPlayer.start();
    }

    /**
     * 播放音乐,无参数
     * 给按钮的点击事件提供的,因为在按钮中没有position,不知道要播放哪一个。
     */
    public void replay() {
        if (mediaPlayer == null) {
            mediaPlayer = new MediaPlayer();
            try {
                if(path != null) {
                    mediaPlayer.setDataSource(path);
                    mediaPlayer.prepare();
                } else {
                    ToastUtils.showToast(getApplicationContext(), "请选择歌曲");
                }
            } catch (IOException e) {
                e.printStackTrace();
            }

        }
        if (!mediaPlayer.isPlaying()) {
            mediaPlayer.start();
        }
    }

    /**
     * 暂停音乐
     * 每次暂停isPause标识设为false并得到当前的播放位置
     */
    public void pause() {
        if (mediaPlayer.isPlaying() && mediaPlayer != null) {
            mediaPlayer.pause();
        }
    }

    /**
     * 停止音乐
     * 这个stop和pause不一样,stop之后再想播放音乐要重新prepare一次
     */
    public void stop() {
        if (mediaPlayer != null && mediaPlayer.isPlaying()) {
            mediaPlayer.stop();
            try {
                mediaPlayer.prepare();
            } catch (IllegalStateException | IOException e) {
                e.printStackTrace();
            }
        }
    }

    @Override
    public boolean onUnbind(Intent intent) {
        return super.onUnbind(intent);
    }

    @Override
    public void onDestroy() {
        if (mediaPlayer != null) {
            mediaPlayer.stop();
            mediaPlayer.release();
        }
    }

}

与作业二不同的是,这个服务的play()函数中用了mediaPlayer.setDataSource(path) 代码来找到歌曲的本地路径,然后再调用mediaPlayer.start() 进行播放。另外,我们的replay()函数,是没有参数的,又必须要需要用到歌曲存储的url,为了不报错,而且用户体验良好一点,我们先判断path为不为空,如果为空,弹出Toast,不然则播放音乐。这里的Toast我们也进行了处理,系统提供的Toast,在我们点击几次之后,即使我们已经停止点击了,它仍会不停地弹出Toast,并且你点几下,它会弹几下。所以我们自己在utils包下,新建一个ToastUtils.java 文件,其代码如下所示:

package edu.whut.xucheng.job3.utils;

import android.content.Context;
import android.widget.Toast;

/**
 *  Toast的工具类,当不停地点击出Toast的时候,Toast不会一直显示
 * Created by dragonhaw on 2016/11/6
 */
public class ToastUtils {
    /** 之前显示的内容 */
    private static String oldMsg;
    /** Toast对象 */
    private static Toast toast = null;
    /** 第一次时间 */
    private static long oneTime = 0;
    /** 第二次时间 */
    private static long twoTime = 0;

    /**
     * 显示Toast
     */
    public static void showToast(Context context, String message) {
        if (toast == null) {
            toast = Toast.makeText(context, message, Toast.LENGTH_SHORT);
            toast.show();
            oneTime = System.currentTimeMillis();
        } else {
            twoTime = System.currentTimeMillis();
            if (message.equals(oldMsg)) {
                if (twoTime - oneTime > Toast.LENGTH_SHORT) {
                    toast.show();
                }
            } else {
                oldMsg = message;
                toast.setText(message);
                toast.show();
            }
        }
        oneTime = twoTime;
    }
}

主页面跟服务都写好了,我们继续来写歌词界面,在activty包下新建LyricActivty.java文件,其代码如下所示:

package edu.whut.xucheng.job3.activity;

import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.widget.TextView;

import com.google.gson.GsonBuilder;

import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLEncoder;

import edu.whut.xucheng.job3.ui.LoadDialog;
import edu.whut.xucheng.job3.R;
import edu.whut.xucheng.job3.entry.LyricListBean;

public class LyricActivity extends AppCompatActivity {
    String result="";
    LyricListBean my_lyricList;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_lyric);
        //新页面接收数据
        Bundle bundle = this.getIntent().getExtras();
        final String Title = bundle.getString("title");
        String Text = bundle.getString("text");
        TextView title2=(TextView)findViewById(R.id.title2);
        TextView text2=(TextView)findViewById(R.id.text2);
        final TextView Lyric_list=(TextView)findViewById(R.id.lyrics_list);
        title2.setText(Title);
        text2.setText(Text);
        LoadDialog.show(this);
        new Thread(new Runnable() {

            @Override
            public void run() {
                String song_name="";
                String path ="http://gecimi.com/api/lyric/";
                try {
                    song_name = URLEncoder.encode(Title,"UTF-8");
                    URL url = new URL(path+song_name);
                    HttpURLConnection conn = (HttpURLConnection)url.openConnection();
                    conn.setRequestMethod("GET");
                    //conn.setReadTimeout(5000);// 设置超时的时间
                    //conn.setConnectTimeout(5000);// 设置链接超时的时间
                    conn.setDoOutput(true);
                    conn.setDoInput(true);
                    if (conn.getResponseCode() == 200) {
                        // 获取响应的输入流对象
                        InputStream is = conn.getInputStream();

                        // 创建字节输出流对象
                        ByteArrayOutputStream os = new ByteArrayOutputStream();
                        // 定义读取的长度
                        int len = 0;
                        // 定义缓冲区
                        byte buffer[] = new byte[1024];
                        // 按照缓冲区的大小,循环读取
                        while ((len = is.read(buffer)) != -1) {
                            // 根据读取的长度写入到os对象中
                            os.write(buffer, 0, len);
                        }
                        // 释放资源
                        is.close();
                        os.close();
                        // 返回字符串
                        result = new String(os.toByteArray());
                        my_lyricList=new GsonBuilder().create().fromJson(result,LyricListBean.class);
                        result="";
                        if(my_lyricList.getResult().size()!=0){
                            for (LyricListBean.ResultBean tmp:my_lyricList.getResult()){
                                result=result+tmp.getLrc()+"\n";
                            }

                        }else{
                            result="未找到匹配歌词";
                        }

                    } else {
                        System.out.println("------------------链接失败-----------------");
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }

                runOnUiThread(new Runnable() {
                    @Override
                    public void run() {
                        LoadDialog.dismiss(LyricActivity.this);
                        Lyric_list.setText(result);
                    }
                });

            }
        }).start();
    }
}

这个也是老师提供的例子的源码,我没有丝毫改动。因为这里用到了gson库,所以我们需要在module里面的(也就是app目录下的)build.gradle文件添加gson的依赖 compile 'com.google.code.gson:gson:2.8.0',如下图所示:

这里写图片描述

可以看到LyricActivity 用到了LyricListBean.java,我们在entry包下面新建一个文件LyricListBean.java , 代码如下所示:

package edu.whut.xucheng.job3.entry;

import java.util.List;

/**
 * Created by 珀石 on 2016/11/4.
 */
public class LyricListBean  {
    /**
     * count : 15
     * code : 0
     * result : [{"aid":2848529,"lrc":"http://s.gecimi.com/lrc/344/34435/3443588.lrc","song":"海阔天空","artist_id":2,"sid":3443588},{"aid":2346662,"lrc":"http://s.gecimi.com/lrc/274/27442/2744281.lrc","song":"海阔天空","artist_id":2396,"sid":2744281},{"aid":1889264,"lrc":"http://s.gecimi.com/lrc/210/21070/2107014.lrc","song":"海阔天空","artist_id":8715,"sid":2107014},{"aid":2075717,"lrc":"http://s.gecimi.com/lrc/236/23651/2365157.lrc","song":"海阔天空","artist_id":8715,"sid":2365157},{"aid":1563419,"lrc":"http://s.gecimi.com/lrc/166/16685/1668536.lrc","song":"海阔天空","artist_id":9208,"sid":1668536},{"aid":1567586,"lrc":"http://s.gecimi.com/lrc/167/16739/1673997.lrc","song":"海阔天空","artist_id":9208,"sid":1673997},{"aid":1571906,"lrc":"http://s.gecimi.com/lrc/167/16796/1679605.lrc","song":"海阔天空","artist_id":9208,"sid":1679605},{"aid":1573814,"lrc":"http://s.gecimi.com/lrc/168/16819/1681961.lrc","song":"海阔天空","artist_id":9208,"sid":1681961},{"aid":1656038,"lrc":"http://s.gecimi.com/lrc/179/17907/1790768.lrc","song":"海阔天空","artist_id":9208,"sid":1790768},{"aid":1718741,"lrc":"http://s.gecimi.com/lrc/187/18757/1875769.lrc","song":"海阔天空","artist_id":9208,"sid":1875769},{"aid":2003267,"lrc":"http://s.gecimi.com/lrc/226/22642/2264296.lrc","song":"海阔天空","artist_id":9208,"sid":2264296},{"aid":2020610,"lrc":"http://s.gecimi.com/lrc/228/22889/2288967.lrc","song":"海阔天空","artist_id":9208,"sid":2288967},{"aid":2051678,"lrc":"http://s.gecimi.com/lrc/233/23323/2332322.lrc","song":"海阔天空","artist_id":9208,"sid":2332322},{"aid":2412704,"lrc":"http://s.gecimi.com/lrc/283/28376/2837689.lrc","song":"海阔天空","artist_id":9208,"sid":2837689},{"aid":2607041,"lrc":"http://s.gecimi.com/lrc/311/31116/3111659.lrc","song":"海阔天空","artist_id":9208,"sid":3111659}]
     */

    private int count;
    private int code;
    /**
     * aid : 2848529
     * lrc : http://s.gecimi.com/lrc/344/34435/3443588.lrc
     * song : 海阔天空
     * artist_id : 2
     * sid : 3443588
     */

    private List<ResultBean> result;

    public int getCount() {
        return count;
    }

    public void setCount(int count) {
        this.count = count;
    }

    public int getCode() {
        return code;
    }

    public void setCode(int code) {
        this.code = code;
    }

    public List<ResultBean> getResult() {
        return result;
    }

    public void setResult(List<ResultBean> result) {
        this.result = result;
    }

    public static class ResultBean {
        private int aid;
        private String lrc;
        private String song;
        private int artist_id;
        private int sid;

        public int getAid() {
            return aid;
        }

        public void setAid(int aid) {
            this.aid = aid;
        }

        public String getLrc() {
            return lrc;
        }

        public void setLrc(String lrc) {
            this.lrc = lrc;
        }

        public String getSong() {
            return song;
        }

        public void setSong(String song) {
            this.song = song;
        }

        public int getArtist_id() {
            return artist_id;
        }

        public void setArtist_id(int artist_id) {
            this.artist_id = artist_id;
        }

        public int getSid() {
            return sid;
        }

        public void setSid(int sid) {
            this.sid = sid;
        }
    }
}

然后再在layout布局文件夹中新建一个activity_lyric.xml文件,代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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:paddingBottom="@dimen/activity_vertical_margin"
    android:paddingLeft="@dimen/activity_horizontal_margin"
    android:paddingRight="@dimen/activity_horizontal_margin"
    android:paddingTop="@dimen/activity_vertical_margin"
    tools:context="edu.whut.xucheng.job3.activity.LyricActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceLarge"
        android:text="Large Text"
        android:id="@+id/title2"
        android:layout_alignStart="@+id/text2" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceMedium"
        android:text="Medium Text"
        android:id="@+id/text2"
        android:layout_below="@+id/title2"
        android:layout_centerHorizontal="true" />

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:textAppearance="?android:attr/textAppearanceSmall"
        android:text="Small Text"
        android:id="@+id/lyrics_list"
        android:layout_marginTop="60dp"
        android:layout_below="@+id/text2"
        android:layout_centerHorizontal="true" />
</RelativeLayout>

在ui包下,将老师的LoadDialog.java 文件复制进去,代码如下所示:

package edu.whut.xucheng.job3.ui;

import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.text.TextUtils;
import android.view.Display;
import android.view.KeyEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.TextView;
import android.widget.Toast;

import edu.whut.xucheng.job3.R;


/**
 * Created by 珀石 on 2016/11/4.
 */
public class LoadDialog extends Dialog {

    /**
     * LoadDialog
     */
    private static LoadDialog loadDialog;
    /**
     * cancelable, the dialog dimiss or undimiss flag
     */
    private boolean cancelable;
    /**
     * if the dialog don't dimiss, what is the tips.
     */
    private String tipMsg;

    /**
     * the LoadDialog constructor
     *
     * @param ctx        Context
     * @param cancelable boolean
     * @param tipMsg     String
     */

    public LoadDialog(final Context ctx, boolean cancelable, String tipMsg) {
        super(ctx);

        this.cancelable = cancelable;
        this.tipMsg = tipMsg;

        this.getContext().setTheme(android.R.style.Theme_DeviceDefault_Dialog_NoActionBar_MinWidth);
        setContentView(R.layout.dialog_layout);
        // 必须放在加载布局后
        setparams();
        TextView tv = (TextView) findViewById(R.id.tvLoad);
        if (!TextUtils.isEmpty(tipMsg)) {
            tv.setVisibility(View.VISIBLE);
            tv.setText(tipMsg);
        }
    }

    private void setparams() {
        this.setCancelable(cancelable);
        this.setCanceledOnTouchOutside(false);
        WindowManager windowManager = getWindow().getWindowManager();
        Display display = windowManager.getDefaultDisplay();
        WindowManager.LayoutParams lp = this.getWindow().getAttributes();
        // Dialog宽度
        lp.width = (int) (display.getWidth() * 0.7);
        Window window = getWindow();
        window.setAttributes(lp);
        window.getDecorView().getBackground().setAlpha(0);
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            if (!cancelable) {
                Toast.makeText(getContext(), tipMsg, Toast.LENGTH_SHORT).show();
                return true;
            }
        }
        return super.onKeyDown(keyCode, event);
    }

    /**
     * show the dialog
     *
     * @param context
     */
    public static void show(Context context) {
        show(context, null, true);
    }

    /**
     * show the dialog
     *
     * @param context Context
     * @param message String
     */
    public static void show(Context context, String message) {
        show(context, message, true);
    }

    /**
     * show the dialog
     *
     * @param context    Context
     * @param resourceId resourceId
     */
    public static void show(Context context, int resourceId) {
        show(context, context.getResources().getString(resourceId), true);
    }

    /**
     * show the dialog
     *
     * @param context    Context
     * @param message    String, show the message to user when isCancel is true.
     * @param cancelable boolean, true is can't dimiss,false is can dimiss
     */
    private static void show(Context context, String message, boolean cancelable) {
        if (context instanceof Activity) {
            if (((Activity) context).isFinishing()) {
                return;
            }
        }
        if (loadDialog != null && loadDialog.isShowing()) {
            return;
        }
        loadDialog = new LoadDialog(context, cancelable, message);
        loadDialog.show();
    }

    /**
     * dismiss the dialog
     */
    public static void dismiss(Context context) {
        try {
            if (context instanceof Activity) {
                if (((Activity) context).isFinishing()) {
                    loadDialog = null;
                    return;
                }
            }

            if (loadDialog != null && loadDialog.isShowing()) {
                Context loadContext = loadDialog.getContext();
                if (loadContext != null && loadContext instanceof Activity) {
                    if (((Activity) loadContext).isFinishing()) {
                        loadDialog = null;
                        return;
                    }
                }
                loadDialog.dismiss();
                loadDialog = null;
            }
        } catch (Exception e) {
            e.printStackTrace();
            loadDialog = null;
        }
    }
}

然后再在entry包下,把老师例子中的MusicLoader.java文件复制进去,如果不想找,也可以直接复制博客中的,MusicLoader.java 代码如下所示:

package edu.whut.xucheng.job3.entry;

import android.annotation.SuppressLint;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore.Audio.Media;
import android.util.Log;

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

/**
 * Created by 珀石 on 2016/11/4.
 */
public class MusicLoader {

    private static final String TAG = "edu.whut.xucheng.job3.entry.MusicLoader";

    private static List<MusicInfo> musicList = new ArrayList<MusicInfo>();

    private static MusicLoader musicLoader;

    private static ContentResolver contentResolver;
    //Uri,指向external的database
    private Uri contentUri = Media.EXTERNAL_CONTENT_URI;
    //projection:选择的列; where:过滤条件; sortOrder:排序。
    private String[] projection = {
            Media._ID,
            Media.DISPLAY_NAME,
            Media.DATA,
            Media.ALBUM,
            Media.ARTIST,
            Media.DURATION,
            Media.SIZE
    };
    private String where =  "mime_type in ('audio/mpeg','audio/x-ms-wma')  and is_music > 0 " ;
    private String sortOrder = Media.DATA;

    public static MusicLoader instance(ContentResolver pContentResolver){
        if(musicLoader == null){
            contentResolver = pContentResolver;
            musicLoader = new MusicLoader();
        }
        return musicLoader;
    }

    @SuppressLint("LongLogTag")
    private MusicLoader(){                                                                                                             //利用ContentResolver的query函数来查询数据,然后将得到的结果放到MusicInfo对象中,最后放到数组中
        Cursor cursor = contentResolver.query(contentUri, projection, where, null, sortOrder);
        if(cursor == null){
            Log.v(TAG,"cursor == null.");
        }else if(!cursor.moveToFirst()){
            Log.v(TAG,"moveToFirst()->false.");
        }else{
            int displayNameCol = cursor.getColumnIndex(Media.DISPLAY_NAME);
            int albumCol = cursor.getColumnIndex(Media.ALBUM);
            int idCol = cursor.getColumnIndex(Media._ID);
            int durationCol = cursor.getColumnIndex(Media.DURATION);
            int sizeCol = cursor.getColumnIndex(Media.SIZE);
            int artistCol = cursor.getColumnIndex(Media.ARTIST);
            int urlCol = cursor.getColumnIndex(Media.DATA);
            do{
                String title = cursor.getString(displayNameCol);
                String album = cursor.getString(albumCol);
                long id = cursor.getLong(idCol);
                int duration = cursor.getInt(durationCol);
                long size = cursor.getLong(sizeCol);
                String artist = cursor.getString(artistCol);
                String url = cursor.getString(urlCol);

                MusicInfo musicInfo = new MusicInfo(id, title);
                musicInfo.setAlbum(album);
                musicInfo.setDuration(duration);
                musicInfo.setSize(size);
                musicInfo.setArtist(artist);
                musicInfo.setUrl(url);
                musicList.add(musicInfo);

            }while(cursor.moveToNext());
        }
    }

    public List<MusicInfo> getMusicList(){
        return musicList;
    }

    public Uri getMusicUriById(long id){
        Uri uri = ContentUris.withAppendedId(contentUri, id);
        return uri;
    }
    //下面是自定义的一个MusicInfo子类
    public static class MusicInfo  {
        private long id;
        private String title;
        private String album;
        private int duration;
        private long size;
        private String artist;
        private String url;

        public MusicInfo(){

        }

        public MusicInfo(long pId, String pTitle){
            id = pId;
            title = pTitle;
        }

        public String getArtist() {
            return artist;
        }

        public void setArtist(String artist) {
            this.artist = artist;
        }

        public long getSize() {
            return size;
        }

        public void setSize(long size) {
            this.size = size;
        }

        public long getId() {
            return id;
        }

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

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }

        public String getAlbum() {
            return album;
        }

        public void setAlbum(String album) {
            this.album = album;
        }

        public int getDuration() {
            return duration;
        }

        public void setDuration(int duration) {
            this.duration = duration;
        }

        public String getUrl() {
            return url;
        }

        public void setUrl(String url) {
            this.url = url;
        }

    }
}

LoadDialog.java 对应的布局文件dialog_layout.xml ,也是在layout布局文件夹中新建,dialog_layout.xml代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent"
    android:gravity="center">

    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="64dp"
        android:layout_centerInParent="true"
        android:background="@drawable/shape_cir_bg">

        <ProgressBar
            android:id="@+id/loading_progress"
            android:layout_width="32dp"
            android:layout_height="32dp"
            android:layout_centerVertical="true"
            android:layout_marginLeft="20dp" />

        <TextView
            android:id="@+id/tvLoad"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_centerVertical="true"
            android:layout_gravity="center"
            android:layout_marginLeft="10dp"
            android:layout_toRightOf="@id/loading_progress"
            android:gravity="center_vertical|left"
            android:text="正在加载..."
            android:textColor="@android:color/white" />
    </RelativeLayout>

</RelativeLayout>

可以看到RelativeLayout 的属性android:background="@drawable/shape_cir_bg" 中使用了shape_cir_bg,故我们需要在drawable目录下,右键drawable,选中New -> Drawable resource file -> filename为shape_cir_bg.xml,RootElement为“selector”,shape_cir_bg.xml代码如下所示:

<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
    android:shape="rectangle">

    <solid android:color="#cc333333" />

    <corners android:radius="5dp" />

</shape>

整个项目的项目结构如图所示:

这里写图片描述

最后,别忘了在AndroidManifest.xml 文件中加入

    <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE"/>
    <uses-permission android:name="android.permission.INTERNET"/>
    ...
    <activity android:name=".activity.LyricActivity"/>
    <service android:name=".service.PlayerService">
    </service>

如下图所示:

这里写图片描述

这样,我们就可以运行看下效果了。

这里写图片描述

这里写图片描述

嘿嘿,这样我们第三次作业就做好了。最好用真机调试,因为模拟器里面是没有歌曲的,当然也可以自己下载几首歌曲。界面丑了一点,大家如果大家喜欢的话,可以自己设计几个好看的按钮。大家写完之后别忘了提交哦!

代码点击此处下载

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值