我有一个SoundPool,我想用不同的片段播放。所以我把它加载到一个单例中。我必须使用什么背景?
object PingSoundPool {
fun loadpings(note: Int) {
val context = Application()
val mAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_GAME)
.build()
val mSoundPool = SoundPool.Builder()
.setMaxStreams(9)
.setAudioAttributes(mAttributes)
.build()
val cping = mSoundPool.load(context, R.raw.cping, 1)
val dbping = mSoundPool.load(context, R.raw.dbping, 1)
[...]
if (note == 0) {}
if(note == 1)
mSoundPool.play(cping, 1f, 1f, 1, -1, 1f)
if(note == 2)
mSoundPool.play(dbping, 1f, 1f, 1, -1, 1f)
[...]
}
}
如果我像这样使用它,将它加载到我的活动onCreate中,就像这个PingSoundPool.loadPings(0)
一样,并在带有PingSoundPool.loadPings(1)
的onClickListener中访问它应该可以工作,不是吗?在运行时,我得到一个像这样的NullPointerExeption:
java.lang.RuntimeException: Unable to start activity
ComponentInfo{com.example.soulfetch2/com.example.soulfetch2.FullscreenActivity}:
java.lang.NullPointerException: Attempt to invoke virtual method
'android.content.res.Resources android.content.Context.getResources()'
on a null object reference
该命令指出了val cping = mSoundPool.load(context, R.raw.cping, 1)
行R.raw。文件存在,但无法以某种方式访问。我想我可能正在使用错误的上下文。或者我以错误的方式实现正确的上下文。无论如何,帮助是非常渺茫的。
编辑:
最初的问题已经解决,但仍然存在问题:每次尝试播放声音时,代码都会重新加载SoundPool。 Hay有人知道如何单独加载它,所以PingSoundPool(this).loadPings(Int)
的调用只是播放声音而不是重新加载一切?
另一件事:当我从一个活动中做PingSoundPool(this).loadPings(Int)
时,一切运作良好。但是,从片段中,我得到一个TypeMismatch“必需:Context,Found:MainFragment”。我可以用PingSoundPool(this.requireContext()).loadPings(2)
或PingSoundPool(this.context!!).loadPings(2)
解决这个问题,但这似乎不是最好的事情。有什么建议?
这是我现在使用的类而不是对象:
class PingSoundPool(context: Context) {
val mAttributes = AudioAttributes.Builder()
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_GAME)
.build()
val mSoundPool = SoundPool.Builder()
.setMaxStreams(9)
.setAudioAttributes(mAttributes)
.build()
val cping = mSoundPool.load(context, R.raw.cping, 1)
val dbping = mSoundPool.load(context, R.raw.dbping, 1)
fun loadPings(note: Int) {
if(note == 1)
mSoundPool.play(cping, 1f, 1f, 1, -1, 1f)
if(note == 2)
mSoundPool.play(dbping, 1f, 1f, 1, -1, 1f)
[...]
}
}
如果您从活动的onCreate中调用它,为什么不将Context
作为参数传递?
功能将是这样的:
fun loadPings(context: Context, note: Int) {
//val context = Application() //Remove this line
val cping = mSoundPool.load(context, R.raw.cping, 1) //Here it's used the parameter context
val dbping = mSoundPool.load(context, R.raw.dbping, 1)
[...]
}
在您的Activity的onCreate中,您可以通过以下方式调用它:
PingSoundPool.loadPings (this, 0)
编辑:
如果您创建PingSoundPool对象,则每次文件都不会重新加载:因此您可以在活动中执行此操作:
class YourActivity ... {
companion object { //So it is accesible from other classes with YourActivity.pingSoundPool
lateinit var pingSoundPool: PingSoundPool;
}
@Override
... onCreate(...) {
pingSoundPool = PingSoundPool(this)
...
}
}
然后,如果你需要播放声音(我需要更改功能名称),你可以使用它
pingSoundPool.load(1) // Inside of YourActivity
YourActivity.pingSoundPool.load(1) // Outside of YourActivity
通过这种方式我也解决了你的最后一个问题,但也许你想知道如何从Context
传递正确的Fragment
对象:在你的Fragment
你可以声明一个Context
对象(或YourActivity
对象,它是Context
的孩子)并分配它是onAttach(..)
方法的一个值,用这种方式:
class YourFragment ... {
private lateinit var mContext : Context
private lateinit var mActivity : YourActivity // You don't need both
override fun onAttach(context: Context?) {
super.onAttach(context)
mContext = context!!
if (context is YourActivity)
mActivity = context
}
}
然后在你的片段中你可以调用PingSoundPool(mContext)
(或PingSoundPool(mActivity)
)。
请注意,onAttach
在任何其他回调方法之前调用,因此也在onCreateView
之前调用。
您还可以使用getContext()
获取Fragment上下文(仅在Kotlin中使用context!!
),但我认为上述解决方案更好,更安全。
您不应该将Application
的实例创建为新的Context
,您需要将现有的Context
传递给您的方法:
fun loadpings(note: Int, ctx: Context) {
...
val cping = mSoundPool.load(ctx, R.raw.cping, 1)
}
如果你从Activity调用该方法只需传递this@YourActivity
:
PingSoundPool.loadpings(0, this@YourActivity)
要不就
PingSoundPool.loadpings(0, this)
问题出在这一行:
val context = Application()
在这里你要创建一个你不应该做的新的Application
对象。你有两个选择:
PingSoundPool
作为构造函数参数使class
成为Context
,并使用它代替:class PingSoundPool(private val context: Context) {
...
fun loadpings(note: Int) {
[...]
val cping = mSoundPool.load(context, R.raw.cping, 1)
val dbping = mSoundPool.load(context, R.raw.dbping, 1)
[...]
}
loadPings
接受Context
作为参数:fun loadPings(note: Int, context: Context) {
[...]
val cping = mSoundPool.load(context, R.raw.cping, 1)
val dbping = mSoundPool.load(context, R.raw.dbping, 1)
[...]
}
然后对于两种方法1.和2.传递你使用的Application
/ Activity
/ Fragment
作为Context
。就个人而言,我更喜欢方法1.因为这会更好地扩展,如果你将来添加更多依赖Context
的方法。
希望有所帮助!