Android开发本地及网络Mp3音乐播放器(七)循环模式与专辑倒影

实现功能:

顺序播放

随机播放

单曲循环

PlayActivity(独立音乐播放界面)的专辑封面图片的倒影效果

截止到目前的源码下载:

http://download.csdn.net/detail/iwanghang/9502218

欢迎移动开发爱好者交流:我的微信是iwanghang

另外,我打算开始找工作,如果沈阳或周边城市公司有意,也请与我联系。

实现效果如图:

实现代码如下:

PlayService如下:

package com.iwanghang.drmplayer;

import android.app.Service;
import android.content.Intent;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnErrorListener;
import android.net.Uri;
import android.os.Binder;
import android.os.IBinder;

import com.iwanghang.drmplayer.utils.MediaUtils;
import com.iwanghang.drmplayer.vo.Mp3Info;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * 音乐播放的服务组件
 * 实现功能:
 * 播放
 * 暂停
 * 下一首
 * 上一首
 * 获取当前歌曲的播放进度
 *
 * 需要在AndroidManifest.xml添加以下代码:
 *<service
 *android:name=".PlayService"
 *android:enabled="true"
 *android:exported="true">
 *</service>
 *
 * 实现功能(播放模式play_mode):
 * 顺序播放
 * 随机播放
 * 单曲循环
 */
public class PlayService extends Service implements OnCompletionListener,OnErrorListener{
    private MediaPlayer mPlayer;
    private int currentPosition;//当前正在播放的歌曲的位置
    ArrayList<Mp3Info> mp3Infos;

    private MusicUpdatrListener musicUpdatrListener;

    //创建一个单实力的线程,用于更新音乐信息
    private ExecutorService es = Executors.newSingleThreadExecutor();

    //播放模式
    public static final int ORDER_PLAY = 1;//顺序播放
    public static final int RANDOM_PLAY = 2;//随机播放
    public static final int SINGLE_PLAY = 3;//单曲循环
    private int play_mode = ORDER_PLAY;//播放模式,默认为顺序播放

    /**
     * @param play_mode
     * ORDER_PLAY = 1;//顺序播放
     * RANDOM_PLAY = 2;//随机播放
     * SINGLE_PLAY = 3;//单曲循环
     */
    //set方法
    public void setPlay_mode(int play_mode) {
        this.play_mode = play_mode;
    }

    //get方法
    public int getPlay_mode() {
        return play_mode;
    }

    private boolean isPause = false;//歌曲播放中的暂停状态

    public boolean isPause(){
        return isPause;
    }

    public PlayService() {
    }

    public int getCurrentPosition(){
        return currentPosition;
    }

    private Random random = new Random();//创建随机对象
    //MediaPlayer.Completion 播放完成 实现播放下一首功能
    //播放完成以后,判断播放模式(曲目循环方式)
    //为了实现循环后,可以显示音乐信息,需要在PlayAcivity的change里添加对应代码
    @Override
    public void onCompletion(MediaPlayer mp) {
        switch (play_mode){
            case ORDER_PLAY://顺序播放
                next();//下一首
                break;
            case RANDOM_PLAY://随机播放
                //currentPosition = random.nextInt(mp3Infos.size());//随机下标为mp3Infos.size()
                //play(currentPosition);
                play(random.nextInt(mp3Infos.size()));
                break;
            case SINGLE_PLAY://单曲循环
                play(currentPosition);
                break;
            default:
                break;
        }
    }
    //MediaPlayer.Error 播放错误 处理实现播放下一首功能出现的错误
    @Override
    public boolean onError(MediaPlayer mp, int what, int extra) {
        mp.reset();//重启
        return false;
    }

    //内部类PlayBinder实现Binder,得到当前PlayService对象
    class PlayBinder extends Binder{
        public PlayService getPlayService(){
            System.out.println("PlayService #1 " + PlayService.this);
            return PlayService.this;
        }
    }

    @Override
    public IBinder onBind(Intent intent) {
        return new PlayBinder();//通过PlayBinder拿到PlayService,给Activity调用
    }

    @Override
    public void onCreate() {
        super.onCreate();
        mPlayer = new MediaPlayer();
        mPlayer.setOnCompletionListener(this);//注册播放完成事件
        mPlayer.setOnErrorListener(this);//注册播放错误事件
        mp3Infos = MediaUtils.getMp3Infos(this);//获取Mp3列表
        es.execute(updateSteatusRunnable);//更新进度值
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        //回收线程
        if (es!=null && !es.isShutdown()){//当进度值等于空,并且,进度值没有关闭
            es.shutdown();
            es = null;
        }
    }

