mirror of
https://git.sr.ht/~thestr4ng3r/chiaki
synced 2025-08-19 13:09:39 -07:00
Add Analog Sticks to Android
This commit is contained in:
parent
8f2275c6ab
commit
37e5de2b0f
11 changed files with 400 additions and 83 deletions
|
@ -0,0 +1,136 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Chiaki.
|
||||||
|
*
|
||||||
|
* Chiaki is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Chiaki is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.graphics.Canvas
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.graphics.Paint
|
||||||
|
import android.graphics.Rect
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.util.AttributeSet
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
|
import com.metallic.chiaki.R
|
||||||
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
class AnalogStickView @JvmOverloads constructor(
|
||||||
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
|
) : View(context, attrs, defStyleAttr)
|
||||||
|
{
|
||||||
|
val radius: Float
|
||||||
|
private val handleRadius: Float
|
||||||
|
private val drawableBase: Drawable?
|
||||||
|
private val drawableHandle: Drawable?
|
||||||
|
|
||||||
|
var state = Vector(0f, 0f)
|
||||||
|
private set(value)
|
||||||
|
{
|
||||||
|
field = value
|
||||||
|
stateChangedCallback?.let { it(field) }
|
||||||
|
}
|
||||||
|
|
||||||
|
var stateChangedCallback: ((Vector) -> Unit)? = null
|
||||||
|
|
||||||
|
private val touchTracker = TouchTracker().also {
|
||||||
|
it.positionChangedCallback = this::updateState
|
||||||
|
}
|
||||||
|
|
||||||
|
private var center: Vector? = null
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as state, but scaled to the circle
|
||||||
|
*/
|
||||||
|
private var handlePosition: Vector = Vector(0f, 0f)
|
||||||
|
|
||||||
|
private val clipBoundsTmp = Rect()
|
||||||
|
|
||||||
|
init
|
||||||
|
{
|
||||||
|
context.theme.obtainStyledAttributes(attrs, R.styleable.AnalogStickView, 0, 0).apply {
|
||||||
|
radius = getDimension(R.styleable.AnalogStickView_radius, 0f)
|
||||||
|
handleRadius = getDimension(R.styleable.AnalogStickView_handleRadius, 0f)
|
||||||
|
drawableBase = getDrawable(R.styleable.AnalogStickView_drawableBase)
|
||||||
|
drawableHandle = getDrawable(R.styleable.AnalogStickView_drawableHandle)
|
||||||
|
recycle()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onDraw(canvas: Canvas)
|
||||||
|
{
|
||||||
|
super.onDraw(canvas)
|
||||||
|
|
||||||
|
val center = center
|
||||||
|
if(center != null)
|
||||||
|
{
|
||||||
|
drawableBase?.setBounds((center.x - radius).toInt(), (center.y - radius).toInt(), (center.x + radius).toInt(), (center.y + radius).toInt())
|
||||||
|
drawableBase?.draw(canvas)
|
||||||
|
|
||||||
|
val handleX = center.x + handlePosition.x * radius
|
||||||
|
val handleY = center.y + handlePosition.y * radius
|
||||||
|
drawableHandle?.setBounds((handleX - handleRadius).toInt(), (handleY - handleRadius).toInt(), (handleX + handleRadius).toInt(),(handleY + handleRadius).toInt())
|
||||||
|
drawableHandle?.draw(canvas)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateState(position: Vector?)
|
||||||
|
{
|
||||||
|
if(radius <= 0f)
|
||||||
|
return
|
||||||
|
|
||||||
|
if(position == null)
|
||||||
|
{
|
||||||
|
center = null
|
||||||
|
state = Vector(0f, 0f)
|
||||||
|
handlePosition = Vector(0f, 0f)
|
||||||
|
invalidate()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val center: Vector = this.center ?: position
|
||||||
|
this.center = center
|
||||||
|
|
||||||
|
val dir = position - center
|
||||||
|
val length = dir.length
|
||||||
|
if(length > 0)
|
||||||
|
{
|
||||||
|
val strength = if(length > radius) 1.0f else length / radius
|
||||||
|
val dirNormalized = dir / length
|
||||||
|
handlePosition = dirNormalized * strength
|
||||||
|
val dirBoxNormalized =
|
||||||
|
if(abs(dirNormalized.x) > abs(dirNormalized.y))
|
||||||
|
dirNormalized / abs(dirNormalized.x)
|
||||||
|
else
|
||||||
|
dirNormalized / abs(dirNormalized.y)
|
||||||
|
state = dirBoxNormalized * strength
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
handlePosition = Vector(0f, 0f)
|
||||||
|
state = Vector(0f, 0f)
|
||||||
|
}
|
||||||
|
|
||||||
|
invalidate()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onTouchEvent(event: MotionEvent): Boolean
|
||||||
|
{
|
||||||
|
touchTracker.touchEvent(event)
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
|
@ -20,13 +20,11 @@ package com.metallic.chiaki.touchcontrols
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.graphics.Canvas
|
import android.graphics.Canvas
|
||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import android.util.Log
|
|
||||||
import android.view.MotionEvent
|
import android.view.MotionEvent
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
|
import androidx.vectordrawable.graphics.drawable.VectorDrawableCompat
|
||||||
import com.metallic.chiaki.R
|
import com.metallic.chiaki.R
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
import kotlin.math.sqrt
|
|
||||||
|
|
||||||
class DPadView @JvmOverloads constructor(
|
class DPadView @JvmOverloads constructor(
|
||||||
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
|
||||||
|
@ -48,7 +46,9 @@ class DPadView @JvmOverloads constructor(
|
||||||
private val dpadIdleDrawable = VectorDrawableCompat.create(resources, R.drawable.control_dpad_idle, null)
|
private val dpadIdleDrawable = VectorDrawableCompat.create(resources, R.drawable.control_dpad_idle, null)
|
||||||
private val dpadLeftDrawable = VectorDrawableCompat.create(resources, R.drawable.control_dpad_left, null)
|
private val dpadLeftDrawable = VectorDrawableCompat.create(resources, R.drawable.control_dpad_left, null)
|
||||||
|
|
||||||
private var pointerId: Int? = null
|
private val touchTracker = TouchTracker().also {
|
||||||
|
it.positionChangedCallback = this::updateState
|
||||||
|
}
|
||||||
|
|
||||||
override fun onDraw(canvas: Canvas)
|
override fun onDraw(canvas: Canvas)
|
||||||
{
|
{
|
||||||
|
@ -75,10 +75,10 @@ class DPadView @JvmOverloads constructor(
|
||||||
drawable?.draw(canvas)
|
drawable?.draw(canvas)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun directionForPosition(x: Float, y: Float): Direction
|
private fun directionForPosition(position: Vector): Direction
|
||||||
{
|
{
|
||||||
val dx = x - width * 0.5f
|
val dx = position.x - width * 0.5f
|
||||||
val dy = y - height * 0.5f
|
val dy = position.y - height * 0.5f
|
||||||
return when
|
return when
|
||||||
{
|
{
|
||||||
dx > abs(dy) -> Direction.RIGHT
|
dx > abs(dy) -> Direction.RIGHT
|
||||||
|
@ -88,20 +88,20 @@ class DPadView @JvmOverloads constructor(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateState(x: Float?, y: Float?)
|
private fun updateState(position: Vector?)
|
||||||
{
|
{
|
||||||
val newState =
|
val newState =
|
||||||
if(x == null || y == null)
|
if(position == null)
|
||||||
null
|
null
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
val xFrac = 2.0f * (x / width.toFloat() - 0.5f)
|
val xFrac = 2.0f * (position.x / width.toFloat() - 0.5f)
|
||||||
val yFrac = 2.0f * (y / height.toFloat() - 0.5f)
|
val yFrac = 2.0f * (position.y / height.toFloat() - 0.5f)
|
||||||
val radiusSq = xFrac * xFrac + yFrac * yFrac
|
val radiusSq = xFrac * xFrac + yFrac * yFrac
|
||||||
if(radiusSq < deadzoneRadius * deadzoneRadius && state != null)
|
if(radiusSq < deadzoneRadius * deadzoneRadius && state != null)
|
||||||
state
|
state
|
||||||
else
|
else
|
||||||
directionForPosition(x, y)
|
directionForPosition(position)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(state != newState)
|
if(state != newState)
|
||||||
|
@ -114,37 +114,7 @@ class DPadView @JvmOverloads constructor(
|
||||||
|
|
||||||
override fun onTouchEvent(event: MotionEvent): Boolean
|
override fun onTouchEvent(event: MotionEvent): Boolean
|
||||||
{
|
{
|
||||||
when(event.actionMasked)
|
touchTracker.touchEvent(event)
|
||||||
{
|
|
||||||
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN ->
|
|
||||||
{
|
|
||||||
if(pointerId == null)
|
|
||||||
{
|
|
||||||
pointerId = event.getPointerId(event.actionIndex)
|
|
||||||
updateState(event.x, event.y)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP ->
|
|
||||||
{
|
|
||||||
if(event.getPointerId(event.actionIndex) == pointerId)
|
|
||||||
{
|
|
||||||
pointerId = null
|
|
||||||
updateState(null, null)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent.ACTION_MOVE ->
|
|
||||||
{
|
|
||||||
val pointerId = pointerId
|
|
||||||
if(pointerId != null)
|
|
||||||
{
|
|
||||||
val pointerIndex = event.findPointerIndex(pointerId)
|
|
||||||
if(pointerIndex >= 0)
|
|
||||||
updateState(event.getX(pointerIndex), event.getY(pointerIndex))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,7 @@
|
||||||
package com.metallic.chiaki.touchcontrols
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
import android.view.LayoutInflater
|
import android.view.LayoutInflater
|
||||||
import android.view.View
|
import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
|
@ -61,30 +62,18 @@ class TouchControlsFragment : Fragment()
|
||||||
l2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { l2State = if(it) 255U else 0U } }
|
l2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { l2State = if(it) 255U else 0U } }
|
||||||
r2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
r2ButtonView.buttonPressedCallback = { controllerState = controllerState.copy().apply { r2State = if(it) 255U else 0U } }
|
||||||
|
|
||||||
leftDpadView.stateChangeCallback = { controllerState = controllerState.copy().apply {
|
val quantizeStick = { f: Float ->
|
||||||
val pos: Pair<Short, Short> = when(it)
|
(Short.MAX_VALUE * f).toShort()
|
||||||
{
|
|
||||||
DPadView.Direction.UP -> Pair(0, Short.MIN_VALUE)
|
|
||||||
DPadView.Direction.DOWN -> Pair(0, Short.MAX_VALUE)
|
|
||||||
DPadView.Direction.LEFT -> Pair(Short.MIN_VALUE, 0)
|
|
||||||
DPadView.Direction.RIGHT -> Pair(Short.MAX_VALUE, 0)
|
|
||||||
null -> Pair(0, 0)
|
|
||||||
}
|
}
|
||||||
leftX = pos.first
|
|
||||||
leftY = pos.second
|
leftAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
|
||||||
|
leftX = quantizeStick(it.x)
|
||||||
|
leftY = quantizeStick(it.y)
|
||||||
}}
|
}}
|
||||||
|
|
||||||
rightDpadView.stateChangeCallback = { controllerState = controllerState.copy().apply {
|
rightAnalogStickView.stateChangedCallback = { controllerState = controllerState.copy().apply {
|
||||||
val pos: Pair<Short, Short> = when(it)
|
rightX = quantizeStick(it.x)
|
||||||
{
|
rightY = quantizeStick(it.y)
|
||||||
DPadView.Direction.UP -> Pair(0, Short.MIN_VALUE)
|
|
||||||
DPadView.Direction.DOWN -> Pair(0, Short.MAX_VALUE)
|
|
||||||
DPadView.Direction.LEFT -> Pair(Short.MIN_VALUE, 0)
|
|
||||||
DPadView.Direction.RIGHT -> Pair(Short.MAX_VALUE, 0)
|
|
||||||
null -> Pair(0, 0)
|
|
||||||
}
|
|
||||||
rightX = pos.first
|
|
||||||
rightY = pos.second
|
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,69 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Chiaki.
|
||||||
|
*
|
||||||
|
* Chiaki is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Chiaki is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
import android.view.MotionEvent
|
||||||
|
|
||||||
|
class TouchTracker
|
||||||
|
{
|
||||||
|
var currentPosition: Vector? = null
|
||||||
|
private set(value)
|
||||||
|
{
|
||||||
|
field = value
|
||||||
|
positionChangedCallback?.let { it(field) }
|
||||||
|
}
|
||||||
|
|
||||||
|
var positionChangedCallback: ((Vector?) -> Unit)? = null
|
||||||
|
|
||||||
|
private var pointerId: Int? = null
|
||||||
|
|
||||||
|
fun touchEvent(event: MotionEvent)
|
||||||
|
{
|
||||||
|
when(event.actionMasked)
|
||||||
|
{
|
||||||
|
MotionEvent.ACTION_DOWN, MotionEvent.ACTION_POINTER_DOWN ->
|
||||||
|
{
|
||||||
|
if(pointerId == null)
|
||||||
|
{
|
||||||
|
pointerId = event.getPointerId(event.actionIndex)
|
||||||
|
currentPosition = Vector(event.x, event.y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_UP, MotionEvent.ACTION_POINTER_UP ->
|
||||||
|
{
|
||||||
|
if(event.getPointerId(event.actionIndex) == pointerId)
|
||||||
|
{
|
||||||
|
pointerId = null
|
||||||
|
currentPosition = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_MOVE ->
|
||||||
|
{
|
||||||
|
val pointerId = pointerId
|
||||||
|
if(pointerId != null)
|
||||||
|
{
|
||||||
|
val pointerIndex = event.findPointerIndex(pointerId)
|
||||||
|
if(pointerIndex >= 0)
|
||||||
|
currentPosition = Vector(event.getX(pointerIndex), event.getY(pointerIndex))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,31 @@
|
||||||
|
/*
|
||||||
|
* This file is part of Chiaki.
|
||||||
|
*
|
||||||
|
* Chiaki is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* Chiaki is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with Chiaki. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.metallic.chiaki.touchcontrols
|
||||||
|
|
||||||
|
import kotlin.math.sqrt
|
||||||
|
|
||||||
|
data class Vector(val x: Float, val y: Float)
|
||||||
|
{
|
||||||
|
operator fun plus(o: Vector) = Vector(x + o.x, y + o.y)
|
||||||
|
operator fun minus(o: Vector) = Vector(x - o.x, y - o.y)
|
||||||
|
operator fun times(s: Float) = Vector(x * s, y * s)
|
||||||
|
operator fun div(s: Float) = this * (1f / s)
|
||||||
|
|
||||||
|
val lengthSq get() = x*x + y*y
|
||||||
|
val length get() = sqrt(lengthSq)
|
||||||
|
}
|
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="512dp"
|
||||||
|
android:height="512dp"
|
||||||
|
android:viewportWidth="135.46666"
|
||||||
|
android:viewportHeight="135.46667">
|
||||||
|
<path
|
||||||
|
android:pathData="M67.733,67.733m-67.733,0a67.733,67.733 0,1 1,135.467 0a67.733,67.733 0,1 1,-135.467 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeLineJoin="miter"
|
||||||
|
android:strokeWidth="4.56389332"
|
||||||
|
android:fillColor="@color/control_primary"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:fillAlpha="1"
|
||||||
|
android:strokeLineCap="butt"/>
|
||||||
|
</vector>
|
|
@ -0,0 +1,15 @@
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:width="512dp"
|
||||||
|
android:height="512dp"
|
||||||
|
android:viewportWidth="135.46666"
|
||||||
|
android:viewportHeight="135.46667">
|
||||||
|
<path
|
||||||
|
android:pathData="M67.733,67.733m-67.733,0a67.733,67.733 0,1 1,135.467 0a67.733,67.733 0,1 1,-135.467 0"
|
||||||
|
android:strokeAlpha="1"
|
||||||
|
android:strokeLineJoin="miter"
|
||||||
|
android:strokeWidth="4.56389332"
|
||||||
|
android:fillColor="@color/control_pressed"
|
||||||
|
android:strokeColor="#00000000"
|
||||||
|
android:fillAlpha="1"
|
||||||
|
android:strokeLineCap="butt"/>
|
||||||
|
</vector>
|
|
@ -2,12 +2,48 @@
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:clipChildren="false">
|
||||||
|
|
||||||
<com.metallic.chiaki.touchcontrols.ControlsBackgroundView
|
<com.metallic.chiaki.touchcontrols.ControlsBackgroundView
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent" />
|
android:layout_height="match_parent" />
|
||||||
|
|
||||||
|
<androidx.constraintlayout.widget.Guideline
|
||||||
|
android:id="@+id/centerGuideline"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_constraintGuide_percent="0.5"
|
||||||
|
android:orientation="vertical" />
|
||||||
|
|
||||||
|
<com.metallic.chiaki.touchcontrols.AnalogStickView
|
||||||
|
android:id="@+id/leftAnalogStickView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintLeft_toLeftOf="parent"
|
||||||
|
app:layout_constraintRight_toLeftOf="@id/centerGuideline"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/dpadView"
|
||||||
|
app:radius="@dimen/control_analog_stick_radius"
|
||||||
|
app:handleRadius="@dimen/control_analog_stick_handle_radius"
|
||||||
|
app:drawableBase="@drawable/control_analog_stick_base"
|
||||||
|
app:drawableHandle="@drawable/control_analog_stick_handle"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<com.metallic.chiaki.touchcontrols.AnalogStickView
|
||||||
|
android:id="@+id/rightAnalogStickView"
|
||||||
|
android:layout_width="0dp"
|
||||||
|
android:layout_height="0dp"
|
||||||
|
app:layout_constraintLeft_toRightOf="@id/centerGuideline"
|
||||||
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
|
app:layout_constraintTop_toBottomOf="@id/faceButtonsLayout"
|
||||||
|
app:radius="@dimen/control_analog_stick_radius"
|
||||||
|
app:handleRadius="@dimen/control_analog_stick_handle_radius"
|
||||||
|
app:drawableBase="@drawable/control_analog_stick_base"
|
||||||
|
app:drawableHandle="@drawable/control_analog_stick_handle"
|
||||||
|
/>
|
||||||
|
|
||||||
<com.metallic.chiaki.touchcontrols.DPadView
|
<com.metallic.chiaki.touchcontrols.DPadView
|
||||||
android:id="@+id/dpadView"
|
android:id="@+id/dpadView"
|
||||||
android:layout_width="128dp"
|
android:layout_width="128dp"
|
||||||
|
@ -19,6 +55,7 @@
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
app:layout_constraintBottom_toBottomOf="parent" />
|
||||||
|
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout
|
<androidx.constraintlayout.widget.ConstraintLayout
|
||||||
|
android:id="@+id/faceButtonsLayout"
|
||||||
android:layout_width="144dp"
|
android:layout_width="144dp"
|
||||||
android:layout_height="144dp"
|
android:layout_height="144dp"
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
app:layout_constraintRight_toRightOf="parent"
|
||||||
|
@ -167,22 +204,4 @@
|
||||||
app:layout_constraintRight_toLeftOf="@id/r2ButtonView"
|
app:layout_constraintRight_toLeftOf="@id/r2ButtonView"
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
app:layout_constraintTop_toTopOf="parent" />
|
||||||
|
|
||||||
<com.metallic.chiaki.touchcontrols.DPadView
|
|
||||||
android:id="@+id/leftDpadView"
|
|
||||||
android:layout_width="128dp"
|
|
||||||
android:layout_height="128dp"
|
|
||||||
android:layout_marginLeft="32dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
app:layout_constraintLeft_toLeftOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
|
||||||
|
|
||||||
<com.metallic.chiaki.touchcontrols.DPadView
|
|
||||||
android:id="@+id/rightDpadView"
|
|
||||||
android:layout_width="128dp"
|
|
||||||
android:layout_height="128dp"
|
|
||||||
android:layout_marginLeft="32dp"
|
|
||||||
android:layout_marginBottom="8dp"
|
|
||||||
app:layout_constraintRight_toRightOf="parent"
|
|
||||||
app:layout_constraintBottom_toBottomOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</androidx.constraintlayout.widget.ConstraintLayout>
|
|
@ -4,4 +4,11 @@
|
||||||
<attr name="drawableIdle" format="reference" />
|
<attr name="drawableIdle" format="reference" />
|
||||||
<attr name="drawablePressed" format="integer" />
|
<attr name="drawablePressed" format="integer" />
|
||||||
</declare-styleable>
|
</declare-styleable>
|
||||||
|
|
||||||
|
<declare-styleable name="AnalogStickView">
|
||||||
|
<attr name="radius" format="dimension" />
|
||||||
|
<attr name="handleRadius" format="dimension" />
|
||||||
|
<attr name="drawableBase" format="reference" />
|
||||||
|
<attr name="drawableHandle" format="reference" />
|
||||||
|
</declare-styleable>
|
||||||
</resources>
|
</resources>
|
|
@ -1,4 +1,6 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<resources>
|
<resources>
|
||||||
<dimen name="control_face_button_size">48dp</dimen>
|
<dimen name="control_face_button_size">48dp</dimen>
|
||||||
|
<dimen name="control_analog_stick_radius">64dp</dimen>
|
||||||
|
<dimen name="control_analog_stick_handle_radius">16dp</dimen>
|
||||||
</resources>
|
</resources>
|
64
assets/controls/stick.svg
Normal file
64
assets/controls/stick.svg
Normal file
|
@ -0,0 +1,64 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<!-- Created with Inkscape (http://www.inkscape.org/) -->
|
||||||
|
|
||||||
|
<svg
|
||||||
|
xmlns:dc="http://purl.org/dc/elements/1.1/"
|
||||||
|
xmlns:cc="http://creativecommons.org/ns#"
|
||||||
|
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
|
||||||
|
xmlns:svg="http://www.w3.org/2000/svg"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
||||||
|
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||||
|
width="512"
|
||||||
|
height="512"
|
||||||
|
viewBox="0 0 135.46666 135.46667"
|
||||||
|
version="1.1"
|
||||||
|
id="svg8"
|
||||||
|
inkscape:version="0.92.4 5da689c313, 2019-01-14"
|
||||||
|
sodipodi:docname="stick.svg">
|
||||||
|
<defs
|
||||||
|
id="defs2" />
|
||||||
|
<sodipodi:namedview
|
||||||
|
id="base"
|
||||||
|
pagecolor="#ffffff"
|
||||||
|
bordercolor="#666666"
|
||||||
|
borderopacity="1.0"
|
||||||
|
inkscape:pageopacity="0.0"
|
||||||
|
inkscape:pageshadow="2"
|
||||||
|
inkscape:zoom="1.4"
|
||||||
|
inkscape:cx="214.39234"
|
||||||
|
inkscape:cy="291.69769"
|
||||||
|
inkscape:document-units="mm"
|
||||||
|
inkscape:current-layer="layer1"
|
||||||
|
showgrid="false"
|
||||||
|
units="px"
|
||||||
|
inkscape:window-width="1918"
|
||||||
|
inkscape:window-height="1048"
|
||||||
|
inkscape:window-x="1920"
|
||||||
|
inkscape:window-y="15"
|
||||||
|
inkscape:window-maximized="1" />
|
||||||
|
<metadata
|
||||||
|
id="metadata5">
|
||||||
|
<rdf:RDF>
|
||||||
|
<cc:Work
|
||||||
|
rdf:about="">
|
||||||
|
<dc:format>image/svg+xml</dc:format>
|
||||||
|
<dc:type
|
||||||
|
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||||
|
<dc:title></dc:title>
|
||||||
|
</cc:Work>
|
||||||
|
</rdf:RDF>
|
||||||
|
</metadata>
|
||||||
|
<g
|
||||||
|
inkscape:label="Ebene 1"
|
||||||
|
inkscape:groupmode="layer"
|
||||||
|
id="layer1"
|
||||||
|
transform="translate(0,-161.53332)">
|
||||||
|
<circle
|
||||||
|
style="fill:#000000;fill-opacity:1;stroke:none;stroke-width:4.56389332;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||||
|
id="path815"
|
||||||
|
cx="67.73333"
|
||||||
|
cy="229.26665"
|
||||||
|
r="67.73333" />
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 1.9 KiB |
Loading…
Add table
Add a link
Reference in a new issue