注:对不住大家,有点粗心了,之前上传的代码确实有问题(原因是AndroidManifest.xml文件里的包名错了,自己在上传代码时手贱改了一下包名,结果AndroidManifest.xml文件中的package属性没有同步更新,所以运行才报错),现已纠正,正确的代码下载链接地址为http://download.csdn.net/detail/stevenhu_223/5789409,欢迎大家指正,共同学习!


前言:在Android系统锁屏模块开发中,有两种形式的开发:一种是基于源码开发;另一种是基于第三方开发。基于源码开发还是比较具有危险性的,相对于第三方开发来说要考虑的问题比较多,而且移植后可能会出现问题,不便于维护。

当然,基于源码开发锁屏要做的工作相对于第三方开发锁屏要做的工作就少了很多,比如在时间和日期的获取和实时更新、充电时显示的电量状态、运营商名字的显示等等。这些在源码中是已经开发好了,直接可以调用实现。

而基于第三方锁屏的开发就要考虑这些问题,而且还要考虑在启动第三方锁屏时如何屏蔽系统原生的锁屏。

最近鄙人在参考其他人已有的成果上,自己尝试着去开发一个基于第三方锁屏Demo。锁屏相关的资源图片都是自己网上找的,算是仿360的星空锁屏吧。也没有创新性,基本上还是典型的中心图标绕圆圈实现解锁或启动其他应用。现在就把效果图和代码贴出来给大家吐槽一下。

一. 星空锁屏功能介绍:

1.解锁、启动拨号、短信、相机界面和功能介绍:

功能实现介绍:

-->中心图标动画显示,用户点中心图标可任意拖动。

-->无论用户拖动中心图标时,手指移动到屏幕的任意一处,中心图标都保持在一定的圆圈范围内移动。

-->若用户拖动中心图标到下方的解锁小图标区域,此时显示白色高亮小图标。用户手指放开时,手机将解锁进入主界面。

-->若用户拖动中心图标到上方的相机小图标区域时,此时显示白色高亮小图标。用户手指放开时,手机将启动相机应用。

-->若用户拖动中心图标到左方的拨号小图标区域时,此时显示白色高亮小图标。用户手指放开时,手机将启动拨号界面。

-->若用户拖动中心图标到右方的短信小图标区域时,此时显示白色高亮小图标。用户手指放开时,手机将启动短信应用。

2.双击可拖动的中心图标显示的音乐播放控制界面和功能介绍:

功能实现介绍:

-->在显示音乐播放控件界面的情况下,用户拖动中心图标时,隐藏音乐控制按钮,显示解锁、启动应用相关图标,进行解锁、启动应用操作。

-->用户可点击上方的音乐控制按钮图标,进行音乐的播放、暂停。(音乐正在播放时显示暂停按钮,暂停时显示播放按钮)

-->用户可点击下方的音乐控制按钮图标,进行音乐的停止。(音乐停止时,上方图标显示音乐播放按钮)

-->用户可点击左方的音乐控制按钮图标,进行上一曲播放(若音乐在暂时状态时点击上一曲,则上方图标显示暂停按钮)

-->用户可点击右方的音乐控制按钮图标,进行下一曲播放(若音乐在暂时状态时点击下一曲,则上方图标显示暂停按钮)

-->当锁屏界面双击切换到控制音乐播放界面时,上方界面日期显示下方会显示正在播放的歌曲相关信息(歌曲、歌手)

-->当正在处于音乐控制界面时,再次双击中心图标,回到原来的锁屏操作界面(依次循环)

二. 星空锁屏代码实现:

1. 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"
android:background="@drawable/suoping"
>

<LinearLayout
android:layout_marginTop="40dip"
android:layout_marginBottom="12dip"
android:layout_marginRight="42dip"
android:layout_gravity="right"
android:layout_width="wrap_content"
android:layout_height="wrap_content">

<!-- 时间显示 -->
<TextView android:id="@+id/time"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textSize="80sp"
android:textAppearance="?android:attr/textAppearanceMedium"
android:layout_marginBottom="6dip"
android:textColor="#ffffff"
/>

<!-- 上午、下午显示 -->
<TextView
android:id="@+id/am_pm"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:ellipsize="none"
android:textSize="20sp"
android:layout_marginLeft="8dip"
android:layout_toRightOf="@id/time"
android:layout_alignBottom="@id/time"/>

</LinearLayout>
<LinearLayout
android:layout_gravity="right"
android:layout_marginRight="42dip"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical">

<!-- 日期显示 -->
<TextView
android:id="@+id/date"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:singleLine="true"
android:textAppearance="?android:attr/textAppearanceMedium"
android:textSize="14dip"/>
</LinearLayout>

<!-- 音乐播放界面歌手名显示,歌手名过长会以跑马灯的形式显示 -->
<TextView
android:id="@+id/artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine = "true"
android:textSize="14dip"
android:layout_gravity="center"
android:textColor="#ffffff"
/>

<!-- 音乐播放界面歌曲名显示 ,歌曲名过长会以跑马灯的形式显示-->
<TextView
android:id="@+id/music"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:ellipsize="marquee"
android:singleLine = "true"
android:textSize="14dip"
android:layout_gravity="center"
android:textColor="#ffffff"
/>

<LinearLayout
android:layout_gravity="center"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
>

<!-- 锁屏核心功能操作界面自定义ViewGrou的显示 -->
<com.phicomm.hu.StarLockView
android:id="@+id/FxView"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>

</LinearLayout>

</LinearLayout>

2. StarLockView.java:

该自定义ViewGroup类为锁屏主要功能实现的核心类。该类负责处理中心图标的动画显示、用户拖动监听操作。同时也处理了解锁图标、应用程序启动图标、音乐控制图标等相关小图标的布局、监听。实现锁屏界面和音乐控制界面的显示和切换,实现发送广播至系统音乐应用的后台服务(MediaPlaybackService),已达到控制后台音乐服务。代码如下:

package com.phicomm.hu;

import com.phicomm.hu.R;
import android.content.Context;
import android.content.Intent;
import android.graphics.Rect;
import android.os.Handler;
import android.os.Vibrator;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.View.OnClickListener;
import android.view.animation.AlphaAnimation;
import android.view.animation.Animation;
import android.view.animation.TranslateAnimation;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;

