如何在 android 中循环动态振动模式?
How to loop a dynamic vibration pattern in android?
好的,所以我有一个更大的应用程序,我实际上不会在这里post。在其中,有一个循环的振动事件,尽管从技术上讲它不是 while
循环 [事件在用户请求时结束] 并且构成 long[] pattern = {0,dot, gap, dash, gap, dot, gap, dot};
的值在每次迭代时都会发生变化所以设置 vibrator.vibrate(pattern, 0);
不是一个选项。
我设置了另一个应用程序来测试有关 Vibrator 的一些事情。我发现 运行 静态模式给出了所需的输出,但是将 onVibrate
方法放在 while 循环中基本上破坏了识别信号的任何能力。我没有找到任何方法让系统知道 phone 当前是否在振动,只有当它可以振动时。所以我决定将 onVibrate
嵌套在一个布尔值后面,该方法将最终控制该控制,并且该控制将是一个回调,该回调将 'pause' 超过模式占用的长度。它最终看起来像这样:
public class MainActivity extends Activity {
Boolean im_vibrating;
CallBack mCallBack;
Vibrator vibrator;
interface MyCallBack {
void offSet(Integer span);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCallBack = new CallBack();
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
im_vibrating = false;
while (true) {
if (!im_vibrating) {
im_vibrating = true;
onVibrate();
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void onVibrate () {
int dot = 200;
int dash = 500;
int gap = 200;
long[] pattern = {
0,
dot, gap, dash, gap, dot, gap, dot
};
vibrator.vibrate(pattern, -1);
int span = dot + gap + dash + gap + dot + gap + dot + dot;
mCallBack.offSet(span);
}
class CallBack implements MyCallBack {
@Override
public void offSet(Integer span) {
final android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
im_vibrating = false;
}
}, span);
}
}
}
然而,尽管没有错误,但这不起作用,最终某些东西阻止了布局的呈现,因此显示只是空白,它振动一次,然后再也不会振动。尽管 mCallBack
自动完成 offSet
SDK 突出显示 offset
声明它从未使用过。
您不能使用 UI 线程来执行长时间的循环操作 - 将它们移到单独的线程中。
您使用 Handler
作为一种线程回调的方案将失败,因为 Handler
实例使用创建它们的线程来完成它们的工作。由于您的处理程序是由 UI 线程创建的,并且您的 UI 线程挂在 onCreate()
方法的 while (true)
循环中,因此不会发生任何事情 - UI 永远不会更新(这就是显示空白的原因)并且处理程序永远不会 运行.
只需使用一个 AsyncTask 或一个单独的 Thread
来进行振动,并且永远不会阻塞 UI 线程。
在主线程中完成的每项操作,即渲染视图和调用活动的线程,都必须尽可能简单。如果不是这种情况,UI 将被阻止。
鉴于此,实现您想要的目标的方法是使用 Handler
。这个 class 允许您将消息分派到给定的线程,在本例中是主线程,即使有延迟。
您的解决方案的一种可能实施方式:
long[] pattern = new long[]{
0,
dot, gap, dash, gap, dot, gap, dot
};
final Handler handler = new Handler();
handler.postDelayed(new Runnable(){
@Override
public void run(){
vibrator.vibrate();
if(!endVibration){
handler.postDelayed(this, timeToRun);
}
}
}, timeToRun);
这将生成一个循环,可以通过将 endVibration 变量设置为 true 来取消循环,并且不会阻塞 UI。
好的,考虑到我上面的双方所说的,我最终得到了这个,这正是我想要的:
MainActivity.java
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Vibrator;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
Vibrator vibrator;
VibratePattern task;
Button stop_button;
Button start_button;
MyTaskParams params;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
stop_button = (Button) findViewById(R.id.button_stop);
stop_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
task.cancel(true);
task = null;
}
});
start_button = (Button) findViewById(R.id.button_start);
start_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (task == null) {
startTask();
}
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private static class MyTaskParams {
int dot, dash, gap;
MyTaskParams (int dot, int dash, int gap) {
this.dot = dot;
this.dash = dash;
this.gap = gap;
}
}
private void startTask() {
params = new MyTaskParams(200,500,200);
task = new VibratePattern();
task.execute(params);
}
public Integer onVibrate (Integer dot, Integer dash, Integer gap) {
long[] pattern = {
0,
dot, gap, dash, gap, dot, gap, dot
};
vibrator.vibrate(pattern, -1);
int span = dot + gap + dash + gap + dot + gap + dot + gap;
return span;
}
private class VibratePattern extends AsyncTask<MyTaskParams, Void, Integer> {
@Override
protected Integer doInBackground(MyTaskParams... params) {
int span;
span = onVibrate(params[0].dot,params[0].dash,params[0].gap);
return span;
}
@Override
protected void onPostExecute(Integer span) {
final android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (!isCancelled()) {
startTask();
}
}
}, span);
}
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_start"
android:id="@+id/button_start"
android:layout_marginTop="90dp"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/button_stop"
android:layout_toStartOf="@+id/button_stop" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_stop"
android:id="@+id/button_stop"
android:layout_alignTop="@+id/button_start"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="80dp"
android:layout_marginEnd="80dp" />
</RelativeLayout>
string.xml
<resources>
<string name="app_name">VibrationTest</string>
<string name="action_settings">Settings</string>
<string name="button_stop">Stop</string>
<string name="button_start">Start</string>
</resources>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="" >
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
startTask
然后可以在每个循环周期加载新值,根据需要动态改变脉冲。谢谢你的帮助。
这对我来说很好
private void startTask() {
G.vibrator.vibrate(new long[]{0, 500, 200, 200, 500, 200, 200}, -1);
G.HANDLER.postDelayed(new Runnable() {
@Override
public void run() {
startTask();
}
}, 3000);
}
好的,所以我有一个更大的应用程序,我实际上不会在这里post。在其中,有一个循环的振动事件,尽管从技术上讲它不是 while
循环 [事件在用户请求时结束] 并且构成 long[] pattern = {0,dot, gap, dash, gap, dot, gap, dot};
的值在每次迭代时都会发生变化所以设置 vibrator.vibrate(pattern, 0);
不是一个选项。
我设置了另一个应用程序来测试有关 Vibrator 的一些事情。我发现 运行 静态模式给出了所需的输出,但是将 onVibrate
方法放在 while 循环中基本上破坏了识别信号的任何能力。我没有找到任何方法让系统知道 phone 当前是否在振动,只有当它可以振动时。所以我决定将 onVibrate
嵌套在一个布尔值后面,该方法将最终控制该控制,并且该控制将是一个回调,该回调将 'pause' 超过模式占用的长度。它最终看起来像这样:
public class MainActivity extends Activity {
Boolean im_vibrating;
CallBack mCallBack;
Vibrator vibrator;
interface MyCallBack {
void offSet(Integer span);
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mCallBack = new CallBack();
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
im_vibrating = false;
while (true) {
if (!im_vibrating) {
im_vibrating = true;
onVibrate();
}
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
public void onVibrate () {
int dot = 200;
int dash = 500;
int gap = 200;
long[] pattern = {
0,
dot, gap, dash, gap, dot, gap, dot
};
vibrator.vibrate(pattern, -1);
int span = dot + gap + dash + gap + dot + gap + dot + dot;
mCallBack.offSet(span);
}
class CallBack implements MyCallBack {
@Override
public void offSet(Integer span) {
final android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
im_vibrating = false;
}
}, span);
}
}
}
然而,尽管没有错误,但这不起作用,最终某些东西阻止了布局的呈现,因此显示只是空白,它振动一次,然后再也不会振动。尽管 mCallBack
自动完成 offSet
SDK 突出显示 offset
声明它从未使用过。
您不能使用 UI 线程来执行长时间的循环操作 - 将它们移到单独的线程中。
您使用 Handler
作为一种线程回调的方案将失败,因为 Handler
实例使用创建它们的线程来完成它们的工作。由于您的处理程序是由 UI 线程创建的,并且您的 UI 线程挂在 onCreate()
方法的 while (true)
循环中,因此不会发生任何事情 - UI 永远不会更新(这就是显示空白的原因)并且处理程序永远不会 运行.
只需使用一个 AsyncTask 或一个单独的 Thread
来进行振动,并且永远不会阻塞 UI 线程。
在主线程中完成的每项操作,即渲染视图和调用活动的线程,都必须尽可能简单。如果不是这种情况,UI 将被阻止。
鉴于此,实现您想要的目标的方法是使用 Handler
。这个 class 允许您将消息分派到给定的线程,在本例中是主线程,即使有延迟。
您的解决方案的一种可能实施方式:
long[] pattern = new long[]{
0,
dot, gap, dash, gap, dot, gap, dot
};
final Handler handler = new Handler();
handler.postDelayed(new Runnable(){
@Override
public void run(){
vibrator.vibrate();
if(!endVibration){
handler.postDelayed(this, timeToRun);
}
}
}, timeToRun);
这将生成一个循环,可以通过将 endVibration 变量设置为 true 来取消循环,并且不会阻塞 UI。
好的,考虑到我上面的双方所说的,我最终得到了这个,这正是我想要的:
MainActivity.java
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Vibrator;
import android.os.Bundle;
import android.view.MenuItem;
import android.view.View;
import android.widget.Button;
public class MainActivity extends Activity {
Vibrator vibrator;
VibratePattern task;
Button stop_button;
Button start_button;
MyTaskParams params;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vibrator = (Vibrator) getSystemService(Context.VIBRATOR_SERVICE);
stop_button = (Button) findViewById(R.id.button_stop);
stop_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
task.cancel(true);
task = null;
}
});
start_button = (Button) findViewById(R.id.button_start);
start_button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (task == null) {
startTask();
}
}
});
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
// Handle action bar item clicks here. The action bar will
// automatically handle clicks on the Home/Up button, so long
// as you specify a parent activity in AndroidManifest.xml.
int id = item.getItemId();
//noinspection SimplifiableIfStatement
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
private static class MyTaskParams {
int dot, dash, gap;
MyTaskParams (int dot, int dash, int gap) {
this.dot = dot;
this.dash = dash;
this.gap = gap;
}
}
private void startTask() {
params = new MyTaskParams(200,500,200);
task = new VibratePattern();
task.execute(params);
}
public Integer onVibrate (Integer dot, Integer dash, Integer gap) {
long[] pattern = {
0,
dot, gap, dash, gap, dot, gap, dot
};
vibrator.vibrate(pattern, -1);
int span = dot + gap + dash + gap + dot + gap + dot + gap;
return span;
}
private class VibratePattern extends AsyncTask<MyTaskParams, Void, Integer> {
@Override
protected Integer doInBackground(MyTaskParams... params) {
int span;
span = onVibrate(params[0].dot,params[0].dash,params[0].gap);
return span;
}
@Override
protected void onPostExecute(Integer span) {
final android.os.Handler handler = new android.os.Handler();
handler.postDelayed(new Runnable() {
@Override
public void run() {
if (!isCancelled()) {
startTask();
}
}
}, span);
}
}
}
activity_main.xml
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" android:layout_width="match_parent"
android:layout_height="match_parent" android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin" tools:context=".MainActivity">
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_start"
android:id="@+id/button_start"
android:layout_marginTop="90dp"
android:layout_alignParentTop="true"
android:layout_toLeftOf="@+id/button_stop"
android:layout_toStartOf="@+id/button_stop" />
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/button_stop"
android:id="@+id/button_stop"
android:layout_alignTop="@+id/button_start"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_marginRight="80dp"
android:layout_marginEnd="80dp" />
</RelativeLayout>
string.xml
<resources>
<string name="app_name">VibrationTest</string>
<string name="action_settings">Settings</string>
<string name="button_stop">Stop</string>
<string name="button_start">Start</string>
</resources>
AndroidManifest.xml
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="" >
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:theme="@style/AppTheme" >
<activity
android:name=".MainActivity"
android:label="@string/app_name" >
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
startTask
然后可以在每个循环周期加载新值,根据需要动态改变脉冲。谢谢你的帮助。
这对我来说很好
private void startTask() {
G.vibrator.vibrate(new long[]{0, 500, 200, 200, 500, 200, 200}, -1);
G.HANDLER.postDelayed(new Runnable() {
@Override
public void run() {
startTask();
}
}, 3000);
}