《精通Android 5 多媒体开发》——第22章,第22.3节开发一个屏保程序

本节书摘来自异步社区《精通Android 5 多媒体开发》一书中的第22章,第22.3节开发一个屏保程序,作者 王石磊,更多章节内容可以访问云栖社区“异步社区”公众号查看

22.3 开发一个屏保程序
精通Android 5 多媒体开发
了解了在Android系统中开发屏保程序的基本原理后,在本节的内容中,将通过一个具体实例的实现流程,来详细讲解开发Android屏保程序的基本流程。本实例的源代码保存在“daima22pingbao”中,下面开始讲解本实例的具体实现流程。

22.3.1 准备素材图片
在本实例中,设置屏保程序轮换显示5幅图片,图片的大小是320×480。本实例的素材图片保存在“resdrawable”目录下,效果如图22-1所示。


《精通Android 5 多媒体开发》——第22章,第22.3节开发一个屏保程序

22.3.2 编写布局文件
本实例的布局文件是main.xml,在里面分别插入了一个ImageView控件、一个TextView和一个EditText,主要代码如下所示。

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
 xmlns:android="http://schemas.android.com/apk/res/android"
 android:background="@drawable/white"
 android:orientation="vertical"
 android:layout_width="fill_parent"
 android:layout_height="fill_parent"
 >
 <ImageView
  android:id="@+id/myImageView1" 
  android:layout_width="wrap_content" 
  android:layout_height="wrap_content"
  android:scaleType="fitCenter" 
  android:layout_gravity="center" />
 <TextView
  android:id="@+id/myTextView1"
  android:layout_width="fill_parent" 
  android:layout_height="wrap_content"
  android:textColor="@drawable/blue"
  android:visible="true"
  android:text="@string/str_set_pwd"/>
 <EditText
  android:id="@+id/myEditText1"
  android:layout_width="wrap_content"
  android:layout_height="wrap_content"
  android:text=""
 />
</LinearLayout>

22.3.3 编写主程序文件
本实例的主程序文件是example.java,其具体实现流程如下所示。

(1)先引入相关class类,然后设置LayoutInflater对象作为新建的AlertDialog,具体代码如下所示:

package irdc.example;

import irdc.example.R;

import java.util.Date;

import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Matrix;
import android.graphics.drawable.BitmapDrawable;
import android.os.Bundle;
import android.os.Handler;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;

public class example extends Activity
{
 private TextView mTextView01;
 private ImageView mImageView01;