public class StarLockView extends ViewGroup implements OnClickListener
{

private static final boolean DBG = true;
private Context mContext;
private Handler mainHandler = null;
private float mx;
private float my;
private int count = 0;
private long firstClick = 0;
private long secondClick = 0;

private int mWidth, mHight;
private int mScreenHalfWidth;
private int mAlphaViewWidth, mAlphaViewHeight;
private int mCenterViewWidth, mCenterViewHeight;
private int mCenterViewTop, mCenterViewBottom;
private int mAlphaViewTop, mAlphaViewBottom;
private int mSmsViewHalfWidth, mSmsViewHalfHeight;
private int mDialViewHalfWidth, mDialViewHalfHeight;
private int mCameraViewHalfWidth, mHalfCameraViewHeight;
private int mUnlockViewHalfWidth, mUnlockViewHalfHeight;
private int mLightViewHalfWidth, mLightViewHalfHeight;
private int mMusicViewHalfWidth, mMusicViewHalfHeight;

private ImageView mSmsView, mDialView, mCameraView, mUnLockView;
private ImageView mCenterView, mAlphaView;
private ImageView mSmsLightView, mUnLockLightView,
mCameraLightView, mDialLightView;

private ImageView mPlayView, mNextView, mPrevView, mStopView;

private Rect smsRect, dialRect, cameraRect, unlockRect;
private Rect mCenterViewRect;

private AlphaAnimation alpha;
private boolean mTracking = false;

private static final String TAG = "FxLockView";
public static final String SHOW_MUSIC = "com.phicomm.hu.action.music";
private static final String SERVICECMD = "com.android.music.musicservicecommand";
private static final String CMDNAME = "command";
private static final String CMDSTOP = "stop";
private static final String CMDPAUSE = "pause";
private static final String CMDPLAY = "play";
private static final String CMDPREV = "previous";
private static final String CMDNEXT = "next";

public StarLockView(Context context, AttributeSet attrs) {
super(context, attrs, 0);
mContext = context;
if(DBG) Log.d(TAG, "FxLockView2");
//connectMediaService();
if(DBG) Log.d(TAG, "FxLockView-->isMusic--->" + MusicInfo.isMusic());
MusicInfo.setMusic(false);
initViews(context);
setViewId();
//setViewOnClick();
onAnimationStart();
}

@Override
protected void onLayout(boolean changed, int l, int t, int r, int b) {
// TODO Auto-generated method stub
if (changed) {
mWidth = r;
mHight = b;
////mHalfWidth >> 1为向右位移1位,相当于mHalfWidth / 2。采用位移的原因是计算效率比较高。
mScreenHalfWidth = mWidth >> 1;

getViewMeasure();
mCenterViewTop = 4 * mHight / 7 - (mCenterViewHeight >> 1);
mCenterViewBottom = 4 * mHight / 7 + (mCenterViewHeight >> 1);
mAlphaViewTop = 4 * mHight / 7 - (mAlphaViewHeight >> 1);
mAlphaViewBottom = 4 * mHight / 7 + (mAlphaViewHeight >> 1);

setChildViewLayout();
setMusicButtonsLayout();
setActivatedViewLayout();
getChildViewRect();

mCenterViewRect = new Rect(mWidth / 2 - mAlphaViewWidth / 2, mAlphaViewTop,
mWidth / 2 + mAlphaViewWidth / 2, mAlphaViewBottom);

}

if(DBG) Log.d(TAG, "l-->" + l);
if(DBG) Log.d(TAG, "t-->" + t);
if(DBG) Log.d(TAG, "r-->" + r);
if(DBG) Log.d(TAG, "b-->" + b);
}

//获取各个图标的宽、高
private void getViewMeasure()
{
mAlphaView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mAlphaViewWidth = mAlphaView.getMeasuredWidth();
mAlphaViewHeight = mAlphaView.getMeasuredHeight();

mCenterView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mCenterViewWidth = mCenterView.getMeasuredWidth();
mCenterViewHeight = mCenterView.getMeasuredHeight();

mSmsView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mSmsViewHalfWidth = (mSmsView.getMeasuredWidth()) >> 1;
mSmsViewHalfHeight = (mSmsView.getMeasuredHeight()) >> 1;

mDialView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mDialViewHalfWidth = (mDialView.getMeasuredWidth()) >> 1;
mDialViewHalfHeight = (mDialView.getMeasuredHeight()) >> 1;

mCameraView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mCameraViewHalfWidth = (mCameraView.getMeasuredWidth()) >> 1;
mHalfCameraViewHeight = (mCameraView.getMeasuredHeight()) >> 1;

mUnLockView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mUnlockViewHalfWidth = (mUnLockView.getMeasuredWidth()) >> 1;
mUnlockViewHalfHeight = (mUnLockView.getMeasuredHeight()) >> 1;

mSmsLightView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mLightViewHalfWidth = (mSmsLightView.getMeasuredWidth()) >> 1;
mLightViewHalfHeight = (mSmsLightView.getMeasuredHeight()) >> 1;

mPlayView.measure(View.MeasureSpec.makeMeasureSpec(0,
View.MeasureSpec.UNSPECIFIED), View.MeasureSpec
.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED));
mMusicViewHalfWidth = (mPlayView.getMeasuredWidth()) >> 1;
mMusicViewHalfHeight = (mPlayView.getMeasuredHeight()) >> 1;
}

//设置高亮图标的布局
private void setActivatedViewLayout()
{
mUnLockLightView.layout(mScreenHalfWidth - mLightViewHalfWidth - 5,
(mCenterViewTop + 2 * mCenterViewHeight) - mLightViewHalfHeight,
mScreenHalfWidth + mLightViewHalfWidth - 5,
(mCenterViewBottom + mCenterViewHeight) + mLightViewHalfHeight);
mSmsLightView.layout((mScreenHalfWidth + 3 * mCenterViewWidth / 2) - 2 * mLightViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mLightViewHalfHeight,
(mScreenHalfWidth + 3 * mCenterViewWidth / 2) + 2 * mLightViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mLightViewHalfHeight);
mDialLightView.layout((mScreenHalfWidth - 3 * mCenterViewWidth / 2) - mLightViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mLightViewHalfHeight,
(mScreenHalfWidth - 3 * mCenterViewWidth / 2) + mLightViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mLightViewHalfHeight);
mCameraLightView.layout(mScreenHalfWidth - mLightViewHalfWidth,
(mCenterViewTop - mCenterViewHeight) - mLightViewHalfHeight,
mScreenHalfWidth + mLightViewHalfWidth,
(mCenterViewBottom - 2 * mCenterViewHeight) + mLightViewHalfHeight);
}

