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

    xiaoxiao2024-01-04  141

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

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

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

    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所示。

    在本实例的实现代码中,声明的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。

    相关资源:敏捷开发V1.0.pptx
    最新回复(0)