    //利用Runnable来实现多线程
    /**
     * Runnable
     * Java中实现多线程有两种途径:继承Thread类或者实现Runnable接口.
     * Runnable接口非常简单,就定义了一个方法run(),继承Runnable并实现这个
     * 方法就可以实现多线程了,但是这个run()方法不能自己调用,必须由系统来调用,否则就和别的方法没有什么区别了.
     * 好处:数据共享
     */
    Runnable updateSteatusRunnable = new Runnable() {//更新状态
        @Override
        public void run() {
            //不断更新进度值
            while (true){
                //音乐更新监听不为空,并且,媒体播放不为空,并且媒体播放为播放状态
                if(musicUpdatrListener!=null && mPlayer!=null && mPlayer.isPlaying()){
                    musicUpdatrListener.onPublish(getCurrentProgress());//获取当前的进度值
                }
                try {
                    Thread.sleep(500);//500毫秒更新一次
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        }
    };

    //播放
    public void play(int position){
        if (position>=0 && position<mp3Infos.size()){
            Mp3Info mp3Info = mp3Infos.get(position);//获取mp3Info对象
            //进行播放,播放前判断
            try {
                mPlayer.reset();//重启
                mPlayer.setDataSource(this, Uri.parse(mp3Info.getUrl()));//资源解析,Mp3地址
                mPlayer.prepare();//准备
                mPlayer.start();//开始(播放)
                currentPosition = position;//保存当前位置到currentPosition,比如第一首,currentPosition = 1
            } catch (IOException e) {
                e.printStackTrace();
            }
            if(musicUpdatrListener!=null){
                musicUpdatrListener.onChange(currentPosition);//更新当前位置
            }
        }
    }

    //暂停
    public void pause(){
        if (mPlayer.isPlaying()){
            mPlayer.pause();
            isPause = true;
        }
    }

    //下一首
    public void next(){
        if (currentPosition>=mp3Infos.size()-1){//如果超出最大值,(因为第一首是0),说明已经是最后一首
            currentPosition = 0;//回到第一首
        }else {
            currentPosition++;//下一首
        }
        play(currentPosition);
    }

    //上一首 previous
    public void prev(){
        if (currentPosition-1<0){//如果上一首小于0,说明已经是第一首
            currentPosition = mp3Infos.size()-1;//回到最后一首
        }else {
            currentPosition--;//上一首
        }
        play(currentPosition);
    }

    //默认开始播放的方法
    public void start(){
        if (mPlayer!=null && !mPlayer.isPlaying()){//判断当前歌曲不等于空,并且没有在播放的状态
            mPlayer.start();

        }
    }

    //获取当前是否为播放状态,提供给MyMusicListFragment的播放暂停按钮点击事件判断状态时调用
    public boolean isPlaying(){
        if (mPlayer!=null){
            return mPlayer.isPlaying();
        }
        return false;
    }

    //获取当前的进度值
    public int getCurrentProgress(){
        if(mPlayer!=null && mPlayer.isPlaying()){//mPlayer不为空,并且,为播放状态
            return mPlayer.getCurrentPosition();
        }
        return 0;
    }
    //getDuration 获取文件的持续时间
    public int getDuration(){
        return mPlayer.getDuration();
    }
    //seekTo 寻找指定的时间位置
    public void seekTo(int msec){
        mPlayer.seekTo(msec);
    }

    //更新状态的接口(PlayService的内部接口),并在BaseActivity中实现
    public interface MusicUpdatrListener{//音乐更新监听器
        public void onPublish(int progress);//发表进度事件(更新进度条)
        public void onChange(int position); //更新歌曲位置.按钮的状态等信息
        //声明MusicUpdatrListener后,添加set方法
    }

    //set方法
    public void setMusicUpdatrListener(MusicUpdatrListener musicUpdatrListener) {
        this.musicUpdatrListener = musicUpdatrListener;
    }
}

PlayActivity如下:

package com.iwanghang.drmplayer;

import android.graphics.Bitmap;
import android.os.Bundle;

import android.os.Handler;
import android.os.Message;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.ImageView;
import android.widget.SeekBar;
import android.widget.TextView;
import android.widget.Toast;

import com.iwanghang.drmplayer.utils.ImageUtils;
import com.iwanghang.drmplayer.utils.MediaUtils;
import com.iwanghang.drmplayer.vo.Mp3Info;

import java.util.ArrayList;

import static com.iwanghang.drmplayer.PlayService.ORDER_PLAY;
import static com.iwanghang.drmplayer.PlayService.RANDOM_PLAY;
import static com.iwanghang.drmplayer.PlayService.SINGLE_PLAY;

/**
 *PlayActivity 点击MyMusicListFragment(本地音乐)底部UI中的专辑封面图片打开的Activity
 */
public class PlayActivity extends BaseActivity implements OnClickListener{
    private TextView textView1_title;//歌名
    private ImageView imageView1_ablum;//专辑封面图片
    private SeekBar seekBar1;//进度条
    private TextView textView1_start_time,textView1_end_time;//开始时间,结束时间
    private ImageView imageView1_play_mode;//菜单
    private ImageView imageView3_previous,imageView2_play_pause,imageView1_next;//上一首,播放暂停,下一首
    private ImageView imageView1_ablum_reflection;//专辑封面图片倒影

    private ArrayList<Mp3Info> mp3Infos;
    //private int position;//当前播放的位置
    private boolean isPause = false;//当前播放的是否为暂停状态
    private static final int UPDATE_TIME = 0x1;//更新播放事件的标记

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_music_play);
        //初始化界面信息
        textView1_title = (TextView) findViewById(R.id.textView1_title);//歌名
        imageView1_ablum = (ImageView) findViewById(R.id.imageView1_ablum);//专辑封面图片
        seekBar1 = (SeekBar) findViewById(R.id.seekBar1);//进度条
        textView1_start_time = (TextView) findViewById(R.id.textView1_start_time);//开始时间
        textView1_end_time = (TextView) findViewById(R.id.textView1_end_time);//结束时间
        imageView1_play_mode = (ImageView) findViewById(R.id.imageView1_play_mode);//菜单
        imageView3_previous = (ImageView) findViewById(R.id.imageView3_previous);//上一首
        imageView2_play_pause = (ImageView) findViewById(R.id.imageView2_play_pause);//播放暂停
        imageView1_next = (ImageView) findViewById(R.id.imageView1_next);//下一首
        imageView1_ablum_reflection = (ImageView) findViewById(R.id.imageView1_ablum_reflection);//专辑封面图片倒影
        //注册按钮点击监听事件
        imageView1_play_mode.setOnClickListener(this);
        imageView2_play_pause.setOnClickListener(this);
        imageView3_previous.setOnClickListener(this);
        imageView1_next.setOnClickListener(this);

        mp3Infos = MediaUtils.getMp3Infos(this);
        //bindPlayService();//绑定服务,异步过程,绑定后需要取得相应的值,来更新界面
        myHandler = new MyHandler(this);

        //以下直接调用change()是不行的,因为异步问题,会发生NullPointerException空指针异常
        //从MyMusicListFragment的专辑封面图片点击时间传过来的position(当前播放的位置)
        //position = getIntent().getIntExtra("position",0);
        //change(position);

        //通过在BaseActivity中绑定Service,添加如下代码实现change()
        //musicUpdatrListener.onChange(playService.getCurrentProgeress());

        //从MyMusicListFragment的专辑封面图片点击时间传过来的isPause(当前播放的是否为暂停状态)
        //isPause = getIntent().getBooleanExtra("isPause",false);
    }

