我的Android进阶之旅------>Android中MediaButtonReceiver广播监听器的机制分析

今天看公司的一段关于MediaButtonReceiver的代码看的比较混乱,幸好看了下面的这篇文章,才能茅塞顿开的理解好代码。在此转载下来,以备以后理解,希望都到这篇文章的人也能够有所帮助。

本文转载于:http://blog.csdn.net/qinjuning/article/details/6938436

在Android中并没有定义MediaButtonReceive这个广播类,MediaButtonReceive只是作为一种通俗的命名方式来响应

   插入耳机后,点击耳机上的按钮(名称:MediaButton)接受该广播事件的类。所有该MEDIA_BUTTON的按下我们就简称

   为MEDIA_BUTTON广播吧。

           

           顾名思义:它显然是一个广播接收器类(BroadbcastReceiver),那么它就具备了BroadbcastReceiver类的使用方式,

   但是,因为它需要通过AudioManager对象注册,所以它有着自己的独特之处(否则我也不会单独拿出来分析,- -),后面我们

   会慢慢的讲解。

 

        点击MEDIA_BUTTON发送的Intent Action 为:

                   

 ACTION_MEDIA_BUTTON  ="android.intent.action.MEDIA_BUTTON"

        Intent 附加值为(Extra)点击MEDIA_BUTTON的按键码 :    

                       

                       //获得KeyEvent对象
                        KeyEvent keyEvent = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
                        //获得Action
                        String intentAction = intent.getAction() ;

AudioManager对象注册MEDIA_BUTTON广播的方法原型为:

public voidregisterMediaButtonEventReceiver(ComponentNameeventReceiver)

          Register a component to be the sole receiverof MEDIA_BUTTON intents

          Parameters:                   

                eventReceiver  : identifier of a BroadcastReceiver that will receive the media button intent. This broadcast receiver

                                   must be declared in the application manifest.

   从注释可知以下两点:

      1、 在AudioManager对象注册一个MediaoButtonRecevie,使它成为MEDIA_BUTTON的唯一接收器(这很重要,

          我们会放在后面讲解)   也就是说只有我能收到,其他的都收不到这个广播了,否则的话大家都收到会照成一定的混乱;

      2、   该广播必须在AndroidManifest.xml文件中进行声明,否则就监听不到该MEDIA_BUTTON广播了。

 

下面我们就简单的写一个MediaButtonReceiver类,并且在AndroidManifest.xml定义

 

1、  自定义的MediaButtonReceiver 广播类

 

package com.qin.mediabutton;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.KeyEvent;

public class MediaButtonReceiver extends BroadcastReceiver {
	private static String TAG = "MediaButtonReceiver";
	@Override
	public void onReceive(Context context, Intent intent) {
		// 获得Action
		String intentAction = intent.getAction();
		// 获得KeyEvent对象
		KeyEvent keyEvent = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);

		Log.i(TAG, "Action ---->" + intentAction + "  KeyEvent----->"+ keyEvent.toString());

		if (Intent.ACTION_MEDIA_BUTTON.equals(intentAction)) {
			// 获得按键字节码
			int keyCode = keyEvent.getKeyCode();
			// 按下 / 松开 按钮
			int keyAction = keyEvent.getAction();
			// 获得事件的时间
			long downtime = keyEvent.getEventTime();

			// 获取按键码 keyCode
			StringBuilder sb = new StringBuilder();
			// 这些都是可能的按键码 , 打印出来用户按下的键
			if (KeyEvent.KEYCODE_MEDIA_NEXT == keyCode) {
				sb.append("KEYCODE_MEDIA_NEXT");
			}
			// 说明:当我们按下MEDIA_BUTTON中间按钮时,实际出发的是 KEYCODE_HEADSETHOOK 而不是
			// KEYCODE_MEDIA_PLAY_PAUSE
			if (KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE == keyCode) {
				sb.append("KEYCODE_MEDIA_PLAY_PAUSE");
			}
			if (KeyEvent.KEYCODE_HEADSETHOOK == keyCode) {
				sb.append("KEYCODE_HEADSETHOOK");
			}
			if (KeyEvent.KEYCODE_MEDIA_PREVIOUS == keyCode) {
				sb.append("KEYCODE_MEDIA_PREVIOUS");
			}
			if (KeyEvent.KEYCODE_MEDIA_STOP == keyCode) {
				sb.append("KEYCODE_MEDIA_STOP");
			}
			// 输出点击的按键码
			Log.i(TAG, sb.toString());
		}
	}
}

