圣诞节祝福小 Demo:JingleBells 背景音乐 + 礼物闪烁下落 + 跑马灯效果

2,092 阅读15分钟
源码已经上传至我的github上:github.com/junmei520/M…

同时我在微博上也以视频的形式展示了Demo的运行效果:weibo.com/u/532359340…   


圣诞将至,闲来无事,于是,便做了一个圣诞祝福小Demo,也祝大家圣诞快乐,每天开心~

由于灵感来的比较迟,所以demo做的很简陋,还请见谅。但我觉得,我的想法还是很好的~


运行效果图:



Demo特点描述:

①一打开Demo便有背景音乐响起。

②具有带闪烁变换的礼物下落效果。

③跑马灯显示滚动文本。


Demo设计的知识点:

① 使用Service开启背景音乐Jingle Bells。

②自定义View实现礼物闪烁变换的下落。

③自定义TextView实现跑马灯效果展示文本。


具体实现如下(由于代码都十分简单,这里我只做简略说明):

一、使用Service开启背景音乐Jingle Bells:

先写一个音乐播放的服务类:

  1. public class MusicService extends Service {  
  2.     @Override  
  3.     public IBinder onBind(Intent intent) {  
  4.         return null;  
  5.     }  
  6.   
  7.     MediaPlayer player;  
  8.   
  9.     @Override  
  10.     public int onStartCommand(Intent intent, int flags,  int startId) {  
  11.         String action = intent.getStringExtra("action");  
  12.         if ("play".equals(action)) {  
  13.             //播放  
  14.             play();  
  15.         } else if ("stop".equals(action)) {  
  16.             //停止  
  17.             stop();  
  18.         }  
  19.   
  20.         return super.onStartCommand(intent, flags, startId);  
  21.     }  
  22.   
  23.   
  24.     private void stop() {  
  25.         if (player != null) {  
  26.             player.stop();  
  27.             player.reset();  
  28.             player.release();//释放加载的文件  
  29.             player = null;//不要忘了!  
  30.         }  
  31.     }  
  32.   
  33.     private void play() {  
  34.         if (player == null) {  
  35.             player = MediaPlayer.create(this, R.raw.jinglebells);  
  36.             player.setLooping(true);  
  37.   
  38.         }  
  39.         if (player != null && !player.isPlaying()) {  
  40.             player.start();  
  41.   
  42.         }  
  43.     }  
  44.   
  45.     @Override  
  46.     public void onDestroy() {  
  47.         super.onDestroy();  
  48.         stop();//停止音乐  
  49.     }  
public class MusicService extends Service {
    @Override
    public IBinder onBind(Intent intent) {
        return null;
    }

    MediaPlayer player;

    @Override
    public int onStartCommand(Intent intent, int flags, int startId) {
        String action = intent.getStringExtra("action");
        if ("play".equals(action)) {
            //播放
            play();
        } else if ("stop".equals(action)) {
            //停止
            stop();
        }

        return super.onStartCommand(intent, flags, startId);
    }


    private void stop() {
        if (player != null) {
            player.stop();
            player.reset();
            player.release();//释放加载的文件
            player = null;//不要忘了!
        }
    }

    private void play() {
        if (player == null) {
            player = MediaPlayer.create(this, R.raw.jinglebells);
            player.setLooping(true);

        }
        if (player != null && !player.isPlaying()) {
            player.start();

        }
    }

    @Override
    public void onDestroy() {
        super.onDestroy();
        stop();//停止音乐
    }

音乐文件放在raw中:



在功能清单文件中进行注册:

  1. <!--功能清单文件中注册服务-->  
  2.         <service android:name=".service.MusicService"  />  
<!--功能清单文件中注册服务-->
        <service android:name=".service.MusicService" />

在MainAcitivity中启动、停止服务:

  1. public class MainActivity extends AppCompatActivity {  
  2.     private Intent intent;  
  3.       
  4.     @Override  
  5.     protected void onCreate(Bundle savedInstanceState) {  
  6.         super.onCreate(savedInstanceState);  
  7.         setContentView(R.layout.activity_main);  
  8.   
  9.         //应用一进入就开启服务,启动音乐播放  
  10.         intent = new Intent(this, MusicService.class);  
  11.         intent.putExtra("action""play");  
  12.         startService(intent);  
  13.     }  
  14.   
  15.     @Override  
  16.     protected void onDestroy() {  
  17.         super.onDestroy();  
  18.         //此处我们简洁化,当activity退出时就直接停止音乐的播放  
  19.         intent.putExtra("action""stop");  
  20.         startService(intent);  
  21.         stopService(intent);  
  22.     }  
  23. }  
public class MainActivity extends AppCompatActivity {
    private Intent intent;
    
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        //应用一进入就开启服务,启动音乐播放
        intent = new Intent(this, MusicService.class);
        intent.putExtra("action", "play");
        startService(intent);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //此处我们简洁化,当activity退出时就直接停止音乐的播放
        intent.putExtra("action", "stop");
        startService(intent);
        stopService(intent);
    }
}

至此,背景音乐的播放完成了。


二、自定义View实现礼物闪烁变换的下落

自定义GiftView继承View:

  1. /** 
  2.  * 自定义礼物散落的view 
  3.  */  
  4. public class GiftView extends View{  
  5.     public GiftView(Context context) {  
  6.         this(context,null);  
  7.     }  
  8.   
  9.     public GiftView(Context context, AttributeSet attrs) {  
  10.         this(context, attrs,0);  
  11.     }  
  12.   
  13.     public GiftView(Context context, AttributeSet attrs, int defStyleAttr) {  
  14.         super(context, attrs, defStyleAttr);  
  15.     }  
  16.   
  17.     private static final Random random =  new Random();  
  18.   
  19.     //准备礼物的图片数组  
  20.     private int[] drawables={R.drawable.p0,R.drawable.p1,R.drawable.p2,R.drawable.p3,R.drawable.p5,  
  21.             R.drawable.p6,R.drawable.p7,R.drawable.p8,R.drawable.p9,R.drawable.p10};  
  22.   
  23.     // 用于画礼物的画笔  
  24.     private final Paint myPaint = new Paint();  
  25.   
  26.     //坐标类的数组---礼物的位置  
  27.     private Coordinate[] gifts = new Coordinate[80];  
  28.   
  29.     //窗体的初始高宽  
  30.     int sHeight = 0;  
  31.     int sWidth = 0;  
  32.     //记录礼物的个数  
  33.     private int giftCount = 0;  
  34.   
  35.     /** 
  36.      * 设置当前窗体的实际宽高 
  37.      */  
  38.     public void SetView(int height, int width) {  
  39.         sHeight = height - 100;  
  40.         sWidth = width;  
  41.     }  
  42.   
  43.     /** 
  44.      * 随机的产生礼物的位置 
  45.      */  
  46.     public void produceGiftRandom(int count) {  
  47.         giftCount = count;  
  48.         for (int i = 0; i < count; i++) {  
  49.             //横坐标和纵坐标都是随机产生的  
  50.             gifts[i] = new Coordinate(random.nextInt(sWidth), -random.nextInt(sHeight));  
  51.         }  
  52.     }  
  53.   
  54.     /** 
  55.      * 通过画笔将礼物绘制上去 
  56.      */  
  57.     @Override  
  58.     public void onDraw(Canvas canvas) {  
  59.         super.onDraw(canvas);  
  60.         for (int x = 0; x < giftCount; x +=  1) {  
  61.             if (gifts[x].mY >= sHeight) {  
  62.                 gifts[x].mY = 0;  
  63.             }  
  64.             // 礼物下落的数值速度  
  65.             gifts[x].mY += 10;  
  66.             // 让礼物飘动起来  
  67.             if (random.nextBoolean()) {  
  68.                 //让水平方向有一个随机移动的速度  
  69.                 int ran = random.nextInt(12);  
  70.                 gifts[x].mX += 2 - ran;  
  71.                 if(gifts[x].mX < 0){  
  72.                     gifts[x].mX = sWidth;  
  73.                 }else if(gifts[x].mX > sWidth){  
  74.                     gifts[x].mX = 0;  
  75.                 }  
  76.             }  
  77.             Resources mResources = getResources();  
  78.             int drawableIndex=random.nextInt(10);  
  79.   
  80.             //不断的切换十张图片造成闪烁的效果  
  81.             canvas.drawBitmap(((BitmapDrawable) mResources.getDrawable(drawables[drawableIndex])).getBitmap(), ((float) gifts[x].mX),  
  82.                     ((float) gifts[x].mY), myPaint);  
  83.         }  
  84.     }  
  85.   
  86.     /** 
  87.      * 自定义一个坐标类 
  88.      */  
  89.     private class Coordinate{  
  90.         public int mX;  
  91.         public int mY;  
  92.   
  93.         public Coordinate(int x, int y) {  
  94.             mX = x;  
  95.             mY = y;  
  96.         }  
  97.     }  
  98.   
  99. }  
/**
 * 自定义礼物散落的view
 */
public class GiftView extends View{
    public GiftView(Context context) {
        this(context,null);
    }

    public GiftView(Context context, AttributeSet attrs) {
        this(context, attrs,0);
    }

    public GiftView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
    }

    private static final Random random = new Random();

    //准备礼物的图片数组
    private int[] drawables={R.drawable.p0,R.drawable.p1,R.drawable.p2,R.drawable.p3,R.drawable.p5,
            R.drawable.p6,R.drawable.p7,R.drawable.p8,R.drawable.p9,R.drawable.p10};

    // 用于画礼物的画笔
    private final Paint myPaint = new Paint();

    //坐标类的数组---礼物的位置
    private Coordinate[] gifts = new Coordinate[80];

    //窗体的初始高宽
    int sHeight = 0;
    int sWidth = 0;
    //记录礼物的个数
    private int giftCount = 0;

    /**
     * 设置当前窗体的实际宽高
     */
    public void SetView(int height, int width) {
        sHeight = height - 100;
        sWidth = width;
    }

    /**
     * 随机的产生礼物的位置
     */
    public void produceGiftRandom(int count) {
        giftCount = count;
        for (int i = 0; i < count; i++) {
            //横坐标和纵坐标都是随机产生的
            gifts[i] = new Coordinate(random.nextInt(sWidth), -random.nextInt(sHeight));
        }
    }

    /**
     * 通过画笔将礼物绘制上去
     */
    @Override
    public void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        for (int x = 0; x < giftCount; x += 1) {
            if (gifts[x].mY >= sHeight) {
                gifts[x].mY = 0;
            }
            // 礼物下落的数值速度
            gifts[x].mY += 10;
            // 让礼物飘动起来
            if (random.nextBoolean()) {
                //让水平方向有一个随机移动的速度
                int ran = random.nextInt(12);
                gifts[x].mX += 2 - ran;
                if(gifts[x].mX < 0){
                    gifts[x].mX = sWidth;
                }else if(gifts[x].mX > sWidth){
                    gifts[x].mX = 0;
                }
            }
            Resources mResources = getResources();
            int drawableIndex=random.nextInt(10);

            //不断的切换十张图片造成闪烁的效果
            canvas.drawBitmap(((BitmapDrawable) mResources.getDrawable(drawables[drawableIndex])).getBitmap(), ((float) gifts[x].mX),
                    ((float) gifts[x].mY), myPaint);
        }
    }

    /**
     * 自定义一个坐标类
     */
    private class Coordinate{
        public int mX;
        public int mY;

        public Coordinate(int x, int y) {
            mX = x;
            mY = y;
        }
    }

}

