Comments added, code cleared

This commit is contained in:
2021-05-11 16:44:05 +03:00
parent e5c0287f51
commit 80391efc44
14 changed files with 239 additions and 234 deletions

View File

@@ -4,31 +4,36 @@ import android.content.Context
import android.graphics.*
import android.text.TextPaint
import android.util.AttributeSet
import android.util.Log
import android.view.Surface
import android.view.SurfaceHolder
import android.view.SurfaceView
import kotlinx.coroutines.*
import kotlin.system.measureTimeMillis
/**
* Класс наследован от SurfaceView
* В нем отрисовывается проход фразы Hello World за 20 кадров
* в превью можно переключать кадры, меняя _frameNumber от 0 до 19
* В режиме редактирования для удобной отладки строка заполняется красным фоном
*
*/
class HelloSurface : SurfaceView, SurfaceHolder.Callback {
private var _helloWorldString: String = ""
private var _textSize: Float = 100f
private var _frameNumber: Int = 7
private var _helloWorldString: String = "" //Строка для отображения
private var _frameNumber: Int = 7 // Инициализация номера фрейма, видно на превью
private var _frameRate: Int = 30 //Частота кадров
private lateinit var textPaint: TextPaint
private lateinit var backgroundPaint: Paint
private lateinit var backgroundPaint: Paint //Фон надписи в режиме редактирвоания
private var textWidth: Float = 0f
private var textHeight: Float = 0f
private var textPositionVertical = 0f
private var frameDelta = 0f
private var mainJob: Job? = null
private var persistentSurface: Surface? = null
private var persistentSurface: Surface? = null //Surface для рендера и сохранения видео
/**
* The text to draw
* Текст для отрисовки
*/
var helloWorldString: String
get() = _helloWorldString
@@ -38,22 +43,24 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
}
/**
* font Size
* Частота кадров
*/
var textSize: Float
get() = _textSize
var frameRate: Int
get() = _frameRate
set(value) {
_textSize = value
invalidateTextPaintAndMeasurements()
_frameRate = value
}
var frameNumber: Int
/**
* Номер фрейма, используется только в режиме редактирования для превью
*/
var frameNumber: Int
get() = _frameNumber
set(value) {
_frameNumber = value % 20
invalidateTextPaintAndMeasurements()
}
init {
holder.addCallback(this)
}
@@ -75,20 +82,14 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
}
private fun attrsInit(attrs: AttributeSet?, defStyle: Int) {
// Load attributes
val a = context.obtainStyledAttributes(
attrs, R.styleable.TestView, defStyle, 0
)
_helloWorldString = a.getString(R.styleable.TestView_helloString) ?: "Test String"
_textSize = a.getDimension(
R.styleable.TestView_textSize,
textSize
)
_frameNumber = a.getInteger(
R.styleable.TestView_frameNumber,
frameNumber
)
_frameRate = a.getInteger(R.styleable.TestView_frameRate, 30)
_frameNumber = a.getInteger(R.styleable.TestView_frameNumber, frameNumber)
a.recycle()
textPaint = TextPaint().apply {
@@ -103,17 +104,20 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
invalidateTextPaintAndMeasurements()
}
fun setExternalSurface(surface: Surface) {
persistentSurface = surface
}
/**
* Перегруженный onDraw остался только для превью
*/
override fun onDraw(canvas: Canvas?) {
super.onDraw(canvas)
if (isInEditMode)
canvas?.let {
drawHello(canvas, _frameNumber)
}
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
@@ -121,38 +125,46 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
}
private fun invalidateTextPaintAndMeasurements() {
val textBounds = Rect()
textPaint.let {
it.textSize = textSize
}
textPaint.getTextBounds(helloWorldString, 0, helloWorldString.length, textBounds)
textPaint.getTextBounds(_helloWorldString, 0, _helloWorldString.length, textBounds)
textHeight = textBounds.height()+0f
textWidth = textBounds.width()+0f
}
/**
* Выделение красным фоном надписи в превью
*/
private fun drawBounds(canvas: Canvas, sx: Int, sy: Int) {
val textBounds = Rect()
textPaint.getTextBounds(helloWorldString, 0, helloWorldString.length, textBounds)
textBounds.right = textPaint.measureText(helloWorldString).toInt()
textPaint.getTextBounds(_helloWorldString, 0, _helloWorldString.length, textBounds)
textBounds.right = textPaint.measureText(_helloWorldString).toInt()
textBounds.offsetTo(sx, sy-textHeight.toInt()+textPaint.descent().toInt())
canvas.drawRect(textBounds, backgroundPaint)
}
/**
* todo comment
*/
fun drawHello(canvas: Canvas, frameNumber: Int) {
/**
* Основной метод прорисовки надписи
* Так как этот метод используется для различных размеров Canvas,
* я решил сделать размер шрифта равным 1/20 от высоты Canvas
*
*/
private fun drawHello(canvas: Canvas, frameN: Int) {
val textSize = canvas.height/20f
textPaint.textSize = textSize
textWidth = textPaint.measureText(helloWorldString)
textWidth = textPaint.measureText(_helloWorldString)
textPositionVertical = canvas.height/2f+textSize/2f
frameDelta = (canvas.width+textWidth)/20f
val textPositionVertical = canvas.height/2f+textSize/2f
val frameDelta = (canvas.width+textWidth)/20f
var xPos = frameNumber*frameDelta-textWidth
val xPos = frameN*frameDelta-textWidth
if (isInEditMode) drawBounds(canvas, xPos.toInt(), textPositionVertical.toInt())
if (isInEditMode) {
invalidateTextPaintAndMeasurements()
drawBounds(canvas, xPos.toInt(), textPositionVertical.toInt())
}
canvas.drawText( helloWorldString, xPos, textPositionVertical, textPaint)
canvas.drawText( _helloWorldString, xPos, textPositionVertical, textPaint)
}
private fun clearCanvas(canvas: Canvas) {
@@ -165,20 +177,31 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
renderLoop()
}
}
@Volatile private var maxFramesRender = 120
@Volatile private var framesRendered = -1
/**
* Метод, запускающий отсчет фреймов для записи
*/
fun startRecording(maxFrames: Int) {
maxFramesRender = maxFrames
framesRendered++
}
var onLastFrame : () -> Unit = @Synchronized {}
/**
* Метод, который будет вызван из корутины после рендера последнего фрейма для записи
* Он будет переназначен во время инициализации енкодера
*/
var onLastFrame : () -> Unit = {}
fun setOnLastFrameRecordedListener(listener: () -> Unit) {
onLastFrame = listener
}
/**
* Отрисовка превью и здесь же рендер фреймов
*/
private suspend fun renderLoop() {
while(GlobalScope.isActive) {
val timeInMillis = measureTimeMillis {
@@ -193,13 +216,15 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
preview()
_frameNumber++
}
if (_frameNumber >= 20) _frameNumber = 0
delay(33-timeInMillis)
}
//Расчет паузы перед отрисовкой следующего фрейма
delay(1000/_frameRate-timeInMillis)
}
}
fun render() {
//Рендер фрейма для записи видео
private fun render() {
persistentSurface?.let {surface ->
val pCanvas = surface.lockCanvas(null)
pCanvas?.let {
@@ -211,8 +236,8 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
}
}
}
fun preview() {
//Такой же рендер, но видимый на View и работающий постоянно
private fun preview() {
val canvas = holder.lockCanvas()
canvas?.let {
clearCanvas(it)
@@ -224,7 +249,7 @@ class HelloSurface : SurfaceView, SurfaceHolder.Callback {
override fun surfaceChanged(holder: SurfaceHolder, format: Int, width: Int, height: Int) {
}
//Остановка корутины при уничтожении surface
override fun surfaceDestroyed(holder: SurfaceHolder) {
runBlocking {
mainJob?.cancelAndJoin()

View File

@@ -1,141 +0,0 @@
package su.rst10h.loopedworld
import android.content.Context
import android.graphics.Canvas
import android.graphics.Paint
import android.graphics.Typeface
import android.text.TextPaint
import android.util.AttributeSet
import android.util.Log
import android.view.SurfaceView
import android.view.View
/**
* TODO: document your custom view class.
*/
class TestView : View {
private var _helloWorldString: String = ""
private var _textSize: Float = 100f // TODO: use a default from R.dimen...
private var _frameNumber: Int = 12
private lateinit var textPaint: TextPaint
private var textWidth: Float = 0f
private var textHeight: Float = 0f
private var textPositionVertical = 0f
private var frameDelta = 0f
/**
* The text to draw
*/
var helloWorldString: String
get() = _helloWorldString
set(value) {
_helloWorldString = value
invalidateTextPaintAndMeasurements()
}
/**
* font Size
*/
var textSize: Float
get() = _textSize
set(value) {
_textSize = value
invalidateTextPaintAndMeasurements()
}
var frameNumber: Int
get() = _frameNumber
set(value) {
_frameNumber = value
invalidateTextPaintAndMeasurements()
}
constructor(context: Context) : super(context) {
init(null, 0)
}
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) {
init(attrs, 0)
}
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(
context,
attrs,
defStyle
) {
init(attrs, defStyle)
}
private fun init(attrs: AttributeSet?, defStyle: Int) {
// Load attributes
val a = context.obtainStyledAttributes(
attrs, R.styleable.TestView, defStyle, 0
)
_helloWorldString = a.getString(R.styleable.TestView_helloString) ?: "Test String Hello"
_textSize = a.getDimension(
R.styleable.TestView_textSize,
textSize
)
_frameNumber = a.getInteger(
R.styleable.TestView_frameNumber,
frameNumber
)
a.recycle()
textPaint = TextPaint().apply {
flags = Paint.ANTI_ALIAS_FLAG
textAlign = Paint.Align.LEFT
typeface = Typeface.DEFAULT_BOLD
}
invalidateTextPaintAndMeasurements()
}
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
invalidateTextPaintAndMeasurements()
}
private fun invalidateTextPaintAndMeasurements() {
textPaint.let {
it.textSize = textSize
textWidth = it.measureText(helloWorldString)
textHeight = it.fontMetrics.bottom
}
invalidate()
}
/**
* todo commen
*/
private fun drawHelloWorld(canvas: Canvas, frameNumber: Int) {
textPositionVertical = canvas.height/2f+textHeight
frameDelta = (width+textWidth)/20f
var xPos = frameNumber*frameDelta-textWidth
// if (isInEditMode) {
// xPos = width/2f-textWidth/2f
// }
helloWorldString.let {
canvas.drawText( it, xPos, textPositionVertical, textPaint)
}
}
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
// TODO: consider storing these as member variables to reduce
val paddingLeft = paddingLeft
val paddingTop = paddingTop
val paddingRight = paddingRight
val paddingBottom = paddingBottom
val contentWidth = width - paddingLeft - paddingRight
val contentHeight = height - paddingTop - paddingBottom
drawHelloWorld(canvas, _frameNumber)
}
}

View File

@@ -3,7 +3,7 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<su.rst10h.loopedworld.TestView
<su.rst10h.loopedworld.HelloSurface
style="@style/Widget.Theme.Inspiry.MyView"
android:layout_width="match_parent"
android:layout_height="match_parent"
@@ -11,6 +11,7 @@
android:paddingBottom="40dp"
app:frameNumber="8"
app:helloString="Hello World"
app:textSize="55sp" />
app:frameRate = "30"
/>
</FrameLayout>

View File

@@ -1,7 +1,7 @@
<resources>
<declare-styleable name="TestView">
<attr name="helloString" format="string" />
<attr name="textSize" format="dimension" />
<attr name="frameRate" format="integer" />
<attr name="frameNumber" format="integer" />
</declare-styleable>
</resources>