2、  在AndroidManifest.xml声明我们定义的广播类。

         <receiver android:name="MediaButtonReceiver">
          <intent-filter >
                <action android:name="android.intent.action.MEDIA_BUTTON"></action>
          </intent-filter>
        </receiver>

         在模拟器上,我们可以手动构造MEDA_BUTTON的广播,并且将它发送出去(后面会介绍)。

         如果有真机测试的话,按下MEDIA_BUTTON是可以接受到MEDIA_BUTTON广播的,如果没有接受到,请关闭所有应用

   程序,在观察效果。

 

  继续我们的下一步分析:

         前面我们说明通过registerMediaButtonEventReceiver(eventReceiver)方法注册时使它成为MEDIA_BUTTON的

     唯一 接收器。这个唯一是怎么实现的呢?
我们在源码中,一步步追本溯源,相信一定可以找到答案,知道这“唯一“是

    怎么来的。

 

第一步、   为AudioManager注册一个MediaButtonReceiver() ;

[java] view plaincopyprint?

  1. //获得AudioManager对象  
  2. AudioManager mAudioManager =(AudioManager)getSystemService(Context.AUDIO_SERVICE);     
  3. //构造一个ComponentName,指向MediaoButtonReceiver类  
  4. //下面为了叙述方便,我直接使用ComponentName类来替代MediaoButtonReceiver类  
  5. ComponentName  mbCN = new ComponentName(getPackageName(),MediaButtonReceiver.class.getName());  
  6. //注册一个MedioButtonReceiver广播监听  
  7. mAudioManager.registerMediaButtonEventReceiver(mbCN);  
  8. //取消注册的方法  
  9. mAudioManager.unregisterMediaButtonEventReceiver(mbCN);  

         

        MediaButtonReceiver就是我们用来接收MEDIA_BUTTON的广播类,下面为了叙述方便和直观上得体验,我直接使用

    ComponentName类来替代真正的MediaoButtonReceiver广播类。

 

   说明 接下来分析的文件路径全部在   frameworks/base/media/java/android/media/ 下

 

 第二步、 进入AudioManager.java进行查看 ,发现如下方法:

 

[java] view plaincopyprint?

  1. //注册的方法为:  
  2. public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {  
  3.       //TODO enforce the rule about the receiver being declared in the manifest  
  4.       //我们继续查看getService()方法,看看IAudioService类到底是什么?  
  5.        IAudioService service = getService();  
  6.       try {  
  7.         //只是简单的调用了service的方法来完成注册,继续跟踪  
  8.           service.registerMediaButtonEventReceiver(eventReceiver);  
  9.        
  10.       } catch (RemoteException e) {  
  11.           Log.e(TAG, "Dead object in registerMediaButtonEventReceiver"+e);  
  12.       }  
  13. }  
  14. //取消注册的方法为  
  15. public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {  
  16.       IAudioService service = getService();    
  17.       try {  
  18.         //只是简单的调用了service的方法来取消注册,,继续跟踪  
  19.           service.unregisterMediaButtonEventReceiver(eventReceiver);  
  20.       } catch (RemoteException e) {  
  21.           Log.e(TAG, "Dead object in unregisterMediaButtonEventReceiver"+e);  
  22.       }  
  23.   }  

  找到getService()方法,其实现为:

	  //看看它到底是什么
	  private static IAudioService getService()
	    {
                // 单例模式,大家懂得
	        if (sService != null) {
	            return sService;
	       }
	       //了解Binder机制 以及AIDL文件的使用,就明白了这不过是通过AIDL文件定义的Java层Binder机制
	        //b为IBinder基类接口
	        IBinder b = ServiceManager.getService(Context.AUDIO_SERVICE);
	       //强制转换后,sService不过是一个客户端对象,IAudioService就是aidl文件定义的接口了
	        sService = IAudioService.Stub.asInterface(b);
	       return sService;
	    }
	//sService对象的声明
          private static IAudioService sService; //单例模式,不足为奇了

         我们知道了AudiaoManager只不过是一个傀儡,所有的方法都是由IAudioService 对象去实现的,通过它的构造方式,

  可以知道它应该是有AIDL文件形成的Binder机制, sService只是客户端对象,那么它的服务端对象在什么地方呢?

  也就是继承了IAudioService.Stub桩的类。

 

