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
Related
I'm developing an app to store TV shows informations. The use can add shows and then view its collection. I want, when adding a show, to be able to also add seasons to it, and several if need be.
I have Show and Season models, and I've created an AddShowActivity with its add_show_activity layout. I've started using Android Studio not long ago so maybe this is not optimal, but I thought of using a RecyclerView inside of my layout, and then recycle an item_add_season layout in order to add as many seasons as I want while creating a show.
However, this has caused several problems to me, to which I couldn't find any answer and am currently lost as to what to do. I've put an Add Season button in my add_show_activity, which is supposed to add a new item_add_season to my RecyclerView, however I didn't know how I should go about doing that. And even if I still haven't tried it, I'm wondering how I'll be able to retrieve my data from outside of my Adapter.
So I've been wondering if it was possible to use a RecyclerView like that in order to add several seasons to my form ? And if not, how should I go about doing that ?
Below are my AddShowActivity and my AddSeasonAdapter (the recyclerview adapter).
class AddShowActivity : AppCompatActivity() {
private lateinit var editTextName: EditText
private lateinit var editTextNote: EditText
private lateinit var confirmButton: Button
private lateinit var addSeasonButton: Button
private lateinit var seasonsRecyclerView: RecyclerView
#SuppressLint("NewApi")
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_add_show)
editTextName = findViewById(R.id.name_input)
editTextNote = findViewById(R.id.note_input)
seasonsRecyclerView = findViewById(R.id.seasons_recycler_view)
seasonsRecyclerView.adapter = AddSeasonAdapter(this, 0, R.layout.item_add_season)
seasonsRecyclerView.layoutManager = LinearLayoutManager(this)
confirmButton = findViewById(R.id.confirm_button)
confirmButton.setOnClickListener{
sendForm()
}
addSeasonButton = findViewById(R.id.add_season_button)
addSeasonButton.setOnClickListener {
// Add a season to the RecyclerView and update its seasonsCount
}
}
#SuppressLint("NewApi")
private fun sendForm(){
val repo = ShowRepository()
val showName = editTextName.text.toString()
val showNote = parseInt(editTextNote.text.toString())
val seasonsList = arrayListOf<SeasonModel>() // Get info from seasons adapter and create seasons list
val show = ShowModel(UUID.randomUUID().toString(), showName, showNote, seasonsList)
repo.insertShow(show)
this.finish()
}
}
class AddSeasonAdapter(val context: AddShowActivity, private var seasonsCount: Int, private val layoutId: Int) : RecyclerView.Adapter<AddSeasonAdapter.ViewHolder>() {
class ViewHolder(view: View) : RecyclerView.ViewHolder(view){
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view = LayoutInflater.from(parent.context).inflate(layoutId, parent, false)
return ViewHolder(view)
}
#SuppressLint("NewApi")
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
}
override fun getItemCount(): Int = seasonsCount
}
I've found a YouTube video explaining exactly how to do it (this one for those who wanna see it).
So basically, the solution is not to use a RecyclerView but instead a LinearLayout in which the seasons are added when clicking on the 'Add season' button. This is quite easy to do, as the only thing to do is to inflate the layout, here my item_add_season, and then add it to the LinearLayout.
So like that:
// The LinearLayout in which items are added
val seasonsList = findViewById<LinearLayout>(R.id.seasons_list)
addSeasonButton.setOnClickListener {
val seasonView: View = layoutInflater.inflate(R.layout.item_add_season, null, false)
// Initialize the seasons items components
val seasonNumber = seasonView.findViewById<EditText>(R.id.season_number_input)
val seasonNote = seasonView.findViewById<EditText>(R.id.season_note_input)
val imageClose = seasonView.findViewById<ImageView>(R.id.image_close)
imageClose.setOnClickListener {
seasonsList.removeView(seasonView)
}
// Add the add_season_layout to the linearLayout
seasonsList.addView(seasonView)
}
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.
What I am trying to do is restart the CountdownTimer object when it is finished. Therefor I put it inside a procedure 'goCountdown', so onFinish() it calls itself. However this gives the following problems:
A procedure goCountdown cannot be called from within the same class
B activity text cannot be updated with time from outside the MainActivity class.
See references '!!! nr !!!' in code below:
the 'goCountdown() function is found (recognized/doesnt give an
error) at place 1 even though the procedure is placed outside the class.
when goCountdown is placed inside the class at nr 2 it is not found at place nr 1 (gives error).
As it only works outside of the class, it is now impossible to update the text on the activity on every tick because the MainActivity isnt accessible.
Questions:
Why does Kotlin / Android Studio not recognize the 'goCountdown()' function when it is placed WITHIN
the same activity class?
Even if that will work, is there a way to acces the texts on the MainActivity from a top level procedure?
Besides my goal to make the timer loop I am trying to understand why it is not working. Thanks for explaining or pointing me to the explanation.
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
tapAction.setOnClickListener{
boTimeRunning = !boTimeRunning
if(boTimeRunning ){
goCountdown() //!!! 1 !!! its fine to call it from here when its outside the class
}
}
//!!! 2 !!!but if fun goCountdown() block is placed here, it is not seen at !!! 1 !!! place
}
}
fun goCountdown(){
object : CountDownTimer1(timeSettings_set * 1000, 1000){
override fun onTick(p0: Long) {
MainActivity.txtBig.text = "sometext" //!!! 3 !!!this doesnt work, also when MainActivity is declared as a variable object.
}
override fun onFinish() {
goCountdown() //primary goal: restart the timer when its done
}
}.start()
}
Is txtBig kotlin synthetic view or a local variable or global variable?
if txtBig is kotlin synthetic view, you should be able to call it.
if txtBig isn't kotlin synthetic veiw, try this code.
class MainActivity : AppCompatActivity() {
private lateinit var txtBigcopy: TextView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
txtBig = findViewById<TextView>(R.id.txt_big) // Your id in xml
txtBigcopy = txtBig
tapAction.setOnClickListener{
boTimeRunning = !boTimeRunning
if(boTimeRunning ){
goCountdown()
}
}
}
fun goCountdown(){
object : CountDownTimer1(timeSettings_set * 1000, 1000){
override fun onTick(p0: Long) {
txtBigcopy.text = "sometext"
}
override fun onFinish() {
goCountdown()
}
}.start()
}
}
I am writing code in kotlin.I want to know how to convert fab.setOnclickListener(this) into kotlin language.?
class MainActivity: appCompatActivity(){
private var fab: FloatingActionButton? = null
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
setSupportActionBar(toolbar)
fab = findViewById(R.id.fab)
fab.setOnClickListener(this)
}
Expected
Convert into kotlin
What #John Joe is trying to say is that you can just use:
fab?.setOnClickListener {
//implement logic
}
There's no need to be passing in this because this refers to the handler of the setOnClickListener method
so, you could also have it :
MainActivity: AppCompatActivity(), View.OnClickListener{
override fun onClick(p0: View?) {
//handle logic for fab here
}
}
Which would then allow you to simply use:
fab?.setOnClickListener(this)
But, the first solution is way simpler.
Side note, because Kotlin has synthetic imports, you probably do not have to use fab = findViewById(R.id.fab) : https://kotlinlang.org/docs/tutorials/android-plugin.html
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)
}
}
}