//设置各图标在FxLockView中的布局
private void setChildViewLayout()
{
mAlphaView.layout(mScreenHalfWidth - mAlphaViewWidth / 2, mAlphaViewTop,
mScreenHalfWidth + mAlphaViewWidth / 2, mAlphaViewBottom);

mCenterView.layout(mScreenHalfWidth - mCenterViewWidth / 2, mCenterViewTop,
mScreenHalfWidth + mCenterViewWidth / 2, mCenterViewBottom);

mSmsView.layout((mScreenHalfWidth + 3 * mCenterViewWidth / 2) - 2 * mSmsViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mSmsViewHalfHeight,
(mScreenHalfWidth + 3 * mCenterViewWidth / 2) + 2 * mSmsViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mSmsViewHalfHeight);

mDialView.layout((mScreenHalfWidth - 3 * mCenterViewWidth / 2) - mDialViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mDialViewHalfHeight,
(mScreenHalfWidth - 3 * mCenterViewWidth / 2) + mDialViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mDialViewHalfHeight);

mCameraView.layout(mScreenHalfWidth - mCameraViewHalfWidth,
(mCenterViewTop - mCenterViewHeight) - mHalfCameraViewHeight,
mScreenHalfWidth + mCameraViewHalfWidth,
(mCenterViewBottom - 2 * mCenterViewHeight) + mHalfCameraViewHeight);

mUnLockView.layout(mScreenHalfWidth - mUnlockViewHalfWidth,
(mCenterViewTop + 2 * mCenterViewHeight) - mUnlockViewHalfHeight,
mScreenHalfWidth + mUnlockViewHalfWidth,
(mCenterViewBottom + mCenterViewHeight) + mUnlockViewHalfHeight);

}

//设置音乐控制按钮布局
private void setMusicButtonsLayout()
{
mNextView.layout((mScreenHalfWidth + 3 * mCenterViewWidth / 2) - 2 * mMusicViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mMusicViewHalfHeight,
(mScreenHalfWidth + 3 * mCenterViewWidth / 2) + 2 * mMusicViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mMusicViewHalfHeight);

mPrevView.layout((mScreenHalfWidth - 3 * mCenterViewWidth / 2) - mMusicViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mMusicViewHalfHeight,
(mScreenHalfWidth - 3 * mCenterViewWidth / 2) + mMusicViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mMusicViewHalfHeight);

mStopView.layout(mScreenHalfWidth - mMusicViewHalfWidth,
(mCenterViewTop + 2 * mCenterViewHeight) - mMusicViewHalfHeight,
mScreenHalfWidth + mMusicViewHalfWidth,
(mCenterViewBottom + mCenterViewHeight) + mMusicViewHalfHeight);

mPlayView.layout(mScreenHalfWidth - mMusicViewHalfWidth,
(mCenterViewTop - mCenterViewHeight) - mMusicViewHalfHeight,
mScreenHalfWidth + mMusicViewHalfWidth,
(mCenterViewBottom - 2 * mCenterViewHeight) + mMusicViewHalfHeight);
}

//创建各图标位置对应的Rect
private void getChildViewRect()
{
smsRect = new Rect((mScreenHalfWidth + 3 * mCenterViewWidth / 2) - 2 * mSmsViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mSmsViewHalfHeight,
(mScreenHalfWidth + 3 * mCenterViewWidth / 2) + 2 * mSmsViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mSmsViewHalfHeight);

dialRect = new Rect((mScreenHalfWidth - 3 * mCenterViewWidth / 2) - mDialViewHalfWidth,
(mCenterViewTop + mCenterViewHeight / 2) - mDialViewHalfHeight,
(mScreenHalfWidth - 3 * mCenterViewWidth / 2) + mDialViewHalfWidth,
(mAlphaViewBottom - mCenterViewHeight / 2) + mDialViewHalfHeight);

cameraRect = new Rect(mScreenHalfWidth - mCameraViewHalfWidth,
(mCenterViewTop - mCenterViewHeight) - mHalfCameraViewHeight,
mScreenHalfWidth + mCameraViewHalfWidth,
(mCenterViewBottom - 2 * mCenterViewHeight) + mHalfCameraViewHeight);

unlockRect = new Rect(mScreenHalfWidth - mUnlockViewHalfWidth,
(mCenterViewTop + 2 * mCenterViewHeight) - mUnlockViewHalfHeight,
mScreenHalfWidth + mUnlockViewHalfWidth,
(mCenterViewBottom + mCenterViewHeight) + mUnlockViewHalfHeight);
}

//获取图标,将获取的图标添加入FxLockView,设置图标的可见性
private void initViews(Context context) {
mAlphaView = new ImageView(context);
mAlphaView.setImageResource(R.drawable.centure2);
setViewsLayout(mAlphaView);
mAlphaView.setVisibility(View.INVISIBLE);

mCenterView = new ImageView(context);
mCenterView.setImageResource(R.drawable.centure1);
setViewsLayout(mCenterView);
mCenterView.setVisibility(View.VISIBLE);

mSmsView = new ImageView(context);
mSmsView.setImageResource(R.drawable.sms);
setViewsLayout(mSmsView);
mSmsView.setVisibility(View.VISIBLE);

mDialView = new ImageView(context);
mDialView.setImageResource(R.drawable.dial);
setViewsLayout(mDialView);
mDialView.setVisibility(View.VISIBLE);

mCameraView = new ImageView(context);
mCameraView.setImageResource(R.drawable.camera);
setViewsLayout(mCameraView);
mCameraView.setVisibility(View.VISIBLE);

mUnLockView = new ImageView(context);
mUnLockView.setImageResource(R.drawable.home);
setViewsLayout(mUnLockView);
mUnLockView.setVisibility(View.VISIBLE);

mNextView = new ImageView(context);
mNextView.setImageResource(R.drawable.next);
setViewsLayout(mNextView);
setMusicButtonBackground(mNextView);

mPrevView = new ImageView(context);
mPrevView.setImageResource(R.drawable.prev);
setViewsLayout(mPrevView);
setMusicButtonBackground(mPrevView);

mPlayView = new ImageView(context);
setPlayViewDrawable();
setViewsLayout(mPlayView);
setMusicButtonBackground(mPlayView);

mStopView = new ImageView(context);
mStopView.setImageResource(R.drawable.stop);
setViewsLayout(mStopView);
setMusicButtonBackground(mStopView);
//mStopView.setVisibility(View.INVISIBLE);

mSmsLightView= new ImageView(context);
setLightDrawable(mSmsLightView);
setViewsLayout(mSmsLightView);
mSmsLightView.setVisibility(INVISIBLE);

mUnLockLightView = new ImageView(context);
setLightDrawable(mUnLockLightView);
setViewsLayout(mUnLockLightView);
mUnLockLightView.setVisibility(INVISIBLE);

mCameraLightView = new ImageView(context);
setLightDrawable(mCameraLightView);
setViewsLayout(mCameraLightView);
mCameraLightView.setVisibility(INVISIBLE);

mDialLightView = new ImageView(context);
setLightDrawable(mDialLightView);
setViewsLayout(mDialLightView);
mDialLightView.setVisibility(INVISIBLE);
}

private void setLightDrawable(ImageView img)
{
img.setImageResource(R.drawable.light);
}

//设置获取图标的参数,并添加到FxLockView
private void setViewsLayout(ImageView image) {
image.setScaleType(ScaleType.CENTER);
image.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT,
LayoutParams.WRAP_CONTENT));
addView(image);
}

