Multiple button without repeating onClickListener - android

I am making a calculator app and for every button, i have to write button.setonclciklistner
I am tired of initializing the button after that on click method
please don't answer to implement on click method I already know that but is there any other method

Yes there is an option. If you do a calculator the gridView is perfect for that. Instead of making tons of onClickListener you can achieve that by multiple cases like in this example shown.

Here are some of the functions from my own calculator based app:
//normal backspace
private fun backSpace() {
form.bs.setOnClickListener {
inputField.text = inputField.text.dropLast(1)
UtilityMethods(context).playClick()
}
}
//long pressing the backspace will clear input field instantly
private fun longPressBS() {
form.bs.setOnLongClickListener {
inputField.text = null
true
}
}
//numeric keys are invoked here
private fun theNumericPad() {
for (i in 0 until 10) {
val resID = context.resources.getIdentifier("n$i", "id", context.packageName)
form.root.findViewById<Button>(resID).setOnClickListener {
if (inputField.text.toString() == "0") {
inputField.text = ""
}
if (inputField.text.toString() == ".") {
inputField.text = "0."
}
inputField.append(i.toString())
//clear custom type
if (!form.rb00.isChecked) {
form.ctField.text = ""
}
//play sound
UtilityMethods(context).playClick()
}
}
}
//the dot
private fun dot() {
form.dot.setOnClickListener {
if (inputField.text.toString() == "") {
inputField.append("0.")
} else {
inputField.append(".")
}
//clear custom type
if (!form.rb00.isChecked) {
form.ctField.text = ""
}
UtilityMethods(context).playClick()
}
}
And the function to play key tap sound is:
//play sounds
//normal key tap sound
fun playClick() {
val am = context.getSystemService(Context.AUDIO_SERVICE) as AudioManager
am.playSoundEffect(AudioManager.FX_KEY_CLICK, 1f)
}
Also to make it easy to understand, variable like form is the view binding of you keyboard layout.

Related

I am trying to make TicTacToe Application