第三步、接下来我们需要找到该IAudioService.aidl文件和真正的服务端对象  

 

   IAudioService.aidl定义如下:

    package android.media;

    import android.content.ComponentName;
    import android.media.IAudioFocusDispatcher;
    /**
     * {@hide}
     */
    interface IAudioService {

        void adjustVolume(int direction, int flags);
        void adjustSuggestedStreamVolume(int direction, int suggestedStreamType, int flags);
        void adjustStreamVolume(int streamType, int direction, int flags);
        void setStreamVolume(int streamType, int index, int flags);
        void setStreamSolo(int streamType, boolean state, IBinder cb);
        void setStreamMute(int streamType, boolean state, IBinder cb);
        int getStreamVolume(int streamType);
        int getStreamMaxVolume(int streamType);
        void setRingerMode(int ringerMode);
        int getRingerMode();
        void setVibrateSetting(int vibrateType, int vibrateSetting);
        int getVibrateSetting(int vibrateType);
        boolean shouldVibrate(int vibrateType);
        void setMode(int mode, IBinder cb);
        int getMode();
        oneway void playSoundEffect(int effectType);
        oneway void playSoundEffectVolume(int effectType, float volume);
        boolean loadSoundEffects();
        oneway void unloadSoundEffects();
        oneway void reloadAudioSettings();
        void setSpeakerphoneOn(boolean on);
        boolean isSpeakerphoneOn();
        void setBluetoothScoOn(boolean on);
        boolean isBluetoothScoOn();
        int requestAudioFocus(int mainStreamType, int durationHint, IBinder cb, IAudioFocusDispatcher l, String clientId);
        int abandonAudioFocus(IAudioFocusDispatcher l, String clientId);
        void unregisterAudioFocusClient(String clientId);
        void registerMediaButtonEventReceiver(in ComponentName eventReceiver);   //这个方法是我们需要弄懂的
        void unregisterMediaButtonEventReceiver(in ComponentName eventReceiver);  //这个方法也是是我们需要弄懂的
        void startBluetoothSco(IBinder cb);
        void stopBluetoothSco(IBinder cb);
    }

   

       真正的服务端对象就是继承了 IAudioService.Stub 桩的类,AudioService就是该服务端对象,其实AudioManager的

  所有操作都是由AudioService来实现的,它才是真正的老大。

第五步、   AudioService.java

    //AudioService类
    public class AudioService extends IAudioService.Stub {
        //.....
    	//仅仅列出我们需要的方法
    	//这儿才是真正的注册MediaButtonReceiver的方法
        public void registerMediaButtonEventReceiver(ComponentName eventReceiver) {
            Log.i(TAG, "  Remote Control   registerMediaButtonEventReceiver() for " + eventReceiver);

            synchronized(mRCStack) {
              //调用它去实现注册ComponentName
                pushMediaButtonReceiver(eventReceiver);
            }
        }

       //在查看pushMediaButtonReceiver()方法  先理解一下两个知识点,很重要的。
        //RemoteControlStackEntry内部类不过是对ComponentName类的进一步封装(感觉没必要在加一层进行封装了)
        private static class RemoteControlStackEntry {
            public ComponentName mReceiverComponent;// 属性
              //TODO implement registration expiration?
            //public int mRegistrationTime;

            public RemoteControlStackEntry() {
            }

            public RemoteControlStackEntry(ComponentName r) {
                mReceiverComponent = r;// 构造函数赋值给mReceiverComponent对象
            }
        }

       //采用了栈存储结构(先进后出)来保存所有RemoteControlStackEntry对象,也就是保存了ComponentName对象
        private Stack<RemoteControlStackEntry> mRCStack = new Stack<RemoteControlStackEntry>();

       //回到pushMediaButtonReceiver()查看,这下该拨开云雾了吧,继续学习
       private void pushMediaButtonReceiver(ComponentName newReceiver) {
         // already at top of stack?
        	//采用了一个栈(前面我们介绍的知识点)来保存所有注册的ComponentName对象
        	//如果当前栈不为空并且栈顶的对象与新注册的ComponentName对象一样,不做任何事,直接返回
            if (!mRCStack.empty() && mRCStack.peek().mReceiverComponent.equals(newReceiver)) {
                return;
            }
            //获得mRCStack栈的迭代器
            Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
            //循环
            while(stackIterator.hasNext()) {
              RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
              //如果当前栈内保存该新注册的ComponentName对象,将它移除,跳出循环
                if(rcse.mReceiverComponent.equals(newReceiver)) {
                    mRCStack.remove(rcse);
                    break;
                }
            }
          //将新注册的ComponentName对象放入栈顶
            mRCStack.push(new RemoteControlStackEntry(newReceiver));
        }
    }

 

小结一下: 


         栈(mRCStack)维护了所有CompoentName对象,对每个CompoentName对象,保证它有且仅有一个,

     新注册的CompoentName对象永远处于栈顶    

 

 我们看下取消注册的方法:

    //我们看下取消注册的方法
    /** see AudioManager.unregisterMediaButtonEventReceiver(ComponentName eventReceiver) */
    public void unregisterMediaButtonEventReceiver(ComponentName eventReceiver) {
        Log.i(TAG, "  Remote Control   unregisterMediaButtonEventReceiver() for " + eventReceiver);

        synchronized(mRCStack) {
        	 //调用removeMediaButtonReceiver方法去实现
            removeMediaButtonReceiver(eventReceiver);
        }
    }

    private void removeMediaButtonReceiver(ComponentName newReceiver) {
        Iterator<RemoteControlStackEntry> stackIterator = mRCStack.iterator();
        while(stackIterator.hasNext()) {
        	 //获得mRCStack栈的迭代器
            RemoteControlStackEntry rcse = (RemoteControlStackEntry)stackIterator.next();
            //如果存在该对象,则移除,跳出循环
            if(rcse.mReceiverComponent.equals(newReceiver)) {
                mRCStack.remove(rcse);
                break;
            }
        }
    }
    

 

          通过对前面的学习,我们知道了AudioManager内部利用一个栈来管理包括加入和移除ComponentName对象,

    新的疑问来了?这个MEDIA_BUTTON广播是如何分发的呢 ?

 
         其实,AudioService.java文件中也存在这么一个MediaoButtonReceiver的广播类,它为系统广播接收器,即用来接收

  系统的MEDIA_BUTTON广播,当它接收到了这个MEDIA_BUTTON广播   ,它会对这个广播进行进一步处理,这个处理过程

   就是我们需要的弄清楚。

 