//设置音乐播放控制按钮的点击触摸反馈背景
private void setMusicButtonBackground(ImageView musicIcon)
{
musicIcon.setBackgroundResource(R.drawable.music_button_bg);
musicIcon.setVisibility(View.INVISIBLE);
}

@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// TODO Auto-generated method stub
final int action = ev.getAction();
final float x = ev.getX();
final float y = ev.getY();

switch (action) {
case MotionEvent.ACTION_DOWN:
//手指点在中心图标范围区域内
if (mCenterViewRect.contains((int) x, (int) y))
{
mTracking = true;
//stopViewAnimation();
onAnimationEnd();
mAlphaView.setVisibility(View.INVISIBLE);
return true;
}
else if(MusicInfo.isMusic())
{
//为音乐控制按钮绑定点击监听
setMusicViewsOnClick();
}
break;

default:
break;
}
if(DBG) Log.d(TAG, "onInterceptTouchEvent()");
//此处返回false,onClick事件才能监听的到
return false;
}

@Override
public boolean onTouchEvent(MotionEvent event) {
/*mTracking为true时,说明中心图标被点击移动
* 即只有在中心图标被点击移动的情况下,onTouchEvent
* 事件才会触发。
*/
if (mTracking)
{
final int action = event.getAction();
final float nx = event.getX();
final float ny = event.getY();

switch (action) {
case MotionEvent.ACTION_DOWN:
showMusicButtons();
break;
case MotionEvent.ACTION_MOVE:
setTargetViewVisible(nx, ny);
//中心图标移动
handleMoveView(nx, ny);
break;
case MotionEvent.ACTION_UP:
mTracking = false;
doTriggerEvent(mx, my);
resetMoveView();
break;
case MotionEvent.ACTION_CANCEL:
mTracking = false;
doTriggerEvent(mx, my);
resetMoveView();
break;
}
}
if(DBG) Log.d(TAG, "onTouchEvent()");
return mTracking || super.onTouchEvent(event);
}

//双击中心图标显示音乐控制按钮
private void showMusicButtons()
{
Intent intent = new Intent(SHOW_MUSIC);
count ++;
if(count == 1)
{
firstClick = System.currentTimeMillis();
}
else if (count == 2)
{
secondClick = System.currentTimeMillis();
if (secondClick - firstClick < 500)
{
//双击监听事件
//Toast.makeText(mContext, "click twice", Toast.LENGTH_LONG).show();
//setTargetViewInvisible();
if(DBG) Log.d(TAG, "count--->" + count);
if(DBG) Log.d(TAG, "isMusic--->" + MusicInfo.isMusic());
if (MusicInfo.isMusic())
{
setMusicButtonVisibility(false);
setTargetViewVisible();
}
else
{
setMusicButtonVisibility(true);
}
intent.putExtra("music_show", MusicInfo.isMusic());
mContext.sendBroadcast(intent);
}
count = 0;
firstClick = 0;
secondClick = 0;
}
}

//平方和计算
private float dist2(float dx, float dy)
{
return dx * dx + dy * dy;
}

private void handleMoveView(float x, float y)
{

int mHalfCenterViewWidth = mCenterViewWidth >> 1;

//Radius为中心图标移动的限定的圆范围区域半径
int Radius = mCenterViewWidth + mHalfCenterViewWidth;

/*若用户手指移动的点与中心点的距离长度大于Radius,则中心图标坐标位置限定在移动区域范围圆弧上。
* 一般是用户拖动中心图标,手指移动到限定圆范围区域外。
*/
if (Math.sqrt(dist2(x - mScreenHalfWidth, y - (mCenterView.getTop() + mCenterViewWidth / 2)
)) > Radius)
{
//原理为x1 / x = r1 / r
x = (float) ((Radius / (Math.sqrt(dist2(x - mScreenHalfWidth, y - (mCenterView.getTop() + mHalfCenterViewWidth)
)))) * (x - mScreenHalfWidth) + mScreenHalfWidth);

y = (float) ((Radius / (Math.sqrt(dist2(x - mScreenHalfWidth, y - (mCenterView.getTop() + mHalfCenterViewWidth)
)))) * (y - (mCenterView.getTop() + mHalfCenterViewWidth)) + mCenterView.getTop() + mHalfCenterViewWidth);
}

mx = x;
my = y;
/*图形的坐标是以左上角为基准的,
* 所以,为了使手指所在的坐标和图标的中心位置一致,
* 中心坐标要减去宽度和高度的一半。
*/
mCenterView.setX((int)x - mCenterView.getWidth()/2);
mCenterView.setY((int)y - mCenterView.getHeight()/2);
ShowLightView(x, y);
invalidate();
}

//监听解锁、启动拨号、相机、短信应用
private void doTriggerEvent(float a, float b)
{
if (smsRect.contains((int)a, (int) b))
{
//stopViewAnimation();
onAnimationEnd();
setTargetViewInvisible(mSmsView);
virbate();
//发送消息到MainActivity类中的mHandler出来
mainHandler.obtainMessage(MainActivity.MSG_LAUNCH_SMS).sendToTarget();
}
else if (dialRect.contains((int)a , (int)b))
{
onAnimationEnd();
setTargetViewInvisible(mDialView);
virbate();
mainHandler.obtainMessage(MainActivity.MSG_LAUNCH_DIAL).sendToTarget();
}
else if (cameraRect.contains((int)a, (int)b))
{
onAnimationEnd();
setTargetViewInvisible(mCameraView);
virbate();
mainHandler.obtainMessage(MainActivity.MSG_LAUNCH_CAMERA).sendToTarget();
}
else if (unlockRect.contains((int)a, (int)b))
{
onAnimationEnd();
setTargetViewInvisible(mUnLockView);
virbate();
mainHandler.obtainMessage(MainActivity.MSG_LAUNCH_HOME).sendToTarget();
}
}

//中心图标拖动到指定区域时显示高亮图标
private void ShowLightView(float a, float b)
{
if (unlockRect.contains((int)a, (int)b))
{
setLightVisible(mUnLockLightView);
}
else if (smsRect.contains((int)a, (int) b))
{
setLightVisible(mSmsLightView);
}
else if (dialRect.contains((int)a , (int)b))
{
setLightVisible(mDialLightView);
}
else if (cameraRect.contains((int)a, (int)b))
{
setLightVisible(mCameraLightView);
}
else
{
setLightInvisible();
}
}

private void setLightVisible(ImageView view)
{
view.setVisibility(View.VISIBLE);
mCenterView.setVisibility(View.INVISIBLE);
}

//隐藏高亮图标
private void setLightInvisible()
{
final View mActivatedViews[] = {mUnLockLightView, mSmsLightView,
mDialLightView, mCameraLightView};
for (View view : mActivatedViews)
{
view.setVisibility(View.INVISIBLE);
}

mCenterView.setVisibility(View.VISIBLE);
}

