首页 > 解决方案 > 制作一个在点击时播放特定声音的 Android 小部件

问题描述

我正在编写一个音板应用程序,让用户可以在他们的主屏幕上放置一些小部件。它由一个由 Sound 类的 RecyclerView 膨胀的 Widget ConfigureActivity 组成:

public class Sound {
    private String name;
    private int sound_id;

    public Sound(String name, int sound_id) {
        this.name= name;
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public int getId() {
        return id;
    }
}

当用户从 ConfigureActivity RecyclerView 中选择一个元素时,就会创建一个小部件。但是,我发现当用户单击它时无法播放声音。

我的适配器看起来像这样:

public class AdapterDatosWidget extends RecyclerView.Adapter<AdapterDatosWidget.ViewHolderDatosWidget> {
    private final ArrayList<Sonido> sonidos;
    private AssignHandler handler;

    public AdapterDatosWidget(ArrayList<Sound> sonidos, AssignHandler assignHandler) {
        this.sonidos = sonidos;
        handler = assignHandler;
    }

    @NonNull
    @Override
    public ViewHolderDatosWidget onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_list_widget, null, false);
        return new AdapterDatosWidget.ViewHolderDatosWidget(view, handler);
    }

    @Override
    public void onBindViewHolder(@NonNull ViewHolderDatosWidget holder, int position) {
        holder.asignarDatos(sonidos.get(position));
    }

    @Override
    public int getItemCount() {
        return sonidos.size();
    }

    public static class ViewHolderDatosWidget extends RecyclerView.ViewHolder {
        Sound s;
        TextView name;
        AssignHandler handler;

        public ViewHolderDatosWidget(@NonNull View itemView, AssignHandler handler) {
            super(itemView);
            name = itemView.findViewById(R.id.sound_name);
            this.handler = handler;
        }

        public void asignarDatos(Sound sonido) {
            s = sonido;
            name.setText(sonido.getNombre());
            name.setOnClickListener(new View.OnClickListener() {
                @Override
                public void onClick(View view) {
                    handler.assign(getAbsoluteAdapterPosition());
                }
            });
        }
    }

    public interface AssignHandler {
        void assign(int position);
    }
}

Configure Activity 正确识别出被按下的声音,它看起来像这样:

public class SoundWidgetConfigureActivity extends Activity implements AdapterDatosWidget.AssignHandler {

    int mAppWidgetId = AppWidgetManager.INVALID_APPWIDGET_ID;
    RecyclerView recyclerView;
    private static final String PREFS_NAME = "com.xvlaze.soundboard.SoundWidget";
    private static final String PREF_PREFIX_KEY = "appwidget_";

    public SoundWidgetConfigureActivity() {
        super();
    }

    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);

        // Set the result to CANCELED.  This will cause the widget host to cancel
        // out of the widget placement if the user presses the back button.
        setResult(RESULT_CANCELED);

        setContentView(R.layout.sound_widget_configure);
        recyclerView = findViewById(R.id.recycler);
        recyclerView.setLayoutManager(new LinearLayoutManager(this));
        AdapterDatosWidget ad = new AdapterDatosWidget(sonidos, this);
        recyclerView.setAdapter(ad);
        findViewById(R.id.add_button).setOnClickListener(mOnClickListener);

        // Find the widget id from the intent.
        Intent intent = getIntent();
        Bundle extras = intent.getExtras();
        if (extras != null) {
            mAppWidgetId = extras.getInt(
                    AppWidgetManager.EXTRA_APPWIDGET_ID, AppWidgetManager.INVALID_APPWIDGET_ID);
        }

        // If this activity was started with an intent without an app widget ID, finish with an error.
        if (mAppWidgetId == AppWidgetManager.INVALID_APPWIDGET_ID) {
            finish();
            return;
        }
    }

    View.OnClickListener mOnClickListener = new View.OnClickListener() {
        public void onClick(View v) {
            final Context context = SoundWidgetConfigureActivity.this;

            // It is the responsibility of the configuration activity to update the app widget
            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
            SoundWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);

            // Make sure we pass back the original appWidgetId
            Intent resultValue = new Intent();
            resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
            setResult(RESULT_OK, resultValue);
            finish();
        }
    };

    // Write the prefix to the SharedPreferences object for this widget
    static void saveTitlePref(Context context, int appWidgetId, String text) {
        SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
        prefs.putString(PREF_PREFIX_KEY + appWidgetId, text);
        prefs.apply();
    }

    // Read the prefix from the SharedPreferences object for this widget.
    // If there is no preference saved, get the default from a resource
    static String loadTitlePref(Context context, int appWidgetId) {
        SharedPreferences prefs = context.getSharedPreferences(PREFS_NAME, 0);
        String titleValue = prefs.getString(PREF_PREFIX_KEY + appWidgetId, null);
        if (titleValue != null) {
            return titleValue;
        } else {
            return context.getString(R.string.appwidget_text);
        }
    }

    static void deleteTitlePref(Context context, int appWidgetId) {
        SharedPreferences.Editor prefs = context.getSharedPreferences(PREFS_NAME, 0).edit();
        prefs.remove(PREF_PREFIX_KEY + appWidgetId);
        prefs.apply();
    }

    @Override
    public void assign(int position) {
        Toast.makeText(this, sonidos.get(position).getName(), Toast.LENGTH_SHORT).show();
        final Context context = SoundWidgetConfigureActivity.this;

        // When the button is clicked, store the string locally
        //String widgetText = mAppWidgetText.getText().toString();
        //saveTitlePref(context, mAppWidgetId, widgetText);

        // It is the responsibility of the configuration activity to update the app widget
        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
        NewAppWidget.updateAppWidget(context, appWidgetManager, mAppWidgetId);

        // Make sure we pass back the original appWidgetId
        Intent resultValue = new Intent();
        resultValue.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mAppWidgetId);
        setResult(RESULT_OK, resultValue);
        finish();
    }
}