MediaButtonBroadcastReceiver 内部类如下:

 

    private class MediaButtonBroadcastReceiver extends BroadcastReceiver {
        @Override
        public void onReceive(Context context, Intent intent) {
        	//获得action ,系统MEDIA_BUTTON广播来了
            String action = intent.getAction();
            //action不正确 直接返回
            if (!Intent.ACTION_MEDIA_BUTTON.equals(action)) {
                return;
            }
          //获得KeyEvent对象
            KeyEvent event = (KeyEvent) intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);
            if (event != null) {
                // if in a call or ringing, do not break the current phone app behavior
                // TODO modify this to let the phone app specifically get the RC focus
                //      add modify the phone app to take advantage of the new API
            	//来电或通话中,不做处理直接返回
                if ((getMode() == AudioSystem.MODE_IN_CALL) ||(getMode() == AudioSystem.MODE_RINGTONE)) {
                    return;
                }
                synchronized(mRCStack) {
                	//栈不为空
                    if (!mRCStack.empty()) {
                        // create a new intent specifically aimed at the current registered listener
                        //构造一个Intent对象 ,并且赋予Action和KeyEvent
                        Intent targetedIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
                        targetedIntent.putExtras(intent.getExtras());
                        //指定该处理Intent的对象为栈顶ComponentName对象的广播类
                            targetedIntent.setComponent(mRCStack.peek().mReceiverComponent);
                        // trap the current broadcast
                        // 终止系统广播
                             abortBroadcast();
                        //Log.v(TAG, " Sending intent" + targetedIntent);
                        //手动发送该广播至目标对象去处理,该广播不再是系统发送的了
                            context.sendBroadcast(targetedIntent, null);
                    }
                    //假设栈为空,那么所有定义在AndroidManifest.xml的监听MEDIA_BUTTON的广播都会处理,
                    //在此过程中如果有任何应用程注册了registerMediaButton 该广播也会立即终止
                }
            }
        }
    }

 

 总结一下MEDIA_BUTTON广播: 

    

         AudioManager也就是AudioService服务端对象内部会利用一个栈来管理所有ComponentName对象,所有对象有且仅有一个,

   新注册的ComponentName总是会位于栈顶。

 

         当系统发送MEDIA_BUTTON,系统MediaButtonBroadcastReceiver 监听到系统广播,它会做如下处理:

                 1、 如果栈为空,则所有注册了该Action的广播都会接受到,因为它是由系统发送的。
                 2、 如果栈不为空,那么只有栈顶的那个广播能接受到MEDIA_BUTTON的广播,手动发送了MEDIA_BUTTON

                      广播,并且指定了目标对象(栈顶对象)去处理该MEDIA_BUTTON 。

 下面分析一下KeyEvent对象里的KeyCode按键,可能的按键码有:

       1、KeyEvent.KEYCODE_MEDIA_NEXT
       2、KeyEvent.KEYCODE_HEADSETHOOK
       3、KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE(已废除,等同于KEYCODE_HEADSETHOOK)
       4、KeyEvent.KEYCODE_MEDIA_PREVIOUS
       5、KeyEvent.KEYCODE_MEDIA_STOP
   
    PS : 在我的真机测试中,按下MEDIA_BUTTON只有KEYCODE_HEADSETHOOK可以打印出来了。

 

下面给出一个小DEMO检验一下我们之前所做的一切,看看MEDIA_BUTTON是如何处理分发广播的。

 

   编写两个MediaButtonReceiver类用来监听MEDIA_BUTTON广播:

 

  1 、China_MBReceiver.java

package com.qin.mediabutton;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import android.view.KeyEvent;

public class China_MBReceiver extends BroadcastReceiver  {

	private static String TAG = "China_MBReceiver" ;
	@Override
	public void onReceive(Context context, Intent intent) {
		//获得Action
		String intentAction = intent.getAction() ;
		//获得KeyEvent对象
		KeyEvent keyEvent = (KeyEvent)intent.getParcelableExtra(Intent.EXTRA_KEY_EVENT);

		Log.i(TAG, "Action ---->"+intentAction + "  KeyEvent----->"+keyEvent.toString());

		if(Intent.ACTION_MEDIA_BUTTON.equals(intentAction)){
			//获得按键字节码
			int keyCode = keyEvent.getKeyCode() ;
			//按下 / 松开 按钮
			int keyAction = keyEvent.getAction() ;
			//获得事件的时间
			long downtime = keyEvent.getEventTime();

			//获取按键码 keyCode
			StringBuilder sb = new StringBuilder();
			//这些都是可能的按键码 , 打印出来用户按下的键
			if(KeyEvent.KEYCODE_MEDIA_NEXT == keyCode){
				sb.append("KEYCODE_MEDIA_NEXT");
			}
			//说明:当我们按下MEDIA_BUTTON中间按钮时,实际出发的是 KEYCODE_HEADSETHOOK 而不是 KEYCODE_MEDIA_PLAY_PAUSE
			if(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE ==keyCode){
				sb.append("KEYCODE_MEDIA_PLAY_PAUSE");
			}
			if(KeyEvent.KEYCODE_HEADSETHOOK == keyCode){
				sb.append("KEYCODE_HEADSETHOOK");
			}
			if(KeyEvent.KEYCODE_MEDIA_PREVIOUS ==keyCode){
				sb.append("KEYCODE_MEDIA_PREVIOUS");
			}
			if(KeyEvent.KEYCODE_MEDIA_STOP ==keyCode){
				sb.append("KEYCODE_MEDIA_STOP");
			}
			//输出点击的按键码
			Log.i(TAG, sb.toString());

		}

	}

}

 

   2 、England_MBReceiver.java同于China_MBRreceiver ,打印Log TAG= "England_MBReceiver"

   3、在AndroidManifest.xml文件定义:

<strong>  <receiver android:name=".China_MBReceiver">
          <intent-filter >
                <action android:name="android.intent.action.MEDIA_BUTTON"></action>
          </intent-filter>
        </receiver>

         <receiver android:name=".Enaland_MBReceiver">
          <intent-filter >
                <action android:name="android.intent.action.MEDIA_BUTTON"></action>
          </intent-filter>
        </receiver></strong>

4、MainActivity .java 我们通过手动构造一个MEDIA_BUTTON广播去查看我们的MediaButtonReceiver类的打印信息。

 

package com.qin.mediabutton;

import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.KeyEvent;

public class MainActivity extends Activity {
    /** Called when the activity is first created. */
    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        //由于在模拟器上测试,我们手动发送一个MEDIA_BUTTON的广播,有真机更好处理了
        Intent mbIntent = new Intent(Intent.ACTION_MEDIA_BUTTON);
        //构造一个KeyEvent对象
        KeyEvent keyEvent = new KeyEvent (KeyEvent.ACTION_DOWN,KeyEvent.KEYCODE_HEADSETHOOK) ;
        //作为附加值添加至mbIntent对象中
        mbIntent.putExtra(Intent.EXTRA_KEY_EVENT, keyEvent);

        //此时China_MBReceiver和England_MBReceiver都会接收到该广播
        sendBroadcast(mbIntent);

        AudioManager mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        //AudioManager注册一个MediaButton对象
        ComponentName chinaCN = new ComponentName(getPackageName(),China_MBReceiver.class.getName());
        //只有China_MBReceiver能够接收到了,它是出于栈顶的。
        //不过,在模拟上检测不到这个效果,因为这个广播是我们发送的,流程不是我们在上面介绍的。
        mAudioManager.registerMediaButtonEventReceiver(chinaCN);
       //sendBroadcast(mbIntent,null);
    }
   //当一个Activity/Service死去时,我们需要取消这个MediaoButtonReceiver的注册,如下
    protected void onDestroy(){
    	super.onDestroy() ;
        AudioManager mAudioManager = (AudioManager)getSystemService(Context.AUDIO_SERVICE);
        ComponentName chinaCN = new ComponentName(getPackageName(),China_MBReceiver.class.getName());
        //取消注册
        mAudioManager.unregisterMediaButtonEventReceiver(chinaCN);
    }
}

       值得注意的一点时,当我们为一个应用程序注册了MediaoButtonReceiver时,在程序离开时,我们需要取消

  MediaoButtonReceiver的注册,在onDestroy()调用unregisterMediaButtonEventReceiver()方法就OK,这样应用程序之间

  的交互就更具逻辑性了。

====================================================================================

  作者:欧阳鹏  欢迎转载,与人分享是进步的源泉!

  转载请保留原文地址:http://blog.csdn.net/ouyang_peng

===================================================================================

时间: 2024-10-18 07:41:39

我的Android进阶之旅------&gt;Android中MediaButtonReceiver广播监听器的机制分析的相关文章

我的Android进阶之旅------&amp;gt;Android利用温度传感器实现带动画效果的电子温度计

     要想实现带动画效果的电子温度计,需要以下几个知识点: 1.温度传感器相关知识. 2.ScaleAnimation动画相关知识,来进行水印刻度的缩放效果. 3.android:layout_weight属性的合理运用,关于android:layout_weight属性的讲解,可以参考:<我的Android进阶之旅------>关于android:layout_weight属性的一个面试题> 地址为:http://blog.csdn.net/ouyang_peng/article/

我的Android进阶之旅------&amp;gt;Android疯狂连连看游戏的实现之状态数据模型(三)

对于游戏玩家而言,游戏界面上看到的"元素"千变万化:但是对于游戏开发者而言,游戏界面上的元素在底层都是一些数据,不同数据所绘制的图片有所差异而已.因此建立游戏的状态数据模型是实现游戏逻辑的重要步骤. 1.定义数据模型 连连看的界面是一个NxM的"网格",每个网格上显示一张图片.而这个网格只需要一个二维数组来定义即可,而每个网格上所显示的图片,对于底层数据模型来说,不同的图片对于着不同的数值即可. 对于上图所示的数据模型,只要让数值为0的网格上不绘制图片,其他数值的网

