Android之简易多媒体应用

2017-12-02  本文已影响48人  bbchond
Android老师布置了个播放音乐跟视频的小实验,本来看了下感觉没啥难度......但是自己写的时候一直告诉我获取不到指定的文件路径,这才想起来Android M(即Android 6.0及以上)版本以上让人蛋疼的权限问题。

Tip:模拟器在Android 6.0一下的可以暂时不用考虑动态权限的申请,然后,不管你模拟器是什么版本,都请在注册文件中先申明读SD卡的权限。

首先,根据作业要求要实现一个淡入淡出的动画效果,那我们就新建一个Activity,并且命名为SplashActivity:

public class SplashActivity extends Activity {

    Animation animation;
    ImageView img;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_splash);
        img= (ImageView) findViewById(R.id.welcome);
        animation = new AlphaAnimation(0,1);
        animation.setDuration(2000);
        img.startAnimation(animation);
        Timer timer=new Timer();
        TimerTask task=new TimerTask() {
            @Override
            public void run() {
                Thread t=new Thread(){
                    @Override
                    public void run() {
                        super.run();
                        Intent intent=new Intent(SplashActivity.this,TabHostDemo.class);
                        startActivity(intent);
                        //防止点击返回键之后停止在登录欢迎界面,因此跳转完成后直接结束该Activity
                        finish();
                    }
                };
                runOnUiThread(t);
            }
        };
        timer.schedule(task,2000);
    }
}

这部分要注意的是,在跳转Activity完成之后,需要在startActivity的下一行执行finish操作,将该Activity结束掉,否则跳转完成之后,用户点击返回按钮,将会卡在该欢迎页面。

淡入淡出的动画效果实现:

animation = new AlphaAnimation(0,1);
animation.setDuration(2000);
img.startAnimation(animation);

这部分其实就是将图片从透明变成不透明......
该Activity的layout文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/welcome"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:background="@mipmap/pic01"/>

</LinearLayout>

然后就是实验要求使用的TableHostDemo部分(仅为满足实验要求,使用了该已过时的Activity,在实际开发中建议使用Fragment+ViewPager替代)

如果你的模拟器是Android 6.0以下,请直接看下列的代码部分即可:

public class TabHostDemo extends TabActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_host_demo);

        TabHost tabHost=getTabHost();
        TabHost.TabSpec spec;
        spec=tabHost.newTabSpec("0");
        spec.setIndicator("帧动画");
        Intent intent;
        intent=new Intent(TabHostDemo.this,FrameActivity.class);
        spec.setContent(intent);
        tabHost.addTab(spec);

        spec=tabHost.newTabSpec("1");
        spec.setIndicator("播放音乐");
        Intent intent1;
        intent1=new Intent(TabHostDemo.this,PlayMusicActivity.class);
        spec.setContent(intent1);
        tabHost.addTab(spec);

        spec=tabHost.newTabSpec("3");
        spec.setIndicator("播放视频");
        Intent intent2;
        intent2=new Intent(TabHostDemo.this,PlayVideoActivity.class);
        spec.setContent(intent2);
        tabHost.addTab(spec);
    }
}

就是简单的TabHost的使用。

重点来了,如果你的模拟器是Android 6.0及以上的Android版本的,请看这里的代码部分:


public class TabHostDemo extends TabActivity {

