Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to replace a FrameLayout with a fragment with Kotlin on Android

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>
like image 759
mrobinson7627 Avatar asked Jun 27 '15 15:06

mrobinson7627


People also ask

How do I add a fragment to Kotlin?

Adding a Fragment to an Activity using the Layout XML File The key properties within the <fragment> element are android:name, which must reference the class associated with the fragment, and tools:layout, which must reference the XML resource file containing the layout of the fragment.

Is FrameLayout deprecated?

This constant was deprecated in API level 28. The view drawing cache was largely made obsolete with the introduction of hardware-accelerated rendering in API 11.

Can fragments replace activity?

At runtime, a FragmentManager can add, remove, replace, and perform other actions with fragments in response to user interaction. Each set of fragment changes that you commit is called a transaction, and you can specify what to do inside the transaction using the APIs provided by the FragmentTransaction class.


2 Answers

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>
like image 176
rciovati Avatar answered Oct 13 '22 15:10

rciovati


Just i want to pay your intention to extension function it's so useful. Here i will share some steps to do that

  1. 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()
}
  1. Create your fragment layout. I have call it father_fragment for example fahter_fragment

        <!-- You can put whatever you want here--> 
    

  2. 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>
  1. 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)

like image 42
DINA TAKLIT Avatar answered Oct 13 '22 16:10

DINA TAKLIT