private void setTargetViewInvisible(ImageView img)
{
img.setVisibility(View.INVISIBLE);
}

private void setTargetViewVisible(float x, float y)
{
if(Math.sqrt(dist2(x - mScreenHalfWidth, y - (mCenterView.getTop() + mCenterViewWidth / 2)
)) > mAlphaViewHeight / 4)
{
if (MusicInfo.isMusic())
{
setMusicButtonVisibility(false);
}
else
{
return;
}
}
}

private void setTargetViewVisible()
{
if(DBG) Log.d(TAG, "setTargetViewVisible()");
final View mTargetViews[] = {mSmsView, mDialView, mUnLockView, mCameraView};
for (View view : mTargetViews)
{
view.setVisibility(View.VISIBLE);
}
MusicInfo.setMusic(false);
}

//show为true时显示音乐播放按钮,false时隐藏
private void setMusicButtonVisibility(boolean show)
{
int visibility = show ? View.VISIBLE : View.INVISIBLE;
if(DBG) Log.d(TAG, "setMusicIconVisible()");
final View mMusicViews[] = {mPlayView, mNextView, mPrevView, mStopView};
for(View view : mMusicViews)
{
view.setVisibility(visibility);
}

//showMusicAnimation(visibility);
if (show)
{
//showMusicAnimation();
MusicInfo.setMusic(show);
}

}

//重置中心图标,回到原位置
private void resetMoveView()
{
mCenterView.setX(mWidth / 2 - mCenterViewWidth /2);
mCenterView.setY((mCenterView.getTop() + mCenterViewHeight / 2) - mCenterViewHeight / 2);
onAnimationStart();
if (MusicInfo.isMusic())
{
setMusicButtonVisibility(true);
}
invalidate();
}

public void setMainHandler(Handler handler)
{
mainHandler = handler;
}

//解锁时震动
private void virbate()
{
Vibrator vibrator = (Vibrator) mContext.getSystemService(Context.VIBRATOR_SERVICE);
vibrator.vibrate(200);
}

//音乐控制按钮绑定点击监听
private void setMusicViewsOnClick()
{
final View mMusicViews[] = {mPlayView, mNextView, mPrevView, mStopView};
for (View view : mMusicViews)
{
view.setOnClickListener(this);
}
}

private void setViewId()
{
if(DBG) Log.d(TAG, "setViewId()");
mPlayView.setId(0);
mNextView.setId(1);
mPrevView.setId(2);
mStopView.setId(3);
}

//音乐播放控制按钮点击事件监听
@Override
public void onClick(View v)
{
// TODO Auto-generated method stub

Intent intent = new Intent();
intent.setAction(SERVICECMD);


if(DBG) Log.d(TAG, "onClick");
/*发送广播到后台服务,实现控制音乐播放。因为音乐后台服务随时会超时自动停止销毁,
* 所以每次点击音乐播放按钮时要先启动后台音乐服务,在保证该服务时启动的情况下才
* 能接收到发送的广播。
*/
switch (v.getId())
{
case 0:

MainActivity.mStatusViewManager.connectMediaService();
if (MusicInfo.isPlaying())
{
//暂停音乐
intent.putExtra(CMDNAME, CMDPAUSE);
mPlayView.setImageResource(R.drawable.play);
}
else
{
//播放音乐
intent.putExtra(CMDNAME, CMDPLAY);
mPlayView.setImageResource(R.drawable.pause);
}

break;

case 1:
MainActivity.mStatusViewManager.connectMediaService();
//下一曲
intent.putExtra(CMDNAME, CMDNEXT);
mPlayView.setImageResource(R.drawable.pause);
break;

case 2:
MainActivity.mStatusViewManager.connectMediaService();
//上一曲
intent.putExtra(CMDNAME, CMDPREV);
mPlayView.setImageResource(R.drawable.pause);
break;

case 3:
//停止
MainActivity.mStatusViewManager.connectMediaService();
intent.putExtra(CMDNAME, CMDSTOP);
mPlayView.setImageResource(R.drawable.play);
break;
}
mContext.sendBroadcast(intent);
}

private void setPlayViewDrawable()
{
if(DBG) Log.d(TAG, "setPlayViewDrawable()-->" + MusicInfo.isPlaying());
mPlayView.setImageResource(MusicInfo.isPlaying() ? R.drawable.pause : R.drawable.play);
}

//停止中心图标动画
@Override
protected void onAnimationEnd() {
// TODO Auto-generated method stub
super.onAnimationEnd();
if (alpha != null)
{
alpha = null;
}
mAlphaView.setAnimation(null);
}

//显示中心图标动画
@Override
protected void onAnimationStart() {
// TODO Auto-generated method stub
super.onAnimationStart();
mAlphaView.setVisibility(View.VISIBLE);

if (alpha == null) {
alpha = new AlphaAnimation(0.0f, 1.0f);
alpha.setDuration(1000);
}
alpha.setRepeatCount(Animation.INFINITE);
mAlphaView.startAnimation(alpha);
}

}

3.MediaControl.java:

自定义接口,StarLockService和StatusViewManager均实现它,代码如下:

package com.phicomm.hu;

public interface MediaControl
{
void connectMediaService();
void registerComponent();
void unregisterComponent();
void initViews();
}

4.MainActivity.java:

继承Activity类,承载锁屏整个界面,覆盖手机home界面,作为第三方锁屏的可视化操作界面;主要负责加载界面布局、处理来自StarLockView发送的解锁、启动相关应用的信息、屏蔽相关的物理按钮;代码如下:

package com.phicomm.hu;
import com.phicomm.hu.R;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import android.view.KeyEvent;
import android.view.Window;
import android.view.WindowManager;

public class MainActivity extends Activity
{
private static final boolean DBG = true;
private static final String TAG = "MainActivity";
public static final int MSG_LAUNCH_HOME = 0;
public static final int MSG_LAUNCH_DIAL = 1;
public static final int MSG_LAUNCH_SMS = 2;
public static final int MSG_LAUNCH_CAMERA = 3;

private StarLockView mLockView;
public static StatusViewManager mStatusViewManager;

/** Called when the activity is first created. */
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
if(DBG) Log.d(TAG, "onCreate()");
this.requestWindowFeature(Window.FEATURE_NO_TITLE);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN);

setContentView(R.layout.main);

initViews();
mStatusViewManager = new StatusViewManager(this, this.getApplicationContext());
//启动音乐服务
mStatusViewManager.connectMediaService();
startService(new Intent(MainActivity.this, StarLockService.class));
mLockView.setMainHandler(mHandler);
}

//接收来自StarLockView发送的消息,处理解锁、启动相关应用的功能
private Handler mHandler = new Handler()
{

@Override
public void handleMessage(Message msg)
{
// TODO Auto-generated method stub
switch (msg.what)
{
case MSG_LAUNCH_HOME:
finish();
break;
case MSG_LAUNCH_SMS:
launchSms();
finish();
break;
case MSG_LAUNCH_DIAL:
launchDial();
finish();
break;
case MSG_LAUNCH_CAMERA:
launchCamera();
finish();
break;
}
}

};

//启动短信应用
private void launchSms() {

//mFocusView.setVisibility(View.GONE);
Intent intent = new Intent();
ComponentName comp = new ComponentName("com.android.mms",
"com.android.mms.ui.ConversationList");
intent.setComponent(comp);
intent.setAction("android.intent.action.VIEW");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}

//启动拨号应用
private void launchDial() {
Intent intent = new Intent(Intent.ACTION_DIAL);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}

//启动相机应用
private void launchCamera() {
Intent intent = new Intent();
ComponentName comp = new ComponentName("com.android.camera",
"com.android.camera.Camera");
intent.setComponent(comp);
intent.setAction("android.intent.action.VIEW");
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
| Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
startActivity(intent);
}

//使home物理键失效
@Override
public void onAttachedToWindow() {
// TODO Auto-generated method stub
this.getWindow().setType(WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
super.onAttachedToWindow();
}

//使back键,音量加减键失效
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
// TODO Auto-generated method stub
return disableKeycode(keyCode, event);
}

private boolean disableKeycode(int keyCode, KeyEvent event)
{
int key = event.getKeyCode();
switch (key)
{
case KeyEvent.KEYCODE_BACK:
case KeyEvent.KEYCODE_VOLUME_DOWN:
case KeyEvent.KEYCODE_VOLUME_UP:
return true;
}
return super.onKeyDown(keyCode, event);
}

@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
if(DBG) Log.d(TAG, "onDestroy()");

}

@Override
protected void onResume()
{
// TODO Auto-generated method stub
super.onResume();
if(DBG) Log.d(TAG, "onResume()");
}

@Override
public void onDetachedFromWindow()
{
// TODO Auto-generated method stub
super.onDetachedFromWindow();
if(DBG) Log.d(TAG, "onDetachedFromWindow()");
//解除监听
mStatusViewManager.unregisterComponent();
}

public void initViews()
{
// TODO Auto-generated method stub
mLockView = (StarLockView) findViewById(R.id.FxView);
}

}

注:若4.0以上的系统,launchCamera()方法中的代码改为

private void launchCamera() {
Intent intent = new Intent();
intent.setAction(MediaStore.INTENT_ACTION_STILL_IMAGE_CAMERA);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
startActivity(intent);
}

5.StarLockService.java

锁屏后台服务类,主要负责动态监听接收按Power键的广播和音乐后台服务的广播;代码实现如下:

package com.phicomm.hu;

import android.app.KeyguardManager;
import android.app.Service;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.IBinder;
import android.util.Log;

public class StarLockService extends Service implements MediaControl
{

private static final boolean DBG = true;
private static final String TAG = "FxLockService";
private Intent mFxLockIntent = null;
private KeyguardManager mKeyguardManager = null ;
private KeyguardManager.KeyguardLock mKeyguardLock = null ;

private MusicBroadcastReceiver mMBR;

@Override
public void onCreate()
{
// TODO Auto-generated method stub
super.onCreate();
refreshInfo();
if (DBG)Log.d(TAG, "-->onCreate()");
mFxLockIntent = new Intent(StarLockService.this, MainActivity.class);
mFxLockIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
registerComponent();
}



@Override
public void onStart(Intent intent, int startId)
{
// TODO Auto-generated method stub
super.onStart(intent, startId);
if(DBG) Log.d(TAG, "-->onStart()");
}



@Override
public void onDestroy()
{
// TODO Auto-generated method stub
super.onDestroy();
if(DBG) Log.d(TAG, "-->onDestroy()");
unregisterComponent();
//被销毁时启动自身,保持自身在后台存活
startService(new Intent(StarLockService.this, StarLockService.class));
}

@Override
public IBinder onBind(Intent arg0)
{
// TODO Auto-generated method stub
if(DBG) Log.d(TAG, "-->onBind()");
return null;
}

@Override
public int onStartCommand(Intent intent, int flags, int startId) {
// TODO Auto-generated method stub
if(DBG) Log.d(TAG, "-->onStartCommand()");
return Service.START_STICKY;
}

//监听来自用户按Power键点亮点暗屏幕的广播
private BroadcastReceiver mScreenOnOrOffReceiver = new BroadcastReceiver() {

@Override
public void onReceive(Context context, Intent intent)
{
if(DBG) Log.d(TAG, "mScreenOffReceiver-->" + intent.getAction());

if (intent.getAction().equals("android.intent.action.SCREEN_ON")
|| intent.getAction().equals("android.intent.action.SCREEN_OFF"))
{
refreshInfo();
mKeyguardManager = (KeyguardManager) context.getSystemService(Context.KEYGUARD_SERVICE);
mKeyguardLock = mKeyguardManager.newKeyguardLock("FxLock");
//屏蔽手机内置的锁屏
mKeyguardLock.disableKeyguard();
//启动该第三方锁屏
startActivity(mFxLockIntent);
}

if(intent.getAction().equals("com.phicomm.hu.action.music"))
{
refreshInfo();
}
}
};

//监听接收后台音乐服务MediaPlaybackService发送的广播,获取广播中携带的播放音乐信息(歌曲、歌手、专辑等)
private class MusicBroadcastReceiver extends BroadcastReceiver
{

@Override
public void onReceive(Context context, Intent intent)
{
// TODO Auto-generated method stub
if(DBG) Log.d(TAG, "MusicBroadcastReceiver-->" + intent.getAction());

MusicInfo.setArtistName(intent.getStringExtra("artist"));
//artistName = intent.getStringExtra("artist");
MusicInfo.setMusicName(intent.getStringExtra("track"));
//musicName = intent.getStringExtra("track");
MusicInfo.setPlaying(intent.getBooleanExtra("playing", false));
//playing = intent.getBooleanExtra("playing", playing);
//String album = intent.getStringExtra("album");
setMusicViewText();

if(DBG) Log.d(TAG, "artistName-->" + MusicInfo.getArtistName());
if(DBG) Log.d(TAG, "musicName-->" + MusicInfo.getMusicName());
if(DBG) Log.d(TAG, "playing-->" + MusicInfo.isPlaying());
//Log.d(TAG, "album-->" + album);

//因为音乐后台服务发送的是粘性广播,所以接收后要删除,不然会保持
removeStickyBroadcast(intent);
}

}

//刷新音乐播放信息
private void refreshInfo()
{
if (MusicInfo.isMusic())
{
setMusicViewText();
}
else
{
setLockViewText();
}
}

//设置显示音乐播放信息
private void setMusicViewText()
{
if(MusicInfo.getArtistName() != null
|| MusicInfo.getMusicName()!= null)
{
/*StringBuilder s = new StringBuilder();
s.append(MusicInfo.getArtistName()).append("-").append(MusicInfo.getMusicName());
MainActivity.mStatusViewManager.mArtistView.setText(s.toString());*/
MainActivity.mStatusViewManager.mArtistView.setText(MusicInfo.getArtistName());
MainActivity.mStatusViewManager.mMusicView.setText(MusicInfo.getMusicName());
}
else
{
MainActivity.mStatusViewManager.mArtistView.setText("no music to be chosen");
}

}

//锁屏界面时隐藏音乐播放信息
private void setLockViewText()
{
MainActivity.mStatusViewManager.mArtistView.setText(null);
}

@Override
public void connectMediaService()
{
// TODO Auto-generated method stub
//MainActivity.mStatusViewManager.connectMediaService();
}

//注册广播监听
@Override
public void registerComponent()
{
// TODO Auto-generated method stub
if(DBG) Log.d(TAG, "registerComponent()");
IntentFilter mScreenOnOrOffFilter = new IntentFilter();
mScreenOnOrOffFilter.addAction("android.intent.action.SCREEN_ON");
mScreenOnOrOffFilter.addAction("android.intent.action.SCREEN_OFF");
mScreenOnOrOffFilter.addAction(StarLockView.SHOW_MUSIC);
StarLockService.this.registerReceiver(mScreenOnOrOffReceiver, mScreenOnOrOffFilter);

if (mMBR == null)
{
mMBR = new MusicBroadcastReceiver();
IntentFilter mFilter = new IntentFilter();
mFilter.addAction("com.android.music.playstatechanged");
mFilter.addAction("com.android.music.metachanged");
mFilter.addAction("com.android.music.queuechanged");
mFilter.addAction("com.android.music.playbackcomplete");
StarLockService.this.registerReceiver(mMBR, mFilter);
}
}

//解除广播监听
@Override
public void unregisterComponent()
{
// TODO Auto-generated method stub
if(DBG) Log.d(TAG, "unregisterComponent()");
if (mScreenOnOrOffReceiver != null)
{
StarLockService.this.unregisterReceiver(mScreenOnOrOffReceiver);
}

if (mMBR != null)
{
StarLockService.this.unregisterReceiver(mMBR);
}
}

@Override
public void initViews() {
// TODO Auto-generated method stub

}

}

6.StatusViewManage.java:

该类主要用来管理在锁屏界面上显示时间(包括12小时制下显示上午和下午)和日期。代码如下:

package com.phicomm.hu;

import java.lang.ref.WeakReference;
import java.text.DateFormatSymbols;
import java.util.Calendar;
import java.util.Date;

import com.phicomm.hu.R;

import android.app.Activity;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.database.ContentObserver;
import android.graphics.Typeface;
import android.os.Handler;
import android.provider.Settings;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.View;
import android.widget.TextView;

//该类主要用来管理在锁屏界面上显示时间(包括12小时制下显示上午和下午)和日期。
public class StatusViewManager implements MediaControl
{

private static final String SYSTEM = "/system/fonts/";
private static final String SYSTEM_FONT_TIME_BACKGROUND = SYSTEM + "AndroidClock.ttf";
private static final String SYSTEM_FONT_TIME_FOREGROUND = SYSTEM + "AndroidClock_Highlight.ttf";

private final static String M12 = "h:mm";
private final static String M24 = "kk:mm";

private TextView mDateView;
private TextView mTimeView;
public TextView mArtistView;
public TextView mMusicView;

private String mDateFormat;
private String mFormat;

private static Activity mActivity;
private AmPm mAmPm;
private Calendar mCalendar;
public ContentObserver mFormatChangeObserver;
public BroadcastReceiver mIntentReceiver;

private final Handler mHandler = new Handler();

private static final Typeface sBackgroundFont;
private static final Typeface sForegroundFont;

private static Context mContext;

static
{
//创建获取字体风格
sBackgroundFont = Typeface.createFromFile(SYSTEM_FONT_TIME_BACKGROUND);
sForegroundFont = Typeface.createFromFile(SYSTEM_FONT_TIME_FOREGROUND);
}

public StatusViewManager(Activity activity, Context context)
{
mContext = context;
mActivity = activity;
initViews();
refreshDate();
}

private View findViewById(int id)
{
return mActivity.findViewById(id);
}

private void refreshDate()
{
if (mDateView != null)
{
//锁屏界面显示日期
mDateView.setText(DateFormat.format(mDateFormat, new Date()));
}
}

private String getString(int id)
{
return mActivity.getString(id);
}

class AmPm {
private TextView mAmPmTextView;
private String mAmString, mPmString;

AmPm(Typeface tf) {
mAmPmTextView = (TextView)findViewById(R.id.am_pm);
if (mAmPmTextView != null && tf != null) {
//设置显示的上午、下午字体风格
mAmPmTextView.setTypeface(tf);
}

//获取显示上午、下午的字符串数组
String[] ampm = new DateFormatSymbols().getAmPmStrings();
mAmString = ampm[0];
mPmString = ampm[1];
}

void setShowAmPm(boolean show) {
if (mAmPmTextView != null) {
mAmPmTextView.setVisibility(show ? View.VISIBLE : View.GONE);
}
}

void setIsMorning(boolean isMorning) {
if (mAmPmTextView != null) {
mAmPmTextView.setText(isMorning ? mAmString : mPmString);
}
}
}

private static class TimeChangedReceiver extends BroadcastReceiver {
private WeakReference<StatusViewManager> mStatusViewManager;
//private Context mContext;

public TimeChangedReceiver(StatusViewManager status) {
mStatusViewManager = new WeakReference<StatusViewManager>(status);
}

@Override
public void onReceive(Context context, Intent intent) {
// Post a runnable to avoid blocking the broadcast.
final boolean timezoneChanged =
intent.getAction().equals(Intent.ACTION_TIMEZONE_CHANGED);
final StatusViewManager status = mStatusViewManager.get();
if (status != null) {
status.mHandler.post(new Runnable() {
public void run() {
if (timezoneChanged) {
status.mCalendar = Calendar.getInstance();
}
status.updateTime();
}
});
} else {
try {
mContext.unregisterReceiver(this);
} catch (RuntimeException e) {
// Shouldn't happen
}
}
}
};

/*监听URI为Settings.System.CONTENT_URI的数据变化,即12小时制还是24小时制
* 的变化(一般来自用户在设置里对时间显示的设置)
*/
private static class FormatChangeObserver extends ContentObserver {
private WeakReference<StatusViewManager> mStatusViewManager;
//private Context mContext;
public FormatChangeObserver(StatusViewManager status) {
super(new Handler());
//创建保存在弱应用中的StatusViewManager对象
mStatusViewManager = new WeakReference<StatusViewManager>(status);
}
@Override
public void onChange(boolean selfChange) {
StatusViewManager mStatusManager = mStatusViewManager.get();
if (mStatusManager != null) {
mStatusManager.setDateFormat();
mStatusManager.updateTime();
} else {
try {
mContext.getContentResolver().unregisterContentObserver(this);
} catch (RuntimeException e) {
// Shouldn't happen
}
}
}
}

//更新时间
private void updateTime()
{
mCalendar.setTimeInMillis(System.currentTimeMillis());

CharSequence newTime = DateFormat.format(mFormat, mCalendar);
mTimeView.setText(newTime);
mAmPm.setIsMorning(mCalendar.get(Calendar.AM_PM) == 0);
}

//设置时间显示格式,如果时间显示为12小时制,则显示上午、下午
private void setDateFormat()
{
mFormat = android.text.format.DateFormat.is24HourFormat(mContext)
? M24 : M12;
mAmPm.setShowAmPm(mFormat.equals(M12));
}

/*该锁屏通过控制系统音乐模块后台服务MediaPlaybackService来实现音乐播放的控制,
* 但是由于系统音乐模块后台服务默认是禁止跨进程启动它的,所以本锁屏是无法跨进去启动该服务的,
* 不过,如果自己的手机有root权限,可以去音乐模块的AndroidManifest.xml文件里修改服务的exported属性,
* 然后编译音乐模块再push入手机,这样就可以做到控制系统音乐应用模块的后天服务音乐播放控制了。
* 注:这个锁屏是基于这样控制音乐播放的,这里的代码注释掉是考虑到别的手机没有root权限;所以锁屏的音乐控制无效
*/
@Override
public void connectMediaService()
{
/*
Intent intent = new Intent();
intent.setClassName("com.android.music", "com.android.music.MediaPlaybackService");
//绑定连接远程服务
mContext.startService(intent);*/
}

@Override
public void registerComponent()
{
// TODO Auto-generated method stub
Log.d("MainActivity", "registerComponent()");

if (mIntentReceiver == null) {
mIntentReceiver = new TimeChangedReceiver(this);
IntentFilter filter = new IntentFilter();
filter.addAction(Intent.ACTION_TIME_TICK);
filter.addAction(Intent.ACTION_TIME_CHANGED);
filter.addAction(Intent.ACTION_TIMEZONE_CHANGED);
mContext.registerReceiver(mIntentReceiver, filter);
}

if (mFormatChangeObserver == null) {
mFormatChangeObserver = new FormatChangeObserver(this);
mContext.getContentResolver().registerContentObserver(
Settings.System.CONTENT_URI, true, mFormatChangeObserver);
}

updateTime();
}

@Override
public void unregisterComponent()
{
// TODO Auto-generated method stub
Log.d("MainActivity", "unregisterComponent()");
if (mIntentReceiver != null) {
mContext.unregisterReceiver(mIntentReceiver);
}
if (mFormatChangeObserver != null) {
mContext.getContentResolver().unregisterContentObserver(
mFormatChangeObserver);
}
mFormatChangeObserver = null;
mIntentReceiver = null;
}

@Override
public void initViews()
{
// TODO Auto-generated method stub
mDateView = (TextView)findViewById(R.id.date);
//定义日期的显示格式,日期显示格式在donotTranslatr.xml文件中定义
mDateFormat = getString(R.string.month_day_year);
mTimeView = (TextView) findViewById(R.id.time);
mTimeView.setTypeface(sBackgroundFont);
mArtistView = (TextView)findViewById(R.id.artist);
mMusicView = (TextView)findViewById(R.id.music);

//启动跑马灯功能
mArtistView.setSelected(true);
mMusicView.setSelected(true);

/*创建AmPm对象,参数为设置的字体风格(如可设为Typeface.DEFAULT_BOLD粗体),
* 此处参数为空,默认情况。
*/
mAmPm = new AmPm(null);
//获取mCalendar对象
mCalendar = Calendar.getInstance();

setDateFormat();
//注册监听
registerComponent();
}

}

