This is what I've tried:
override fun onBackPressed() {
nicknameInput.clearFocus()
finish()
}
It only closes soft keyboard but not activity and it also not clears the focus.
The solution here:
Android - onBackPressed close soft keyboard AND activity
only works for SearchView
EDIT:
This is almost the whole activity:
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_set_nickname)
prefs = getSharedPreferences("userData", 0)
val preset = intent.getStringExtra("preset")
nicknameInput.setText(preset)
openSoftKeyboard(this, nicknameInput)
nicknameBG.setOnClickListener {
finish()
}
btnNotNowNickname.setOnClickListener {
finish()
}
btnSaveNickname.setOnClickListener {
checkNickname()
}
nicknameInput.setOnKeyListener(View.OnKeyListener { v, keyCode, event ->
// Log.d("pikabo", keyCode.toString())
if(keyCode == KeyEvent.KEYCODE_ENTER) {
checkNickname()
return#OnKeyListener true
}
false
})
} // ON CREATE
private fun openSoftKeyboard(context: Context, view: View) {
view.requestFocus()
this.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE)
}
Only the function checkNickname() is missing which isn't necessary here for sure
Using this you can listen for key pressed on edittext and pass your edittext view to hide function to close it.
replace view with your edittext reference.
view.setOnKeyListener(object : View.OnKeyListener {
override fun onKey(p0: View?, keyCode: Int, event: KeyEvent?): Boolean {
if (event!!.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK ) {
hide(view)
onBackPressed()
return true
}
return false
})
and using it you can close your keyboard
private fun hide(editText: EditText) {
try {
val inputMethodManager = this.getSystemService(Activity.INPUT_METHOD_SERVICE) as InputMethodManager
inputMethodManager.hideSoftInputFromWindow(editText.windowToken, 0)
} catch ( e:Exception) {
Log.d("TAG",e.message+"")
}
}
Related
I want to clear focus of TextInputEditTexts when the user taps outside of them. In order to do that I use the code from this answer. Because that does not work in dialogs I added the following code in my bottom sheet, as described here:
class MySheet(): BottomSheetDialogFragment() {
...
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : Dialog(requireActivity(), theme) {
override fun dispatchTouchEvent(event: MotionEvent): Boolean {
if (event.action == MotionEvent.ACTION_DOWN) {
val v = currentFocus
if (v is TextInputEditText) {
val outRect = Rect()
v.getGlobalVisibleRect(outRect)
if (!outRect.contains(event.rawX.toInt(), event.rawY.toInt())) {
v.clearFocus()
val imm: InputMethodManager =
context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(v.getWindowToken(), 0)
}
}
}
return super.dispatchTouchEvent(event)
}
}
}
}
But now, the bottom sheet is displayed as a normal dialog with margins on all sides. How can I force the normal behaviour of a modal bottom sheet?
I'm trying to handle the back button in a BottomSheetDialogFragment, which is a DialogFragment, using 'androidx.activity:activity-ktx:1.1.0-alpha01' and 'androidx.fragment:fragment-ktx:1.2.0-alpha01'.
handleOnBackPressed() is not called and the DialogFragment is dismissed. The OnBackPressedCallback is enabled when the back button is pressed.
I think the DialogFragment is intercepting the back button press, because the ComponentActivity never calls mOnBackPressedDispatcher.onBackPressed();
Is there a way to override the DialogFragment handling of back button press?
It's really hard to understand what Google Android Dev making. Anyway, i found a solution without using interfaces.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return object : Dialog(requireContext(), theme) {
override fun onBackPressed() {
// handle back press
}
}
}
Just override the onCreateDialog and also override onBackPressed inside it.
I found a solution, but I hope the library will take care of this usecase.
Create a custom BottomSheetDialog:
class BackPressBottomSheetDialog(context: Context, #StyleRes theme: Int,
private val callback: BackPressBottomSheetDialogCallback) :
BottomSheetDialog(context, theme) {
override fun onBackPressed() {
if (callback.shouldInterceptBackPress()) callback.onBackPressIntercepted()
else super.onBackPressed()
}
}
And its interface
interface BackPressBottomSheetDialogCallback {
fun shouldInterceptBackPress(): Boolean
fun onBackPressIntercepted()
}
Then in your BottomSheetDialogFragment
private val dialogCallback = object : BackPressBottomSheetDialogCallback {
override fun shouldInterceptBackPress(): Boolean {
//TODO should you intercept the back button?
}
override fun onBackPressIntercepted() {
//TODO what happens when you intercept the back button press
}
}
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return BackPressBottomSheetDialog(requireContext(), theme, dialogCallback)
}
My solution for Kotlin:
Override below method in BottomSheetDialogFragment.
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).apply {
setOnKeyListener { _: DialogInterface, keyCode: Int, keyEvent: KeyEvent ->
if (keyCode == KeyEvent.KEYCODE_BACK && keyEvent.action == KeyEvent.ACTION_UP) {
// your code
// return true if you want keep dialog visible
// return false if you want to dismiss dialog anyway
return#setOnKeyListener true
}
return#setOnKeyListener false
}
}
}
One more solution:
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).also {
val componentDialog = it as ComponentDialog
componentDialog.onBackPressedDispatcher.addCallback(this) {
val backPressedConsumed = yourLegacyLogic()
if (backPressedConsumed.not()) {
isEnabled = false
requireActivity().onBackPressedDispatcher.onBackPressed()
isEnabled = true
}
}
}
}
Explanation
Back-pressed events are passed into the dialog of the DialogFragment, therefore you should register OnBackpressedCallback for the dialog.
Disabling the callback (isEnabled = false) allows you to prevent the consumption of the next back-pressed event.
Once you disabled the callback you're retriggering the back pressed-event (calling requireActivity().onBackPressed()) and it will be consumed by someone else, e.g. your navigation will pop the back stack.
After that, you should enable the callback (isEnabled = true) to receive the next back-pressed events.
I want to perform the actions when the button is pressed not just clicked
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
var ib:ImageView=findViewById(R.id.imageView4) as ImageView
var final=MediaPlayer.create(this,R.raw.`m1`)
var anim=AnimationUtils.loadAnimation(this,R.anim.rotate)
ib.setOnTouchListener(){
textView.text="rip"
final.start();
ib.startAnimation(anim);
}
}
}
I want the actions to be performed when the button is held and stop when they are not .
this seems to be working fine but the compiler gives a warning
ib.setOnTouchListener(View.OnTouchListener { view, motionEvent ->
when (motionEvent.action){
MotionEvent.ACTION_DOWN -> {
textView4.text="IGNORING"
ib.startAnimation(anim)
final.setLooping(true)
final.start()
}
MotionEvent.ACTION_UP -> {
textView4.text="IGNORE"
ib.clearAnimation()
final.pause()
}
}
return#OnTouchListener true
})
I'm trying to reset a button to become clickable once a key on the keyboard is pressed. It works with the hardware keyboard but not with the emulator keyboard. How would one implement this?
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val button = findViewById<Button>(R.id.log_in_button)
val email = findViewById<EditText>(R.id.email_field)
button.isClickable = false
button.alpha = .5f
email.setOnKeyListener(object : View.OnKeyListener {
override fun onKey(v: View, keyCode: Int, event: KeyEvent): Boolean {
if(event.action == KeyEvent.ACTION_DOWN) {
button.isClickable = true
button.alpha = 1f
return true
}
return false
}
})
}
I have a RecyclerView that contains EditText child elements. I would like to hide the soft keyboard when the selected EditText is scrolled off screen. How can I tell when the EditText is no longer on screen? Is there some event listener that I can attach to the EditText element to tell?
Implement onTouchListener like this:
yourRecycleView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
imm.hideSoftInputFromWindow(v.getWindowToken(), 0);
return false;
}
});
This is what worked for me : I used RecyclerView.OnScrollListener with PublishRelay for debouncing events.
class RecyclerViewActivity : Activity(){
...
private val scrollableRelay = PublishRelay.create<Unit>()
private val disposable = CompositeDisposable()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
recyclerView.addOnScrollListener(object: RecyclerView.OnScrollListener(){
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
scrollableRelay.accept(Unit)
}
})
scrollableRelay
.debounce(100, TimeUnit.MILLISECONDS)
.subscribe({
if (currentFocus == recyclerView) {
hideKeyboard()
}
})
.addTo(disposable)
}
override fun onDestroy() {
disposable.onDestroy()
}
}
Once the view is scrolled from the screen the focus goes up to recyclerView.
So, we can implement this functionality using RecyclerView.OnScrollListener.
onScrolled tracks even slightest scroll of the view. That's why we need to add debounce that we'll not receive a lot of events.
hideKeyboard and addTo are extension functions:
fun Disposable.addTo(compositeDisposable: CompositeDisposable) {
compositeDisposable.add(this)
}
fun Activity.hideKeyboard() =
currentFocus?.let {
val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(it.windowToken, 0)
}
yourRecycleView.setOnTouchListener(new OnTouchListener() {
#Override
public boolean onTouch(View v, MotionEvent event) {
edittext.clearFocus(); //hidden keyboard
return false;
}
});
My solution:
editText.setOnFocusChangeListener((v, hasFocus) -> {
Handler handler = new Handler();
if (!hasFocus) {
handler.postDelayed(() -> {
if (!editText.hasFocus()) {
InputMethodManager imm = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE);
imm.toggleSoftInput(0, 0);
}
}, 200);
}
});
Kotlin solution that dismisses the keyboard when:
the focused View is scrolled off the RecyclerView
or if the user presses the Enter key, and the OS was unable to find something to focus on that is inside the RecyclerView...
// dismiss the keyboard when it is no longer focusing on anything inside the recyclerview
recyclerView.viewTreeObserver.addOnGlobalFocusChangeListener { _, newFocus: View? ->
if (newFocus == recyclerView || recyclerView !in (newFocus?.ancestors ?: emptySequence())) {
recyclerView.context.inputService.hideSoftInputFromWindow(recyclerView.windowToken, 0)
}
}
val Context.inputService get() = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
val View.ancestors: Sequence<View> get() = generateSequence(this) { it.parent as? View }
Been facing the same issue in BottomSheet . Took me a while to realize that to get currentFocus i have to use dialog.currentFocus instead of activity.currentFocus . Below is my Solution maybe it helps someone .
binding.recyclerView.addOnScrollListener(object:RecyclerView.OnScrollListener(){
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
super.onScrollStateChanged(recyclerView, newState)
if (newState == RecyclerView.SCROLL_STATE_IDLE) {
dialog?.currentFocus?.let {
if(it is RecyclerView){
val imm = requireActivity().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
imm.hideSoftInputFromWindow(it.windowToken, 0)
}
}
}
}
})