在布局中使用自定义View:

  1. <!--在布局中使用自定义View-->  
  2.     <com.chrismas.shiyu.mychristmas.view.GiftView  
  3.         android:id="@+id/gift"  
  4.         android:layout_width="match_parent"  
  5.         android:layout_height="match_parent" />  
<!--在布局中使用自定义View-->
    <com.chrismas.shiyu.mychristmas.view.GiftView
        android:id="@+id/gift"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

在MainActivity中进行相关操作:

  1. public class MainActivity extends AppCompatActivity {  
  2.     private Intent intent;  
  3.     //礼物总个数  
  4.     private int GIFTCOUNT = 30;  
  5.     GiftView giftView = null;  
  6.   
  7.     //使用handler进行消息的处理,不断进行重绘  
  8.     private Handler mHandler = new Handler() {  
  9.         @Override  
  10.         public void handleMessage(Message msg) {  
  11.             if (msg.what == 1) {  
  12.                 //重绘  
  13.                 giftView.invalidate();  
  14.                 mHandler.sendEmptyMessageDelayed(1100);  
  15.             }  
  16.         }  
  17.     };  
  18.   
  19.     @Override  
  20.     protected void onCreate(Bundle savedInstanceState) {  
  21.         super.onCreate(savedInstanceState);  
  22.         setContentView(R.layout.activity_main);  
  23.   
  24.         //应用一进入就开启服务,启动音乐播放  
  25.         intent = new Intent(this, MusicService.class);  
  26.         intent.putExtra("action""play");  
  27.         startService(intent);  
  28.   
  29.         //产生礼物洒落效果  
  30.         giftView = (GiftView) findViewById(R.id.gift);  
  31.   
  32.         // 获取当前屏幕的高宽  
  33.         DisplayMetrics dm = new DisplayMetrics();  
  34.         getWindowManager().getDefaultDisplay().getMetrics(dm);  
  35.         giftView.SetView(dm.heightPixels, dm.widthPixels);  
  36.         // 不断更新礼物  
  37.         update();  
  38.   
  39.     }  
  40.   
  41.     public void update() {  
  42.         giftView.produceGiftRandom(GIFTCOUNT);  
  43.         //发送延迟消息  
  44.         mHandler.sendEmptyMessageDelayed(1100);  
  45.     }  
  46.   
  47.     @Override  
  48.     protected void onDestroy() {  
  49.         super.onDestroy();  
  50.         //此处我们简洁化,当activity退出时就直接停止音乐的播放  
  51.         intent.putExtra("action""stop");  
  52.         startService(intent);  
  53.         stopService(intent);  
  54.     }  
  55. }  