I am trying to make TicTacToe Application, I have already implemented the "player Vs player" part but I am having trouble with implementing the player Vs computer. I have a function called playgame. For update_player I am doing manually and for update computer, I am doing it using random, and I think this is causing the issue as I am checking if my boardStatus is already filled, if it's filled I am calling my function again. I read online that all the calculation should be done on thread, I tried implementing it but I think I am doing it wrong. Please Help!
Here's my code for reference:
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.Button
import kotlinx.android.synthetic.main.activity_computer.*
import java.util.*
class ComputerActivity : AppCompatActivity() {
var player = true
var turnCount = 0
var boardStatus = Array(3) { IntArray(3) }
lateinit var board: Array<Array<Button>>
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_computer)
board = arrayOf(
arrayOf(First, Second, Third),
arrayOf(Fourth, Fifth, Sixth),
arrayOf(Seventh, Eighth, Ninth)
)
playGame()
}
private fun playGame(){
Status.text = "Player's Turn"
for (i in 0..2) {
var flag: Boolean = false
for (j in 0..2) {
if (player) {
Status.text = "Player's Turn"
update_player(player)
Thread.sleep(2000)
player = false
turnCount++
checkWinner()
if (turnCount == 9) {
Status.text = "Game Draw"
flag = true;
}
} else {
Status.text = "Computer's Turn"
update_computer(player)
Thread.sleep(2000)
player = true
turnCount++
checkWinner()
if (turnCount == 9){
Status.text = "Game Draw"
flag = true
}
}
}
if(flag == true)
break
}
changeBoard()
resetBtn.setOnClickListener{
player = true;
turnCount = 0
changeBoard()
}
}
private fun update_player(player:Boolean){
for(i in board){
for(button in i){
button.setOnClickListener{
when(it.id){
R.id.First->{
updateBoardStatus(row = 0, column = 0,player)
}
R.id.Second->{
updateBoardStatus(row = 0, column = 1,player)
}
R.id.Third->{
updateBoardStatus(row = 0, column = 2,player)
}
R.id.Fourth->{
updateBoardStatus(row = 1, column = 0,player)
}
R.id.Fifth->{
updateBoardStatus(row = 1, column = 1,player)
}
R.id.Sixth->{
updateBoardStatus(row = 1, column = 2,player)
}
R.id.Seventh->{
updateBoardStatus(row = 2, column = 0,player)
}
R.id.Eighth->{
updateBoardStatus(row = 2, column = 1,player)
}
R.id.Ninth->{
updateBoardStatus(row = 2, column = 2,player)
}
}
}
}
}
}
private fun update_computer(player:Boolean){
var row:Int = 0
var column:Int = 0
Thread {
row = (0..2).random()
column = (0..2).random()
}.start()
if(boardStatus[row][column] == 0 || boardStatus[row][column]==1)
update_computer(player)
else
updateBoardStatus(row, column, player)
}
private fun updateBoardStatus(row:Int, column:Int, player:Boolean){
val text = if (player) "X" else "0"
val value = if (player) 1 else 0
board[row][column].apply {
isEnabled = false
setText(text)
}
boardStatus[row][column] = value
}
private fun checkWinner(){
//Horizontal --- rows
for (i in 0..2) {
if (boardStatus[i][0] == boardStatus[i][1] && boardStatus[i][0] == boardStatus[i][2]) {
if (boardStatus[i][0] == 1) {
result("Player Won!!")
break
} else if (boardStatus[i][0] == 0) {
result("Computer Won")
break
}
}
}
//Vertical --- columns
for (i in 0..2) {
if (boardStatus[0][i] == boardStatus[1][i] && boardStatus[0][i] == boardStatus[2][i]) {
if (boardStatus[0][i] == 1) {
result("Player Won!!")
break
} else if (boardStatus[0][i] == 0) {
result("Computer Won!!")
break
}
}
}
//First diagonal
if (boardStatus[0][0] == boardStatus[1][1] && boardStatus[0][0] == boardStatus[2][2]) {
if (boardStatus[0][0] == 1) {
result("Player Won!!")
} else if (boardStatus[0][0] == 0) {
result("Computer won!!")
}
}
//Second diagonal
if (boardStatus[0][2] == boardStatus[1][1] && boardStatus[0][2] == boardStatus[2][0]) {
if (boardStatus[0][2] == 1) {
result("Player Won!!")
} else if (boardStatus[0][2] == 0) {
result("Computer Won!!")
}
}
}
private fun result(res:String){
Status.text = res
if(res.contains("Won")){
disableButton()
}
else{
}
}
private fun disableButton(){
for(i in board){
for(button in i){
button.isEnabled = false
}
}
}
private fun changeBoard(){
for (i in 0..2) {
for (j in 0..2) {
boardStatus[i][j] = -1
}
}
for (i in board) {
for (button in i) {
button.isEnabled = true
button.text = ""
}
}
}
}
Your code is trying to put the whole sequence of actions of the game in a function that is called once and then expects player button-presses to happen internally. Button listeners will fire some time in the future, after the function already returns. You need to think in terms of there being a function that is called each time a button is pressed to do the next stage of the game.
To fix this:
Remove the playGame() function.
Remove the player parameter from update_player() since it's always true. And change the function name to initializeButtons. Call it once in onCreate(). You only have to add listeners to the buttons one time and they will work repeatedly.
Also remove the player parameter from update_computer() for the same reason as above. And remove the threading so it looks like:
private fun update_computer() {
val row = (0..2).random()
val column = (0..2).random()
if (boardStatus[row][column] == 0 || boardStatus[row][column] == 1)
update_computer()
else
updateBoardStatus(row, column, player)
}
Then at the end of the updateBoardStatus function call checkWinner(). checkWinner() should return a Boolean, so in updateBoardStatus(), if no win condition has been found and player is true, it should call update_computer().
So what you have now instead of trying to run the game from one function, you set up button listeners one time to start the game. When a button is pressed, it takes the player turn, which then triggers updateBoardStatus, which then triggers the computer turn, which then triggers updateBoardStatus again, and then does nothing if no one won. All of that happens synchronously/instantly on the main thread, so now the game is back to waiting for a button press from the user to repeat the sequence of events.
Also, the status text view has limited usefulness. Since the computer takes its turns instantly, it's not possible to ever see the words "Computer's turn". If you want to do that, you'll have to create an artificial delay, so you would have to disable all the buttons and then do something like call postRunnable({ startPlayerTurn() }, 1000L), where the startPlayerTurn() re-enables the appropriate buttons and makes it say, "Player turn" again.