我的Android进阶之旅------&amp;gt;Android疯狂连连看游戏的实现之开发游戏界面(二)

连连看的游戏界面十分简单,大致可以分为两个区域: 游戏主界面区 控制按钮和数据显示区 1.开发界面布局 本程序使用一个RelativeLayout作为整体的界面布局元素,界面布局上面是一个自定义组件,下面是一个水平排列的LinearLayout. 下面是本程序的布局文件:/res/layout/main.xml <?xml version="1.0" encoding="utf-8"?> <RelativeLayout xmlns:android=

我的Android进阶之旅------&amp;gt;Android疯狂连连看游戏的实现之加载界面图片和实现游戏Activity(四)

正如在<我的Android进阶之旅------>Android疯狂连连看游戏的实现之状态数据模型(三)>一文中看到的,在AbstractBoard的代码中,当程序需要创建N个Piece对象时,程序会直接调用ImageUtil的getPlayImages()方法去获取图片,该方法会随机从res/drawable目录中取得N张图片. 下面是res/drawable目录视图: 为了让getPlayImages()方法能随机从res/drawable目录中取得N张图片,具体实现分为以下几步: 通

我的Android进阶之旅------&amp;gt; Android为TextView组件中显示的文本添加背景色

通过上一篇文章 我的Android进阶之旅------> Android在TextView中显示图片方法 (地址:http://blog.csdn.net/ouyang_peng/article/details/46916963)      我们学会了在TextView中显示图片的方法,现在我们来学习如何为TextView组件中显示的文本添加背景色.要求完成的样子如图所示: 首先来学习使用BackgroundColorSpan对象设置文字背景色,代码如下: TextView textView=(

我的Android进阶之旅------&amp;gt;Android权限参考大全

访问登记属性 android.permission.ACCESS_CHECKIN_PROPERTIES ,读取或写入登记check-in数据库属性表的权限 获取错略位置 android.permission.ACCESS_COARSE_LOCATION,通过WiFi或移动基站的方式获取用户错略的经纬度信息,定位精度大概误差在30~1500米 获取精确位置 android.permission.ACCESS_FINE_LOCATION,通过GPS芯片接收卫星的定位信息,定位精度达10米以内 访问定

我的Android进阶之旅------&amp;gt;Android颜色值(#AARRGGBB)透明度百分比和十六进制对应关系以及计算方法

我的Android进阶之旅-->Android颜色值(RGB)所支持的四种常见形式 透明度百分比和十六进制对应关系表格 透明度 十六进制 100% FF 99% FC 98% FA 97% F7 96% F5 95% F2 94% F0 93% ED 92% EB 91% E8 90% E6 89% E3 88% E0 87% DE 86% DB 85% D9 84% D6 83% D4 82% D1 81% CF 80% CC 79% C9 78% C7 77% C4 76% C2 75% B

我的Android进阶之旅------&amp;gt;Android颜色值(RGB)所支持的四种常见形式

Android中颜色值是通过红(Red).绿(Green).蓝(Blue)三原色,以及一个透明度(Alpha)值来表示的,颜色值总是以井号(#)开头,接下来就是Alpha-Red-Green-Blue的形式.其中Alpha值可以省略,如果省略了Alpha的值,那么该颜色默认是完全不透明的. Android的颜色值支持常见的四种形式如下所示: #RGB:分别指定红.绿.蓝三原色的值(只支持0~f这16级颜色)来代表颜色. #ARGB:分别指定红.绿.蓝三原色的值(只支持0~f这16级颜色)及透明度

我的Android进阶之旅------&amp;gt;Android疯狂连连看游戏的实现之游戏效果预览(一)

今天看完了李刚老师的<疯狂Android讲义>一书中的第18章<疯狂连连看>,从而学会了如何编写一个简单的Android疯狂连连看游戏.      开发这个流行的小游戏,难度适中,而且能充分激发学习热情,适合Android初学者来说是一个不错的选择.对于该游戏的开发,需要重点掌握单机游戏的界面分析和数据建模能力:游戏玩家严重看到的是游戏界面,但是在开发者眼中看到的应该是数据模型.除此之外,单机游戏通常需要一个比较美观的界面,需要通过自定义View来实现游戏主界面.      开发连