LibGDX Box2D Physics Direction Inverted - kotlin

For some reason in my world physics direction is inverted.I mean -9.8 gravity is acting like 9.8 gravity so objects are going up.Horizontal velocity is also inverted.Something must be wrong with x,y coordinate system.
world = World(Vector2(0f,-9.8f),false) pushes objects up
is there a problem with PPM ?
package com.mygdx.game
import com.badlogic.gdx.ApplicationAdapter
import com.badlogic.gdx.Gdx
import com.badlogic.gdx.Input
import com.badlogic.gdx.graphics.GL20
import com.badlogic.gdx.math.Vector2
import com.badlogic.gdx.physics.box2d.*
import com.badlogic.gdx.graphics.OrthographicCamera
import com.badlogic.gdx.math.Vector3
import com.badlogic.gdx.physics.box2d.World
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer
class MyGdxGame : ApplicationAdapter() {
val PPM = 32f
private lateinit var camera : OrthographicCamera
private lateinit var world :World
private lateinit var player:Body
private lateinit var platform:Body
private lateinit var b2dr:Box2DDebugRenderer
override fun create() {
var w = Gdx.graphics.width
var h = Gdx.graphics.height
camera = OrthographicCamera()
camera.setToOrtho(false, w / 2f, h / 2f)
world = World(Vector2(0f,-9.8f),false)
b2dr = Box2DDebugRenderer()
player = createBox(2f,10f,32,32,false)
platform = createBox(0f,0f,64,32,true)
}
override fun render() {
update(Gdx.graphics.deltaTime)
Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT)
b2dr.render(world,camera.combined.scl(PPM))
}
override fun resize(width: Int, height: Int) {
camera.setToOrtho(false, width / 2f, height / 2f)
}
override fun dispose() {
world.dispose()
b2dr.dispose()
}
fun update(delta:Float){
world.step(1/60f,6,2)
inputUpdate(delta)
cameraUpdate(delta)
}
fun inputUpdate(delta:Float){
var horizontalForce = 0f;
if(Gdx.input.isKeyPressed(Input.Keys.LEFT)){
horizontalForce -=1
}
if(Gdx.input.isKeyPressed(Input.Keys.RIGHT)){
horizontalForce +=1
}
if(Gdx.input.isKeyJustPressed(Input.Keys.UP)){
player.applyForceToCenter(0f,300f,false)
}
player.setLinearVelocity(horizontalForce*5,player.linearVelocity.y)
}
fun cameraUpdate(delta:Float){
var position:Vector3 = camera.position
position.x = player.position.x * PPM // if you get box2d units multiply with ppm
position.y = player.position.y * PPM
camera.position.set(position)
camera.update()
}
fun createBox(x:Float,y:Float,width:Int,height:Int,isStatic:Boolean):Body{
var pBody:Body
var def:BodyDef
def = BodyDef()
if(isStatic){
def.type = BodyDef.BodyType.StaticBody
}
else{
def.type = BodyDef.BodyType.DynamicBody
}
def.position.set(x/PPM,y/PPM)
def.fixedRotation = true
pBody = world.createBody(def)
var shape:PolygonShape = PolygonShape()
shape.setAsBox(width/2/PPM,height/2/PPM) // takes from center this is 32x32 box
pBody.createFixture(shape,1.0f)
shape.dispose()//clean it
return pBody
}
}

It's all correct with box2d and your code.
-9.81 pushes objects up and 9.81 pull objects down.
In the World constructor, you set the gravity of the world.
And Gravity is a natural phenomenon by which all things with mass or
energy—including planets, stars, galaxies, and even light—are brought
toward (or gravitate toward) one another.
Basic Gravity is the force with which object attract each other.
So on Earth gravity is the force with which objects are attracted to the center of the earth.
On Earth, we have a gravity of ~ 9.81 means all Objects will pull to the center of the earth with a force of ~ 9.81 m/s².
Finally is it logical that if gravity is a force which pulls objects down that -gravity will push objects up.
On Earth, we have a gravity of ~ 9.81 not a gravity of ~ -9.81.
If it were, we would all end up outside in the universe.

Related

Libgdx 3d particle error on initialization

