I have a Datalogic Memor K – a mobile computer which runs Android 9 and has a hardware keyboard.
In my application, I can move the focus/highlight between controls, including TextView controls with isFocusableInTouchMode = true, manually using the hardware arrow keys (see below).
But when I do it programmatically, requestFocus() returns true, but the TextView does not highlight, although it seems to get the focus, because the next move with the arrow keys starts from it.
I create a project from the Empty activity template.
For better visibility, I add the following to the res/value/themes.xml file:
<!-- Customize your theme here. -->
<item name="android:textSize">25sp</item>
<item name="colorControlHighlight">#color/purple_700</item>
</style>
</resources>
To keep my post shorter, I don't use a layout file and do everything in the code of the main activity:
#file:SuppressLint("SetTextI18n")
package lv.trialto.myapplication
import android.annotation.SuppressLint
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.ViewGroup
import android.view.ViewGroup.LayoutParams.MATCH_PARENT
import android.view.ViewGroup.LayoutParams.WRAP_CONTENT
import android.widget.Button
import android.widget.LinearLayout
import android.widget.TextView
import android.widget.Toast
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val numbers = arrayOf("One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Nine")
var nextNumberIndex = 0
val rootLinearLayout = LinearLayout(this).apply {
layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)
orientation = LinearLayout.VERTICAL
}
val textViewList = mutableListOf<TextView>()
for (y in 1..3) {
val horizontalLinearLayout = LinearLayout(this).apply {
layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
orientation = LinearLayout.HORIZONTAL
}
rootLinearLayout.addView(horizontalLinearLayout)
for (x in 1..3) {
val cell = TextView(this).apply {
width = (100 * context.resources.displayMetrics.density + 0.5).toInt()
isFocusableInTouchMode = true
text = numbers[nextNumberIndex++]
}
textViewList.add(cell)
horizontalLinearLayout.addView(cell)
}
}
rootLinearLayout.addView(Button(this).apply {
layoutParams = ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)
text = "Focus random cell"
setOnClickListener {
val randomTextView = textViewList[textViewList.indices.random()]
val saveText = randomTextView.text
randomTextView.text = "HERE!"
val result = randomTextView.requestFocus()
Toast.makeText(context, "result = $result", Toast.LENGTH_LONG).show()
lifecycleScope.launch {
delay(1000)
randomTextView.text = saveText
}
}
})
setContentView(rootLinearLayout)
}
}
What am I doing wrong?
Also, what is the correct name of this focus/highlight/cursor?
Related
My app has 2 screens and works correctly in development so far. It already has 2 buttons ('toss' and 'startGame'which work correctly. Now I have added a further button called 'balls', designed to change the background colours of 4 TextViews ('ball1' to 'ball4'). However, pressing it once does nothing, and pressing it again causes the app to stop, with the displayed message "GC Clicker has stopped" or "GC Clicker keeps stopping".
Here is the first section of my MainActivity.kt:
package com.example.golfclicker
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import android.widget.EditText
import android.widget.TextView
import android.graphics.Color
import android.widget.Button
import android.content.Intent
import android.media.AudioManager
import android.media.SoundPool
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val soundPool:SoundPool?
// Link variables to xml views
val player1Name = findViewById<EditText>(R.id.PersonName1)
val handicap1 = findViewById<EditText>(R.id.hpInput1)
val player2Name = findViewById<EditText>(R.id.PersonName2)
val handicap2 = findViewById<EditText>(R.id.hpInput2)
val startGame = findViewById<Button>(R.id.startGame)
val toss = findViewById<Button>(R.id.toss)
val bkgnd1 = findViewById<TextView>(R.id.Background1)
val bkgnd1a = findViewById<TextView>(R.id.Background1a)
val bkgnd1b = findViewById<TextView>(R.id.Background1b)
val bkgnd2 = findViewById<TextView>(R.id.Background2)
val bkgnd2a = findViewById<TextView>(R.id.Background2a)
val bkgnd2b = findViewById<TextView>(R.id.Background2b)
val balls = findViewById<Button>(R.id.balls)
val ball1 = findViewById<TextView>(R.id.ball1)
val ball2 = findViewById<TextView>(R.id.ball2)
val ball3 = findViewById<TextView>(R.id.ball3)
val ball4 = findViewById<TextView>(R.id.ball4)
var priSec = true // True:Primaries False:Secondaries
var tossedP1 = "" //String values to pass to Intent
var tossedHp1 =""
var tossedP2 = ""
var tossedHp2 =""
var tossed = false //has Toss button been used?
//Set up soundpool and load the sound file
soundPool = SoundPool(2,AudioManager.STREAM_MUSIC,0)
val sound1 = soundPool.load(baseContext,R.raw.computer_keyboard,1)
val sound2 = soundPool.load(baseContext,R.raw.coins,1)
//Handle balls button click
balls.setOnClickListener {
if(priSec==true) {
with(ball1) { setBackgroundColor(Color.parseColor("button_blue")) }
with(ball2) { setBackgroundColor(Color.parseColor("red")) }
with(ball3) { setBackgroundColor(Color.parseColor("black")) }
with(ball4) { setBackgroundColor(Color.parseColor("yellow")) }
} else {
with(ball1) { setBackgroundColor(Color.parseColor("green")) }
with(ball2) {setBackgroundColor(Color.parseColor("pink")) }
with(ball3) {setBackgroundColor(Color.parseColor("brown")) }
with(ball4) {setBackgroundColor(Color.parseColor("white")) }
}
priSec = !priSec //toggle primary/secondary
}
//Handle 'Toss' button click
toss.setOnClickListener {
var ran = (0..1).random() //Toss coin
// val ran = 1 //checking name-swapping
Any suggestions would be welcome.
Color.parseColor(...) works in different way than you think.
You can parse color like this:
Color.parseColor("#ff77bb")
In other words, you must pass hex code of color
this line is wrong .. because you didn't define the color
setBackgroundColor(Color.parseColor("button_blue")
try
setBackgroundColor(Color.parseColor("#39b8db")
or
context.getResources().getColor(android.R.color.white)
I finally found the following works to use colours defined in colors.xml.
I didn't want to include hex colours in my main code, as I may want to edit them at a later date.
ball1.setBackgroundColor(getResources().getColor(R.color.button_blue))
Thanks Niaj; this is similar to your last suggestion.
I am working on localizing my Android application using Kotlin and want to set the content to spanish everytime the radio button is pressed. For that, whenever the button is clicked I fire up a LanguageActivity which basically is supposed to set the locale value to espanol. However, I notice that when the LanguageActivity loads, the screen turns white and nothing happens unless I hit the back button. I don't see any error message too, so I'm not sure what's happening. I am loading the activity from within a non-activity NewsFragment class. I'm using Android API 30.
Also, I'm pretty much new to all to this and would greatly appreciate if you could provide feedback on the localization logic in LanguageActivity and ContextUtilsand suggest any better approach.
NewsFragment
internal fun showLanguagePopup(anchorView: View, gravity: Int) {
val popupBinding = DataBindingUtil.inflate<ItemNewsLanguagePopupBinding>(layoutInflater, R.layout.item_news_language_popup, null, false)
viewModel.availableLanguages
.forEachIndexed { index, language ->
val button = layoutInflater.inflate(R.layout.item_news_language_popup_button, popupBinding.gLanguages, false) as RadioButton
button.text = language.toNameString(activity!!)
button.id = index
button.isChecked = viewModel.getCurrentLanguage() == language
popupBinding.gLanguages.addView(button)
}
val popupWindow = PopupWindow(popupBinding.root, LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
popupWindow.elevation = 10f
}
popupBinding.bCancel.setOnClickListener { popupWindow.dismiss() }
popupBinding.bOk.setOnClickListener {
val selectedLanguageOrdinal = popupBinding.gLanguages.checkedRadioButtonId
if (selectedLanguageOrdinal >= 0) {
viewModel.setupLanguage(viewModel.availableLanguages[selectedLanguageOrdinal])
val intent = Intent(activity, LanguageActivity::class.java)
//intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
drawerLogicView.onBackPressed()
}
popupWindow.dismiss()
}
popupWindow.showAsDropDown(anchorView, 0, -anchorView.height / 2, gravity)
}
LanguageActivity
package com.oigetit.oigetit.ui.news.list
import android.content.Context
import android.content.ContextWrapper
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.oigetit.oigetit.utility.ContextUtils
import java.util.*
class LanguageActivity : AppCompatActivity() {
override fun attachBaseContext(newBase: Context) {
val localeToSwitchTo = Locale("es")
val localeUpdatedContext: ContextWrapper? = ContextUtils.updateLocale(newBase, localeToSwitchTo)
super.attachBaseContext(localeUpdatedContext)
}
}
ContextUtils
package com.oigetit.oigetit.utility
import android.content.Context
import android.content.ContextWrapper
import android.content.res.Configuration
import android.content.res.Resources
import android.os.Build
import android.os.LocaleList
import java.util.*
class ContextUtils(base: Context?) : ContextWrapper(base) {
companion object {
fun updateLocale(context: Context, localeToSwitchTo: Locale?): ContextWrapper? {
var context = context
val resources: Resources = context.resources
val configuration: Configuration = resources.getConfiguration() // 1
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
val localeList = LocaleList(localeToSwitchTo) // 2
LocaleList.setDefault(localeList) // 3
configuration.setLocales(localeList) // 4
} else {
configuration.setLocale(localeToSwitchTo) // 5
}
//val resources: Resources = context.resources
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N_MR1) {
context = context.createConfigurationContext(configuration) // 6
} else
{
context = context.createConfigurationContext(configuration)
}
return ContextUtils(context) // 8
}
}
}
You didn't start your activity with that intent.
add startActivity(intent) below the val intent = Intent(activity, LanguageActivity::class.java) line.
I am loading the activity from within a non-activity NewsFragment class and don't want to use the MainActivity for loading
For this part of your question, fragments are completely depend on activities, therefore they can't start another activity inside themselves.
package com.example.firstapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.widget.EditText
import kotlinx.android.synthetic.main.activity_main.*
import kotlin.random.Random
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val deEditText = findViewById<EditText>(R.id.de) as EditText
val ateEditText = findViewById<EditText>(R.id.ate) as EditText
Randomize.setOnClickListener{
val de = Integer.parseInt(deEditText.text.toString())
val ate = Integer.parseInt(ateEditText.text.toString())
RandomDisplay.text = (Random.nextInt(de - ate) + ate).toString()
}
}
}
I'm trying to create an app that gets two values and picks a random integer between them. I don't know what I'm doing wrong, and I really hope someone can help me. Thanks in advance.
val deEditText = findViewById<EditText>(R.id.de) as EditText
val ateEditText = findViewById<EditText>(R.id.ate) as EditText
Random.nextInt(de - ate)
if value from deEditText minus value from ateEditText is zero or negative, Radom.nextInt would throw IllegalArgumentException
// minimize the end bound to 1 by adding max()
RandomDisplay.text = (Random.nextInt(max(1, de - ate)) + ate).toString()
I'm new in app development. When I run my code on emulator/phone with an android version less or equal to 7 the app is crashing. If I run the app on emulator/phone with android higher or equals to 7.1 is working without any problems.
The compiler is not giving to me any problem.
SDKversion is 28.
I have analyzed my code a lot and I found that the problem is caused by some "if statement". If I comment those parts of code, the app is working also on android 7.
There are 2 relevant classes that caused to me this problem. Both of these classes have a progress bar and when the progress bar reach 100, my app needs to do some actions.
For making this actions I have implemented an if statement.
Home class:
package com.example.adamo3
import android.content.Intent
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.support.v7.app.ActionBar
import android.support.v7.app.AppCompatActivity
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.view.animation.AnimationUtils
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import com.example.myapplication.R
import kotlinx.android.synthetic.main.activity_home.*
class Home :AppCompatActivity() {
internal var pStatus = 0
private val handler = Handler()
internal lateinit var tv: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_home)
setSupportActionBar(findViewById(R.id.toolbar))
//home navigation
//supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.displayOptions
supportActionBar?.title = null
//setting progressBar
val drawable = resources.getDrawable(R.drawable.circular)
val mProgress = findViewById<View>(R.id.progressBar) as ProgressBar
mProgress.progress = 0 // Main Progress
mProgress.secondaryProgress = 100 // Secondary Progress
mProgress.max = 100 // Maximum Progress
mProgress.progressDrawable = drawable
/* ObjectAnimator animation = ObjectAnimator.ofInt(mProgress, "progress", 0, 100);
animation.setDuration(50000);
animation.setInterpolator(new DecelerateInterpolator());
animation.start();*/
tv = findViewById<View>(R.id.tv2) as TextView
Thread(Runnable {
while (pStatus < 100) {
pStatus += 1
Thread.sleep(200)
handler.post {
mProgress.progress = pStatus
tv2.text = pStatus.toString() + "%"
}
try {
// Sleep for 200 milliseconds.
// Just to display the progress slowly
Thread.sleep(16) //thread will take approx 3 seconds to finish
} catch (e: InterruptedException) {
e.printStackTrace()
}
if(pStatus == 100) {
//if i comment this part, this class will works
var t = textView6.text.toString().toInt()
t += 1
textView6.text = t.toString()
pStatus = 0
}
}
}).start()
}
Class Reward:
package com.example.adamo3
import android.content.Intent
import android.graphics.drawable.AnimationDrawable
import android.os.Bundle
import android.os.Handler
import android.support.constraint.ConstraintLayout
import android.support.v7.app.AppCompatActivity
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.ProgressBar
import android.widget.TextView
import android.widget.Toast
import com.example.myapplication.R
import kotlinx.android.synthetic.main.activity_home.*
import kotlinx.android.synthetic.main.activity_reward.*
class Reward : AppCompatActivity() {
private val handler = Handler()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_reward)
setSupportActionBar(findViewById(R.id.toolbar4))
//home navigation
supportActionBar?.setDisplayHomeAsUpEnabled(true)
supportActionBar?.setTitle(null)
// val drawable = res.getDrawable(R.drawable.circular)
val mProgress = findViewById<View>(R.id.progressBar2) as ProgressBar
mProgress.progress = 0 // Main Progress
//mProgress.secondaryProgress = 100 // Secondary Progress
mProgress.max = 100 // Maximum Progress
// mProgress.progressDrawable = drawable
Thread(Runnable {
while (mProgress.progress < 100) {
mProgress.progress += 1
// Thread.sleep(1000)
handler.post {
mProgress.progress = mProgress.progress
}
try {
// Sleep for 200 milliseconds.
// Just to display the progress slowly
Thread.sleep(16) //thread will take approx 3 seconds to finish
} catch (e: InterruptedException) {
e.printStackTrace()
}
if (mProgress.progress == 33){
runOnUiThread(Runnable{
var t = textView13.text.toString().toInt()
t = (t-1)
textView13.text = t.toString()
})
}
if (mProgress.progress == 66){
runOnUiThread(Runnable{
var t = textView13.text.toString().toInt()
t = (t-1)
textView13.text = t.toString()
})
}
if (mProgress.progress == 100) {
//if i comment this part, this class will works
runOnUiThread(Runnable{
imageView6.visibility = View.VISIBLE
textView3.visibility = View.VISIBLE
textView13.visibility = View.INVISIBLE
})
textView12.visibility = View.INVISIBLE
}
}
}).start()
}
More in details, crashes are caused by
"Class -> Home": when the progress bar reach 100, one value inside a textview must increase by 1
"Class -> Reward": when the progress bar reach 100, one textview should become invisible and other 2 visibile.
Errors that the logcat is showing to me are:
2019-07-14 01:06:04.884 1753-1753/? E/RichInputConnection: Unable to connect to the editor to retrieve text.
2019-07-14 01:06:04.884 1753-1753/? W/RichInputConnection: Unable to connect to the editor. Setting caps mode without knowing text.
UPDATE--> java stack trace
2019-07-14 02:03:29.391 6450-6517/com.example.adamo3 E/AndroidRuntime: FATAL
EXCEPTION: Thread-5
Process: com.example.adamo3, PID: 6450
android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original
thread that created a view hierarchy can touch its views.
at android.view.ViewRootImpl.checkThread(ViewRootImpl.java:6855)
at android.view.ViewRootImpl.invalidateChildInParent(ViewRootImpl.java:1075)
at android.view.ViewGroup.invalidateChild(ViewGroup.java:5242)
at android.view.View.invalidateInternal(View.java:13574)
at android.view.View.invalidate(View.java:13538)
at android.view.View.invalidate(View.java:13522)
at android.widget.TextView.checkForRelayout(TextView.java:7346)
at android.widget.TextView.setText(TextView.java:4479)
at android.widget.TextView.setText(TextView.java:4336)
at android.widget.TextView.setText(TextView.java:4311)
at com.example.adamo3.Home$onCreate$1.run(Home.kt:84)
at java.lang.Thread.run(Thread.java:761)
I am not 100% sure but try this in your Thread block:
Replace this:
handler.post {
mProgress.progress = pStatus
tv2.text = pStatus.toString() + "%"
}
with this:
Handler(Looper.getMainLooper()).post {
mProgress.progress = pStatus
tv2.text = pStatus.toString() + "%"
}
This my first kotlin/java code. I want learn from this.
When I push "OK" button of Android keyboard, I want my app do same thing as when I push my button with android:onClick="onAnswerClick"
I know my code is not very good. You can't help me to know how to optimize it
I don't know if its a good idea to learn to code on android with kotlin.
import android.annotation.SuppressLint
import android.app.Activity
import android.os.Bundle
import android.view.KeyEvent
import android.view.View
import android.view.inputmethod.EditorInfo
import android.widget.*
import android.widget.AdapterView
import android.widget.Toast
import android.widget.Spinner
import kotlinx.android.synthetic.main.activity_main.*
import android.widget.EditText
class MainActivity : Activity() {
class Calculateurs{
fun aire(r: Double): Double{
return 3.141592 * r * r
}
fun circonference(r: Double): Double{
return 2.0 * 3.141592 * r
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val spinner = findViewById<Spinner>(R.id.spinner) as Spinner
// Create an ArrayAdapter using the string array and a default spinner layout
val adapter = ArrayAdapter.createFromResource(this,
R.array.mesure_array, android.R.layout.simple_spinner_item)
// Specify the layout to use when the list of choices appears
adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item)
// Apply the adapter to the spinner
spinner.adapter = adapter
}
#SuppressLint("SetTextI18n")
fun onAnswerClick(view: View) {
val answer = (findViewById<EditText>(R.id.editText2) as EditText).text.toString()
val spinner = findViewById<View>(R.id.spinner) as Spinner
var rayon = java.lang.Double.parseDouble(answer)
if (spinner.getSelectedItem() == "Diamètre"){
rayon = rayon *2
}
if (rayon < 0) {
val toasty = Toast.makeText(applicationContext, "Ce nombre est négatif !", Toast.LENGTH_SHORT)
toasty.show()
} else if (rayon > 0) {
val c = Calculateurs()
val tc = findViewById<TextView>(R.id.cire) as TextView
tc.text = "" + c.circonference(rayon)
val ta = findViewById<TextView>(R.id.aire) as TextView
ta.text = "" + c.aire(rayon)
} else {
val toasty = Toast.makeText(applicationContext, "Ce nombre est nul !", Toast.LENGTH_SHORT)
toasty.show()
}
}
}
I think your question is how do you get your app to respond to the enter or done key when you press it in an EditText.
Since you did not show your layout, I will make a few assumptions.
Lets say you have and EditText that has the following attributes (plus others)
<EditText
android:id="#+id/field"
android:imeOptions="actionDone"
android:inputType="text"
...
app:layout_constraintTop_toTopOf="parent" />
Here is an example activity that will respond to hitting the enter key.
class MainActivity : AppCompatActivity() {
lateinit var field: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
field = findViewById(R.id.field) as EditText
}
override fun onResume() {
super.onResume()
field.setOnEditorActionListener( { textView, action, event ->
var handled = false
if (action == EditorInfo.IME_ACTION_DONE) {
println("call your method here")
handled = true
}
handled
})
}
You can call your onAnswerClick() in the location of the println("call your method here") statement, passing in textView if you like.
This example makes use of Kotlin's SAM Conversion which is covered in this section of the Kotlin documentation, specifically it uses the SAM conversion to create a TextView.OnEditorActionListener from a lambda expression
As Kamil Kulig, points out, there is definitely more samples and examples using Java than Kotlin, but I don't think you have to start with Java. You will need to learn enough Java to be able to understand what's going on in examples and documentation however.