    private String[] permissions = {Manifest.permission.READ_EXTERNAL_STORAGE};

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_tab_host_demo);

        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
            //检查该权限是否已经获取
            int i = ContextCompat.checkSelfPermission(this, permissions[0]);
            // 权限是否已经 授权 GRANTED---授权  DENIED---拒绝
            if (i != PackageManager.PERMISSION_GRANTED){
                // 如果没有授予该权限,就去提示用户请求
                showDialogTipUserRequestPermission();
            }
        }

        TabHost tabHost=getTabHost();
        TabHost.TabSpec spec;
        spec=tabHost.newTabSpec("0");
        spec.setIndicator("帧动画");
        Intent intent;
        intent=new Intent(TabHostDemo.this,FrameActivity.class);
        spec.setContent(intent);
        tabHost.addTab(spec);

        spec=tabHost.newTabSpec("1");
        spec.setIndicator("播放音乐");
        Intent intent1;
        intent1=new Intent(TabHostDemo.this,PlayMusicActivity.class);
        spec.setContent(intent1);
        tabHost.addTab(spec);

        spec=tabHost.newTabSpec("3");
        spec.setIndicator("播放视频");
        Intent intent2;
        intent2=new Intent(TabHostDemo.this,PlayVideoActivity.class);
        spec.setContent(intent2);
        tabHost.addTab(spec);
    }

    // 提示用户该请求权限的弹出框
    private void showDialogTipUserRequestPermission(){
        new AlertDialog.Builder(this)
                .setTitle("没有SD卡读取权限!")
                .setMessage("没有SD卡读权限将造成程序异常")
                .setPositiveButton("立即开启", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        startRequestPermission();
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        finish();
                    }
                }).setCancelable(false).show();
    }

    private void startRequestPermission(){
        ActivityCompat.requestPermissions(this,permissions,321);
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);

        if (requestCode == 321){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
                if (grantResults[0] != PackageManager.PERMISSION_GRANTED){
                    //判断用户是否点击了不再提醒
                    boolean b = shouldShowRequestPermissionRationale(permissions[0]);
                    if (!b){
                        //提示用户去设置界面手动开启权限
                        showDialogTipUserGoToAppSetting();
                    } else {
                        finish();
                    }
                } else {
                    Toast.makeText(this, "权限获取成功", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }

    private void showDialogTipUserGoToAppSetting(){
        new AlertDialog.Builder(this)
                .setTitle("存储权限不可用!")
                .setMessage("请在设置中立即开启!")
                .setPositiveButton("确认", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        Intent intent = new Intent();
                        intent.setAction(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
                        Uri uri = Uri.fromParts("package",getPackageName(),null);
                        intent.setData(uri);

                        startActivityForResult(intent,123);
                    }
                })
                .setNegativeButton("取消", new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        finish();
                    }
                }).setCancelable(false).show();
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);

        if (requestCode == 123){
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M){
                int i = ContextCompat.checkSelfPermission(this,permissions[0]);
                //权限是否已经授权:GRANTED--授权 DENIED--拒绝
                if (i != PackageManager.PERMISSION_GRANTED){
                    //如果没有授权
                    Toast.makeText(this, "请到设置界面允许授权", Toast.LENGTH_SHORT).show();
                }
            }
        }
    }
}

考虑到部分同学IDE不是Android Studio的情况,因此在这里没有直接compile封装好的权限包,而是遵照百度到的教程老老实实的敲了一遍。大体上就是打开app时,会弹出窗口叫用户进行权限授予:由于读写SD卡被认为是危险权限,因此必须进行动态权限的申请。

具体过程不再赘述,想要了解的请看下方链接中的文章。
具体链接:https://www.cnblogs.com/xmcx1995/p/5870191.html

该Activity的layout文件:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TabHost
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@android:id/tabhost">

        <LinearLayout
            android:layout_width="match_parent"
            android:layout_height="match_parent"
            android:orientation="vertical">

            <TabWidget
                android:id="@android:id/tabs"
                android:layout_width="match_parent"
                android:layout_height="wrap_content">
            </TabWidget>

            <FrameLayout
                android:id="@android:id/tabcontent"
                android:layout_width="match_parent"
                android:layout_height="match_parent">

            </FrameLayout>

        </LinearLayout>

    </TabHost>

</LinearLayout>

后面便是多媒体文件应用的几个Activity了

第一个FrameActivity:

public class FrameActivity extends Activity {

    private SoundPool sp;
    private int id1;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_frame);

        ImageView imageView = (ImageView) findViewById(R.id.frame_image);
        imageView.setImageResource(R.drawable.frame_animation);

        Button buttonStart = (Button) findViewById(R.id.start);
        Button buttonPause = (Button) findViewById(R.id.pause);

        sp=new SoundPool(2, AudioManager.STREAM_MUSIC,0);
        id1=sp.load(FrameActivity.this,R.raw.readygo,1);

        final AnimationDrawable frameAnimation = (AnimationDrawable) imageView
                .getDrawable();

        buttonStart.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                frameAnimation.start();
                sp.play(id1,1,1,1,0,1);
            }
        });

        buttonPause.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                frameAnimation.stop();
            }
        });
    }
}

layout文件:

<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <ImageView
        android:id="@+id/frame_image"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:src="@drawable/frame_animation"
        android:layout_alignParentTop="true"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="168dp" />

    <Button
        android:id="@+id/start"
        android:text="开始播放动画"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_marginTop="16dp"
        android:layout_below="@+id/frame_image"
        android:layout_centerHorizontal="true" />

    <Button
        android:id="@+id/pause"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="停止播放动画"
        android:layout_marginTop="15dp"
        android:layout_below="@+id/start"
        android:layout_alignStart="@+id/start" />