I'm having a rendering issue for 3d particles created with the flame particle editor in libgdx.
I'm using a particle system to render 3d particles created with flame, I followed the tutorial in the libgdx' official site: code taken from: , but when I try to render them, I get the following error:
Cannot invoke "com.badlogic.gdx.graphics.g3d.particles.batches.ParticleBatch.draw(com.badlogic.gdx.graphics.g3d.particles.renderers.ParticleControllerRenderData)" because "this.batch" is null
Here's my code:
class MyGame : ApplicationAdapter(){
//3d stuff
lateinit var cam: PerspectiveCamera
lateinit var camController: CameraInputController
lateinit var modelBatch: ModelBatch
lateinit var environment: Environment
lateinit var model: Model
lateinit var mb:ModelBuilder
//lateinit var ground:ModelInstance
lateinit var ecs:World
//creating the bullet world
lateinit var broadphase: btBroadphaseInterface
lateinit var dynamicsWorld: btDynamicsWorld
lateinit var constraintSolver: btConstraintSolver
lateinit var collisionConfig: btCollisionConfiguration
lateinit var dispatcher: btDispatcher
lateinit var contactListener: MyContactListener
//debug drawer
lateinit var debugDrawer: DebugDrawer
//particle effects
lateinit var particleSystem:ParticleSystem
lateinit var pointSpriteBatch:PointSpriteParticleBatch
override fun create() {
Bullet.init()
//initializing 3d stuff
mb = ModelBuilder()
initializeWorld()
initializeBullet()
ecs= world {
injectables {
add(mb)
add(modelBatch)
add(cam)
add(camController)
add(environment)
add(dynamicsWorld)
add(debugDrawer)
add(contactListener)
}
components {
onAdd(BulletComponent, BulletComponent.onAdd)
onRemove(BulletComponent, BulletComponent.onRemove)
}
systems {
//the model system works just fine!
add(ModelSystems())
//this is going to be fun
add(BulletSystem())
}
}
with(ecs){//creating the ground
mb.begin()
//ok so for every model you create you giv it an id (makes everything more flexible)
mb.node().id="ground"
mb.part(
"ground",
GL20.GL_TRIANGLES,
(VertexAttributes.Usage.Position or VertexAttributes.Usage.Normal).toLong(),
Material(
ColorAttribute.createDiffuse(Color.RED)
)
)
.box(0f, 0f, 0f, 5f, 1f, 5f)
model=mb.end()
var motionState=MotionState(ModelInstance(model).transform)
val ground=entity {
//it+=ModelComponent(ModelInstance(model))
it+=BulletComponent(motionState, btBoxShape(Vector3(2.5f, 0.5f, 2.5f)),0f)
}
}
}
private fun initializeBullet() {
collisionConfig = btDefaultCollisionConfiguration()
dispatcher = btCollisionDispatcher(collisionConfig)
broadphase = btDbvtBroadphase()
//setting the dynamic world
constraintSolver= btSequentialImpulseConstraintSolver()
dynamicsWorld= btDiscreteDynamicsWorld(dispatcher,broadphase, constraintSolver, collisionConfig)
dynamicsWorld.gravity= Vector3(0f, -1.8f, 0f)
contactListener = MyContactListener()
debugDrawer=DebugDrawer().apply {
debugMode= btIDebugDraw.DebugDrawModes.DBG_DrawWireframe
}
dynamicsWorld.debugDrawer=debugDrawer
}
fun initializeWorld(){
modelBatch = ModelBatch()
environment = Environment()
environment.set(ColorAttribute(ColorAttribute.AmbientLight, 0.4f, 0.4f, 0.4f, 1f))
environment.add(DirectionalLight().set(0.8f, 0.8f, 0.8f, -1f, -0.8f, -0.2f))
cam = PerspectiveCamera(
67f, Gdx.graphics.width.toFloat(), Gdx.graphics.height
.toFloat()
)
cam.position[3f, 7f] = 10f
cam.lookAt(0f, 4f, 0f)
cam.near = 1f
cam.far = 300f
cam.update()
camController = CameraInputController(cam)
Gdx.input.inputProcessor = camController
//initializing particle effects
particleSystem=ParticleSystem()
pointSpriteBatch = PointSpriteParticleBatch()
pointSpriteBatch.setCamera(cam)
particleSystem.add(pointSpriteBatch)
//adding the particle effect to an asset manager
var assets=AssetManager()
var loadParam=ParticleEffectLoader.ParticleEffectLoadParameter(particleSystem.batches)
assets.load("myfirst3dparticles.pfx",
ParticleEffect::class.java, loadParam)
assets.finishLoading()
//passing the particle effect from the asset manager to the particle system
val originalEffect: ParticleEffect = assets.get("myfirst3dparticles.pfx")
// we cannot use the originalEffect, we must make a copy each time we create new particle effect
val effect = originalEffect.copy()
effect.init()
effect.start() // optional: particle will begin playing immediately
particleSystem.add(effect)
}
override fun render() {
Gdx.gl.glClearColor(0f, 0f, 0f, 1f)
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT or GL20.GL_DEPTH_BUFFER_BIT)
Gdx.gl.glEnable(GL20.GL_TEXTURE_2D)
//always update the camera if you're gonna make changes to it
//cam.update()
camController.update()
modelBatch.begin(cam)
//modelBatch.render(ground, environment)
//setting particles to be rendered
particleSystem.update()
particleSystem.begin()
particleSystem.draw()
particleSystem.end()
modelBatch.render(particleSystem)
ecs.update(Gdx.graphics.deltaTime)
modelBatch.end()
}