    //把播放服务的绑定和解绑放在onResume,onPause里,好处是,每次回到当前Activity就获取一次播放状态
    @Override
    protected void onResume() {
        super.onResume();
        bindPlayService();
    }

    @Override
    protected void onPause() {
        super.onPause();
        unbindPlayService();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        unbindPlayService();//解绑服务
    }

    //Handler用于更新已经播放时间
    private static MyHandler myHandler;
    static class MyHandler extends Handler{
        private PlayActivity playActivity;
        public MyHandler(PlayActivity playActivity){
            this.playActivity = playActivity;
        }
        @Override
        public void handleMessage(Message msg) {
            super.handleMessage(msg);
            if (playActivity!=null){
                switch (msg.what){
                    case UPDATE_TIME://更新时间(已经播放时间)
                        playActivity.textView1_start_time.setText(MediaUtils.formatTime(msg.arg1));
                        break;
                }
            }
        }
    }

    @Override
    public void publish(int progress) {
        //以下是是直接调用线程,但是不能这样做,会报错,线程异常
        //textView1_start_time.setText(MediaUtils.formatTime(progress));
        //所以,我们需要使用Handler
        Message msg = myHandler.obtainMessage(UPDATE_TIME);//用于更新已经播放时间
        msg.arg1 = progress;//用于更新已经播放时间
        myHandler.sendMessage(msg);//用于更新已经播放时间

        seekBar1.setProgress(progress);
    }

    @Override
    public void change(int position) {
        //if (this.playService.isPlaying()) {//获取是否为播放状态
        System.out.println("PlayActivity #100 position = " + position);
        Mp3Info mp3Info = mp3Infos.get(position);
        textView1_title.setText(mp3Info.getTitle());//设置歌名
        //获取专辑封面图片
        Bitmap albumBitmap = MediaUtils.getArtwork(this, mp3Info.getId(), mp3Info.getAlbumId(), true, false);
        //改变播放界面专辑封面图片
        imageView1_ablum.setImageBitmap(albumBitmap);
        textView1_end_time.setText(MediaUtils.formatTime(mp3Info.getDuration()));//设置结束时间
        imageView2_play_pause.setImageResource(R.mipmap.app_music_pause);//设置暂停图片
        seekBar1.setProgress(0);//设置当前进度为0
        seekBar1.setMax((int)mp3Info.getDuration());//设置进度条最大值为MP3总时间
        if (playService.isPlaying()){
            imageView2_play_pause.setImageResource(R.mipmap.app_music_pause);
        }else {
            imageView2_play_pause.setImageResource(R.mipmap.app_music_play);
        }

        if(imageView1_ablum != null) {
            imageView1_ablum_reflection.setImageBitmap(ImageUtils.createReflectionBitmapForSingle(albumBitmap));//显示倒影
        }
        switch (playService.getPlay_mode()){
            case ORDER_PLAY://顺序播放
                imageView1_play_mode.setImageResource(R.mipmap.app_mode_order);
                //imageView2_play_pause.setTag(ORDER_PLAY);
                break;
            case PlayService.RANDOM_PLAY://随机播放
                imageView1_play_mode.setImageResource(R.mipmap.app_mode_random);
                //imageView2_play_pause.setTag(RANDOM_PLAY);
                break;
            case PlayService.SINGLE_PLAY://单曲循环
                imageView1_play_mode.setImageResource(R.mipmap.app_mode_single);
                //imageView2_play_pause.setTag(SINGLE_PLAY);
                break;
            default:
                break;
        }
    }

        //点击事件
        @Override
        public void onClick(View v) {
            switch (v.getId()){
            case R.id.imageView2_play_pause: {//播放暂停按钮
                if (playService.isPlaying()) {//如果是播放状态
                    imageView2_play_pause.setImageResource(R.mipmap.app_music_play);//设置播放图片
                    playService.pause();
                } else {
                    if (playService.isPause()) {
                        imageView2_play_pause.setImageResource(R.mipmap.app_music_pause);//设置暂停图片
                        playService.start();//播放事件
                    } else {
                        //只有打开APP没有点击任何歌曲,同时,直接点击暂停播放按钮时.才会调用
                        playService.play(0);
                    }
                }
                break;
            }
            case R.id.imageView1_next:{//下一首按钮
                playService.next();//下一首
                break;
            }
            case R.id.imageView3_previous:{//上一首按钮
                playService.prev();//上一首
                break;
            }
            case R.id.imageView1_play_mode:{//循环模式按钮
                //int mode = (int) imageView1_play_mode.getTag();
                switch (playService.getPlay_mode()){
                    case ORDER_PLAY:
                        imageView1_play_mode.setImageResource(R.mipmap.app_mode_random);
                        //imageView1_play_mode.setTag(RANDOM_PLAY);
                        playService.setPlay_mode(RANDOM_PLAY);
                        Toast.makeText(getApplicationContext(),"随机播放",Toast.LENGTH_SHORT).show();
                        break;
                    case RANDOM_PLAY:
                        imageView1_play_mode.setImageResource(R.mipmap.app_mode_single);
                        //imageView1_play_mode.setTag(SINGLE_PLAY);
                        playService.setPlay_mode(SINGLE_PLAY);
                        Toast.makeText(getApplicationContext(),"单曲循环",Toast.LENGTH_SHORT).show();
                        break;
                    case SINGLE_PLAY:
                        imageView1_play_mode.setImageResource(R.mipmap.app_mode_order);
                        //imageView1_play_mode.setTag(ORDER_PLAY);
                        playService.setPlay_mode(ORDER_PLAY);
                        Toast.makeText(getApplicationContext(),"顺序播放",Toast.LENGTH_SHORT).show();
                        break;
                }
                break;
            }
            default:
                break;
        }
    }
}

ImageUtils如下:

package com.iwanghang.drmplayer.utils;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;

import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.CompressFormat;
import android.graphics.Bitmap.Config;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.ColorMatrix;
import android.graphics.ColorMatrixColorFilter;
import android.graphics.LinearGradient;
import android.graphics.Matrix;
import android.graphics.Paint;
import android.graphics.PorterDuff;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.Shader;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;

/**
 * 图片工具类
 * Created by iwanghang on 16/4/25.
 */
public class ImageUtils {
    /**图片的八个位置**/
    public static final int TOP = 0;			//上
    public static final int BOTTOM = 1;			//下
    public static final int LEFT = 2;			//左
    public static final int RIGHT = 3;			//右
    public static final int LEFT_TOP = 4;		//左上
    public static final int LEFT_BOTTOM = 5;	//左下
    public static final int RIGHT_TOP = 6;		//右上
    public static final int RIGHT_BOTTOM = 7;	//右下

    /**
     * 图像的放大缩小方法
     * @param src		源位图对象
     * @param scaleX	宽度比例系数
     * @param scaleY	高度比例系数
     * @return 返回位图对象
     */
    public static Bitmap zoomBitmap(Bitmap src, float scaleX, float scaleY) {
        Matrix matrix = new Matrix();
        matrix.setScale(scaleX, scaleY);
        Bitmap t_bitmap = Bitmap.createBitmap(src, 0, 0, src.getWidth(), src.getHeight(), matrix, true);
        return t_bitmap;
    }

    /**
     * 图像放大缩小--根据宽度和高度
     * @param src
     * @param width
     * @param height
     * @return
     */
    public static Bitmap zoomBimtap(Bitmap src, int width, int height) {
        return Bitmap.createScaledBitmap(src, width, height, true);
    }

    /**
     * 将Drawable转为Bitmap对象
     * @param drawable
     * @return
     */
    public static Bitmap drawableToBitmap(Drawable drawable) {
        return ((BitmapDrawable)drawable).getBitmap();
    }

    /**
     * 将Bitmap转换为Drawable对象
     * @param bitmap
     * @return
     */
    public static Drawable bitmapToDrawable(Bitmap bitmap) {
        Drawable drawable = new BitmapDrawable(bitmap);
        return drawable;
    }

    /**
     * Bitmap转byte[]
     * @param bitmap
     * @return
     */
    public static byte[] bitmapToByte(Bitmap bitmap) {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        bitmap.compress(Bitmap.CompressFormat.PNG, 100, out);
        return out.toByteArray();
    }

    /**
     * byte[]转Bitmap
     * @param data
     * @return
     */
    public static Bitmap byteToBitmap(byte[] data) {
        if(data.length != 0) {
            return BitmapFactory.decodeByteArray(data, 0, data.length);
        }
        return null;
    }