public class MainActivity extends AppCompatActivity {
    private Intent intent;
    //礼物总个数
    private int GIFTCOUNT = 30;
    GiftView giftView = null;

    //使用handler进行消息的处理,不断进行重绘
    private Handler mHandler = new Handler() {
        @Override
        public void handleMessage(Message msg) {
            if (msg.what == 1) {
                //重绘
                giftView.invalidate();
                mHandler.sendEmptyMessageDelayed(1, 100);
            }
        }
    };

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

        //应用一进入就开启服务,启动音乐播放
        intent = new Intent(this, MusicService.class);
        intent.putExtra("action", "play");
        startService(intent);

        //产生礼物洒落效果
        giftView = (GiftView) findViewById(R.id.gift);

        // 获取当前屏幕的高宽
        DisplayMetrics dm = new DisplayMetrics();
        getWindowManager().getDefaultDisplay().getMetrics(dm);
        giftView.SetView(dm.heightPixels, dm.widthPixels);
        // 不断更新礼物
        update();

    }

    public void update() {
        giftView.produceGiftRandom(GIFTCOUNT);
        //发送延迟消息
        mHandler.sendEmptyMessageDelayed(1, 100);
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        //此处我们简洁化,当activity退出时就直接停止音乐的播放
        intent.putExtra("action", "stop");
        startService(intent);
        stopService(intent);
    }
}

好了,带闪烁效果的礼物下落也实现了。

三、自定义TextView实现跑马灯效果展示文本

自定义TextView:

  1. public class MyTextView extends TextView {  
  2.   
  3.     public MyTextView(Context context) {  
  4.         super(context);  
  5.     }  
  6.   
  7.     public MyTextView(Context context, AttributeSet attrs) {  
  8.         super(context, attrs);  
  9.     }  
  10.   
  11.     public MyTextView(Context context, AttributeSet attrs, int defStyle) {  
  12.         super(context, attrs, defStyle);  
  13.     }  
  14.   
  15.     //关键在这  
  16.     @Override  
  17.     public boolean isFocused() {  
  18.         return true;  
  19.     }  
  20.   
  21. }  
public class MyTextView extends TextView {

    public MyTextView(Context context) {
        super(context);
    }

    public MyTextView(Context context, AttributeSet attrs) {
        super(context, attrs);
    }

    public MyTextView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
    }

    //关键在这
    @Override
    public boolean isFocused() {
        return true;
    }

}

在布局中使用,并进行相关设置(如无限循环滚动等)

  1. <!--跑马灯效果-->  
  2.     <com.chrismas.shiyu.mychristmas.view.MyTextView  
  3.         android:layout_width="230dp"  
  4.         android:layout_height="wrap_content"  
  5.         android:layout_centerHorizontal="true"  
  6.         android:layout_marginTop="20dp"  
  7.         android:ellipsize="marquee"  
  8.         android:marqueeRepeatLimit="marquee_forever"  
  9.         android:singleLine="true"  
  10.         android:text="Hello,我是诗雨!在这里祝大家圣诞快乐,开心快乐每一天!"  
  11.         android:textColor="#009900"  
  12.         android:textSize="18sp" />  
<!--跑马灯效果-->
    <com.chrismas.shiyu.mychristmas.view.MyTextView
        android:layout_width="230dp"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="20dp"
        android:ellipsize="marquee"
        android:marqueeRepeatLimit="marquee_forever"
        android:singleLine="true"
        android:text="Hello,我是诗雨!在这里祝大家圣诞快乐,开心快乐每一天!"
        android:textColor="#009900"
        android:textSize="18sp" />

其实跑马灯效果的实现也可以不用自定义TextView,只要在代码中在进行相关设置就可以了。但是我个人比较喜欢用自定义。


后记:

我也深知自己是Android界的小菜鸟,还有好多东西需要去学习。

也希望各位前辈多多指教,我一定虚心接纳并认真地进行改正!