Android 微信摇一摇功能实现详细介绍
android 微信摇一摇功能实现,最近学习传感器,就想实现摇一摇的功能,上网查了些资料,就整理下。如有错误,还请指正。
开发环境
- android studio 2.2.1
- jdk1.7
- api 24
- gradle 2.2.1
相关知识点
- 加速度传感器
- 补间动画
- 手机震动 (vibrator)
- 较短 声音/音效 的播放 (soundpool)
案例:
我们接下来分析一下这个案例, 当用户晃动手机时, 会触发加速传感器, 此时加速传感器会调用相应接口供我们使用, 此时我们可以做一些相应的动画效果, 震动效果和声音效果. 大致思路就是这样. 具体功能点:
用户晃动后两张图片分开, 显示后面图片
晃动后伴随震动效果, 声音效果
根据以上的简单分析, 我们就知道该怎么做了, just now
先搭建布局
布局没啥可说的, 大家直接看代码吧
<?xml version="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 protected void onstart() { 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 protected void onpause() { // 务必要在pause中注销 msensormanager // 否则会造成界面退出后摇一摇依旧生效的bug if (msensormanager != null) { msensormanager.unregisterlistener(this); } super.onpause(); }
note: 至于为什么我们要在onstart和onpause中就行sensormanager的注册和注销, 就是因为, 防止在界面退出(包括按home键)时, 摇一摇依旧生效(代码中有注释)
step3: 在step1中的注册监听事件方法中, 我们传入了当前activity对象, 故让其实现回调接口, 得到以下方法
/////////////////////////////////////////////////////////////////////////// // sensoreventlistener回调方法 /////////////////////////////////////////////////////////////////////////// @override public void onsensorchanged(sensorevent event) { int type = event.sensor.gettype(); if (type == sensor.type_accelerometer) { //获取三个方向值 float[] values = event.values; float x = values[0]; float y = values[1]; float z = values[2]; if ((math.abs(x) > 17 || math.abs(y) > 17 || math .abs(z) > 17) && !isshake) { isshake = true; // todo: 2016/10/19 实现摇动逻辑, 摇动后进行震动 thread thread = new thread() { @override public void run() { 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 (interruptedexception e) { e.printstacktrace(); } } }; thread.start(); } } } @override public void onaccuracychanged(sensor sensor, int accuracy) { }
note: 当用户晃动手机会调用onsensorchanged方法, 可以做一些相应的操作
为解决动画和震动延迟, 我们开启了一个子线程来实现.
子线程中会通过发送handler消息, 先开始动画效果, 并伴随震动和声音 ,先把handler的实现放一放, 我们再来看一下震动和声音初始化动画, 震动和音效实现
step 1: 先获取到震动相关的服务,注意要加权限. 至于音效, 我们采用soundpool来播放, 在这里非常感谢vincent 的贴子, 好初始化soundpool
震动权限
<uses-permission android:name="android.permission.vibrate"/>
//初始化soundpool msoundpool = new soundpool(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内存泄漏, 采用了软引用方式
private static class myhandler extends handler { private weakreference<mainactivity> mreference; private mainactivity mactivity; public myhandler(mainactivity activity) { mreference = new weakreference<mainactivity>(activity); if (mreference != null) { mactivity = mreference.get(); } } @override public void handlemessage(message msg) { super.handlemessage(msg); switch (msg.what) { case start_shake: //this method requires the caller to hold the permission vibrate. 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; case again_shake: mactivity.mvibrator.vibrate(300); break; case end_shake: //整体效果结束, 将震动设置为false mactivity.isshake = false; // 展示上下两种图片回来的效果 mactivity.startanimation(true); break; } } }
note: 内容不多说了, 代码注释中很详细, 还有一个startanimation方法
我先来说一下它的参数, true表示布局中两张图片从打开到关闭的动画, 反之, false是从关闭到打开状态, 上代码
step3: startanimaion方法上的实现
/** * 开启 摇一摇动画 * * @param isback 是否是返回初识状态 */ private void startanimation(boolean isback) { //动画坐标移动的位置的类型是相对自己的 int type = animation.relative_to_self; float topfromy; float toptoy; float bottomfromy; float bottomtoy; if (isback) { topfromy = -0.5f; toptoy = 0; bottomfromy = 0.5f; bottomtoy = 0; } else { topfromy = 0; toptoy = -0.5f; bottomfromy = 0; bottomtoy = 0.5f; } //上面图片的动画效果 translateanimation topanim = new translateanimation( type, 0, type, 0, type, topfromy, type, toptoy ); topanim.setduration(200); //动画终止时停留在最后一帧~不然会回到没有执行之前的状态 topanim.setfillafter(true); //底部的动画效果 translateanimation bottomanim = new translateanimation( type, 0, type, 0, type, bottomfromy, type, bottomtoy ); bottomanim.setduration(200); bottomanim.setfillafter(true); //大家一定不要忘记, 当要回来时, 我们中间的两根线需要gone掉 if (isback) { bottomanim.setanimationlistener(new animation.animationlistener() { @override public void onanimationstart(animation animation) {} @override public void onanimationrepeat(animation animation) {} @override public void onanimationend(animation animation) { //当动画结束后 , 将中间两条线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上, 希望大家多多支持!
感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!