将上下文从“自定义视图”传递到类

时间:2019-07-07 15:57:15

标签: android class kotlin

我读到在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()
}

}

1 个答案:

答案 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() } }

在此处了解有关进程,线程和处理程序的更多信息: