Передача контекста из пользовательского представления в класс - PullRequest
0 голосов
/ 07 июля 2019

Я прочитал, что плохая практика вызывать invalidate () внутри метода onDraw, поэтому я хочу иметь возможность вызывать invalidate () из моего класса Ball для метода onDraw, который находится в классе Game.

В другом посте я прочитал, что создание объекта класса Game, а затем вызов этого объекта, например gameClass.invalidate (), - лучший способ сделать это, и я пытаюсь выяснить, как это сделать.

У меня естьПроблемы с передачей контекста и атрибутов.

Вот мой класс CustomView:

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

Вот класс, в котором мне нужно вызвать invalidate: есть ли лучший способ для вызова invalidate () здесь?

val gameClass = Game (как мне передать контекст и атрибуты здесь?)

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

}

вот мой xml

<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout
    xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:tools="http://schemas.android.com/tools"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    tools:context=".MainActivity">

<view android:layout_width="0dp" 
      android:layout_height="0dp"
      class="com.example.myapplication.Game"
      id="@+id/view4"
      app:layout_constraintTop_toTopOf="parent"
      app:layout_constraintBottom_toBottomOf="parent"
      app:layout_constraintStart_toStartOf="parent"
      app:layout_constraintEnd_toEndOf="parent"/>

1 Ответ

0 голосов
/ 07 июля 2019

Хорошо, обо всем по порядку.Это может быть опечатка в вашем вопросе, но в соответствии с вашим кодом класс Game создает экземпляр объекта Ball, а класс Ball создает экземпляр объекта Game.Это называется циклическая зависимость , и вам это не нужно, потому что это приведет к ошибке 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()

Вот как мы можем это сделать.Поскольку ваше представление выполняется в потоке пользовательского интерфейса, нам нужно получить в него Handler и попросить пользовательский интерфейс непрерывно запускать Runnable с помощью Handler.post().Этот Runnable будет содержать код, который обновит ваш Ball объект и вызовет invalidate().К счастью, сам класс View содержит метод post(), который совпадает с Handler.post(), поэтому мы можем безопасно вызывать этот метод внутри нашего Game класса, потому что Game наследует View.

Здеськод:

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() из MainActivity

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

Чтобы остановить анимацию, просто позвоните stopAnimation()

Узнайте больше о процессах, потоках и обработчике здесь:

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...