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上,希望大家多多支持!
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!