Android 微信摇一摇功能实现详细介绍
Android微信摇一摇功能实现,最近学习传感器,就想实现摇一摇的功能,上网查了些资料,就整理下。如有错误,还请指正。
开发环境
- AndroidStudio2.2.1
- JDK1.7
- API24
- Gradle2.2.1
相关知识点
- 加速度传感器
- 补间动画
- 手机震动(Vibrator)
- 较短声音/音效的播放(SoundPool)
案例:
我们接下来分析一下这个案例,当用户晃动手机时,会触发加速传感器,此时加速传感器会调用相应接口供我们使用,此时我们可以做一些相应的动画效果,震动效果和声音效果.大致思路就是这样.具体功能点:
用户晃动后两张图片分开,显示后面图片
晃动后伴随震动效果,声音效果
根据以上的简单分析,我们就知道该怎么做了,Justnow
先搭建布局
布局没啥可说的,大家直接看代码吧
<?xmlversion="1.0"encoding="utf-8"?> <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:id="@+id/activity_main" android:layout_width="match_parent" android:layout_height="match_parent" android:background="#ff222222" android:orientation="vertical" tools:context="com.lulu.weichatshake.MainActivity"> <RelativeLayout android:layout_width="match_parent" android:layout_height="match_parent"> <!--摇一摇中心图片--> <ImageView android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" android:src="@mipmap/weichat_icon"/> <LinearLayout android:gravity="center" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:layout_alignParentTop="true" android:layout_alignParentLeft="true" android:layout_alignParentStart="true"> <!--顶部的横线和图片--> <LinearLayout android:gravity="center_horizontal|bottom" android:id="@+id/main_linear_top" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:src="@mipmap/shake_top" android:id="@+id/main_shake_top" android:layout_width="wrap_content" android:layout_height="100dp"/> <ImageView android:background="@mipmap/shake_top_line" android:id="@+id/main_shake_top_line" android:layout_width="match_parent" android:layout_height="5dp"/> </LinearLayout> <!--底部的横线和图片--> <LinearLayout android:gravity="center_horizontal|bottom" android:id="@+id/main_linear_bottom" android:layout_width="match_parent" android:layout_height="wrap_content" android:orientation="vertical"> <ImageView android:background="@mipmap/shake_bottom_line" android:id="@+id/main_shake_bottom_line" android:layout_width="match_parent" android:layout_height="5dp"/> <ImageView android:src="@mipmap/shake_bottom" android:id="@+id/main_shake_bottom" android:layout_width="wrap_content" android:layout_height="100dp"/> </LinearLayout> </LinearLayout> </RelativeLayout> </LinearLayout>
得到加速度传感器的回调接口
step1:在onStart()方法中获取传感器的SensorManager
@Override protectedvoidonStart(){ super.onStart(); //获取SensorManager负责管理传感器 mSensorManager=((SensorManager)getSystemService(SENSOR_SERVICE)); if(mSensorManager!=null){ //获取加速度传感器 mAccelerometerSensor=mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER); if(mAccelerometerSensor!=null){ mSensorManager.registerListener(this,mAccelerometerSensor,SensorManager.SENSOR_DELAY_UI); } } }
step2:紧接着我们就要在Pause中注销传感器
@Override protectedvoidonPause(){ //务必要在pause中注销mSensorManager //否则会造成界面退出后摇一摇依旧生效的bug if(mSensorManager!=null){ mSensorManager.unregisterListener(this); } super.onPause(); }
Note:至于为什么我们要在onStart和onPause中就行SensorManager的注册和注销,就是因为,防止在界面退出(包括按Home键)时,摇一摇依旧生效(代码中有注释)
step3:在step1中的注册监听事件方法中,我们传入了当前Activity对象,故让其实现回调接口,得到以下方法
/////////////////////////////////////////////////////////////////////////// //SensorEventListener回调方法 /////////////////////////////////////////////////////////////////////////// @Override publicvoidonSensorChanged(SensorEventevent){ inttype=event.sensor.getType(); if(type==Sensor.TYPE_ACCELEROMETER){ //获取三个方向值 float[]values=event.values; floatx=values[0]; floaty=values[1]; floatz=values[2]; if((Math.abs(x)>17||Math.abs(y)>17||Math .abs(z)>17)&&!isShake){ isShake=true; //TODO:2016/10/19实现摇动逻辑,摇动后进行震动 Threadthread=newThread(){ @Override publicvoidrun(){ super.run(); try{ Log.d(TAG,"onSensorChanged:摇动"); //开始震动发出提示音展示动画效果 mHandler.obtainMessage(START_SHAKE).sendToTarget(); Thread.sleep(500); //再来一次震动提示 mHandler.obtainMessage(AGAIN_SHAKE).sendToTarget(); Thread.sleep(500); mHandler.obtainMessage(END_SHAKE).sendToTarget(); }catch(InterruptedExceptione){ e.printStackTrace(); } } }; thread.start(); } } } @Override publicvoidonAccuracyChanged(Sensorsensor,intaccuracy){ }
Note:当用户晃动手机会调用onSensorChanged方法,可以做一些相应的操作
为解决动画和震动延迟,我们开启了一个子线程来实现.
子线程中会通过发送Handler消息,先开始动画效果,并伴随震动和声音,先把Handler的实现放一放,我们再来看一下震动和声音初始化动画,震动和音效实现
step1:先获取到震动相关的服务,注意要加权限.至于音效,我们采用SoundPool来播放,在这里非常感谢Vincent的贴子,好初始化SoundPool
震动权限
<uses-permissionandroid:name="android.permission.VIBRATE"/>
//初始化SoundPool mSoundPool=newSoundPool(1,AudioManager.STREAM_SYSTEM,5); mWeiChatAudio=mSoundPool.load(this,R.raw.weichat_audio,1); //获取Vibrator震动服务 mVibrator=(Vibrator)getSystemService(VIBRATOR_SERVICE);
Note:大家可能发现SoundPool的构造方法已经过时,不过不用担心这是Api21之后过时的,所以也不算太”过时”吧
step2:接下来我们就要介绍Handler中的实现了,为避免Activity内存泄漏,采用了软引用方式
privatestaticclassMyHandlerextendsHandler{ privateWeakReference<MainActivity>mReference; privateMainActivitymActivity; publicMyHandler(MainActivityactivity){ mReference=newWeakReference<MainActivity>(activity); if(mReference!=null){ mActivity=mReference.get(); } } @Override publicvoidhandleMessage(Messagemsg){ super.handleMessage(msg); switch(msg.what){ caseSTART_SHAKE: //ThismethodrequiresthecallertoholdthepermissionVIBRATE. mActivity.mVibrator.vibrate(300); //发出提示音 mActivity.mSoundPool.play(mActivity.mWeiChatAudio,1,1,0,0,1); mActivity.mTopLine.setVisibility(View.VISIBLE); mActivity.mBottomLine.setVisibility(View.VISIBLE); mActivity.startAnimation(false);//参数含义:(不是回来)也就是说两张图片分散开的动画 break; caseAGAIN_SHAKE: mActivity.mVibrator.vibrate(300); break; caseEND_SHAKE: //整体效果结束,将震动设置为false mActivity.isShake=false; //展示上下两种图片回来的效果 mActivity.startAnimation(true); break; } } }
Note:内容不多说了,代码注释中很详细,还有一个startAnimation方法
我先来说一下它的参数,true表示布局中两张图片从打开到关闭的动画,反之,false是从关闭到打开状态,上代码
step3:startAnimaion方法上的实现
/** *开启摇一摇动画 * *@paramisBack是否是返回初识状态 */ privatevoidstartAnimation(booleanisBack){ //动画坐标移动的位置的类型是相对自己的 inttype=Animation.RELATIVE_TO_SELF; floattopFromY; floattopToY; floatbottomFromY; floatbottomToY; if(isBack){ topFromY=-0.5f; topToY=0; bottomFromY=0.5f; bottomToY=0; }else{ topFromY=0; topToY=-0.5f; bottomFromY=0; bottomToY=0.5f; } //上面图片的动画效果 TranslateAnimationtopAnim=newTranslateAnimation( type,0,type,0,type,topFromY,type,topToY ); topAnim.setDuration(200); //动画终止时停留在最后一帧~不然会回到没有执行之前的状态 topAnim.setFillAfter(true); //底部的动画效果 TranslateAnimationbottomAnim=newTranslateAnimation( type,0,type,0,type,bottomFromY,type,bottomToY ); bottomAnim.setDuration(200); bottomAnim.setFillAfter(true); //大家一定不要忘记,当要回来时,我们中间的两根线需要GONE掉 if(isBack){ bottomAnim.setAnimationListener(newAnimation.AnimationListener(){ @Override publicvoidonAnimationStart(Animationanimation){} @Override publicvoidonAnimationRepeat(Animationanimation){} @Override publicvoidonAnimationEnd(Animationanimation){ //当动画结束后,将中间两条线GONE掉,不让其占位 mTopLine.setVisibility(View.GONE); mBottomLine.setVisibility(View.GONE); } }); } //设置动画 mTopLayout.startAnimation(topAnim); mBottomLayout.startAnimation(bottomAnim); }
至此核心代码已经介绍完毕,但是还有部分小细节不得不提一下
细枝末节
大家要在初始化View之前将上下两条横线GONE掉,用GONE是不占位的
mTopLine.setVisibility(View.GONE);
mBottomLine.setVisibility(View.GONE);
2.咱们的摇一摇最好是只竖屏(毕竟我也没见过横屏的摇一摇),加上下面代码
//设置只竖屏
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
完整代码
源码我已经发在了github上,希望大家多多支持!
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!