How can we detect which keyboard is open, Numeric or Alpha Numeric in android Webview?

Hi i am new here i need to know how can i detect which keyboard type is open in android webview here is my try
KeyboardUtils.addKeyboardToggleListener(this, new KeyboardUtils.SoftKeyboardToggleListener()
{
#Override
public void onToggleSoftKeyboard(boolean isVisible)
{
if(isVisible){
InputMethodManager imm =(InputMethodManager)MainActivity.this.getSystemService(Context.INPUT_METHOD_SERVICE);
Log.d(TAG, "onToggleSoftKeyboard: "+imm.getCurrentInputMethodSubtype().getMode());
//
}
Log.d("keyboard", "keyboard visible: "+isVisible);
}
});
You can use below code to check the type
private fun getKeyboardType(){
val devicesIds = InputDevice.getDeviceIds()
for (deviceId in devicesIds) {
val device = InputDevice.getDevice(deviceId)
device.keyboardType //returns an integer
// Now compare this integer with predefined constant values : https://developer.android.com/reference/android/text/InputType
}
}
Compare the integer values with constants here
You can check when keyboard opens/closes using a listener.
val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
// Keep a reference to the last state of the keyboard
private var lastState: Boolean = activity.isKeyboardOpen()
override fun onGlobalLayout() {
val isOpen = activity.isKeyboardOpen()
if (isOpen == lastState) {
return
} else {
dispatchKeyboardEvent(isOpen)
lastState = isOpen
}
}
}
And add that listener in activity lifecycle callback
override fun onResume() {
super.onResume()
val view = getRootView()
view.viewTreeObserver.addOnGlobalLayoutListener(listener)
}
And remember to remove listener when activity is no longer active
override fun onPause() {
super.onPause()
val view = getRootView()
view.viewTreeObserver.removeOnGlobalLayoutListener(listener)
}

How it is possible using Kotlin function features make this piece of code nicer?

I have these piece of codes and I don't like that I have mapNotNull and inside I have else cases which returns null, I think my kotlin knowledge is not enough to make it better, any thoughts?
return config.mainMenus.mapNotNull { mainMenu ->
val subPage = config.subPages.find {
if (mainMenu.actions.isNotEmpty()) {
it.id == mainMenu.actions.first().contentId
} else {
false
}
}
if (subPage?.items?.isNotEmpty() != null) {
MenuItem(mainMenu.type, mainMenu.titles, mainMenu.actions[0].type, subPage.items)
} else {
null
}
}
}
val programs = cards.mapNotNull { card ->
if (card is Program) {
epgRepository.getProgramProgress(currentTime = currentTime, program = card)
} else {
null
}
}
You can replace the previous code with
return config.mainMenus.mapNotNull { mainMenu ->
config.subPages
.find{mainMenu.actions.firstOrNull()?.contentId?.equals(it.id)?:false}
?.let{menu->
MenuItem(mainMenu.type, mainMenu.titles, mainMenu.actions[0].type, menu.items).takeIf{menu.items?.isNotEmpty()==true}
}?:null
}
}
Te second one could be
val programs = cards.
filterIsInstance<Program>().
map { epgRepository.getProgramProgress(currentTime = currentTime, program = card)}
In this case you firstly filter the collection getting only the elements that are programs and only those are converted to the type that the function getProgramProcess return

android mapbox select feature on click