What is the unit of Canvas's size when I use Compose?

In Code A, I get val width = this.size.width when I use Canvas of Compose.
Could you tell me what the unit of the width? is it px? or is it dp?
Code A
Box(
modifier=Modifier.matchParentSize()
){
Canvas(
modifier = Modifier
) {
val width = this.size.width
}
}
Canvas size comes from DrawScope interface which is Size that contains Float properties width and height which is in px.
interface DrawScope : Density {
/**
* The current [DrawContext] that contains the dependencies
* needed to create the drawing environment
*/
val drawContext: DrawContext
/**
* Center of the current bounds of the drawing environment
*/
val center: Offset
get() = drawContext.size.center
/**
* Provides the dimensions of the current drawing environment
*/
val size: Size
get() = drawContext.size
/**
* The layout direction of the layout being drawn in.
*/
val layoutDirection: LayoutDirection
// draw functions
}
And Canvas is a Spacer with Modifier.drawBehind
#Composable
fun Canvas(modifier: Modifier, onDraw: DrawScope.() -> Unit) =
Spacer(modifier.drawBehind(onDraw))
because onDraw being a lambda with DrawScope receiver we access DrawScope properties.

Do I need set isAntiAlias when I use Android Compose Canvas?

I have read the article.
I know that maybe I need to set isAntiAlias=true when I use nativeCanvas just like Code A.
Do I need set isAntiAlias when I use Android Compose Canvas such as Code B ? If so, how can I do it?
Code A
#Composable
fun ArcTextExample() {
val paint = Paint().asFrameworkPaint()
Canvas(modifier = Modifier.fillMaxSize()) {
paint.apply {
isAntiAlias = true
textSize = 24f
typeface = Typeface.create(Typeface.DEFAULT, Typeface.BOLD)
}
drawIntoCanvas {
val path = Path()
path.addArc(RectF(0f, 100f, 200f, 300f), 270f, 180f)
it.nativeCanvas.drawTextOnPath("Hello World Example", path, 0f, 0f, paint)
}
}
}
Code B
#Composable
fun setCanvas() {
Canvas(
modifier = Modifier
.fillMaxSize()
) {
val canvasWidth = size.width
val canvasHeight = size.height
drawLine(
start = Offset(x = 0f, y = canvasHeight),
end = Offset(x = canvasWidth, y = 0f),
color = Color.Blue
)
}
}
Usually, isAntiAlias is used when drawing text to the canvas, Because it will smoothen the edges
You have to access the native canvas from the Android framework in order to draw some text, you can call all the methods related to the native Canvas like drawText() or drawVertices()
To apply a style to the text, a Paint object must be used, and we can change isAntiAlias property just like the code A.
And in compose there is no concept of Paint because compose already took care of those and made easy to draw shapes, but for drawing texts you can only do this with help of nativeCanvas and paint where you can declare AntiAlias_Flag to true for smoothening the edges.

function call in onActivityCreated() triggers Observer every time the app restarts