</RelativeLayout>

根据实验的要求,在我们点击开始按钮的时候,同时要播放一次"ready go"这个mp3文件,因此我们需要实例化一个SoundPool,用于播放该音乐文件。

首先,我们在res文件夹下新建一个raw文件夹,用于存放音乐文件:

raw文件夹的新建
之后将音乐文件放入raw文件夹,然后用id1=sp.load(FrameActivity.this,R.raw.readygo,1);这行代码获取音乐文件的SoundId。

接下来是动画部分:我们可以看到,在layout文件中我们给imageView的src指定的是android:src="@drawable/frame_animation"这样一行代码,@drawable/frame_animation其实是我们在drawable文件夹下新建的一个XML文件,其实就是多张图片连起来显示让人认为是动画的一个效果;

frame_animation下的代码:
<animation-list xmlns:android="http://schemas.android.com/apk/res/android"
    android:oneshot="false">

    <item android:drawable="@drawable/m1" android:duration="50"/>
    <item android:drawable="@drawable/m2" android:duration="50"/>
    <item android:drawable="@drawable/m3" android:duration="50"/>
    <item android:drawable="@drawable/m4" android:duration="50"/>
    <item android:drawable="@drawable/m5" android:duration="50"/>
    <item android:drawable="@drawable/m6" android:duration="50"/>
    <item android:drawable="@drawable/m7" android:duration="50"/>
    <item android:drawable="@drawable/m8" android:duration="50"/>

</animation-list>

我们给每张图片通过duration来指定显示时间为50ms,来达到多张图片连续播放像是在播放动画的效果,其实就是“帧动画”。

然后是播放音乐部分的Activity:
public class PlayMusicActivity extends Activity {

    ListView listView;
    List<String> listName,listPath;
    MediaPlayer mediaPlayer;
    SeekBar seekBar;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_play_music);

        listView = (ListView) findViewById(R.id.listView);
        //指定要播放的音乐文件的路径
        File path = new File(Environment.getExternalStorageDirectory()+"/Music");
        listName = new ArrayList<String>();
        listPath = new ArrayList<String>();
        getMusicLists(path);
        ArrayAdapter<String> adapter = new ArrayAdapter<String>(PlayMusicActivity.this,
                android.R.layout.simple_list_item_1,listName);
        listView.setAdapter(adapter);

        mediaPlayer = new MediaPlayer();
        seekBar = (SeekBar) findViewById(R.id.seekBar);
        setListener();

        //拖动SeekBar改变歌曲进度
        seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() {
            @Override
            public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {

            }

            @Override
            public void onStartTrackingTouch(SeekBar seekBar) {

            }

            @Override
            public void onStopTrackingTouch(SeekBar seekBar) {
                //用seekTo直接跳转进度条进度到seekBar当前拖动到的进度位置
                mediaPlayer.seekTo(seekBar.getProgress());
            }
        });
    }

    //listView的单击事件,根据点击到的item来播放对应的歌曲
    private void setListener() {
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                try {
                    if( mediaPlayer != null){
                        mediaPlayer.stop();
                        mediaPlayer.reset();
                    }
                    //根据position来播放点击到的歌曲
                    mediaPlayer.setDataSource(PlayMusicActivity.this, Uri.parse(listPath.get(position)));
                    //mediaPlayer的准备过程
                    mediaPlayer.prepare();
                    mediaPlayer.start();
                    //seekBar获取进度条最大值、设置进度条初始进度为0
                    seekBar.setMax(mediaPlayer.getDuration());
                    seekBar.setProgress(0);
                    ChangeSeekBar();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        });
    }

    //进度条当前位置的获取
    private void ChangeSeekBar() {
        final Handler handler = new Handler(){
            @Override
            public void handleMessage(Message msg) {
                super.handleMessage(msg);
                //用msg.arg1来传递进度值
                seekBar.setProgress(msg.arg1);
            }
        };
        Thread t = new Thread(){
            @Override
            public void run() {
                super.run();
                while (seekBar.getProgress() < seekBar.getMax()){
                    //获取当前进度
                    int progress = mediaPlayer.getCurrentPosition();
                    Message msg = handler.obtainMessage();
                    //设置msg.arg1的值为progress,实现进度获取
                    msg.arg1 = progress;
                    handler.sendMessage(msg);

                    try {
                        Thread.sleep(100);
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                }
            }
        };

        t.start();
    }

    //将获取到的音乐文件的名字显示在listView中
    private void getMusicLists(File path) {
        if (path != null){
            if (path.isDirectory()){
                File[] files = path.listFiles();
                for (int i = 0; i < files.length; i++) {
                    getMusicLists(files[i]);
                }
            } else {
                String musicName = path.getName();
                String musicPath = path.getAbsolutePath();
                if (musicName.endsWith(".mp3")){
                    listName.add(musicName);
                    listPath.add(musicPath);
                }
            }
        }
    }
}

