Accelerometer
title: Accelerometer Examples tags: - Sensor chapter: Accelerometer post_status: publish date: 2022-10-12 summary: Learn Accelerometer usage in android via simple examples. custom_field: chapter: Accelerometer Tutorial chapter_description: An accelerometer is a tool that measures proper acceleration. Let us look at example usage in android.
Step by step Accelerometer examples for android.
Learn programmatic usage of accelerometer in your mobile app.
What is an Accelerometer?
An accelerometer is a tool that measures proper acceleration. Proper acceleration is the acceleration of a body in its own instantaneous rest frame; this is different from coordinate acceleration, which is acceleration in a fixed coordinate system
Majprity of high-end modern smartphones tend to be fitted with an accelerometer. Thus we need to learn how to use it programmatically.
Here are some examples:
Kotlin OpenGL with Accelerometer Sensor
Simple android project with implementation of openGL SE circle shape, accelerometer sensor for measuring phone rotation.
Full Example
Let us look at a full android OpenGL sample project.
Step 1. Dependencies
We need to add some dependencies in our app/build.gradle
file as shown below:
(a). build.gradle
Our app-level
build.gradle
.
We Prepare our dependencies as shown below. You may use later versions.
At the top of our app/build.gradle
we will apply the following 3 plugins:
- Our
com.android.application
plugin. - Our
kotlin-android
plugin. - Our
kotlin-android-extensions
plugin.
We then declare our app dependencies under the dependencies
closure. We will need the following 4 dependencies:
- Our
Kotlin-stdlib
library. - Our
Core-ktx
library. - Our
Appcompat
library. - Our
Constraintlayout
library.
Here is our full app/build.gradle
:
apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
android {
compileSdkVersion 30
buildToolsVersion "29.0.3"
defaultConfig {
applicationId "pl.polsl.openglapp"
minSdkVersion 26
targetSdkVersion 30
versionCode 1
versionName "1.0"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
}
}
}
dependencies {
implementation fileTree(dir: "libs", include: ["*.jar"])
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.3.2'
implementation 'androidx.appcompat:appcompat:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.4'
testImplementation 'junit:junit:4.12'
androidTestImplementation 'androidx.test.ext:junit:1.1.2'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.3.0'
}
Step 2. Our Android Manifest
(a). AndroidManifest.xml
Our
AndroidManifest
file.
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="pl.polsl.openglapp">
<uses-feature android:glEsVersion="0x00020000" android:required="true" />
<supports-gl-texture android:name="GL_OES_compressed_ETC1_RGB8_texture" />
<supports-gl-texture android:name="GL_OES_compressed_paletted_texture" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MainActivity"
android:screenOrientation="landscape">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Step 3. Design Layouts
(a). activity_main.xml
Our
activity_main
layout.
Design your XML layout using the following 2 UI widgets:
androidx.constraintlayout.widget.ConstraintLayout
TextView
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Step 4. Write Code
Finally we need to write our code as follows:
(a). GLCircle.kt
Our
GLCircle
class.
Create a Kotlin file named GLCircle.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
Resources
from theandroid.content.res
package.GLES20
from theandroid.opengl
package.Matrix
from theandroid.opengl
package.Log
from theandroid.util
package.
We will be creating the following methods:
getModelMatrix(): FloatArray
.draw(parameter)
- We pass aFloatArray?
object as a parameter.
(a). Our getModelMatrix()
function
Write the getModelMatrix()
function as follows:
(b). Our draw()
function
Write the draw()
function as follows:
fun draw(mvpMatrix: FloatArray?){
GLES20.glUseProgram(mProgram)
positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also {
GLES20.glEnableVertexAttribArray(it)
GLES20.glVertexAttribPointer(
it,
COORDS_PER_VERTEX,
GLES20.GL_FLOAT,
false,
vertexStride,
mVertexBuffer
)
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
GLES20.glUniform4fv(mColorHandle, 1, color, 0)
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 364)
GLES20.glDisableVertexAttribArray(it)
}
}
Here is the full code:
package replace_with_your_package_name
import GLRenderer.Companion.loadShader
import android.content.res.Resources
import android.opengl.GLES20
import android.opengl.Matrix
import android.util.Log
import java.nio.ByteBuffer
import java.nio.ByteOrder
import java.nio.FloatBuffer
import kotlin.math.PI
import kotlin.math.cos
import kotlin.math.sin
const val COORDS_PER_VERTEX = 3
var circleCoords = floatArrayOf( // in counterclockwise order:
0.0f, 0.622008459f, 0.0f, // top
-0.5f, -0.311004243f, 0.0f, // bottom left
0.5f, -0.311004243f, 0.0f // bottom right
)
class Circle {
public val mModelMatrix = FloatArray(16)
val color = floatArrayOf(0f, 0f, 1f, 1.0f)
private var positionHandle: Int = 0
private var mColorHandle: Int = 0
private var mMVPMatrixHandle = 0
private val mVertexBuffer: FloatBuffer
private val vertexCount: Int = circleCoords.size / COORDS_PER_VERTEX
private val vertexStride: Int = COORDS_PER_VERTEX * 4
private val vertices = FloatArray(364 * 3)
private var mProgram: Int
private val vertexShaderCode =
"uniform mat4 uMVPMatrix;" +
"attribute vec4 vPosition;" +
"void main() {" +
" gl_Position = uMVPMatrix * vPosition;" +
"}"
private val fragmentShaderCode =
"precision mediump float;" +
"uniform vec4 vColor;" +
"void main() {" +
" gl_FragColor = vColor;" +
"}"
init {
Matrix.setIdentityM(mModelMatrix, 0)
vertices[0] = 0F
vertices[1] = 0F
vertices[2] = 0F
val ratio = Resources.getSystem().displayMetrics.widthPixels.toFloat() / Resources.getSystem().displayMetrics.heightPixels.toFloat()
for (i in 1..363) {
vertices[i * 3 + 0] = (ratio * 0.1 * cos(3.14 / 180 * i.toFloat())).toFloat()
vertices[i * 3 + 1] = (ratio * 0.1* sin(3.14 / 180 * i.toFloat())).toFloat()
vertices[i * 3 + 2] = 0F
}
val vertexByteBuffer = ByteBuffer.allocateDirect(vertices.size * 4)
vertexByteBuffer.order(ByteOrder.nativeOrder())
mVertexBuffer = vertexByteBuffer.asFloatBuffer()
mVertexBuffer.put(vertices)
mVertexBuffer.position(0)
val vertexShader = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)
val fragmentShader = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)
mProgram = GLES20.glCreateProgram()
GLES20.glAttachShader(mProgram, vertexShader)
GLES20.glAttachShader(mProgram, fragmentShader)
GLES20.glLinkProgram(mProgram)
}
fun getModelMatrix(): FloatArray {
return mModelMatrix;
}
fun draw(mvpMatrix: FloatArray?){
GLES20.glUseProgram(mProgram)
positionHandle = GLES20.glGetAttribLocation(mProgram, "vPosition").also {
GLES20.glEnableVertexAttribArray(it)
GLES20.glVertexAttribPointer(
it,
COORDS_PER_VERTEX,
GLES20.GL_FLOAT,
false,
vertexStride,
mVertexBuffer
)
mColorHandle = GLES20.glGetUniformLocation(mProgram, "vColor")
GLES20.glUniform4fv(mColorHandle, 1, color, 0)
mMVPMatrixHandle = GLES20.glGetUniformLocation(mProgram, "uMVPMatrix")
GLES20.glUniformMatrix4fv(mMVPMatrixHandle, 1, false, mvpMatrix, 0)
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_FAN, 0, 364)
GLES20.glDisableVertexAttribArray(it)
}
}
}
(b). GLRenderer.kt
Our
GLRenderer
class.
Create a Kotlin file named GLRenderer.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
Context
from theandroid.content
package.Sensor
from theandroid.hardware
package.SensorEvent
from theandroid.hardware
package.SensorEventListener
from theandroid.hardware
package.SensorManager
from theandroid.hardware
package.GLES20
from theandroid.opengl
package.GLSurfaceView
from theandroid.opengl
package.Matrix
from theandroid.opengl
package.Log
from theandroid.util
package.
Next create a class that derives from GLSurfaceView.Renderer,
and add its contents as follows:
We will be overriding the following functions:
onSurfaceCreated(unused: GL10, config: EGLConfig)
.onDrawFrame(unused: GL10)
.onSensorChanged(event: SensorEvent?)
.onAccuracyChanged(p0: Sensor?, p1: Int)
.onSurfaceChanged(unused: GL10, width: Int, height: Int)
.
We will be creating the following methods:
loadShader(type: Int, shaderCode: String): Int
.
(a). Our loadShader()
function
Write the loadShader()
function as follows:
fun loadShader(type: Int, shaderCode: String): Int{
return GLES20.glCreateShader(type).also{ shader ->
GLES20.glShaderSource(shader, shaderCode)
GLES20.glCompileShader(shader)
}
}
Here is the full code:
import android.content.Context
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import javax.microedition.khronos.egl.EGLConfig
import javax.microedition.khronos.opengles.GL10
import android.opengl.GLES20
import android.opengl.GLSurfaceView
import android.opengl.Matrix
import android.util.Log
import pl.polsl.openglapp.Circle
import kotlin.math.abs
class GLRenderer(private var context: Context) : GLSurfaceView.Renderer, SensorEventListener {
private val vPMatrix = FloatArray(16)
private lateinit var mCircle: Circle;
private val projectionMatrix = FloatArray(16)
private val viewMatrix = FloatArray(16)
private var ratio = 0f
private var rotationMatrix = FloatArray(16)
private val sensorManager: SensorManager = context.getSystemService(Context.SENSOR_SERVICE) as SensorManager
private var rotation: Float = 0f
private var currentVelocity = 1
private var velocity = 1
private var direction = 1
private var timestamp: Float = 0f
override fun onSurfaceCreated(unused: GL10, config: EGLConfig) {
val sensorAcc: Sensor = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
sensorManager.registerListener(this, sensorAcc, SensorManager.SENSOR_DELAY_NORMAL)
GLES20.glClearColor(0.5f, 1.0f, 0.5f, 1.0f)
mCircle = Circle ()
}
override fun onDrawFrame(unused: GL10) {
val scratch = FloatArray(16)
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT)
Matrix.setLookAtM(viewMatrix, 0, 0f, 0f, -3f, 0f, 0f, 0f, 0f, 1.0f, 0.0f)
Matrix.multiplyMM(vPMatrix, 0, projectionMatrix, 0, viewMatrix, 0)
if(mCircle.getModelMatrix()[12] >= ratio * 0.9){
if(direction == 1){
velocity/=2
}else{
velocity = 1
}
} else if(mCircle.getModelMatrix()[12] <= -ratio * 0.9){
if(direction == -1){
velocity/=2
}else{
velocity = 1
}
}
Matrix.translateM(mCircle.getModelMatrix(), 0, direction * (abs(rotation)/150) * velocity, 0f, 0f)
Matrix.multiplyMM(scratch, 0, vPMatrix, 0, mCircle.getModelMatrix(), 0)
mCircle.draw(scratch)
}
override fun onSensorChanged(event: SensorEvent?){
Log.d("Sensor","onSensorChange")
if (timestamp != 0f && event != null) {
rotationMatrix = event.values
rotation = rotationMatrix[1]
if(rotation < 0){
direction = 1
} else if(rotation > 0){
direction = -1
}
Log.d("xAxis: ", (rotationMatrix[1]).toString())
}
timestamp = event?.timestamp?.toFloat() ?: 0f
}
override fun onAccuracyChanged(p0: Sensor?, p1: Int) {
}
override fun onSurfaceChanged(unused: GL10, width: Int, height: Int) {
GLES20.glViewport(0, 0, width, height)
ratio = width.toFloat() / height.toFloat()
Matrix.frustumM(projectionMatrix, 0, -ratio, ratio, -1f, 1f, 3f, 7f)
}
companion object{
fun loadShader(type: Int, shaderCode: String): Int{
return GLES20.glCreateShader(type).also{ shader ->
GLES20.glShaderSource(shader, shaderCode)
GLES20.glCompileShader(shader)
}
}
}
}
(c). GLView.kt
Our
GLView
class.
Create a Kotlin file named GLView.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
Context
from theandroid.content
package.GLSurfaceView
from theandroid.opengl
package.
Next create a class that derives from GLSurfaceView(context)
and add its contents as follows:
Here is the full code:
package replace_with_your_package_name
import GLRenderer
import android.content.Context
import android.opengl.GLSurfaceView
class GLView(context: Context) : GLSurfaceView(context) {
private val renderer: GLRenderer
init {
setEGLContextClientVersion(2)
renderer = GLRenderer(context)
setRenderer(renderer)
}
}
(d). MainActivity.kt
Our
MainActivity
class.
Create a Kotlin file named MainActivity.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
GLSurfaceView
from theandroid.opengl
package.AppCompatActivity
from theandroidx.appcompat.app
package.Bundle
from theandroid.os
package.
Next create a class that derives from AppCompatActivity
and add its contents as follows:
We will be overriding the following functions:
onCreate(savedInstanceState: Bundle?)
.
Here is the full code:
package replace_with_your_package_name
import android.opengl.GLSurfaceView
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
class MainActivity : AppCompatActivity() {
private lateinit var gLView: GLSurfaceView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
gLView = GLView(this)
setContentView(gLView)
}
}
Reference
Download the code below:
No. | Link |
---|---|
1. | Download Full Code |
2. | Read more here. |
3. | Follow code author here. |
Simple Accelerometer Example
Practice of accelerometer in android devices, let's move that ball.
Here is the demo project:
Step 1. Our Android Manifest
We will need to look at our AndroidManifest.xml
.
(a). AndroidManifest.xml
Our
AndroidManifest
file.
Here we will add the following permission:
- Our
VIBRATE
permission.
Here is the full Android Manifest file:
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.projects.enzoftware.metalball">
<uses-permission android:name="android.permission.VIBRATE" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:roundIcon="@mipmap/ic_launcher_round"
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity android:name=".MetalBall">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
</application>
</manifest>
Step 2. Design Layouts
Here are the layouts for this project:
(a). activity_metal_ball.xml
Our
activity_metal_ball
layout.
Inside your /res/layout/
directory create an xml layout file named activity_metal_ball.xml
.
Add these two widgets:
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.projects.enzoftware.metalball.MetalBall">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>
Step 3. Write Code
Finally we need to write our code as follows:
(a). MetalBall.kt
Our
MetalBall
class.
Create a Kotlin file named MetalBall.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
Service
from theandroid.app
package.BluetoothClass
from theandroid.bluetooth
package.Context
from theandroid.content
package.ActivityInfo
from theandroid.content.pm
package.Bitmap
from theandroid.graphics
package.BitmapFactory
from theandroid.graphics
package.Canvas
from theandroid.graphics
package.Point
from theandroid.graphics
package.Sensor
from theandroid.hardware
package.SensorEvent
from theandroid.hardware
package.SensorEventListener
from theandroid.hardware
package.SensorManager
from theandroid.hardware
package.Build
from theandroid.os
package.AppCompatActivity
from theandroid.support.v7.app
package.Bundle
from theandroid.os
package.Vibrator
from theandroid.os
package.*
from theandroid.view
package.
Next create a class that derives from AppCompatActivity
and add its contents as follows:
We will be overriding the following functions:
onCreate(savedInstanceState: Bundle?)
.onAccuracyChanged(sensor: Sensor?, accuracy: Int)
.onSensorChanged(event: SensorEvent?)
.onResume()
.onPause()
.run()
.surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int)
.surfaceDestroyed(holder: SurfaceHolder?)
.surfaceCreated(holder: SurfaceHolder?)
.draw(canvas: Canvas?)
.onDraw(canvas: Canvas?)
.
We will be creating the following methods:
setRunning(parameter)
- This function will take aBoolean
object as a parameter.updateMe(inx : Float , iny : Float)
.
(a). Our setRunning()
function
Write the setRunning()
function as follows:
(b). Our updateMe()
function
Write the updateMe()
function as follows:
fun updateMe(inx : Float , iny : Float){
lastGx += inx
lastGy += iny
cx += lastGx
cy += lastGy
if(cx > (Windowwidth - picWidth)){
cx = (Windowwidth - picWidth).toFloat()
lastGx = 0F
if (noBorderX){
vibratorService!!.vibrate(100)
noBorderX = false
}
}
else if(cx < (0)){
cx = 0F
lastGx = 0F
if(noBorderX){
vibratorService!!.vibrate(100)
noBorderX = false
}
}
else{ noBorderX = true }
if (cy > (Windowheight - picHeight)){
cy = (Windowheight - picHeight).toFloat()
lastGy = 0F
if (noBorderY){
vibratorService!!.vibrate(100)
noBorderY = false
}
}
else if(cy < (0)){
cy = 0F
lastGy = 0F
if (noBorderY){
vibratorService!!.vibrate(100)
noBorderY= false
}
}
else{ noBorderY = true }
invalidate()
}
Here is the full code:
package replace_with_your_package_name
import android.app.Service
import android.bluetooth.BluetoothClass
import android.content.Context
import android.content.pm.ActivityInfo
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.Canvas
import android.graphics.Point
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import android.os.Build
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.os.Vibrator
import android.view.*
class MetalBall : AppCompatActivity() , SensorEventListener {
private var mSensorManager : SensorManager ?= null
private var mAccelerometer : Sensor ?= null
var ground : GroundView ?= null
override fun onCreate(savedInstanceState: Bundle?) {
requestWindowFeature(Window.FEATURE_NO_TITLE)
super.onCreate(savedInstanceState)
// get reference of the service
mSensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager
// focus in accelerometer
mAccelerometer = mSensorManager!!.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)
// setup the window
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
window.setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
WindowManager.LayoutParams.FLAG_FULLSCREEN)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB){
window.decorView.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE
View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
View.SYSTEM_UI_FLAG_FULLSCREEN
View.SYSTEM_UI_FLAG_IMMERSIVE
}
// set the view
ground = GroundView(this)
setContentView(ground)
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
}
override fun onSensorChanged(event: SensorEvent?) {
if (event != null) {
ground!!.updateMe(event.values[1] , event.values[0])
}
}
override fun onResume() {
super.onResume()
mSensorManager!!.registerListener(this,mAccelerometer,
SensorManager.SENSOR_DELAY_GAME)
}
override fun onPause() {
super.onPause()
mSensorManager!!.unregisterListener(this)
}
class DrawThread (surfaceHolder: SurfaceHolder , panel : GroundView) : Thread() {
private var surfaceHolder :SurfaceHolder ?= null
private var panel : GroundView ?= null
private var run = false
init {
this.surfaceHolder = surfaceHolder
this.panel = panel
}
fun setRunning(run : Boolean){
this.run = run
}
override fun run() {
var c: Canvas ?= null
while (run){
c = null
try {
c = surfaceHolder!!.lockCanvas(null)
synchronized(surfaceHolder!!){
panel!!.draw(c)
}
}finally {
if (c!= null){
surfaceHolder!!.unlockCanvasAndPost(c)
}
}
}
}
}
}
class GroundView(context: Context?) : SurfaceView(context), SurfaceHolder.Callback{
// ball coordinates
var cx : Float = 10.toFloat()
var cy : Float = 10.toFloat()
// last position increment
var lastGx : Float = 0.toFloat()
var lastGy : Float = 0.toFloat()
// graphic size of the ball
var picHeight: Int = 0
var picWidth : Int = 0
var icon:Bitmap ?= null
// window size
var Windowwidth : Int = 0
var Windowheight : Int = 0
// is touching the edge ?
var noBorderX = false
var noBorderY = false
var vibratorService : Vibrator ?= null
var thread : MetalBall.DrawThread?= null
init {
holder.addCallback(this)
//create a thread
thread = MetalBall.DrawThread(holder, this)
// get references and sizes of the objects
val display: Display = (getContext().getSystemService(Context.WINDOW_SERVICE) as WindowManager).defaultDisplay
val size:Point = Point()
display.getSize(size)
Windowwidth = size.x
Windowheight = size.y
icon = BitmapFactory.decodeResource(resources,R.drawable.ball)
picHeight = icon!!.height
picWidth = icon!!.width
vibratorService = (getContext().getSystemService(Service.VIBRATOR_SERVICE)) as Vibrator
}
override fun surfaceChanged(holder: SurfaceHolder?, format: Int, width: Int, height: Int) {
}
override fun surfaceDestroyed(holder: SurfaceHolder?) {
}
override fun surfaceCreated(holder: SurfaceHolder?) {
thread!!.setRunning(true)
thread!!.start()
}
override fun draw(canvas: Canvas?) {
super.draw(canvas)
if (canvas != null){
canvas.drawColor(0xFFAAAAA)
canvas.drawBitmap(icon,cx,cy,null)
}
}
override public fun onDraw(canvas: Canvas?) {
if (canvas != null){
canvas.drawColor(0xFFAAAAA)
canvas.drawBitmap(icon,cx,cy,null)
}
}
fun updateMe(inx : Float , iny : Float){
lastGx += inx
lastGy += iny
cx += lastGx
cy += lastGy
if(cx > (Windowwidth - picWidth)){
cx = (Windowwidth - picWidth).toFloat()
lastGx = 0F
if (noBorderX){
vibratorService!!.vibrate(100)
noBorderX = false
}
}
else if(cx < (0)){
cx = 0F
lastGx = 0F
if(noBorderX){
vibratorService!!.vibrate(100)
noBorderX = false
}
}
else{ noBorderX = true }
if (cy > (Windowheight - picHeight)){
cy = (Windowheight - picHeight).toFloat()
lastGy = 0F
if (noBorderY){
vibratorService!!.vibrate(100)
noBorderY = false
}
}
else if(cy < (0)){
cy = 0F
lastGy = 0F
if (noBorderY){
vibratorService!!.vibrate(100)
noBorderY= false
}
}
else{ noBorderY = true }
invalidate()
}
}
Reference
Download the code below:
No. | Link |
---|---|
1. | Download Full Code |
2. | Read more here. |
3. | Follow code author here. |
Accelerometer Experimentation
Experimentation with how to use the accelerometer to make cool effects on Android in Kotlin.
Below is a full android sample to demonstrate Accelerometer concept.
Step 1. Design Layouts
(a). activity_main.xml
Our
activity_main
layout.
Design your XML layout using the following 2 UI widgets and ViewGroups:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#212121"
tools:context=".MainActivity">
<TextView
android:id="@+id/tv_square"
android:layout_width="200dp"
android:layout_height="200dp"
android:layout_centerInParent="true"
android:background="#EF5350"
android:gravity="center"
android:text="Hello"
android:textColor="@color/white"
android:textSize="20sp" />
</RelativeLayout>
Step 2. Write Code
Finally we need to write our code as follows:
(a). MainActivity.kt
Our
MainActivity
class.
Create a Kotlin file named MainActivity.kt
.
We will then add imports from android SDK and other packages. Here are some of the imports we will use in this class:
Color
from theandroid.graphics
package.Sensor
from theandroid.hardware
package.SensorEvent
from theandroid.hardware
package.SensorEventListener
from theandroid.hardware
package.SensorManager
from theandroid.hardware
package.AppCompatActivity
from theandroidx.appcompat.app
package.Bundle
from theandroid.os
package.TextView
from theandroid.widget
package.AppCompatDelegate
from theandroidx.appcompat.app
package.
Next create a class that derives from AppCompatActivity
and add its contents as follows:
We will be overriding the following functions:
onCreate(savedInstanceState: Bundle?)
.onSensorChanged(event: SensorEvent?)
.onAccuracyChanged(sensor: Sensor?, accuracy: Int)
.onDestroy()
.
We will be creating the following methods:
setUpSensorStuff()
.
(a). Our setUpSensorStuff()
function
WesetUpSensorStuff()
as follows:
private fun setUpSensorStuff() {
// Create the sensor manager
sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
// Specify the sensor you want to listen to
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
sensorManager.registerListener(
this,
accelerometer,
SensorManager.SENSOR_DELAY_FASTEST,
SensorManager.SENSOR_DELAY_FASTEST
)
}
}
Here is the full code:
package replace_with_your_package_name
import android.graphics.Color
import android.hardware.Sensor
import android.hardware.SensorEvent
import android.hardware.SensorEventListener
import android.hardware.SensorManager
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.TextView
import androidx.appcompat.app.AppCompatDelegate
class MainActivity : AppCompatActivity(), SensorEventListener {
private lateinit var sensorManager: SensorManager
private lateinit var square: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
// Keeps phone in light mode
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
square = findViewById(R.id.tv_square)
setUpSensorStuff()
}
private fun setUpSensorStuff() {
// Create the sensor manager
sensorManager = getSystemService(SENSOR_SERVICE) as SensorManager
// Specify the sensor you want to listen to
sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER)?.also { accelerometer ->
sensorManager.registerListener(
this,
accelerometer,
SensorManager.SENSOR_DELAY_FASTEST,
SensorManager.SENSOR_DELAY_FASTEST
)
}
}
override fun onSensorChanged(event: SensorEvent?) {
// Checks for the sensor we have registered
if (event?.sensor?.type == Sensor.TYPE_ACCELEROMETER) {
//Log.d("Main", "onSensorChanged: sides ${event.values[0]} front/back ${event.values[1]} ")
// Sides = Tilting phone left(10) and right(-10)
val sides = event.values[0]
// Up/Down = Tilting phone up(10), flat (0), upside-down(-10)
val upDown = event.values[1]
square.apply {
rotationX = upDown * 3f
rotationY = sides * 3f
rotation = -sides
translationX = sides * -10
translationY = upDown * 10
}
// Changes the colour of the square if it's completely flat
val color = if (upDown.toInt() == 0 && sides.toInt() == 0) Color.GREEN else Color.RED
square.setBackgroundColor(color)
square.text = "up/down ${upDown.toInt()}\nleft/right ${sides.toInt()}"
}
}
override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {
return
}
override fun onDestroy() {
sensorManager.unregisterListener(this)
super.onDestroy()
}
}
Reference
Download the code below:
No. | Link |
---|---|
1. | Download Full Code |
2. | Read more here. |
3. | Follow code author here. |