in my fragment I am observing a live data in a function and in that observer, some sharedPreferences are changed. The function is then called inside onActivityCreated() .The problem is whenever I restart my app the onActivityCreated() gets called which in turn calls that function which in turn observes the live data and thus changes the value of sharedPreference which I don't want.
code to my fragment is attached.
package com.example.expensemanager.ui
import android.app.AlertDialog
import android.content.Context
import android.content.SharedPreferences
import android.graphics.Color
import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.core.content.ContextCompat
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.example.expensemanager.R
import com.github.mikephil.charting.data.PieData
import com.github.mikephil.charting.data.PieDataSet
import com.github.mikephil.charting.data.PieEntry
import kotlinx.android.synthetic.main.fragment_transaction_list.*
import kotlinx.android.synthetic.main.set_balance_details.view.*
import org.eazegraph.lib.models.PieModel
class TransactionListFragment : Fragment() {
//declaring the view model
private lateinit var viewModel: TransactionListViewModel
var cashAmount:Float = 0F
var bankAmount:Float = 0F
override fun onCreate(savedInstanceState: Bundle?){
super.onCreate(savedInstanceState)
//setHasOptionsMenu(true)
//(activity as AppCompatActivity?)!!.setSupportActionBar(addAppBar)
viewModel = ViewModelProvider(this)
.get(TransactionListViewModel::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
return inflater.inflate(R.layout.fragment_transaction_list, container, false)
}
override fun onActivityCreated(savedInstanceState: Bundle?) {
super.onActivityCreated(savedInstanceState)
//recycler view for showing all the transactions
with(transaction_list){
layoutManager = LinearLayoutManager(activity)
adapter = TransactionAdapter {
findNavController().navigate(
TransactionListFragmentDirections.actionTransactionListFragmentToTransactionDetailFragment(
it
)
)
}
}
//code for the floating action button in the main screen
add_transaction.setOnClickListener{
findNavController().navigate(
//here id is passed 0 because the transaction is being added the first time
TransactionListFragmentDirections.actionTransactionListFragmentToTransactionDetailFragment(
0
)
)
}
addAppBar.setOnMenuItemClickListener { menuItem ->
when(menuItem.itemId){
R.id.calendar_button -> {
findNavController().navigate(TransactionListFragmentDirections.actionTransactionListFragmentToCalanderViewFragment())
true
}
R.id.monthly_cards -> {
findNavController().navigate(TransactionListFragmentDirections.actionTransactionListFragmentToMonthlyCardsFragment())
true
}
else -> false
}
}
see_all_transactions.setOnClickListener {
findNavController().navigate(TransactionListFragmentDirections.actionTransactionListFragmentToAllTransactionsFragment())
}
//submitting the new list of upcoming Transactions after getting it from the db
viewModel.upcomingTransactions.observe(viewLifecycleOwner, Observer {
(transaction_list.adapter as TransactionAdapter).submitList(it)
})
val sharedPreferences: SharedPreferences = this.requireActivity().getSharedPreferences("OnboardingDetails", Context.MODE_PRIVATE)
val monthlyBudget = sharedPreferences.getFloat("monthlyBudget",0F)
var totalBalance = monthlyBudget*12
net_balance.text = totalBalance.toString()
Log.d("netbalance",totalBalance.toString())
//the net balance (yearly) is calculated wrt the transactions already done
viewModel.sumOfTransactions.observe(viewLifecycleOwner, Observer {
if (it != null) {
totalBalance += it
net_balance.text = totalBalance.toString()
}
})
val budgetPreferences: SharedPreferences =
this.requireActivity().getSharedPreferences("Balance_details", Context.MODE_PRIVATE)
val editor: SharedPreferences.Editor = budgetPreferences.edit()
//setting pie chart initially to 0
setPieChart(budgetPreferences,editor)
//observing the cash details and the bank details to update the text view and the pie chart
observeBalance(budgetPreferences,editor)
//GraphCardView code
//button for setting the balance details
set_balance_details.setOnClickListener {
setBalanceDetails(budgetPreferences,editor)
}
}
//dialog box for setting the balance details
private fun setBalanceDetails(budgetPreferences: SharedPreferences,editor: SharedPreferences.Editor) {
val dialog = LayoutInflater.from(requireContext()).inflate(
R.layout.set_balance_details,
null
)
//AlertDialogBuilder
val mBuilder = AlertDialog.Builder(requireContext())
.setView(dialog)
//show dialog
val mAlertDialog = mBuilder.show()
dialog.save_details.setOnClickListener {
cashAmount = dialog.cash_amount.editText?.text.toString().toFloat()
bankAmount = dialog.bank_amount.editText?.text.toString().toFloat()
//saving the cashAmount and bankAmount to shared preferences for future use
editor.putFloat("cashAmount", cashAmount).apply()
editor.putFloat("bankAmount", bankAmount).apply()
//setting the pie chart with new values
setPieChart(budgetPreferences, editor)
mAlertDialog.dismiss()
}
dialog.cancel_details.setOnClickListener { mAlertDialog.dismiss() }
mAlertDialog.show()
}
private fun observeBalance(budgetPreferences: SharedPreferences,editor: SharedPreferences.Editor) {
//getting the cashAmount and bankAmount and updating the views with live data
var cashAmount = budgetPreferences.getFloat("cashAmount", 0F)
var bankAmount = budgetPreferences.getFloat("bankAmount", 0F)
viewModel.cashAmount.observe(viewLifecycleOwner, Observer {
if (it != null) {
cashAmount += it
cash.text = "CASH : ${cashAmount}"
Log.d("observeCash",cashAmount.toString())
editor.putFloat("cashAmount",cashAmount).apply()//find solution to this
setPieChart(budgetPreferences,editor)
}
})
viewModel.bankAmount.observe(viewLifecycleOwner, Observer {
if (it != null) {
bankAmount+=it
bank.text = "BANK : ${bankAmount}"
Log.d("observeBank",bankAmount.toString())
editor.putFloat("cashAmount",cashAmount).apply()
setPieChart(budgetPreferences,editor)
}
})
setPieChart(budgetPreferences,editor)
}
//https://www.geeksforgeeks.org/how-to-add-a-pie-chart-into-an-android-application/ use this for reference
private fun setPieChart(budgetPreferences: SharedPreferences,editor: SharedPreferences.Editor) {
val cashAmount = budgetPreferences.getFloat("cashAmount", 0f)
val bankAmount = budgetPreferences.getFloat("bankAmount", 0f)
Log.d("pieCank",cashAmount.toString())
Log.d("pieBank",bankAmount.toString())
cash.text = "CASH : ${cashAmount}"
bank.text = "BANK : ${bankAmount}"
val pieEntries = arrayListOf<PieEntry>()
pieEntries.add(PieEntry(cashAmount))
pieEntries.add(PieEntry(bankAmount))
pieChart.animateXY(1000, 1000)
// setup PieChart Entries Colors
val pieDataSet = PieDataSet(pieEntries, "This is Pie Chart Label")
pieDataSet.setColors(
ContextCompat.getColor(requireActivity(), R.color.blue1),
ContextCompat.getColor(requireActivity(), R.color.blue2)
)
val pieData = PieData(pieDataSet)
// setip text in pieChart centre
//piechart.setHoleColor(R.color.teal_700)
pieChart.setHoleColor(getColorWithAlpha(Color.BLACK, 0.0f))
// hide the piechart entries tags
pieChart.legend.isEnabled = false
// now hide the description of piechart
pieChart.description.isEnabled = false
pieChart.description.text = "Expanses"
pieChart.holeRadius = 40f
// this enabled the values on each pieEntry
pieData.setDrawValues(true)
pieChart.data = pieData
}
fun getColorWithAlpha(color: Int, ratio: Float): Int {
var newColor = 0
val alpha = Math.round(Color.alpha(color) * ratio)
val r = Color.red(color)
val g = Color.green(color)
val b = Color.blue(color)
newColor = Color.argb(alpha, r, g, b)
return newColor
}
}
As seen when app restarts , viewModel.cashAmount gets triggered giving undesired outputs.
What can i do to avoid this .
Activities can get recreated a lot, like when you rotate the screen, or if it's in the background and the system destroys it to free up some memory. Right now, every time that happens your code doesn't know whether it's getting the current value, or a brand new one, but one of those should perform a calculation, and the other should just update the display.
The problem is your calculation logic is tied in with the UI state - it's being told what to display, and also deciding if that counts as a new user action or not. And it has no way of knowing that. Your logic needs to go something like
things observe LiveData values -> update to display new values when they come in
user clicks a button -> do calculation with the value they've entered
calculation result returns -> LiveData gets updated with new value
LiveData value changes -> things update to show the new value
that way a calculation happens specifically in response to a user action, like through a button click. LiveData observers only reflect the current state, so it doesn't matter if they see the same value lots of times, they're just redrawing a pie chart or whatever.
You can use a LiveData to watch for a stream of values, but the thing about UI components is that sometimes they're there to see them, and sometimes they're not. And LiveData is specifically made to push updates to active observers, but not inactive ones - and always provide the most recent value to a new observer, or one that becomes active.
So in that case it works more like "here's the current situation", and that fits better with displaying things, where it doesn't matter if you repeat yourself. That's why you can't do this kind of "handle everything exactly one time" thing in your UI - unless you're literally responding to a UI event like a button click

Kotlin Coroutines Run blocking not working as expected and Cannot block execution of for loops

In my app i'm executing two for loops however those for loops need to scheduled in a order here is the use case:
There are two for loops :
1- ImageStickerslist
2-TextStickerslist
What i want to do is after imagestickerslist if properly finised only then textstickerslist will be executed.
Here imagesticker list consists of url path which is used to load images from glide however if those images are of high resolution it eventually makes the thread continue even if the image is not yet loaded from url. To solve this tried adding blocking calls to glide on ready and full method but it won't prove to be of any help. I'm very confused how blocking calls work any help into this will be really appreciated.
Here's my code where for loops are executed:
runBlocking {
launch {
imagestickers.forEach {
runBlocking {
var image = it.path
var x = it.x
var y = it.y
image!!.log()
setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
}
}
}.join()
textstickers.forEach {
runBlocking {
var text = it.text.toString()
var color = it.color
var font = it.font
var size = it.size
var x = it.x
var y = it.y
setTextSticker(text, Color.parseColor(color), size!!, x!!, y!!)
}
}
}
Here are my two methods where main computation is taking place:
fun setimagestickers(path:String,x:Int,y:Int,w:Int,h:Int){
Glide.with(this#NewStickerActivity).asBitmap().timeout(6000000).load(path).into(object : CustomTarget<Bitmap>() {
override fun onLoadCleared(placeholder: Drawable?) {
}
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
var size: ViewGroup.LayoutParams
var bmp1 = resource
size = UiHelper.getHeightWidth(this#NewStickerActivity, (w).toInt(), (h).toInt())
var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
var drawable = BitmapDrawable(resources, resizedBitmap)
var dsImageSticker = DrawableSticker(drawable)
dsImageSticker.setTag("ImageSticker")
var pm: List<Int>
if (density > 3.0) {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
} else {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
}
Log.i("Hmmm:", pm.get(0).toFloat().toString() + "::" + pm.get(1).toFloat().toString())
stickerView.addStickerAndSetMatrix1(
dsImageSticker,
pm.get(0).toFloat(),
pm.get(1).toFloat()
)
}
})
}
fun setTextSticker(text: String, color: Int,size: Int, x: Int, y: Int){
val bmp1: Bitmap
val drawable: Drawable
var l: List<Int>
if (density > 3.0) {
l = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y * 1.07).toInt())
} else {
l = UiHelper.getmargins(this#NewStickerActivity, x.toInt(), y.toInt())
}
//var tf = Typeface.createFromFile(assets,"fonts/"+path)
var tf = Typeface.createFromAsset(assets, "fonts/Myriad Pro Bold SemiExtended.ttf")
bmp1 = createBitmapFromLayoutWithText(this#NewStickerActivity, size, text, color, 0f, tf, 0f, 0f, color, Gravity.LEFT)
drawable = BitmapDrawable(resources, bmp1)
var dsTextSticker = DrawableSticker(drawable)
dsTextSticker.setTag("textSticker")
Log.i("Hmmm:", l.get(0).toFloat().toString() + "::" + l.get(1).toFloat().toString())
/*if (rotate) {
stic.addStickerAndSetrotate(
dsTextSticker, rotationdegress,
l.get(0).toFloat(),
l.get(1).toFloat()
)
} else {*/
stickerView.addStickerAndSetMatrix1(
dsTextSticker,
l.get(0).toFloat(),
l.get(1).toFloat())
}
UPDATE:
I got this working without coroutines by incrementing and fetching images in a sequence:
Firstly i took a Int and then kept incrementing until it reached list size here is my code:
Firstly i did this :
var i = 0
setimagestickers(imagestickers.get(i).path!!, imagestickers.get(i).x!!, imagestickers.get(i).y!!, imagestickers.get(i).width!!, imagestickers.get(i).height!!)
After that inside on resource ready did the trick!!
Glide.with(this#NewStickerActivity).asBitmap().timeout(6000000).load(path).into(object : CustomTarget<Bitmap>() {
override fun onLoadCleared(placeholder: Drawable?) {
}
override fun onResourceReady(resource: Bitmap, transition: Transition<in Bitmap>?) {
var size: ViewGroup.LayoutParams
var bmp1 = resource
size = UiHelper.getHeightWidth(this#NewStickerActivity, (w).toInt(), (h).toInt())
var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
var drawable = BitmapDrawable(resources, resizedBitmap)
var dsImageSticker = DrawableSticker(drawable)
dsImageSticker.setTag("ImageSticker")
var pm: List<Int>
if (density > 3.0) {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
} else {
pm = UiHelper.getmargins(this#NewStickerActivity, (x).toInt(), (y).toInt())
}
Log.i("Hmmm:", pm.get(0).toFloat().toString() + "::" + pm.get(1).toFloat().toString())
stickerView.addStickerAndSetMatrix1(
dsImageSticker,
pm.get(0).toFloat(),
pm.get(1).toFloat()
)
i++
if(i < imagestickers.size){
setimagestickers(imagestickers.get(i).path!!, imagestickers.get(i).x!!, imagestickers.get(i).y!!, imagestickers.get(i).width!!, imagestickers.get(i).height!!)
}
else{
if(textstickers.isNullOrEmpty()){
loader!!.hide()
}
else {
setTextSticker(textstickers.get(j).text!!, Color.parseColor(textstickers.get(j).color), textstickers.get(j).size!!, textstickers.get(j).x!!, textstickers.get(j).y!!)
}
}
}
})
However i'm still wondering how can i solve it with coroutines rather than this approach!!!
Basically, you're making async Glide calls without suspending the coroutine until they are done. The first thing you must change is introduce a suspend fun getImageSticker():
suspend fun getSticker(path: String): Bitmap =
suspendCancellableCoroutine { continuation -> Glide
.with(this#NewStickerActivity)
.asBitmap()
.timeout(6000000)
.load(path)
.into(object : CustomTarget<Bitmap>() {
override fun onResourceReady(resource: Bitmap, x: Transition<in Bitmap>?) {
continuation.resume(resource)
}
})
}
Note that this returns a Bitmap. Now you can just call it as if it was a regular, blocking function, and use its result from the caller side:
suspend fun setimagestickers(path: String, x: Int, y: Int, w: Int, h: Int) {
val resource = getSticker(path)
var size: ViewGroup.LayoutParams
var bmp1 = resource
size = UiHelper.getHeightWidth(this#NewStickerActivity, (w).toInt(), (h).toInt())
var resizedBitmap = Bitmap.createScaledBitmap(bmp1, size.width, size.height, false)
}
Now, if you want to parallelize setimagestickers calls, launch them in a loop:
launch {
imagestickers.map {
launch {
var image = it.path
var x = it.x
var y = it.y
image!!.log()
setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
}
}.joinAll()
textstickers.forEach {
...
}
}
None of your code will benefit from runBlocking or the coroutines you launch.
If you are aiming to simply process each of your Image type objects in paralellel due to the long running nature of each task, then you can do something like this:
fun main() {
runBlocking {
imagestickers.map {
launch {
var image = it.path
var x = it.x
var y = it.y
image!!.log()
setimagestickers(image!!, x!!, y!!, it.width!!, it.height!!)
}
}.joinAll()
textstickers.map {
launch {
var text = it.text.toString()
var color = it.color
var font = it.font
var size = it.size
var x = it.x
var y = it.y
setTextSticker(text, Color.parseColor(color), size!!, x!!, y!!)
}
}.joinAll()
}
}
Why I removed some of the stuff you were doing:
The nested runBlocking calls you had did nothing. `runBlocking means "block until whatever is inside this block is finished". Your for loops were not async, and would run until they were done anyway.
The first coroutine launch you had did nothing as well. You launched it, and then immediately called join. This means that you launch a coroutine, but then immediately wait until it is finished before proceeding.
What this does:
Rather than loops, we map each image object to a list of jobs for concurrently running coroutines.
We then wait for every job to finish in each list before proceeding to the next list.
Please Note: It does not look like any of your code benefits from having non-blocking IO, in which case, this parralelism will be limited to the amount of threads you have. That fetching of the image will block the thread it is on (regardless of using coroutines) unless you use a non-blocking library that properly yields while it fetches your image.