I'm using mapbox, with GeoJsonSource and symbollayer. When user clicks on feature it should change a color. I handle this logic with following code and it works, but it is too slow and takes several second to change icon color.
Here I configure symbol layer, add icon changelogic for 'PROPERTY_SELECTED':
mapBoxMap?.addLayer(SymbolLayer(markerStyleLayerIdentifier, markerSourceIdentifier)
.withProperties(
PropertyFactory.iconImage(markerImage),
PropertyFactory.iconAllowOverlap(false),
PropertyFactory.iconImage(match(
get(PROPERTY_SELECTED), literal(0),
literal(markerImage),
literal(markerImageSelected)
))
))
on map click features objects are update:
override fun onMapClick(point: LatLng) {
val screenPoint = mapBoxMap?.projection?.toScreenLocation(point)
var features = mapBoxMap?.queryRenderedFeatures(screenPoint
?: return, markerStyleLayerIdentifier)
if ((features ?: return).isNotEmpty()) {
var feature = features[0]
showMarkerInfo(feature)
doAsync {
var featureList = featureCollection?.features()
var id = feature.getNumberProperty(PROPERTY_STOP_ID)
if (featureList != null) {
for (i in 0 until featureList.size) {
var fId = featureList[i].getNumberProperty(PROPERTY_STOP_ID)
if (fId == id) {
featureList[i].properties()?.addProperty(PROPERTY_SELECTED, 1)
} else {
featureList[i].properties()?.addProperty(PROPERTY_SELECTED, 0)
}
}
uiThread {
refreshSource()
}
}
}
}
}
and refresh source :
private fun refreshSource() {
var source = mapBoxMap?.getSource(markerSourceIdentifier) as GeoJsonSource?
if (source != null && featureCollection != null) {
source.setGeoJson(featureCollection)
}
}
after 'refreshSource' is called , it takes several time before icon update. In my case there are 2050 features is source. Is there any better way to implement it ? Or any way to optimise this solution ?
here is a second , faster way from github answer:
var selectedLayer = mapBoxMap?.getLayer(markerSelectedStyleLayerIdentifier) as SymbolLayer?
var id = feature.getNumberProperty(PROPERTY_STOP_ID)
var selectedExpression = any(
eq(get(PROPERTY_STOP_ID), literal(id.toString()))
)
selectedLayer?.filter = selectedExpression
you can see whole issue there
https://github.com/mapbox/mapbox-java/issues/892

Properly extend a Widget class using Kotlin

The code below is basically changing the state of a Button Widget:
enum class State { unable, enable }
fun configureState(currentState:State, button:Button ,colorInt :Int = Color.BLACK) = when (currentState)
{
State.unable -> {
button.isClickable = false
button.setBackgroundColor(Color.LTGRAY)
button.setTextColor(Color.WHITE)
}
State.enable -> {
button.isClickable = true
button.setBackgroundColor(colorInt)
button.setTextColor(Color.WHITE)
}
}
The goal is to extend the Button Widget and avoid code repetition inside all my activities.
Yes it is easy to just extend via function, if I didn't have the enum State, by doing something like this:
fun Button.configureState(state:Int) = when(state) {
0 -> { //do unable stuff }
1 -> { // do enable stuff }
else -> { // do nada }
}
My question would be what's the proper way to extend with the enum state where I could access it via Extension Function for example:
fun Button.configureWith(state: this.State) = when (state) {
this.State.unable -> { }
this.State.enable -> { }
}
P.S again: I'm new to Kotlin :), any ideas .. all welcome :)
Your code treat the State as a parameter for changing the button state only. It does not necessary to be declared inside the subclass of Button. You can declare it outside of it with an extension function.
enum class ButtonState { unable, enable }
fun Button.configureWith(state: ButtonState, colorInt: Int = Color.BLACK) = when (state) {
ButtonState.unable -> {
clickable = false
setBackgroundColor(Color.LTGRAY)
}
ButtonState.enable -> {
clickable = true
setBackgroundColor(colorInt)
}
}

Categories

Resources