如何在 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);
  }