Hy,
I have a problem, and I can't figure out how to fix it.
So first of all, I used TextInputLayout with AutoCompleteText view, to show a drow down style, and it works well, but when I rotate the screen or couse configuration change like dark mode/light mode switch, the autcompleteTextView doesn't show the dropdown. I know it is an open issue, but I didn't find a working solution.
I tired setFreezesText = false, but it worked when I rotate the screen, and in on configuration change, any other case it didn't work.
layout:
<com.google.android.material.textfield.TextInputLayout
android:id="#+id/opportunity_recording_hr_person_auto_complete_container"
style="#style/Widget.MaterialComponents.TextInputLayout.OutlinedBox.ExposedDropdownMenu"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/responsible_hr_person_label">
<com.google.android.material.textfield.MaterialAutoCompleteTextView
android:id="#+id/opportunity_recording_hr_person_auto_complete_content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</com.google.android.material.textfield.TextInputLayout>
fragment:
override fun onConfigurationChanged(newConfig: Configuration) {
super.onConfigurationChanged(newConfig)
binding.opportunityRecordingHrPersonAutoCompleteContent.freezesText = false
}
So after that, I changed this, to simple AutoCompleteTextView, and if it gets focus, I show the dropdown list, but unfortunately, it doesn't work, because when configuration changes happen I get this error: Unable to add window -- token null is not valid; is your activity running?
layout:
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/name_of_project_label"
android:labelFor="#+id/opportunity_recording_customer_auto_complete_content"/>
<AutoCompleteTextView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/opportunity_recording_customer_auto_complete_content"
android:inputType="text"/>
fragment:
// I call this on onCiewCreated()
private fun initListeners() {
binding.opportunityRecordingCustomerAutoCompleteContent.setOnFocusChangeListener { _, isFocused ->
if (isFocused)
binding.opportunityRecordingCustomerAutoCompleteContent.showDropDown()
}
}
Is there any way to fix one of the problems at least?
Related
I have quite a complex problem to do with Android views. I am creating a paint application, and I have two views: a transparent background view and the pixel art board.
For both views, I want the height and width to be calculated off of the distance between view A and B:
Instead of calculating the distance between these two views, I simply 'constraint' a view in the middle like so, and then extract its height by using its measuredHeight property (and yes, you could also calculate the distance between view A and B in the code, but my problem still remains when I try that):
Now, here's the XML code:
<?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"
android:background="#color/fragment_background_color_daynight"
tools:context=".activities.canvas.CanvasActivity">
<View
android:id="#+id/activityCanvas_topView"
android:layout_width="match_parent"
android:layout_height="90dp"
android:background="#color/fragment_background_color_daynight"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.therealbluepandabear.pixapencil.customviews.colorswitcherview.ColorSwitcherView
android:id="#+id/activityCanvas_colorSwitcherView"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="16dp"
app:isPrimarySelected="false"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_colorPickerRecyclerView"
app:layout_constraintEnd_toEndOf="#+id/activityCanvas_topView"
app:layout_constraintTop_toTopOf="#+id/activityCanvas_colorPickerRecyclerView"
app:primaryColor="#android:color/holo_green_dark"
app:secondaryColor="#color/black" />
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/activityCanvas_colorPickerRecyclerView"
android:layout_width="0dp"
android:layout_height="50dp"
android:layout_marginStart="16dp"
android:layout_marginEnd="16dp"
android:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_topView"
app:layout_constraintEnd_toStartOf="#+id/activityCanvas_colorSwitcherView"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="#+id/activityCanvas_primaryFragmentHost"
tools:listitem="#layout/color_picker_layout" />
<FrameLayout
android:id="#+id/activityCanvas_distanceContainer"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_tabLayout"
app:layout_constraintEnd_toEndOf="#+id/activityCanvas_primaryFragmentHost"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/activityCanvas_topView" />
<com.google.android.material.card.MaterialCardView
android:id="#+id/fragmentOuterCanvas_canvasFragmentHostCardViewParent"
style="#style/activityCanvas_canvasFragmentHostCardViewParent_style"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:elevation="1dp"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_tabLayout"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/activityCanvas_topView">
<!-- At runtime, the width and height will be calculated -->
<com.therealbluepandabear.pixapencil.customviews.transparentbackgroundview.TransparentBackgroundView
android:id="#+id/activityCanvas_transparentBackgroundView"
android:layout_width="0dp"
android:layout_height="0dp" />
</com.google.android.material.card.MaterialCardView>
<com.google.android.material.tabs.TabLayout
android:id="#+id/activityCanvas_tabLayout"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:tabStripEnabled="false"
app:layout_constraintBottom_toTopOf="#+id/activityCanvas_viewPager2"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_tools_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_filters_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_color_palettes_str" />
<com.google.android.material.tabs.TabItem
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/activityCanvas_tab_brushes_str" />
</com.google.android.material.tabs.TabLayout>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/activityCanvas_viewPager2"
android:layout_width="0dp"
android:layout_height="110dp"
app:layout_constraintBottom_toBottomOf="#+id/activityCanvas_primaryFragmentHost"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<androidx.coordinatorlayout.widget.CoordinatorLayout
android:id="#+id/activityCanvas_coordinatorLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<FrameLayout
android:id="#+id/activityCanvas_primaryFragmentHost"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
Of course, when it comes to calculating, I thought it would be best practise to utilize AndroidX's OneShotPreDrawListener, like so:
OneShotPreDrawListener.add(binding.root) {
binding.activityCanvasTransparentBackgroundView!!.setViewWidth(binding.activityCanvasDistanceContainer!!.measuredHeight)
binding.activityCanvasTransparentBackgroundView!!.setViewHeight(binding.activityCanvasDistanceContainer!!.measuredHeight)
}
Now, for some reason, the result looks like so:
Why is this the case!
I did some debugging, and when I log the height of view C, I get the following:
This is wrong. So, as an experiment, I added a GlobalLayoutListener to detect when exactly the view's constraints get resolved:
binding.activityCanvasDistanceContainer?.viewTreeObserver?.addOnGlobalLayoutListener( object : ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.d("M_LOG", binding.activityCanvasDistanceContainer?.measuredHeight.toString())
}
})
Result:
So, after the first couple of times it's 438, until after the 4th/5th time it shoots up to 1000.
I am really confused why this is happening, as I want to run the event when the constraints have been fully resolved and calculated, but using OneShotPreDrawListener (or any other alternative) is just running the event when the view has been drawn, but not yet when it has been positioned properly.
I am confused what to do. How can I run an event when the view's constraints have been fully calculated?
Edit for Chetichamp:
I have debugged this and I think I can reproduce this error and tell you in what scenario it occurs and in which scenario it does not.
Basically in my app, prior to activity creation, I have a fragment called NewProjectFragment, which looks like so:
Here's the code for when the 'Done' button is pressed:
binding.fragmentNewCanvasDoneButton.setOnClickListener {
checkForTitleError()
checkForWidthError()
checkForHeightError()
if (!invalidTitle && !invalidWidth && !invalidHeight) {
try {
val title =
binding.fragmentNewCanvasProjectTitleTextInputEditText.text.toString()
val widthValue: Int =
binding.fragmentNewCanvasWidthTextInputEditText.text.toString().toInt()
val heightValue: Int =
binding.fragmentNewCanvasHeightTextInputEditText.text.toString().toInt()
if (widthValue + heightValue >= 2000 && (requireActivity() as MainActivity).showLargeCanvasSizeWarning) {
val frameLayout: FrameLayout =
this#setOnClickListeners.activity?.layoutInflater?.inflate(
R.layout.dont_show_large_canvas_warning_again_checkbox,
requireView().findViewById(android.R.id.content),
false
)
as FrameLayout
val checkBox = frameLayout.getChildAt(0) as MaterialCheckBox
requireActivity().showDialog(
getString(R.string.generic_warning_in_code_str),
getString(R.string.dialog_large_canvas_warning_text_in_code_str),
getString(R.string.dialog_large_canvas_warning_positive_button_text_in_code_str),
{ _, _ ->
if (checkBox.isChecked) {
(requireActivity() as MainActivity).showLargeCanvasSizeWarning =
false
with((requireActivity() as MainActivity).sharedPreferenceObject.edit()) {
putBoolean(
StringConstants.Identifiers.SHARED_PREFERENCE_SHOW_LARGE_CANVAS_SIZE_WARNING_IDENTIFIER,
(requireActivity() as MainActivity).showLargeCanvasSizeWarning
)
apply()
}
}
caller.onDoneButtonPressed(
title,
widthValue,
heightValue,
paramSpotLightInProgress
)
},
getString(R.string.dialog_unsaved_changes_negative_button_text_in_code_str),
{ _, _ ->
},
frameLayout
)
} else {
caller.onDoneButtonPressed(
title,
widthValue,
heightValue,
paramSpotLightInProgress
)
}
} catch (exception: Exception) {
HapticFeedbackWrapper.performHapticFeedback(binding.fragmentNewCanvasDoneButton)
}
} else {
HapticFeedbackWrapper.performHapticFeedback(binding.fragmentNewCanvasDoneButton)
}
}
As you can see, it has a listener, so the code for the listener, which is in MainActivity, is like so (maybe this is causing the issue? and I just don't need a listener like this? I don't know if you think this is why I get the issue):
fun MainActivity.extendedOnDoneButtonPressed(projectTitle: String, width: Int, height: Int, spotLightInProgress: Boolean) {
startActivity(
Intent(this, CanvasActivity::class.java)
.putExtra(StringConstants.Extras.PROJECT_TITLE_EXTRA, projectTitle)
.putExtra(StringConstants.Extras.WIDTH_EXTRA, width)
.putExtra(StringConstants.Extras.HEIGHT_EXTRA, height)
.putExtra(StringConstants.Extras.SPOTLIGHT_IN_PROGRESS_EXTRA, spotLightInProgress)
)
}
Now, what I come to the conclusion is that all of this extra work is causing a delay, because when you simply tap on a pre-existing project, we can see that the intent is much simpler:
fun MainActivity.extendedOnCreationTapped(param: PixelArt) {
startActivity(
Intent(this, CanvasActivity::class.java)
.putExtra(StringConstants.Extras.INDEX_EXTRA, pixelArtData.indexOf(param))
.putExtra(StringConstants.Extras.PROJECT_TITLE_EXTRA, param.title))
}
With a simple intent like so, the problem doesn't get reproduced, and it sizes properly.
What I relaized is that the work done in NewProject fragment is causing a delay, and when I simply scrap out the work and perfom a simple intent, the problem is 'fixed'. I don't know how to fix this but hopefully it can help with finding a solution.
Debugging even further
When I debug the issue even further, I notice something strange. The measuredHeight of the root layout jumps up by one thousand:
This is not observed when the creation is tapped with the simple intent. I am lost for words as to how strange this bug is, I've never seen this in my life.
There is something going on with your code that is touching your views that is causing the multiple layouts. Without that code being posted, it is impossible to say what.
To help you with debugging, set the one shot to the following:
OneShotPreDrawListener.add(binding.root) {
Log.d("Applog", "OneShot")
binding.activityCanvasTransparentBackgroundView.layoutParams.width =
binding.activityCanvasDistanceContainer.measuredWidth
binding.activityCanvasTransparentBackgroundView.layoutParams.height =
binding.activityCanvasDistanceContainer.measuredHeight
binding.activityCanvasTransparentBackgroundView.requestLayout()
}
In my test, when placing this in the onCreateView() function of a fragment, "OneShot" is logged just once and the size of the view is adjusted as you say it should be. (btw, requestLayout() was required in this test.)
I would take a look at any other code that touches your views to see if something else is triggering the multiple calls: maybe setWidth() or setHeight().
I will add that I have noticed in the past that ConstraintLayout will call the global layout listener multiple times. Usually, I would say, the listener is removed on the first call, so this behavior would not be noticed.
Instead of the global layout listener or the predraw listener, you could try a layout listener:
binding.root.doOnLayout {
// Your code here
}
or
binding.root.doOnNextLayout {
// Your code here
}
doOnNextLayout is guaranteed to be called only once, while doOnLayout may be called multiple times.
Wow guys.
I finally found a solution, after 12 hours of nonstop debugging, almost going clinically insane, and logging and destroying my codebase.
I realized that the keyboard from the NewProjectFragment was cutting the view, causing the bug. Holy moly... I am ecstatic that I finally found why this is happening but at the same time shocked how I didn't discover this before!
The solution was to add the following to the activity:
<activity
android:name="com.therealbluepandabear.pixapencil.activities.canvas.CanvasActivity"
android:windowSoftInputMode="stateHidden" // this
android:hardwareAccelerated="true"
android:exported="false" />
Prior to this, the windowSoftInputMode was adjustResize -- causing the bug.
So I completed the first lesson of the Kotlin Course by Google on Udacity and after that I went on to make my own changes to the app that make them look better (just to learn, I don't care about the app). I made some changes like adding dark mode and customizing the UI.
I want the dark mode menu to work properly, like when I click on light mode it should recreate the activity with light mode enabled and should also check the light mode MenutItem. I also want the app to remember the settings so that if I choose Follow System during one session of the app and then close the app, it should keep following the system even after opening the app. I figured this should be done with SharedPreferences but I don't know how to properly do it. As you can see in the video attached below, when I click on any of the dark mode settings options, they don't automatically change the settings, I need to restart the app and only then do I see any changes in the activity that matches to whatever option I selected before closing the app.
Note: I want to mention that I am not very experienced with Android Development or Kotlin, I only started learning 2 months ago. I also want to mention that I DO NOT care about the app itself but I am rather using this app to learn how to get things done. I don't care if my app gets every feature I
would want it to have if I don't also learn how to do those things. It's not the app but the knowledge that I am getting from it that I care about, this is to say that please if you have an answer, do explain how that works.
This video shows the behavior of the app: https://drive.google.com/file/d/1SnN0r351OGF4xQNCtI1wtgouSFVROKzg/view?usp=sharing
I added a toolbar to my layout file of the main (and the only) activity. Here's the xml code for the layout resource:
<?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:id="#+id/diceRollerActivity"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
tools:context=".MainActivity">
<com.google.android.material.appbar.AppBarLayout
android:id="#+id/appBarLayout"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent">
<com.google.android.material.appbar.MaterialToolbar
android:id="#+id/activity_toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:title="#string/app_name" />
</com.google.android.material.appbar.AppBarLayout>
<ImageView
android:id="#+id/dice_image"
android:layout_width="275dp"
android:layout_height="335dp"
android:contentDescription="#+id/diceImage_imageView_description"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintVertical_bias="0.395"
app:srcCompat="#drawable/empty_dice" />
<Button
android:id="#+id/roll_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="#dimen/Container_Button_Padding"
android:text="#string/roll"
android:textAlignment="center"
android:textAllCaps="false"
android:textColor="#color/white"
android:textSize="25sp"
app:backgroundTint="#color/bold_button_color"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.497"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="#+id/dice_image"
app:layout_constraintVertical_bias="0.32"
app:rippleColor="#color/button_ripple" />
</androidx.constraintlayout.widget.ConstraintLayout>
and here is my the Kotlin code for the same activity:
package com.example.diceroller
import android.content.Context
import android.content.SharedPreferences
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.view.Menu
import android.view.MenuInflater
import android.view.MenuItem
import android.widget.*
import androidx.appcompat.app.AppCompatDelegate
class MainActivity : AppCompatActivity() {
lateinit var diceImage: ImageView
lateinit var sharedPreferences: SharedPreferences
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(findViewById(R.id.activity_toolbar))
val rollButton: Button = findViewById(R.id.roll_button)
diceImage = findViewById(R.id.dice_image)
rollButton.setOnClickListener {
rollDice()
}
sharedPreferences = getSharedPreferences(getString(R.string.app_name), Context.MODE_PRIVATE)
when (sharedPreferences.getString("Night_Mode_State", getString(R.string.follow_system))) {
getString(R.string.follow_system) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM)
getString(R.string.light_mode) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO)
getString(R.string.dark_mode) -> AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES)
}
}
override fun onCreateOptionsMenu(menu: Menu): Boolean {
val inflater: MenuInflater = menuInflater
inflater.inflate(R.menu.actionbar, menu)
return true
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {
when (item.itemId) {
R.id.follow_system -> sharedPreferences.edit().putString("Night_Mode_State", getString(R.string.follow_system)).apply()
R.id.light_mode -> sharedPreferences.edit().putString("Night_Mode_State", getString(R.string.light_mode)).apply()
R.id.dark_mode -> sharedPreferences.edit().putString("Night_Mode_State", getString(R.string.dark_mode)).apply()
}
return true
}
private fun rollDice() {
when ((1..6).random()) {
1 -> diceImage.setImageResource(R.drawable.dice_1)
2 -> diceImage.setImageResource(R.drawable.dice_2)
3 -> diceImage.setImageResource(R.drawable.dice_3)
4 -> diceImage.setImageResource(R.drawable.dice_4)
5 -> diceImage.setImageResource(R.drawable.dice_5)
else -> diceImage.setImageResource(R.drawable.dice_6)
}
}
}
This gist includes other files that I think are required to answer this question properly, namely: The menu resource file, the colors resource file and the styles resource file: https://gist.github.com/sbeve72/36a71919f1f965c5dcd466db1e099b4f.js"
I have 2 button and 1 textview in my layout. I had just trained, but saw some bugs. When users starts the cronometer they can unlimited click on the start button how can i fix it ?
I'm really newbie in the android development. Can you give advices for me, while i was watching my udemy lesson, i could catch the codes and basics. After that, when i try to build some basic projects for improve my skills, it gets hard. I have many syntax errors. I know what to do, but I'm confusing the order.
What can i do to do get better ?
main_activity.kt
var runnable : Runnable = Runnable { }
var handler : Handler = Handler( )
var number = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
//Cronometer have bugs
// User can click the start button for unlimited
}
fun startTime(view: View) {
runnable = object : Runnable {
override fun run() {
number++
textView2.text = "$number"
handler.postDelayed(this,1000)
}
}
handler.post(runnable)
}
fun stopTime(view:View) {
handler.removeCallbacks(runnable)
}
}
```
activity_main.xml
```<?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">
<Button
android:id="#+id/button3"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="90dp"
android:onClick="startTime"
android:text="START"
app:layout_constraintBaseline_toBaselineOf="#+id/button4"
app:layout_constraintStart_toStartOf="parent" />
<Button
android:id="#+id/button4"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="49dp"
android:layout_marginTop="88dp"
android:onClick="stopTime"
android:text="STOP"
app:layout_constraintStart_toEndOf="#+id/button3"
app:layout_constraintTop_toBottomOf="#+id/textView2" />
<TextView
android:id="#+id/textView2"
android:layout_width="180dp"
android:layout_height="40dp"
android:layout_marginTop="109dp"
android:gravity="center"
android:text="0"
android:textSize="24sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
```
Your question is not clear. If you want to prevent the user from clicking on the buttons more than once, you could disable them as soon as a click event is detected. For example, in your startTime method you could add
(view as Button).enabled = false
You need to cast your view to Button because the enabled property is not present in the View class (it comes from the TextView class actually, but Button inherits from it).
Then, you need to enable it again when you click on the stop button. You will need to retrieve a reference to your start button somehow, so you can access and enable it from your stopTime method. I'll leave that as an exercise for you ;).
ConstraintLayout intermittently fails to layout correctly when a view is set from GONE to VISIBLE shortly after an activity is resumed:
<android.support.constraint.ConstraintLayout
android:id="#+id/constraint_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<TextView
android:id="#+id/text1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintTop_toTopOf="parent"/>
<TextView
android:id="#+id/text2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
app:layout_constraintLeft_toRightOf="#id/text1"
app:layout_constraintTop_toTopOf="parent"/>
</android.support.constraint.ConstraintLayout>
override fun onResume() {
super.onResume()
text1.text = ""
text1.visibility = View.GONE
text2.text = ""
text2.visibility = View.GONE
text1.postDelayed({
text1.text = "Hello"
text1.visibility = View.VISIBLE
text2.text = "World"
text2.visibility = View.VISIBLE
}, 100
)
}
Full source code here
Instrumenting the TextView class reveals that the TextView instances are measured correctly but their width is set to 0 when they are laid out.
I wonder if the ConstraintLayout LinearSystem is non-deterministic. Are there maps that are iterated over where the iteration order is undefined? (I've seen this with Cassowary)
I'm looking to your statement in github page:
ConstraintLayout intermittently fails to layout correctly when a view is set from GONE to VISIBLE shortly after an activity is resumed
I've checked out your project and changed 100ms to 1000ms.
Here's the output:
It seems to me, that you expect that the moment you perform textview.setVisibility(View.GONE) you expect the view to be not visible. That's not the way android works. You are merely posting an event to the MessageQueue that would be handled later by Looper, and this 100ms is not enough for human eye to see those changes happening.
This was a bug in the ConstraintLayout fixed in constraint-layout:1.1.0-beta2 https://issuetracker.google.com/issues/65613481
I need an EditText that looks like this onError:
calling onError looks like this instead :
Note: the app is running on SDK 19 (4.4.2)
min SDK is 1
Is there a method similar to setError that does this automatically,
or do I have to write the code for it ?
Thank you
There's no need to use a third-party library since Google introduced the TextInputLayout as part of the design-support-library.
Following a basic example:
Layout
<android.support.design.widget.TextInputLayout
android:id="#+id/text_input_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:errorEnabled="true">
<android.support.design.widget.TextInputEditText
android:id="#+id/edit_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter your name" />
</android.support.design.widget.TextInputLayout>
Note: By setting app:errorEnabled="true" as an attribute of the TextInputLayout it won't change it's size once an error is displayed - so it basically blocks the space.
Code
In order to show the Error below the EditText you simply need to call #setError on the TextInputLayout (NOT on the child EditText):
TextInputLayout til = (TextInputLayout) findViewById(R.id.text_input_layout);
til.setError("You need to enter a name");
Result
To hide the error and reset the tint simply call til.setError(null).
Note
In order to use the TextInputLayout you have to add the following to your build.gradle dependencies:
dependencies {
compile 'com.android.support:design:25.1.0'
}
Setting a custom color
By default the line of the EditText will be red. If you need to display a different color you can use the following code as soon as you call setError.
editText.getBackground().setColorFilter(getResources().getColor(R.color.red_500_primary), PorterDuff.Mode.SRC_ATOP);
To clear it simply call the clearColorFilter function, like this:
editText.getBackground().clearColorFilter();
Call myTextInputLayout.setError() instead of myEditText.setError().
These container and containment have double functionality on setting errors. Functionality you need is container's one. But you could require minimal version of 23 for that.
Your EditText should be wrapped in a TextInputLayout
<android.support.design.widget.TextInputLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:id="#+id/tilEmail">
<EditText
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:inputType="textEmailAddress"
android:ems="10"
android:id="#+id/etEmail"
android:hint="Email"
android:layout_marginTop="10dp"
/>
</android.support.design.widget.TextInputLayout>
To get an error message like you wanted, set error to TextInputLayout
TextInputLayout tilEmail = (TextInputLayout) findViewById(R.id.tilEmail);
if (error){
tilEmail.setError("Invalid email id");
}
You should add design support library dependency. Add this line in your gradle dependencies
compile 'com.android.support:design:22.2.0'
reVerse's answer is great but it didn't point out how to remove the floating error tooltip kind of thing
You'll need edittext.setError(null) to remove that.
Also, as someone pointed out, you don't need TextInputLayout.setErrorEnabled(true)
Layout
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/edittext"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Enter something" />
</android.support.design.widget.TextInputLayout>
Code
TextInputLayout til = (TextInputLayout) editText.getParent();
til.setError("Your input is not valid...");
editText.setError(null);
If anybody still facing the error with using google's design library as mentioned in the answer then, please use this as commented by #h_k which is -
Instead of calling setError on TextInputLayout, You might be using setError on EditText itself.
TextInputLayout til = (TextInputLayout)editText.getParent();
til.setErrorEnabled(true);
til.setError("some error..");
private EditText edt_firstName;
private String firstName;
edt_firstName = findViewById(R.id.edt_firstName);
private void validateData() {
firstName = edt_firstName.getText().toString().trim();
if (!firstName.isEmpty(){
//here api call for ....
}else{
if (firstName.isEmpty()) {
edt_firstName.setError("Please Enter First Name");
edt_firstName.requestFocus();
}
}
}