Widget 代码如下所示:

public class SoundWidget extends AppWidgetProvider {

    @Override
    public void onUpdate(Context context, AppWidgetManager appWidgetManager, int[] appWidgetIds) {
        RemoteViews img = new RemoteViews(context.getPackageName(), R.layout.sound_widget);
        Intent playIntent = new Intent(context, PlaySoundService.class);
        PendingIntent playPendingIntent = PendingIntent.getService(
                context, 0, playIntent, 0);

        img.setOnClickPendingIntent(R.id.widget, playPendingIntent);


        // There may be multiple widgets active, so update all of them
        for (int appWidgetId : appWidgetIds) {
            updateAppWidget(context, appWidgetManager, appWidgetId);
        }
    }

    @Override
    public void onDeleted(Context context, int[] appWidgetIds) {
        // When the user deletes the widget, delete the preference associated with it.
        for (int appWidgetId : appWidgetIds) {
            SoundWidgetConfigureActivity.deleteTitlePref(context, appWidgetId);
        }
    }

    @Override
    public void onEnabled(Context context) {
        // Enter relevant functionality for when the first widget is created
    }

    @Override
    public void onDisabled(Context context) {
        // Enter relevant functionality for when the last widget is disabled
    }

    static void updateAppWidget(Context context, AppWidgetManager appWidgetManager,
                                int appWidgetId) {

        // Construct the RemoteViews object
        RemoteViews views = new RemoteViews(context.getPackageName(), R.layout.sound_widget);

        // Instruct the widget manager to update the widget
        appWidgetManager.updateAppWidget(appWidgetId, views);
    }
}

该服务的代码如下所示:

public class PlaySoundService extends Service {
    private static final String TAG = null;
    MediaPlayer player;
    public IBinder onBind(Intent arg0) {

        return null;
    }
    @Override
    public void onCreate() {
        super.onCreate();
        player = MediaPlayer.create(this, R.raw.soundid);
        //configure other settings
    }
    public int onStartCommand(Intent intent, int flags, int startId) {
        player.start();
        return Service.START_STICKY;
    }

    public void onStart(Intent intent, int startId) {
        // TO DO
    }
    public IBinder onUnBind(Intent arg0) {
        // TO DO Auto-generated method
        return null;
    }

    public void onStop() {

    }
    public void onPause() {

    }
    @Override
    public void onDestroy() {
        player.stop();
        player.release();
    }
}

如果您通过调用来重现此示例(请使用一些示例 mp3 文件填充 res/raw 文件夹):

Intent svc=new Intent(this, BackgroundSoundService.class);
startService(svc);

在 MainActivity 中,您会看到每当打开应用程序时都会立即播放声音。我错过了什么吗?我简直不敢相信它有那么复杂。

标签: androidwidgetandroid-widgetandroid-mediaplayer

解决方案


推荐阅读