    /**
     * 绘制带圆角的图像
     * @param src
     * @param radius
     * @return
     */
    public static Bitmap createRoundedCornerBitmap(Bitmap src, int radius) {
        final int w = src.getWidth();
        final int h = src.getHeight();
        // 高清量32位图
        Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
        Paint paint = new Paint();
        Canvas canvas = new Canvas(bitmap);
        canvas.drawARGB(0, 0, 0, 0);
        paint.setColor(0xff424242);
        // 防止边缘的锯齿
        paint.setFilterBitmap(true);
        Rect rect = new Rect(0, 0, w, h);
        RectF rectf = new RectF(rect);
        // 绘制带圆角的矩形
        canvas.drawRoundRect(rectf, radius, radius, paint);

        // 取两层绘制交集,显示上层
        paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.SRC_IN));
        // 绘制图像
        canvas.drawBitmap(src, rect, rect, paint);
        return bitmap;
    }

    /**
     * 创建选中带提示图片
     * @param context
     * @param srcId
     * @param tipId
     * @return
     */
    public static Drawable createSelectedTip(Context context, int srcId, int tipId) {
        Bitmap src = BitmapFactory.decodeResource(context.getResources(), srcId);
        Bitmap tip = BitmapFactory.decodeResource(context.getResources(), tipId);
        final int w = src.getWidth();
        final int h = src.getHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
        Paint paint = new Paint();
        Canvas canvas = new Canvas(bitmap);
        //绘制原图
        canvas.drawBitmap(src, 0, 0, paint);
        //绘制提示图片
        canvas.drawBitmap(tip, (w - tip.getWidth()), 0, paint);
        return bitmapToDrawable(bitmap);
    }

    /**
     * 带倒影的图像
     * @param src
     * @return
     */
    public static Bitmap createReflectionBitmap(Bitmap src) {
        // 两个图像间的空隙
        final int spacing = 4;
        final int w = src.getWidth();
        final int h = src.getHeight();
        // 绘制高质量32位图
        Bitmap bitmap = Bitmap.createBitmap(w, h + h / 2 + spacing, Config.ARGB_8888);
        // 创建燕X轴的倒影图像
        Matrix m = new Matrix();
        m.setScale(1, -1);
        Bitmap t_bitmap = Bitmap.createBitmap(src, 0, h / 2, w, h / 2, m, true);

        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        //	绘制原图像
        canvas.drawBitmap(src, 0, 0, paint);
        // 绘制倒影图像
        canvas.drawBitmap(t_bitmap, 0, h + spacing, paint);
        // 线性渲染-沿Y轴高到低渲染
        Shader shader = new LinearGradient(0, h + spacing, 0, h + spacing + h / 2, 0x70ffffff, 0x00ffffff, Shader.TileMode.MIRROR);
        paint.setShader(shader);
        // 取两层绘制交集,显示下层。
        paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
        // 绘制渲染倒影的矩形
        canvas.drawRect(0, h + spacing, w, h + h / 2 + spacing, paint);
        return bitmap;
    }

    /**
     * 独立的倒影图像
     * @param src
     * @return
     */
    public static Bitmap createReflectionBitmapForSingle(Bitmap src) {
        final int w = src.getWidth();
        final int h = src.getHeight();
        // 绘制高质量32位图
        Bitmap bitmap = Bitmap.createBitmap(w, h / 2, Config.ARGB_8888);
        // 创建沿X轴的倒影图像
        Matrix m = new Matrix();
        m.setScale(1, -1);
        Bitmap t_bitmap = Bitmap.createBitmap(src, 0, h / 2, w, h / 2, m, true);

        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        // 绘制倒影图像
        canvas.drawBitmap(t_bitmap, 0, 0, paint);
        // 线性渲染-沿Y轴高到低渲染
        Shader shader = new LinearGradient(0, 0, 0, h / 2, 0x70ffffff,
                0x00ffffff, Shader.TileMode.MIRROR);
        paint.setShader(shader);
        // 取两层绘制交集。显示下层。
        paint.setXfermode(new PorterDuffXfermode(Mode.DST_IN));
        // 绘制渲染倒影的矩形
        canvas.drawRect(0, 0, w, h / 2, paint);
        return bitmap;
    }

    public static Bitmap createGreyBitmap(Bitmap src) {
        final int w = src.getWidth();
        final int h = src.getHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        Paint paint = new Paint();
        // 颜色变换的矩阵
        ColorMatrix matrix = new ColorMatrix();
        // saturation 饱和度值,最小可设为0,此时对应的是灰度图;为1表示饱和度不变,设置大于1,就显示过饱和
        matrix.setSaturation(0);
        ColorMatrixColorFilter filter = new ColorMatrixColorFilter(matrix);
        paint.setColorFilter(filter);
        canvas.drawBitmap(src, 0, 0, paint);
        return bitmap;
    }

    /**
     * 保存图片
     * @param src
     * @param filepath
     * @param format:[Bitmap.CompressFormat.PNG,Bitmap.CompressFormat.JPEG]
     * @return
     */
    public static boolean saveImage(Bitmap src, String filepath, CompressFormat format) {
        boolean rs = false;
        File file = new File(filepath);
        try {
            FileOutputStream out = new FileOutputStream(file);
            if(src.compress(format, 100, out)) {
                out.flush();	//写入流
            }
            out.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
        return rs;
    }

    /**
     * 添加水印效果
     * @param src		源位图
     * @param watermark	水印
     * @param direction	方向
     * @param spacing 间距
     * @return
     */
    public static Bitmap createWatermark(Bitmap src, Bitmap watermark, int direction, int spacing) {
        final int w = src.getWidth();
        final int h = src.getHeight();
        Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
        Canvas canvas = new Canvas(bitmap);
        canvas.drawBitmap(src, 0, 0, null);
        if(direction == LEFT_TOP) {
            canvas.drawBitmap(watermark, spacing, spacing, null);
        } else if(direction == LEFT_BOTTOM){
            canvas.drawBitmap(watermark, spacing, h - watermark.getHeight() - spacing, null);
        } else if(direction == RIGHT_TOP) {
            canvas.drawBitmap(watermark, w - watermark.getWidth() - spacing, spacing, null);
        } else if(direction == RIGHT_BOTTOM) {
            canvas.drawBitmap(watermark, w - watermark.getWidth() - spacing, h - watermark.getHeight() - spacing, null);
        }
        return bitmap;
    }

    /**
     * 合成图像
     * @param direction
     * @param bitmaps
     * @return
     */
    public static Bitmap composeBitmap(int direction, Bitmap... bitmaps) {
        if(bitmaps.length < 2) {
            return null;
        }
        Bitmap firstBitmap = bitmaps[0];
        for (int i = 0; i < bitmaps.length; i++) {
            firstBitmap = composeBitmap(firstBitmap, bitmaps[i], direction);
        }
        return firstBitmap;
    }

    /**
     * 合成两张图像
     * @param firstBitmap
     * @param secondBitmap
     * @param direction
     * @return
     */
    private static Bitmap composeBitmap(Bitmap firstBitmap, Bitmap secondBitmap,
                                        int direction) {
        if(firstBitmap == null) {
            return null;
        }
        if(secondBitmap == null) {
            return firstBitmap;
        }
        final int fw = firstBitmap.getWidth();
        final int fh = firstBitmap.getHeight();
        final int sw = secondBitmap.getWidth();
        final int sh = secondBitmap.getHeight();
        Bitmap bitmap = null;
        Canvas canvas = null;
        if(direction == TOP) {
            bitmap = Bitmap.createBitmap(sw > fw ? sw : fw, fh + sh, Config.ARGB_8888);
            canvas = new Canvas(bitmap);
            canvas.drawBitmap(secondBitmap, 0, 0, null);
            canvas.drawBitmap(firstBitmap, 0, sh, null);
        } else if(direction == BOTTOM) {
            bitmap = Bitmap.createBitmap(fw > sw ? fw : sw, fh + sh, Config.ARGB_8888);
            canvas = new Canvas(bitmap);
            canvas.drawBitmap(firstBitmap, 0, 0, null);
            canvas.drawBitmap(secondBitmap, 0, fh, null);
        } else if(direction == LEFT) {
            bitmap = Bitmap.createBitmap(fw + sw, sh > fh ? sh : fh, Config.ARGB_8888);
            canvas = new Canvas(bitmap);
            canvas.drawBitmap(secondBitmap, 0, 0, null);
            canvas.drawBitmap(firstBitmap, sw, 0, null);
        } else if(direction == RIGHT) {
            bitmap = Bitmap.createBitmap(fw + sw, fh > sh ? fh : sh,
                    Config.ARGB_8888);
            canvas = new Canvas(bitmap);
            canvas.drawBitmap(firstBitmap, 0, 0, null);
            canvas.drawBitmap(secondBitmap, fw, 0, null);
        }
        return bitmap;
    }

}