该说的我都写在注释里了......文章最下面会给出源码文件的链接。
layout布局:

<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:weightSum="1">

    <TextView
        android:textSize="20sp"
        android:layout_marginTop="20sp"
        android:text="音乐列表"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ListView
        android:layout_marginTop="10sp"
        android:id="@+id/listView"
        android:layout_width="match_parent"
        android:layout_height="377dp"
        android:layout_weight="0.75">
    </ListView>

    <SeekBar
        android:id="@+id/seekBar"
        android:layout_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

这里ListView的大小我是在IDE内直接拖动更改的,不同的人显示的效果不一样,有需要还麻烦自己更改大小了。

接下来是播放视频的PlayVideoActivity:
public class PlayVideoActivity extends Activity {

    ListView listView;
    List<String> listName,listPath;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_playvideo);

        listView = (ListView) findViewById(R.id.videoList);
        //指定要读的视频文件存放的路径
        final File path = new File(Environment.getExternalStorageDirectory()+"/Movies");
        listName = new ArrayList<String>();
        listPath = new ArrayList<String>();
        getVideoLists(path);
        ArrayAdapter<String> video_adapter = new ArrayAdapter<String>(PlayVideoActivity.this,
                android.R.layout.simple_list_item_1,listName);
        listView.setAdapter(video_adapter);

        //listView点击事件
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = new Intent(PlayVideoActivity.this, VideoPlayTest.class);
                //获取当前ListView中被选中的item的text值,其中text1是simple_list_item_1中的textView的id
                TextView name = (TextView) view.findViewById(android.R.id.text1);
                String getName = name.getText().toString();
                //传递选中的视频文件的路径
                intent.putExtra("path",path+"/"+getName);
                startActivity(intent);
            }
        });
    }

    //将视频文件的名字显示在listView中
    private void getVideoLists(File path) {
        if (path != null){
            if (path.isDirectory()){
                File[] files = path.listFiles();
                for (int i = 0; i < files.length; i++) {
                    getVideoLists(files[i]);
                }
            } else {
                String movieName = path.getName();
                String moviePath = path.getAbsolutePath();
                if (movieName.endsWith(".3gp")){
                    listName.add(movieName);
                    listPath.add(moviePath);
                }
            }
        }
    }
}

(没啥好说的,看注释吧...)
对应的layout文件:

<LinearLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <TextView
        android:textSize="20sp"
        android:layout_marginTop="20sp"
        android:text="视频列表"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content" />

    <ListView
        android:id="@+id/videoList"
        android:layout_width="match_parent"
        android:layout_height="match_parent">
    </ListView>

</LinearLayout>

该activity中单击视频名字之后,将会跳转到一个视频播放的VideoPlayTest:

public class VideoPlayTest extends Activity {

    private VideoView videoView;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_video_play_test);
        videoView = (VideoView) findViewById(R.id.video_view);
        initVideoPath();
    }

    private void initVideoPath() {
        //获取刚才的Activity传输过来的播放路径
        String filePath = getIntent().getStringExtra("path");
        File file = new File(filePath);
        videoView.setVideoPath(file.getPath()); // 指定视频文件的路径
        videoView.start();//播放视频
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        if (videoView != null) {
            videoView.suspend();
        }
    }
}

对应的layout:

<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_width="match_parent"
        android:layout_height="wrap_content" />

</LinearLayout>

其中yout中就放了一个VideoView,然后JAVA代码只管播放就好了,基本上能满足本次安卓作业的需求吧。

附带实验5的源码文件连接:https://gitee.com/bbchond/Exp5_Media_Frame_Video

上一篇下一篇

猜你喜欢

热点阅读