I am trying to change the height of the ContraintLayout inside the post block.
like so:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
changeHeight()
}
private fun changeHeight() {
val layoutView = findViewById<ConstraintLayout>(R.id.block1)
layoutView.post {
Log.d("222", "1===========")
layoutView.run {
Log.d("222", "2===========")
layoutParams.height = 1000
setBackgroundColor(Color.BLUE)
}
Log.d("222", "3===========")
}
}
}
It doesn't work at all.
And the logs are printed out and the color was changed too.
If it removed the layoutView.post then it works.
I want to figure out why this happened, anyone knows? thanks!
I tried to move the changeHeight() into a button listener to see if the view isn't attached or something relative view. But still the same.
Related
I don't have enough skills to code. But I have to make this program. so please help me...
I want to make function to slide the screen sideways in Android but it isn't working what I made. how can I do? please tell me.
PS: I want to use 'View group' and this code is on the MainActivity.kt
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val sv = SlidingView(this)
val v1 = View.inflate(this, R.layout.t1, null)
val v2 = View.inflate(this, R.layout.t2, null)
sv.addView(v1)
sv.addView(v2)
setContentView(sv)
setContentView(R.layout.activity_main)
}
}
Seems like you are overriding your SlidingView with activity_main, you are calling setContentView twice and the latest call overrides the first one, so your view gets replaced with what's in layout activity_main, if you do not have anything in activity_main, do remove setContentView(R.layout.activity_main)
or
if you want SlidingView to be a part of activity_main, then add it to activity_main xml instead.
I would like to use the viewTreeObserver so I can listen to when the layout is finished loading and then get coordinates of some views in that layout. I followed the advice here:
How to get the absolute coordinates of a view
I translated the code to Kotlin, however the function in the listener is never being called.
My code is very simple:
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val layoutInflater: LayoutInflater = LayoutInflater.from(applicationContext)
val view: View = layoutInflater.inflate(R.layout.activity_main, null)
view.viewTreeObserver.addOnGlobalLayoutListener(object: ViewTreeObserver.OnGlobalLayoutListener {
override fun onGlobalLayout() {
Log.d("TAG", "Called!!!")
}
})
setContentView(R.layout.activity_main)
The view you inflate and get the viewTreeObserver from is never added to the hierchy. So this observer never calls any events.
When you call setContentView(R.layout.activity_main) a new view is inflated which never had the listener added.
You could use setContentView(view) instead.
As for the title, I am a beginner of Android. When running github's sceneform-sample, I found that when the model appeared on the screen, a white circle would appear under the model. What methods can I use to remove the white circle at the bottom?
I have uploaded a picture so that you can understand my question more intuitively.
Just don't call node.select(), it should be fine.
I think you may have found an answer to your question in GitHub but for completeness here are some examples in Kotlin, setting the SelectionVisualiser, which allows you determine what is shown or not shown under a selected TransformableNode.
Example 1
Changing the SelectionVisualiser for the entire activity:
class StartViewMainActivity : AppCompatActivity() {
//included in the class variables
private lateinit var defaultSelectionVisualiser:SelectionVisualizer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//As part of OnCreate, set the selection visualiser
arFragment.transformationSystem.selectionVisualizer = CustomSelectionVisualiser()
}
private class CustomSelectionVisualiser(): SelectionVisualizer {
//Class providing a custom TransformableNode SelectionVisualiser
//This particular custom visualiser shows no image
override fun applySelectionVisual(node:BaseTransformableNode){}
override fun removeSelectionVisual(node:BaseTransformableNode){}
}
}
Example 2
Changing the SelectionVisualiser programatically during part of your program flow:
class StartViewMainActivity : AppCompatActivity() {
//included in the class variables
private lateinit var defaultSelectionVisualiser:SelectionVisualizer
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//As part of OnCreate, get the default selection visualiser
defaultSelectionVisualiser = arFragment.transformationSystem.selectionVisualizer
}
fun toggelSelectionVisulaisers() {
//Switch between the default and the custom selectionVisualisers
if (arFragment.transformationSystem.selectionVisualizer == defaultSelectionVisualiser) {
arFragment.transformationSystem.selectionVisualizer = CustomSelectionVisualiser) else
} else {
arFragment.transformationSystem.selectionVisualizer = defaultSelectionVisualiser arFragment.transformationSystem.selectionVisualizer = CustomSelectionVisualiser) else
}
private class CustomSelectionVisualiser(): SelectionVisualizer {
//Class providing a custom TransformableNode SelectionVisualiser
//This particular custom visualiser shows no image
override fun applySelectionVisual(node:BaseTransformableNode){}
override fun removeSelectionVisual(node:BaseTransformableNode){}
}
}
Yes, you can,
there is two way one is don't select the model,
like don't call bellow method
node.select()
Second one is aplly transperent selection visualiser
First Create a Transparent Visualiser class like bellow
class BlanckSelectionVisualizer : SelectionVisualizer {
override fun applySelectionVisual(var1: BaseTransformableNode) {}
override fun removeSelectionVisual(var1: BaseTransformableNode) {}
}
Then after applying it on ArFragmnet like bellow
arFragment.transformationSystem.selectionVisualizer = BlanckSelectionVisualizer()
I suggest use second one
I want to start Activity with content transition. But the content transition doesn't work. Why and how to fix it?
This is my code:
class PolishStartActivity : AbsActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
initContentTransition()
setContentView(R.layout.activity_polish_start)
}
private fun initContentTransition() {
window.apply {
requestFeature(Window.FEATURE_CONTENT_TRANSITIONS)
enterTransition = TransitionSet().apply {
addTransition(Slide(Gravity.TOP).addTarget(R.id.tvBooksTitle).addTarget(R.id.tvBooksDescription))
addTransition(Fade().addTarget(R.id.cvSearchBox))
// addTransition(Fade()) only this transition works fine
duration = 1000L
}
}
}
}
P.S. I start my Activity with ActivityOptions.
After many attempts and experiments, I found the fix for this problem.
On my root ViewGroup (in my case it is ConstraintLayout) add this line:
android:transitionGroup="false"
Yes! Now you can set targets for transitions and it will be work!
Put initContentTransition() before super.onCreate();
class PolishStartActivity : AbsActivity()
{
override fun onCreate(savedInstanceState: Bundle?)
{
initContentTransition()
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_polish_start)
}
..................
..................
}
I know I can use an id attribute with Anko to identify a view:
class MainActivityUI : AnkoComponent<MainActivity> {
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
frameLayout {
textView {
id = R.id.text
}
}
}
}
Then obtain it in the Activity using the find() function (or by using Kotlin Android Extensions):
class MainActivity : AppCompatActivity() {
private val textView by lazy {
find<TextView>(R.id.text)
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
MainActivityUI().setContentView(this)
textView.text = "Hello World"
}
}
But I feel like I am missing something; the only place the README mentions the find function or Kotlin Android Extensions is in the section titled Supporting Existing Code:
You don't have to rewrite all your UI with Anko. You can keep your old
classes written in Java. Moreover, if you still want (or have) to
write a Kotlin activity class and inflate an XML layout for some
reason, you can use View properties, which would make things easier:
// Same as findViewById(), simpler to use
val name = find<TextView>(R.id.name)
name.hint = "Enter your name"
name.onClick { /*do something*/ }
You can make your code even more compact by using Kotlin Android
Extensions.
Which makes it seem like the find function is only meant for supporting "old" XML code.
So my question is this; is using an id along with the find function the correct way of accessing a View from the Activity using Anko? Is there a more "Anko" way of handling this? Or am I missing some other benefit of Anko that makes accessing the View from the Activity irrelevant?
And a second related question; if this is the correct way of accessing a View from the Activity, is there a way of creating an id resource (i.e. "#+id/") from within an AnkoComponent? Rather than creating each id in the ids.xml file.
So, why still use XML id to locate the View? since we already use the Anko instead of the XML.
In my opinion, we can store the view elements inside the AnkoComponent instead of the find view's id method. Check the code blow:
class MainActivityUI : AnkoComponent<MainActivity> {
lateinit var txtView: TextView
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
frameLayout {
txtView = textView {
id = R.id.text // the id here is useless, we can delete this line.
}
}
}
}
class MainActivity : AppCompatActivity() {
lateinit var mainUI : MainActivityUI
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mainUI = MainActivityUI()
mainUI.setContentView(this)
mainUI.txtView.text = "Hello World"
}
}
Do not use id to identify views with Anko DSL! It is unnecessary and useless because Anko was designed to get rid off XML layouts. Instead use this pattern:
class ActivityMain : AppCompatActivity() {
var mTextView: TextView // put it here to be accessible everywhere
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
ActivityMainUI().setContentView(this)
}
fun yourClassMethod() {
// So this is am example how to get the textView
// defined in your Anko DSL class (not by id!):
mTextView.text = "bla-bla-bla"
}
}
class ActivityMainUI : AnkoComponent<ActivityMain> {
override fun createView(ui: AnkoContext<ActivityMain>) = with(ui) {
// your fancy interface with Anko DSL:
verticalLayout {
owner.mTextView = textView
}
}
}
Please note the UI class definition:
class ActivityMainUI : AnkoComponent<ActivityMain> {
If you put there your activity class name in brackets then all its public variables become accessible via owner in UI class body so you can assing them there.
But you may put AppCompatActivity easily and make some universal class which might be cloned. In this case use lateinit var mTextView :
TextView in the body of UI class as described in Jacob's answer here.
I believe that, as you can add behavior to your Anko files, you don't have to instantiate your views in the activity at all.
That can be really cool, because you can separate the view layer even more. All the code that acts in your views can be inserted in the Anko files. So all you have to do is to call your activity's methods from the Anko and not instantiate any view.
But if you need to instantiate any view... you can use Kotlin Android Extensions in your activity.
Exemple:
Code in your activity:
seekBar.setOnSeekBarChangeListener(object: OnSeekBarChangeListener {
override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) {
// Something
}
override fun onStartTrackingTouch(seekBar: SeekBar?) {
// Just an empty method
}
override fun onStopTrackingTouch(seekBar: SeekBar) {
// Another empty method
}
})
Code in Anko:
seekBar {
onSeekBarChangeListener {
onProgressChanged { seekBar, progress, fromUser ->
// Something
}
}
}
Now the code is in AnkoComponent. No need to instantiate the view.
Conclusion:
It's a more 'Anko' way to program if you put all your view logic in the AnkoComponents, not in your activities.
Edit:
As an exemple of a code where you don't have to instantiate a view:
In your Anko:
var networkFeedback : TextView = null
override fun createView(ui: AnkoContext<MainActivity>) = with(ui) {
frameLayout {
textView {
id = R.id.text2
networkFeedback = this
onClick {
ui.owner.doSomething(2, this)
}
}
}
}
fun networkFeedback(text: String){
networkFeedback.text = text
}
In your activity:
class MainActivity : AppCompatActivity() {
overriding fun onCreate{
[...]
val mainUi = AnkoUi()
// some dynamic task...
mainUi.networkFeedback("lalala")
}
fun doSomething(number: Int, callback: TextView){
//Some network or database task goes here!
//And then, if the operation was successful
callback.text = "Something has changed..."
}
This is a very different approach. I'm not so sure if I like it or not, but this is a whole different discussion...
To generalize the question a bit: How can one make an AnkoComponent that is encapsulated, can be used from the DSL and can have its data programmatically set after creation?
Here is how I did it using the View.tag:
class MyComponent: AnkoComponent<Context> {
lateinit var innerds: TextView
override fun createView(ui: AnkoContext<Context>): View {
val field = with(ui) {
linearLayout {
innerds = complexView("hi")
}
}
field.setTag(this) // store the component in the View
return field
}
fun setData(o:SomeObject) { innerds.setStuff(o.getStuff()) }
}
inline fun ViewManager.myComponent(theme: Int = 0) = myComponent(theme) {}
inline fun ViewManager.myComponent(theme: Int = 0, init: MyComponent.() -> Unit) =
ankoView({ MyComponent(it) }, theme, init)
// And then you can use it anywhere the Anko DSL is used.
class SomeUser : AnkoComponent<Context>
{
lateinit var instance:View
override fun createView(ui: AnkoContext<Context>): View {
linearLayout {
instance = myComponent {}
}
}
fun onDataChange(o:SomeObject) {
(instance.Tag as MyComponent).setData(o)
}
}
}