时间: 2024-11-02 22:57:31

Android开发本地及网络Mp3音乐播放器(七)循环模式与专辑倒影的相关文章

Android开发本地及网络Mp3音乐播放器(十八)新下载歌曲加载准备SdcardSearchToList

扫描本地文件加载到item 不限于应用到音乐播放器,其他Android工程,也可以使用SdcardSearchToList 其他Android工程也可以使用,我现在做了很小的Demo 后续将博文,将在播放器里,加入这一功能,来实现刚下载好的音乐,加载到播放器 这个Demo的源码下载: http://download.csdn.net/album/detail/3105 名称为:SdcardSearchToList,如果没有显示,可能还没有审核完成 不下载也可以,因为只有1个java文件,2个xm

Android开发本地及网络Mp3音乐播放器(十五)网络音乐及歌词下载功能实现

实现功能: 实现网络音乐歌词下载功能(下载音乐的同时,下载对应歌词) 下载好的歌词目前不在播放器内,可以通过文件浏览器查看. 后续将博文,将实现本地音乐歌词下载和已下载音乐扫描功能. 因为,没有自己的服务器,所以网络音乐所有相关功能(包含搜索音乐.下载音乐.下载歌词)均无法保证时效性,建议,尽快下载和练习:如果你下载时候,已经因为我采集的服务器更改规则,请给我留言,如果可以解决,我将在有空的时候献上新的源码. 截止到目前的源码下载: http://download.csdn.net/album/

Android开发本地及网络Mp3音乐播放器(九)音乐收藏与列表切换

