android - 制作一个在点击时播放特定声音的 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 中,您会看到每当打开应用程序时都会立即播放声音。我错过了什么吗?我简直不敢相信它有那么复杂。
解决方案
推荐阅读
- c# - 如何在richtexbox或winforms中以正确格式加载和编辑.docx/.doc文件或任何其他控件?
- java - 如何在 gradle idea 上修复 jvm 选项将 javafx 11 导出到 com.jfoenix?
- javascript - ReCharts 多行
- azure-devops - 构建在我的本地成功,但在 vsts azure DevOps 上失败
- java - Apache Commons HttpAsyncClient 是否支持 GZIP?
- python - Savgol 过滤数据框列
- c# - 如何从 json 文件中为 DescriptionAttribute 设置描述字符串?
- ios - 错误:DiskCookieStorage 将策略从 2 更改为 0,cookie 文件
- mpi - 打开 MPI 4.0 内核和内存绑定
- python - 如何识别 Python 中的默认方法值设置?