 /* LayoutInflater对象作为新建AlertDialog之用 */
 private LayoutInflater mInflater01;
(2)定义mView01,用于输入解锁的View。通过menu选项identifier,用以识别对应的事件,具体代码如下所示。

/* 输入解锁的View */
private View mView01;
private EditText mEditText01,mEditText02;

/* menu选项identifier,用以识别事件 */
static final private int MENU_ABOUT = Menu.FIRST;
static final private int MENU_EXIT = Menu.FIRST+1;
private Handler mHandler01 = new Handler();
private Handler mHandler02 = new Handler();
private Handler mHandler03 = new Handler();
private Handler mHandler04 = new Handler();
(3)分别定义控制User静止与否的Counter,控制FadeIn与Fade Out的Counter,控制循序替换背景图ID的Counter,具体代码如下所示。

/* 控制User静止与否的Counter */
private int intCounter1, intCounter2;
/* 控制FadeIn与Fade Out的Counter */
private int intCounter3, intCounter4;
/* 控制循序替换背景图ID的Counter */
private int intDrawable=0;
(4)设置timePeriod,设置当静止超过<em>n</em>秒将自动进入屏幕保护,具体代码如下所示。

/* 上一次User有动作的Time Stamp */
private Date lastUpdateTime;
/* 计算User共几秒没有动作 */
private long timePeriod;
/* 静止超过n秒将自动进入屏幕保护 */
private float fHoldStillSecond = (float) 5;
private boolean bIfRunScreenSaver;
private boolean bFadeFlagOut, bFadeFlagIn = false;
private long intervalScreenSaver = 1000;
private long intervalKeypadeSaver = 1000;
private long intervalFade = 100;
private int screenWidth, screenHeight;
(5)设置每5秒置换一次图片,并设置使用Screen Saver保存需要用到的背景图,具体代码如下所示。

/* 每n秒置换图片 */
private int intSecondsToChange = 5;

/* 设置Screen Saver需要用到的背景图 */
private static int[] screenDrawable = new int[]
{
 R.drawable.pingbao1,
 R.drawable.pingbao 2,
 R.drawable.pingbao 3,
 R.drawable.pingbao 4,
 R.drawable.pingbao 5
};
(6)设置在setContentView之前调用全屏幕显示,通过lastUpdateTime初始取得User用户触碰手机的时间,并用recoverOriginalLayout()来初始化Layout屏幕上的Widget可见性,具体代码如下所示。

@Override
public void onCreate(Bundle savedInstanceState)
{
 super.onCreate(savedInstanceState);

 /* 必须在setContentView之前调用全屏幕显示 */
 requestWindowFeature(Window.FEATURE_NO_TITLE);
 getWindow().setFlags
 (
  WindowManager.LayoutParams.FLAG_FULLSCREEN,
  WindowManager.LayoutParams.FLAG_FULLSCREEN
 );
 setContentView(R.layout.main);

 /* onCreate all Widget */
 mTextView01 = (TextView)findViewById(R.id.myTextView1);
 mImageView01 = (ImageView)findViewById(R.id.myImageView1);
 mEditText01 = (EditText)findViewById(R.id.myEditText1);

 /* 初始取得User触碰手机的时间 */
 lastUpdateTime = new Date(System.currentTimeMillis());

 /* 初始化Layout上的Widget可见性 */
 recoverOriginalLayout();
}
(7)设置Menu群组ID,然后通过menu.add创建具有SubMenu的Menu,最后创建退出Menu,具体代码如下所示。

@Override
public boolean onCreateOptionsMenu(Menu menu)
{
 // TODO Auto-generated method stub

 /* Menu群组ID */
 int idGroup1 = 0;

 /* The order position of the item */
 int orderMenuItem1 = Menu.NONE;
 int orderMenuItem2 = Menu.NONE+1;

 /* 创建具有SubMenu的Menu */
 menu.add
 (
  idGroup1, MENU_ABOUT, orderMenuItem1, R.string.app_about
 );
 /* 创建退出Menu */

 menu.add(idGroup1, MENU_EXIT, orderMenuItem2, R.string.str_exit);
 menu.setGroupCheckable(idGroup1, true, true);

 return super.onCreateOptionsMenu(menu);
}
(8)根据用户选择的Menu,显示对应的AlertDialog提示框,具体代码如下所示。

@Override
public boolean onOptionsItemSelected(MenuItem item)
{
 // TODO Auto-generated method stub
 switch(item.getItemId())
 {
  case (MENU_ABOUT):
   new AlertDialog.Builder
   (
    example.this
   ).setTitle(R.string.app_about).setIcon
   (
    R.drawable.hippo
   ).setMessage
   (
    R.string.app_about_msg
   ).setPositiveButton(R.string.str_ok,
   new DialogInterface.OnClickListener()
   {
    public void onClick
    (DialogInterface dialoginterface, int i)
    {
    }
   }).show();
   break;
  case (MENU_EXIT):
   /* 离开程序 */
   finish();
   break;
 }
 return super.onOptionsItemSelected(item);
}
(9)用mTasks01监控User没有动作的运行线程,通过timePeriod计算User静止不动的时间间距,如果静止不懂查过设置的5秒,则运行对应的线程,具体代码如下所示。

/* 监控User没有动作的运行线程 */
private Runnable mTasks01 = new Runnable() 
{
 public void run() 
 {
  intCounter1++;
  Date timeNow = new Date(System.currentTimeMillis());

  /* 计算User静止不动的时间间距 */
  timePeriod =
  (long)timeNow.getTime() - (long)lastUpdateTime.getTime();

  float timePeriodSecond = ((float)timePeriod/1000);

  /* 如果超过时间静止不动 */
  if(timePeriodSecond>fHoldStillSecond)
  {
   /* 静止超过时间第一次的标记 */
   if(bIfRunScreenSaver==false)
   {
    /* 启动运行线程2 */
    mHandler02.postDelayed(mTasks02, intervalScreenSaver);

    /* Fade Out*/
    if(intCounter1%(intSecondsToChange)==0)
    {
     bFadeFlagOut=true;
     mHandler03.postDelayed(mTasks03, intervalFade);
    }
    else
    {
     /* 在Fade Out后立即Fade In */
     if(bFadeFlagOut==true)
     {
      bFadeFlagIn=true;
      mHandler04.postDelayed(mTasks04, intervalFade);
     }
     else
     {
      bFadeFlagIn=false;
      intCounter4 = 0;
      mHandler04.removeCallbacks(mTasks04);
     }
     intCounter3 = 0;
     bFadeFlagOut = false;
    }
    bIfRunScreenSaver = true;
   }
   else
   {
    /* screen saver 正在运行中 */

    /* Fade Out*/
    if(intCounter1%(intSecondsToChange)==0)
    {
     bFadeFlagOut=true;
     mHandler03.postDelayed(mTasks03, intervalFade);
    }
    else
    {
     /* 在Fade Out后立即Fade In */
     if(bFadeFlagOut==true)
     {
      bFadeFlagIn=true;
      mHandler04.postDelayed(mTasks04, intervalFade);
     }
     else
     {
      bFadeFlagIn=false;
      intCounter4 = 0;
      mHandler04.removeCallbacks(mTasks04);
     }
     intCounter3 = 0;
     bFadeFlagOut=false;
    }
   }
  }
  else
  {
   /* 当User没有动作的间距未超过时间 */
   bIfRunScreenSaver = false;
   /* 恢复原来的Layout Visible*/
   recoverOriginalLayout();
  }

  /* 以LogCat监看User静止不动的时间间距 */
  Log.i
  (
   "HIPPO",
   "Counter1:"+Integer.toString(intCounter1)+
   "/"+
   Float.toString(timePeriodSecond));

  /* 反复运行运行线程1 */
  mHandler01.postDelayed(mTasks01, intervalKeypadeSaver);
 } 
};
(10)定义mTasks02,设置每1秒运行一次屏保程序,并隐藏原有Layout上面的Widget,并调用ScreenSaver()加载图片,即轮换显示预设的5幅图片,具体代码如下所示。

/* Screen Saver Runnable */
private Runnable mTasks02 = new Runnable() 
{
 public void run() 
 {
  if(bIfRunScreenSaver==true)
  {
   intCounter2++;

   hideOriginalLayout();
   showScreenSaver();

   //Log.i("HIPPO", "Counter2:"+Integer.toString(intCounter2));
   mHandler02.postDelayed(mTasks02, intervalScreenSaver);
  }
  else
  {
   mHandler02.removeCallbacks(mTasks02);
  }
 } 
};
(11)定义mTasks03,通过setAlpha设置ImageView的透明度渐暗下去,具体代码如下所示。

/* Fade Out特效Runnable */
private Runnable mTasks03 = new Runnable() 
{
 public void run() 
 {
  if(bIfRunScreenSaver==true && bFadeFlagOut==true)
  {
   intCounter3++;

   /* 设置ImageView的透明度渐暗下去 */
   mImageView01.setAlpha(255-intCounter3*28);

   Log.i("HIPPO", "Fade out:"+Integer.toString(intCounter3));
   mHandler03.postDelayed(mTasks03, intervalFade);
  }
  else
  {
   mHandler03.removeCallbacks(mTasks03);
  }
 } 
};
(12)定义mTasks03,通过setAlpha设置设置ImageView的透明度渐亮起来,具体代码如下所示。

/* Fade In特效Runnable */
private Runnable mTasks04 = new Runnable() 
{
 public void run() 
 {
  if(bIfRunScreenSaver==true && bFadeFlagIn==true)
  {
   intCounter4++;

   /* 设置ImageView的透明度渐亮起来 */
   mImageView01.setAlpha(intCounter4*28);

   mHandler04.postDelayed(mTasks04, intervalFade);
   Log.i("HIPPO", "Fade In:"+Integer.toString(intCounter4));
  }
  else
  {
   mHandler04.removeCallbacks(mTasks04);
  }
 } 
};
(13)先定义recoverOriginalLayout()方法,用于恢复原有的Layout可视性;然后定义hideOriginalLayout()方法,用于隐藏原有应用程序里的布局配置组件,具体代码如下所示。

/* 恢复原有的Layout可视性 */
private void recoverOriginalLayout()
{
 mTextView01.setVisibility(View.VISIBLE);
 mEditText01.setVisibility(View.VISIBLE);
 mImageView01.setVisibility(View.GONE);
}
/* 隐藏原有应用程序里的布局配置组件 */
private void hideOriginalLayout()
{
 /* 将欲隐藏的Widget写在此 */
 mTextView01.setVisibility(View.INVISIBLE);
 mEditText01.setVisibility(View.INVISIBLE);
}

/* 开始ScreenSaver */
private void showScreenSaver()
{
 /* 屏幕保护之后要做的事件写在此*/

 if(intDrawable>4)
 {
  intDrawable = 0;
 }

 DisplayMetrics dm=new DisplayMetrics();
 getWindowManager().getDefaultDisplay().getMetrics(dm);
 screenWidth = dm.widthPixels;
 screenHeight = dm.heightPixels;
 Bitmap bmp=BitmapFactory.decodeResource(getResources(),screenDrawable[intDrawable]);
(14)通过Matrix设置比例,使用Matrix.postScale设置维度ReSize,通过resizedBitmap对象设置图文件至屏幕分辨率,新建Drawable对象myNewBitmapDrawable用于放大图文件至全屏幕,通过setVisibility(View.VISIBLE)使ImageView可见,具体代码如下所示。

 /* Matrix比例 */ 
 float scaleWidth = ((float) screenWidth) / bmp.getWidth();
 float scaleHeight = ((float) screenHeight) / bmp.getHeight() ;

 Matrix matrix = new Matrix(); 
 /* 使用Matrix.postScale设置维度ReSize */ 
 matrix.postScale(scaleWidth, scaleHeight);

 /* ReSize图文件至屏幕分辨率 */
 Bitmap resizedBitmap = Bitmap.createBitmap
 (
  bmp,0,0,bmp.getWidth(),bmp.getHeight(),matrix,true
 );

 /* 新建Drawable放大图文件至全屏幕 */
 BitmapDrawable myNewBitmapDrawable = 
   new BitmapDrawable(resizedBitmap); 
 mImageView01.setImageDrawable(myNewBitmapDrawable);

 /* 使ImageView可见 */
 mImageView01.setVisibility(View.VISIBLE);

 /* 每间隔设置秒数置换图片ID,于下一个runnable2才会生效 */
 if(intCounter2%intSecondsToChange==0)
 {
  intDrawable++;
 }
}
(15)定义方法onUserWakeUpEvent(),实现解锁和加密处理,具体代码如下所示。

public void onUserWakeUpEvent()
{
 if(bIfRunScreenSaver==true)
 {
  try
  {
   /* LayoutInflater.from取得此Activity的context */
   mInflater01 = LayoutInflater.from(example.this);

   /* 创建解锁密码使用View的Layout */
   mView01 = mInflater01.inflate(R.layout.securescreen, null);

   /* 于对话框中唯一的EditText等待输入解锁密码 */
   mEditText02 =
   (EditText) mView01.findViewById(R.id.myEditText2);

   /* 创建AlertDialog */
   new AlertDialog.Builder(this)
   .setView(mView01)
   .setPositiveButton("OK",
   new DialogInterface.OnClickListener()
   {
    public void onClick(DialogInterface dialog, int whichButton)
    {
     /* 比较输入的密码与原Activity里的设置是否相符 */
     if(mEditText01.getText().toString().equals
      (mEditText02.getText().toString()))
     {
      /* 当密码正确才解锁屏幕保护装置 */
      resetScreenSaverListener();
     }
    }
   }).show();
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }
  }
 }
(16)定义方法updateUserActionTime(),用于统计用户单击键盘或屏幕的时间间隔,具体实现流程如下所示。

第一步:取得单击按键事件时的系统Time Millis。
第二步:重新计算单击按键距离上一次静止的时间间距。
方法updateUserActionTime()的具体代码如下所示。

public void updateUserActionTime()
{
 /* 取得单击按键事件时的系统Time Millis */
 Date timeNow = new Date(System.currentTimeMillis());

 /* 重新计算单击按键距离上一次静止的时间间距 */
 timePeriod =
 (long)timeNow.getTime() - (long)lastUpdateTime.getTime();
 lastUpdateTime.setTime(timeNow.getTime());
}
(17)定义方法resetScreenSaverListener()来重新设置屏幕,具体实现流程如下所示。

第一步:删除现有的Runnable。
第二步:取得单击按键事件时的系统Time Millis。
第三步:重新计算单击按键距离上一次静止的时间间距。
第四步:通过bIfRunScreenSaver取消屏保。
第五步:恢复原来的Layout Visible。
方法resetScreenSaverListener()的具体代码如下所示。

public void resetScreenSaverListener()
{
 /* 删除现有的Runnable */
 mHandler01.removeCallbacks(mTasks01);
 mHandler02.removeCallbacks(mTasks02);

 /* 取得单击按键事件时的系统Time Millis */
 Date timeNow = new Date(System.currentTimeMillis());
 /* 重新计算单击按键距离上一次静止的时间间距 */
 timePeriod =
 (long)timeNow.getTime() - (long)lastUpdateTime.getTime();
 lastUpdateTime.setTime(timeNow.getTime());

 /* for Runnable2,取消屏幕保护 */
 bIfRunScreenSaver = false;

 /* 重置Runnable1与Runnable1的Counter */
 intCounter1 = 0;
 intCounter2 = 0;

 /* 恢复原来的Layout Visible*/
 recoverOriginalLayout();

 /* 重置postDelayed()的Runnable */
 mHandler01.postDelayed(mTasks01, intervalKeypadeSaver);
}
(18)定义onKeyDown(int keyCode, KeyEvent event),用于监听用户的触摸单击事件,具体代码如下所示。

@Override
public boolean onKeyDown(int keyCode, KeyEvent event)
{
 // TODO Auto-generated method stub
 if(bIfRunScreenSaver==true && keyCode!=4)
 {
  /* 当屏幕保护程序正在运行中,触动解除屏幕保护程序 */
  onUserWakeUpEvent();
 }
 else
 {
  /* 更新User未触动手机的时间戳记 */
  updateUserActionTime();
 }
 return super.onKeyDown(keyCode, event);
}
@Override
public boolean onTouchEvent(MotionEvent event)
{
 // TODO Auto-generated method stub
 if(bIfRunScreenSaver==true)
 {
  /* 当屏幕保护程序正在运行中,触动解除屏幕保护程序 */
  onUserWakeUpEvent();
 }
 else
 {
  /* 更新User未触动手机的时间戳记 */
  updateUserActionTime();
 }
 return super.onTouchEvent(event);
}

@Override
protected void onResume()
{
 // TODO Auto-generated method stub
 mHandler01.postDelayed(mTasks01, intervalKeypadeSaver);
 super.onResume();
}
(19)定义方法onPause()来删除正在运行中的运行线程mHandler01、mHandler02、mHandler03和mHandler01,具体代码如下所示。

