我读到在onDraw方法中调用invalidate()是一种不好的做法,因此我希望能够将Ball类中的invalidate()调用到Game类中的onDraw方法。
在另一篇文章中,我读到创建Game类的对象,然后像gameClass.invalidate()那样调用该对象是最好的方法,并且我试图弄清楚该怎么做。
我无法传递上下文和属性。
这是我的CustomView类:
string text = File.ReadAllText(openFileDialog.FileName, Encoding.GetEncoding(1252));
string[] textLines = text.Split(new[] { Environment.NewLine }, StringSplitOptions.None);
int l = textLines.Length - 1;
for (int i = l; i >= 0; i--)
{
string[] questions = textLines[i].Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
int n = questions.Length;
for (int y = 0; y < n; y++)
textLines[i] = textLines[i] + "\n" + questions[y].Trim();
}
txtEditor.Text = string.Join("\n", textLines);
这是我需要调用无效的课程: 有没有更好的方法在这里调用invalidate()
val gameClass = Game(我如何在此处传递上下文和属性?)
class Game(context: Context?, attrs: AttributeSet?) : View(context,
attrs){
private val ball1 = Ball(width/2 - 50.toDouble(),150.0,20.0)
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.apply {
drawOval((width / 2) - 50,
ball1.posy.toFloat() - 50f,
(width / 2) + 50,
ball1.posy.toFloat() + 50f,
circleColor)
}
ball1.update()
}
这是我的xml
class Ball(var posx: Double, var posy:Double,var velocity: Double){
//how do i pass the context and attrs here?
val gameClass = Game(...)
fun update(){
posy += 10
gameClass.invalidate()
}
}
答案 0 :(得分:0)
首先,首先。这可能是您输入问题的错字,但是根据您的代码,Game
类正在实例化一个Ball
对象,而Ball
类正在实例化一个Game
对象。这称为circular dependency,并且您不希望这样做,因为它会导致StackOverFlow
错误(讽刺吗?)。因此,从Game
类中删除Ball
对象。
第二,由于您要在ball1.update()
内部调用onDraw()
,因此在invalidate()
内部调用ball1.update()
与在ball1.update()
之后调用它没什么不同。在这两种情况下,您都是在invalidate()
内部调用onDraw()
。
invalidate()
的目的是告诉我们,只要我们更改与视图相关的任何数据,就可以重新绘制视图。由于您要通过调用onDraw()
来更改ball1.update()
内部与视图相关的数据,因此紧随其后调用invalidate()
是明智的选择。这样,它将适用于您的情况。
class Game(context: Context, attributes: AttributeSet): View(context,attributes) {
private val paint :Paint = Paint(ANTI_ALIAS_FLAG)
private val ball1 = Ball(width/2 - 50.toDouble(),150.0,20.0)
init{
paint.color = Color.CYAN
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.apply {
drawOval(
(width / 2) - 50f,
ball1.posy.toFloat() - 50f,
(width / 2) + 50f,
ball1.posy.toFloat() + 50f,
paint)
ball1.update()
invalidate()
}
}
}
但是onDraw()
并不是呼叫invalidate()
的最佳位置。与性能有关的问题很多,因此最好不要onDraw()
。
在这里阅读:
我假设您想为视图添加动画。因此,最好阅读animation上的文档。
但是无论如何,让我们继续。如果您要避免从invalidate()
调用onDraw()
,但仍然希望获得相同的结果。最好的选择是在Game
类内创建另一个函数,该函数将开始更新Ball
对象并连续调用invalidate()
的过程。我将此方法命名为startAnimation()
这就是我们可以做到的。由于您的视图是在UI线程上运行的,因此我们需要获取一个Handler
并告诉UI使用Handler.post()
来连续运行Runnable。该Runnable
将包含将更新您的Ball
对象并调用invalidate()
的代码。幸运的是View
类本身包含与post()
相同的Handler.post()
方法,因此我们可以安全地在Game
类中调用此方法,因为Game
继承了{{ 1}}。
这是代码:
View
然后我们通过从 MainActivity
调用class Game(context: Context, attributes: AttributeSet): View(context,attributes) {
private lateinit var runnable : Runnable // reference to the runnable object
private val ball1 = Ball(width/2 - 50.toDouble(),150.0,20.0)
private val paint :Paint = Paint(ANTI_ALIAS_FLAG)
//This is the constructor of the class
init{
paint.color = Color.CYAN
//Here we initialize our Runnable and put the code that we want to run once we call startAnimation()
runnable = Runnable {
ball1.update()
invalidate()
//Calling post() inside here will loop the above code and you will see a smooth animation
post(runnable)
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
canvas.apply {
drawOval(
(width / 2) - 50f,
ball1.posy.toFloat() - 50f,
(width / 2) + 50f,
ball1.posy.toFloat() + 50f,
paint)
}
}
//This is the new function I am talking about
fun startAnimation()
{
post(runnable)
}
fun stopAnimation()
{
removeCallbacks(runnable)
}
}
来启动动画
startAnimation()
要停止动画,只需调用class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val game = findViewById<Game>(R.id.view)
game.startAnimation()
}
}
在此处了解有关进程,线程和处理程序的更多信息: