I'm trying to develop an Android app with Kotlin, but I've hit a bit of a snag when trying to dynamically move fragments around. What I'm trying to do is replace a FrameLayout in an Activity's layout with a fragment. Currently whenever I try to run my app it just shows a white screen under the toolbar leading me to believe that the fragment is not being added to the FrameLayout the way I expected.
Here is my main Activity where I do the first transaction:
package net.ma.ttrobinson.kchan
import android.content.Intent
import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.support.v7.widget.Toolbar
import android.util.Log
import android.view.Menu
import android.view.MenuItem
import android.view.View
import android.widget.Button
import android.widget.EditText
import com.androidquery.callback.AjaxStatus
import net.ma.ttrobinson.kchan.api.ChanThread
import net.ma.ttrobinson.kchan.api.Request
import org.jdeferred.DoneCallback
import org.jdeferred.FailCallback
import org.json.JSONArray
import org.json.JSONObject
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val toolbar = findViewById(R.id.toolbar) as Toolbar
setSupportActionBar(toolbar)
getSupportFragmentManager().beginTransaction()
.replace(R.id.main_content_frame, MainFragment())
.commit()
}
}
Here is the fragment I'm attempting to create. It simply inflates a view while adding a callback for when a button is pressed:
package net.ma.ttrobinson.kchan
import android.os.Bundle
import android.support.v4.app.Fragment
import android.util.Log
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.Button
import android.widget.EditText
import com.androidquery.callback.AjaxStatus
import net.ma.ttrobinson.kchan.api.ChanThread
import net.ma.ttrobinson.kchan.api.Request
import org.jdeferred.DoneCallback
import org.jdeferred.FailCallback
/**
* Holds the main content
*/
class MainFragment : Fragment() {
companion object {
val TAG = "MainFragment"
}
val debugBoard = "g"
val debugThread = "48667796"
override fun onCreateView(inflater : LayoutInflater, parent : ViewGroup?, savedInstanceState : Bundle?) : View {
val v = inflater.inflate(R.layout.fragment_main, parent, false)
val boardText = v.findViewById(R.id.board_text) as EditText
val threadText = v.findViewById(R.id.thread_text) as EditText
val request = Request(getActivity())
boardText.setText(debugBoard)
threadText.setText(debugThread)
val button = v.findViewById(R.id.submit) as Button
button.setOnClickListener({ v ->
val board = boardText.getText().toString()
val thread = threadText.getText().toString().toInt()
val promise = request.getThread(board, thread)
promise.done({ thread ->
val fm = getActivity().getSupportFragmentManager()
fm.beginTransaction()
.replace(R.id.main_content_frame, ThreadFragment(thread), ThreadFragment.TAG)
.addToBackStack(null)
.commit()
}).fail({ result ->
Log.v(TAG, "Failed to get thread")
})
})
return v
}
}
Here's the layout that the main Activity uses with setContentView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/main_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
And lastly here's the layout that the fragment is inflating:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<EditText
android:id="#+id/board_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="#string/board_hint"/>
<EditText
android:id="#+id/thread_text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="phone"
android:hint="#string/thread_hint"/>
<Button
android:id="#+id/submit"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/submit"/>
</LinearLayout>
I feel like this is a fairly straightforward setup of a main Activity that has a FrameLayout that can be swapped out with other fragments. Does anyone know what I might be doing wrong?
In the meantime I suppose I'll try to make a simpler case to try to replicate the behaviour.
EDIT: Such a simple solution. I forgot to add android:orientation="vertical" in my LinearLayout. Here's the updated code:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/main_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
The problem is that the Activity's LinearLayout should have orientation="vertical":
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<include
android:id="#+id/toolbar"
layout="#layout/toolbar" />
<FrameLayout
android:id="#+id/main_content_frame"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Just i want to pay your intention to extension function it's so useful.
Here i will share some steps to do that
Creat a kotlin file called util for example and add those function which allow to add and replace the fragment
util.kt
fun AppCompatActivity.addFragment(fragment: Fragment, frameId: Int){
supportFragmentManager.inTransaction { add(frameId, fragment) }
}
fun AppCompatActivity.replaceFragment(fragment: Fragment, frameId: Int) {
supportFragmentManager.inTransaction{replace(frameId, fragment)}
}
inline fun FragmentManager.inTransaction(func: FragmentTransaction.() -> FragmentTransaction) {
beginTransaction().func().commit()
}
Create your fragment layout. I have call it father_fragment for example
fahter_fragment
<!-- You can put whatever you want here-->
In your Activity xml create a layout which will caontain the fragment:
activity_principal.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.tharwa.tdm2_exo2.Principal">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:id="#+id/container"
>
</FrameLayout>
</android.support.design.widget.CoordinatorLayout>
Now in youe activity you can easily create the fragment
Principal
class Principal : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_principal)
//Create and add the fragment
val fatherView=FatherPresentation()
addFragment(fatherView,R.id.container)
}
}
if you want to replace the fragment from your activity it's easy just do
replaceFragment(Fragment_you_want_to_replace_with(),id_of_of_your_frame_layout)
Related
i cant get my drawer base to show in my main activity layout.
I'm using kotlin and i have tried even changing add drawerbase as an extension but still wouldnt work. When ever i just change the binding to either ActivityMainBinding or DrawerBaseActivityBinding it just opens one or the other as the activity.
I'm trying to make it where the Drawer opens up in my Main activity when the app opens up as well as opens when going to any other activity i plan to add.
MainActivity.kt
package com.cameron.armymaintenance
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import com.cameron.armymaintenance.databinding.ActivityMainBinding
class MainActivity : AppCompatActivity() {
private lateinit var binding: ActivityMainBinding
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
setContentView(binding.root)
}
}
DrawerBaseActivity.kt
package com.cameron.armymaintenance
import android.view.MenuItem
import android.view.View
import android.widget.FrameLayout
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.widget.Toolbar
import androidx.drawerlayout.widget.DrawerLayout
import com.google.android.material.navigation.NavigationView
class DrawerBaseActivity : AppCompatActivity(), NavigationView.OnNavigationItemSelectedListener {
var drawerLayout: DrawerLayout? = null
override fun setContentView(view: View?) {
drawerLayout = layoutInflater.inflate(R.layout.activity_drawer_base, null) as DrawerLayout
val container = drawerLayout!!.findViewById<FrameLayout>(R.id.activityContainer)
container.addView(view)
setContentView(R.layout.activity_drawer_base)
val toolbar = drawerLayout!!.findViewById<Toolbar>(R.id.toolBar)
setSupportActionBar(toolbar)
val navigationView = drawerLayout!!.findViewById<NavigationView>(R.id.nav_view)
navigationView.setNavigationItemSelectedListener(this)
val toggle = ActionBarDrawerToggle(
this,
drawerLayout,
toolbar,
R.string.menu_drawer_open,
R.string.menu_drawer_open
)
drawerLayout!!.addDrawerListener(toggle)
toggle.syncState()
}
override fun onNavigationItemSelected(item: MenuItem): Boolean {
return false
}
private fun allocateActivityTitle(titleString: String?) {
if (supportActionBar != null) {
supportActionBar!!.title = titleString
}
}
}
activitydrawerbase.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.drawerlayout.widget.DrawerLayout
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=".DrawerBaseActivity"
tools:openDrawer="start"
android:fitsSystemWindows="true">
<include layout="#layout/content_layout"
android:layout_height="match_parent"
android:layout_width="match_parent"/>
<com.google.android.material.navigation.NavigationView
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:layout_gravity="start"
app:headerLayout="#layout/main_drawer_header"
app:menu="#menu/main_drawer_menu"/>
</androidx.drawerlayout.widget.DrawerLayout>
contentlayout.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"
android:layout_width="match_parent"
android:layout_height="match_parent">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="#+id/appBarLayout"
app:layout_constraintTop_toTopOf="parent">
<androidx.appcompat.widget.Toolbar
android:id="#+id/toolBar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="#color/Gray"
app:titleTextColor="#color/White"/>
</com.google.android.material.appbar.AppBarLayout>
<FrameLayout
android:layout_width="match_parent"
android:layout_height="0dp"
android:id="#+id/activityContainer"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#id/appBarLayout"/>
</androidx.constraintlayout.widget.ConstraintLayout>
XML File
<?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">
<app.rive.runtime.kotlin.RiveAnimationView
android:id="#+id/animationView"
android:layout_width="match_parent"
android:layout_height="50dp"
android:layout_marginLeft="10dp"
android:layout_marginBottom="10dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:riveResource="#raw/loading_bar" />
</androidx.constraintlayout.widget.ConstraintLayout>
Kotlin file
package com.example.basicriveapp
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import androidx.startup.AppInitializer
import app.rive.runtime.kotlin.RiveAnimationView
import app.rive.runtime.kotlin.RiveInitializer
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
AppInitializer.getInstance(this)
.initializeComponent(RiveInitializer::class.java);
var animationView = findViewById<RiveAnimationView>(R.id.animationView);
animationView.pause();
}
}
Since I am trying to pause the Animation, it is not working and Can you please explain how to use play() and stop() methods with RiveAnimationView
You need to specify the animation name you want to play/pause inside the parentheses:
animationView.pause("animation name");
animationView.play("animation name");
You can get the animation name from the rive editor under the panel called "Animations"
I thought android had a diff mechanism to avoid redraw whole View tree when I invoke View::invalidate. Through checking the source code of View::invalidate, I find the invalidate
will propagate to ViewRootImpl and invoke ViewRootImpl::performTraversals. This method then draw whole view tree(except hardware acceleration enabled).
I try to find the code that View/ViewGroup can skip draw accroding dirty region, But I failed.Seems like erevry view will invoke onDraw
Below case, why does #id/frame_layout invoke onDraw when I change child #id/front backgroud color? Should't it refresh view locally?
layou.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"
android:theme="#style/Theme.AppCompat.NoActionBar"
tools:context=".MainActivity">
<FrameLayout
android:id="#+id/frame_container"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintBottom_toBottomOf="#id/btn"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:id="#+id/layout_back"
android:background="#color/black"
android:layout_width="100dp"
android:layout_height="100dp"
/>
<FrameLayout
android:id="#+id/layout_front"
android:background="#color/white"
android:layout_width="100dp"
android:layout_height="100dp"
/>
</FrameLayout>
<com.google.android.material.button.MaterialButton
android:id="#+id/btn"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="switch color"
app:layout_constraintBottom_toBottomOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
package com.flybutter.demo
import android.graphics.Color
import android.os.*
import android.util.Log
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import android.widget.Toast
import androidx.lifecycle.LifecycleObserver
import com.bumptech.glide.Glide
import com.flybutter.demo.databinding.ActivityMainBinding
private const val TAG = "MainActivity"
class MainActivity : AppCompatActivity() {
lateinit var binding: ActivityMainBinding
var c = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
binding = ActivityMainBinding.inflate(layoutInflater)
binding.frameLayout.viewTreeObserver.addOnDrawListener {
Log.d(TAG, "onCreate: container ondraw")
}
binding.layoutFront.viewTreeObserver.addOnDrawListener {
Log.d(TAG, "onCreate: front on draw")
}
binding.layoutFront.viewTreeObserver.addOnDrawListener {
Log.d(TAG, "onCreate: back on draw")
Log.d(TAG,">>>>>>>>>>>>>>>>>>>")
}
binding.btn.setOnClickListener {
binding.layoutFront.setBackgroundColor( if (c) Color.WHITE else Color.GRAY)
c = !c
}
setContentView(binding.root)
}
}
I am New to Android . I don't know how to get the data of EditText in Android .
Here is my Layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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:orientation="vertical"
tools:context=".MainActivity2">
<EditText
android:id="#+id/edOne"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<EditText
android:id="#+id/edSecond"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
<Button
android:id="#+id/btnAdd"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
Here is my code :
val one=edOne.text.toString()
btnAdd.setOnClickListener{
val two=edOne.text.toString()
Log.e("Tag",one +" "+two)
}
one is calling from onCreate() method . and at the time of creation of you onCreate() you don't have any data in editText one .So you can't get any data from one if you have not added from xml .
So just move your one code into click listener.
val one=edOne.text.toString()
btnAdd.setOnClickListener{
val two=edOne.text.toString()
Log.e("Tag",one +" "+two)
}
You can access related View elements with findViewById and use them later.
import androidx.appcompat.app.AppCompatActivity
import android.os.Bundle
import android.util.Log
import android.widget.Button
import android.widget.EditText
class MainActivity : AppCompatActivity() {
private lateinit var edOne : EditText
private lateinit var btnAdd : Button
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
edOne = findViewById(R.id.edOne)
btnAdd = findViewById(R.id.btnAdd)
val one=edOne.text.toString()
btnAdd.setOnClickListener {
val two=edOne.text.toString()
Log.e("Tag",one +" "+two)
}
}
}
I am using the new kotlin extension for view binding but I am having this annoying warning red underline message custom_button.setOnClickListener{} stating "Overload resolution ambiguity.."
I am trying to use my custom button class which extends Button class.
My activity class
package com.example.myapplication
import android.support.v7.app.AppCompatActivity
import android.os.Bundle
import kotlinx.android.synthetic.main.activity_main.*
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
custom_button.setOnClickListener { }
}
}
Custom button class
package com.example.myapplication
import android.content.Context
import android.widget.Button
class CustomButton(context: Context?) : Button(context)
XML
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.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">
<com.example.myapplication.CustomButton
android:id="#+id/custom_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</android.support.constraint.ConstraintLayout>