 @Override
 protected void onPause()
 {
  // TODO Auto-generated method stub

  try
  {
   /* 删除运行中的运行线程 */
   mHandler01.removeCallbacks(mTasks01);
   mHandler02.removeCallbacks(mTasks02);
   mHandler03.removeCallbacks(mTasks03);
   mHandler04.removeCallbacks(mTasks04);
  }
  catch(Exception e)
  {
   e.printStackTrace();
  }
  super.onPause();
 }
}

至此,整个实例介绍完毕。执行后如果超过5秒不动键盘或屏幕,则会进入屏保状态,如图22-2所示。可以设置屏保密码,当输入正确的密码后才能解除屏保,如图22-3所示。

《精通Android 5 多媒体开发》——第22章,第22.3节开发一个屏保程序

在本实例的实现代码中,声明的4个Runnable是整个程序的重点,这4个Runnable的具体说明如下所示。

mTasks01:设置每1秒检查一次timePeriod,并监视是否超过5秒未触发。超过5秒则将blRunScreenSaver这个flag更改为true,并启动mTasks02。
mTasks02:设置每1秒运行一次屏保程序,并隐藏原有Layout上面的Widget,并调用ScreenSaver()加载图片,即轮换显示预设的5幅图片。
mTasks03:是Fade-Out特效使用的Runable,每0.1秒运行一个scale。
mTasks04:是Fade-In特效使用的Runable,每0.1秒运行一个scale。

上一篇:unix domain socket进程凭据


下一篇:车辆管理系统之编码过程总结(十一)