实现功能: 使用快速开发框架xUtils中的DbUtils模块,为音乐收藏功能做准备 实现PlayActivity(独立音乐播放界面)收藏.取消收藏按钮 实现MainActivity(主界面)菜单选择事件进入MyLoveMusicActivity(音乐收藏界面) 实现本地音乐列表与音乐收藏列表切换功能 (目前源码,只实现了音乐收藏列表,菜单中最近播放列表后续会进行补充) 截止到目前的源码下载: http://download.csdn.net/detail/iwanghang/9504916 x

Android开发本地及网络Mp3音乐播放器(十)最近播放界面与数据保存更新

实现功能: 实现MyLoveMusicActivity(音乐收藏界面) 实现MyRecordMusicActivity(最近播放界面) 实现MyMusicListFragment(本地音乐界面)ItemClick事件发生后,切换到PlayActivity(独立音乐播放界面)只播放 本地音乐. 实现MyLoveMusicActivity(音乐收藏界面)ItemClick事件发生后,切换到PlayActivity(独立音乐播放界面)只播放 音乐收藏. 实现MyRecordMusicActivity(

Android开发本地及网络Mp3音乐播放器(八)状态存取与界面滑动

实现功能: 退出应用时,保存歌曲位置(也就是当前是第几首歌曲) 退出应用时,保存播放模式(也就是用户设置的是顺序播放/随机播放/单曲循环) 进入应用时,读取歌曲位置 进入应用时,读取播放模式 实现PlayActivity(独立音乐播放界面)的专辑图片layout滑动,滑动后展示歌词layout (目前源码,只实现了专辑图片layout与歌词layout,歌词layout的信息填充后续会进行补充) 截止到目前的源码下载: http://download.csdn.net/detail/iwangh

Android开发本地及网络Mp3音乐播放器(十四)网络音乐下载功能实现

实现功能: 实现网络音乐搜索功能 实现网络音乐下载功能 下载好的音乐目前不在播放器内,可以通过文件浏览器查看. 后续将博文,将实现歌词和下载音乐扫描功能. 经过将近4天,才发布这一篇博客,明显更新博客速度变慢了,因为没有自己的音乐服务器,网络上的音乐网站在这边博客发表的2016年5月已经很难找到免费且在线下载的了,就算找到解析也是一个难题.如果你在练习同样功能的Demo,请尽快下载练习,因为博主也无法保证我Demo下载功能时效性,因为我解析的音乐网站可能分分钟修改网页源码!!! 截止到目前的源码

Android开发本地及网络Mp3音乐播放器(六)实现独立音乐播放界面

实现功能: 功能1:点击MyMusicListFragment(本地音乐)底部UI中的专辑封面图片打开的PlayActivity(独立音乐播放界面) PlayActivity中,显示正在播放的歌名 PlayActivity中,显示专辑封面图片(大图) PlayActivity中,显示上一首按钮,实现对应功能 PlayActivity中,显示暂停播放按钮,实现对应功能 PlayActivity中,显示下一首,实现对应功能 功能2:实现同步MyMusicListFragment(本地音乐界面)和Pl

Android开发本地及网络Mp3音乐播放器(十六)歌词显示及滚动事件实现、ViewPager使用

实现功能: 歌词显示及滚动事件实现 ViewPager使用 后续将博文,将实现已下载音乐扫描功能和已存在歌曲歌词下载. 因为,没有自己的服务器,所以网络音乐所有相关功能(包含搜索音乐.下载音乐.下载歌词)均无法保证时效性,建议,尽快下载和练习:如果你下载时候,已经因为我采集的服务器更改规则,请给我留言,如果可以解决,我将在有空的时候献上新的源码. 截止到目前的源码下载: http://download.csdn.net/album/detail/3105 (最新的,请下载最后一个,本博文对应版本

Android开发本地及网络Mp3音乐播放器(十七)已存在歌曲歌词下载

实现功能: 已存在歌曲歌词下载 后续将博文,将实现已下载音乐扫描功能. 因为,没有自己的服务器,所以网络音乐所有相关功能(包含搜索音乐.下载音乐.下载歌词)均无法保证时效性,建议,尽快下载和练习:如果你下载时候,已经因为我采集的服务器更改规则,请给我留言,如果可以解决,我将在有空的时候献上新的源码. 截止到目前的源码下载: http://download.csdn.net/album/detail/3105 (最新的,请下载最后一个,本博文对应版本2.2 :如果需要逐步实现的过程,请下载所有)