7.MusicInfo.java:

该类用于更新和保存来自音乐应用的音乐播放状态、歌曲名、歌手等相关的音乐信息。同时向外部提供保存的相关音乐信息;(类似JavaBean) 。代码如下:

package com.phicomm.hu;

public class MusicInfo
{

private static boolean isMusic;
private static boolean playing;
private static String artistName;
private static String musicName;
public static boolean isMusic()
{
return isMusic;
}
public static void setMusic(boolean isMusic)
{
MusicInfo.isMusic = isMusic;
}
public static boolean isPlaying()
{
return playing;
}
public static void setPlaying(boolean playing)
{
MusicInfo.playing = playing;
}
public static String getArtistName()
{
return artistName;
}
public static void setArtistName(String artistName)
{
MusicInfo.artistName = artistName;
}
public static String getMusicName()
{
return musicName;
}
public static void setMusicName(String musicName)
{
MusicInfo.musicName = musicName;
}

}

关于Android第三方锁屏开发的介绍就到此结束了,呵呵,代码着实有点多,大家想吐槽的尽管槽吧。

最后,请注意以下几点事项

1. 该锁屏通过控制系统音乐模块后台服务MediaPlaybackService来实现音乐播放的控制,但是由于系统音乐模块后台服务默认是禁止跨进程启动它的,所以本锁屏是无法跨进去启动该服务的;

2. 正是因为很多手机音乐模块的后台服务默认是禁止跨进程调用的,所以代码中我注释了很多涉及到调用音乐会引起程序异常的代码(新的代码下载地址链接中的代码已纠正);不然运行程序会有问题;

3. 不过,如果自己的手机有root权限,可以去音乐模块的AndroidManifest.xml文件里修改服务类的exported属性,然后编译音乐模块再push入手机,这样就可以做到控制系统音乐应用模块的后天服务音乐播放控制了。
4. 当然也可以自己编写一个类似音乐模块中的后台服务类控制来控制手机中的音频播放


更多相关文章

  1. Android实现桌面图标通知数量角标
  2. 在两台设备上播放一首音乐
  3. android MPChart图标使用详解
  4. Eclipse工程图标左下角报错

随机推荐

  1. Android磁盘管理-系统源码分析(1)
  2. Android 系统概要 ——《第一行代码 Andr
  3. Android 图形系统剖析
  4. Android数据加密之MD5加密
  5. Android Trick 7: 把TextView中的文字添
  6. Android中RelativeLayout各个属性 androi
  7. 最全最好用的Android(安卓)Studio插件整
  8. Android下SQLite数据库编程学习系列之二-
  9. Android中的MVC、MVP、MVVM
  10. 用PHP编写Android应用程序