简体   繁体   English

如何在Android中循环动态振动模式?

[英]How to loop a dynamic vibration pattern in android?

OK, so I have a much larger application that I am not actually going to post here. 好,所以我有一个更大的应用程序,实际上我不会在这里发布。 In it, there is a vibration event that is looped through, though technically it is not a while loop [the event ends at the users request] and the values that make up the long[] pattern = {0,dot, gap, dash, gap, dot, gap, dot}; 在其中,有一个振动事件循环通过,尽管从技术上讲它不是while循环[该事件在用户请求时结束],并且构成long[] pattern = {0,dot, gap, dash, gap, dot, gap, dot}; change at every iteration so setting vibrator.vibrate(pattern, 0); 每次迭代都更改,因此设置vibrator.vibrate(pattern, 0); is not an option. 不是一个选择。

I set up another application to test some things about Vibrator. 我设置了另一个应用程序来测试有关Vibrator的某些信息。 I found that running a static pattern gave a desired output, but putting the onVibrate method in a while loop basically destroyed any ability to recognized the signal. 我发现运行静态模式会提供所需的输出,但是将onVibrate方法放入while循环中基本上会破坏任何识别信号的能力。 I didn't find any way for the system to know if the phone was currently vibrating only if it could vibrate. 我没有找到任何方法让系统知道手机是否在振动,仅是它能振动。 So I decided to nest onVibrate behind a boolean that the method would ultimate control and that control would be a callback that would 'pause' for more than the length the pattern took up. 因此,我决定将onVibrate嵌套在一个布尔值后面,该方法将最终控制该控件,并且该控件将是一个“暂停”超过模式占用长度的回调。 It ended up looking like this: 最终看起来像这样:

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);
    }
}

} }

However, despite having no errors, this doesn't work, something ends up blocking the rendering of the layout so the display is just blank, it vibrates once and then never again. 但是,尽管没有错误,但这仍然行不通,最终导致某些东西阻塞了布局的呈现,因此显示只是空白,它振动一次,然后再也不振动。 Despite the fact that mCallBack auto completed offSet the SDK highlights offset stating that it is never used. 尽管mCallBack自动完成了offSet ,SDK还是突出显示了offset ,表明它从未使用过。

Every operation done in the main thread, the one that renders views and in which the activities are called, must be as simple as it could. 在主线程中完成的每个操作(呈现视图并在其中调用活动的操作)必须尽可能地简单。 If this is not the case, the UI will be blocked. 如果不是这种情况,UI将被阻止。

Given that, the way to achieve something like you want is using a Handler . 在这种情况下,实现所需功能的方法是使用Handler This class allows you to dispatch messages to a given thread, in this case, the main one, even with delayed times. 此类允许您将消息调度到给定线程(在这种情况下为主线程),即使延迟也是如此。

One possible implementation for your solution: 解决方案的一种可能实现:

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);

This will generate a loop that can be cancelled by setting the endVibration variable to true and that is not blocking the UI. 这将生成一个循环,可以通过将endVibration变量设置为true来取消该循环,并且不会阻塞UI。

You cannot use the UI thread to do long looping actions - move them into a separate thread. 您不能使用UI线程执行长时间循环的操作-将它们移到单独的线程中。

Your scheme to use a Handler as a sort of threaded callback will fail because Handler instances use the thread they were created on to do their work. 您将Handler用作某种线程回调的方案将失败,因为Handler实例使用在其上创建的线程来执行其工作。 Since your handler is created by the UI thread and you've got the UI thread hanging in a while (true) loop in your onCreate() method, nothing will happen - the UI will never update (which is why the display is blank) and the handler will never run. 由于您的处理程序是由UI线程创建的,并且UI线程挂在onCreate()方法中的while (true)循环中,因此不会发生任何事情onCreate()永远不会更新(这就是显示为空白的原因)并且处理程序将永远不会运行。

Just use an AsyncTask or aa separate Thread to do the vibration and never block the UI thread. 只需使用AsyncTask或单独的Thread进行振动,就不会阻塞UI线程。

OK taking into consideration what both parties above me said I ended up with this, which was exactly what I was looking for: 好吧,考虑到我上方的双方所说的,我最终做到了这一点,这正是我想要的:

MainActivity.java 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 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 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 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 could then load up new values every loop cycle dynamically changing the pulse as needed. 然后, startTask可以在每个循环周期加载新值,从而根据需要动态更改脉冲。 Ty for your help. 泰语为您提供帮助。

it was nice for me 对我很好

 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);
  }

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM