Bagaimana cara menambahkan Fragmen ke Aktivitas dengan tampilan konten yang dibuat secara program

240

Saya ingin menambahkan Fragmen ke Aktivitas yang mengimplementasikan tata letaknya secara terprogram. Saya melihat-lihat dokumentasi Fragment tetapi tidak banyak contoh yang menggambarkan apa yang saya butuhkan. Berikut adalah jenis kode yang saya coba tulis:

public class DebugExampleTwo extends Activity {

    private ExampleTwoFragment mFragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout frame = new FrameLayout(this);
        if (savedInstanceState == null) {
            mFragment = new ExampleTwoFragment();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(frame.getId(), mFragment).commit();
        }

        setContentView(frame);
    }
}

...

public class ExampleTwoFragment extends Fragment {

    @Override
    public View onCreateView(LayoutInflater inflater, 
                             ViewGroup container, 
                             Bundle savedInstanceState) {
        Button button = new Button(getActivity());
        button.setText("Hello There");
        return button;
    }
}

Kode ini mengkompilasi tetapi macet di awal, mungkin karena saya FragmentTransaction.add()salah. Apa cara yang benar untuk melakukan ini?

Tony Wong
sumber

Jawaban:

198

Ternyata ada lebih dari satu masalah dengan kode itu. Sebuah fragmen tidak dapat dinyatakan seperti itu, di dalam file java yang sama dengan aktivitas tetapi tidak sebagai kelas publik. Kerangka kerja ini mengharapkan konstruktor fragmen (tanpa parameter) menjadi publik dan terlihat. Memindahkan fragmen ke dalam Aktivitas sebagai kelas dalam, atau membuat file java baru untuk perbaikan fragmen itu.

Masalah kedua adalah bahwa ketika Anda menambahkan fragmen dengan cara ini, Anda harus memberikan referensi ke tampilan yang mengandung fragmen, dan tampilan itu harus memiliki id khusus. Menggunakan id default akan merusak aplikasi. Berikut kode yang diperbarui:

public class DebugExampleTwo extends Activity {

    private static final int CONTENT_VIEW_ID = 10101010;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        FrameLayout frame = new FrameLayout(this);
        frame.setId(CONTENT_VIEW_ID);
        setContentView(frame, new LayoutParams(
            LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT));

        if (savedInstanceState == null) {
            Fragment newFragment = new DebugExampleTwoFragment();
            FragmentTransaction ft = getFragmentManager().beginTransaction();
            ft.add(CONTENT_VIEW_ID, newFragment).commit();
        }
    }

    public static class DebugExampleTwoFragment extends Fragment {
        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            EditText v = new EditText(getActivity());
            v.setText("Hello Fragment!");
            return v;
        }
    }
}
Tony Wong
sumber
115
Jika Anda hanya ingin menggunakan fragmen sebagai tampilan konten aktivitas tingkat atas, maka Anda dapat menggunakannya ft.add(android.R.id.content, newFragment). Hanya diperlukan untuk membuat tata letak khusus dan mengatur id jika wadah fragmen bukan tampilan konten aktivitas.
Tony Wong
25
Daripada mengkodekan id, Anda dapat mendefinisikannya dalam XML dan merujuknya seperti biasa (R.id.myid).
Jason Hanley
1
Saya tidak tahu bagaimana melakukan itu, tetapi ingat bahwa id hanya harus unik dalam lingkup yang Anda butuhkan untuk menggunakannya.
Jason Hanley
2
id hanya perlu unik dalam levelnya dalam hirarki tata letak yang berisi saat ini. Jadi katakanlah itu dibungkus dalam tata letak linier, hanya perlu unqiue di antara pandangan lain dalam tata letak linier itu.
Shaun
1
Anda dapat membuat ID secara dinamis menggunakan setId (View.NO_ID) dan kemudian getId () untuk melihat apa itu.
enl8enmentnow
71

Inilah yang saya dapatkan setelah membaca komentar Tony Wong :

public class DebugExampleTwo extends BaseActivity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        addFragment(android.R.id.content,
                    new DebugExampleTwoFragment(),
                    DebugExampleTwoFragment.FRAGMENT_TAG);
    }

}

...

public abstract class BaseActivity extends Activity {

    protected void addFragment(@IdRes int containerViewId,
                               @NonNull Fragment fragment,
                               @NonNull String fragmentTag) {
        getSupportFragmentManager()
                .beginTransaction()
                .add(containerViewId, fragment, fragmentTag)
                .disallowAddToBackStack()
                .commit();
    }

    protected void replaceFragment(@IdRes int containerViewId,
                                   @NonNull Fragment fragment,
                                   @NonNull String fragmentTag,
                                   @Nullable String backStackStateName) {
        getSupportFragmentManager()
                .beginTransaction()
                .replace(containerViewId, fragment, fragmentTag)
                .addToBackStack(backStackStateName)
                .commit();
    }

}

...

public class DebugExampleTwoFragment extends Fragment {

    public static final String FRAGMENT_TAG = 
        BuildConfig.APPLICATION_ID + ".DEBUG_EXAMPLE_TWO_FRAGMENT_TAG";

    // ...

}

Kotlin

Jika Anda menggunakan Kotlin, pastikan untuk melihat apa yang disediakan oleh ekstensi Kotlin oleh Google atau tulis sendiri.

JJD
sumber
Jangan lakukan itu! Periksa if (savedInstanceState == null)sebelum pembuatan fragmen, atau setelah memutar layar, Anda akan memiliki dua fragmen atau penataan ulang fragmen. Jangan menggunakan addmetode sama sekali! Hanya replace. Atau Anda akan memiliki perilaku aneh.
CoolMind
Di mana Anda mendapatkan nilai untuk "backStackStateName"? (Saat menggunakan fungsi ganti)
vikzilla
@vikzilla Anda dapat menemukan jawaban yang cukup bagus di sini dan di dokumen . Singkatnya: backStackStateNamestring adalah sesuatu yang didefinisikan oleh Anda.
JJD
34
    public class Example1 extends FragmentActivity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            super.onCreate(savedInstanceState);
          DemoFragment fragmentDemo = (DemoFragment) 
          getSupportFragmentManager().findFragmentById(R.id.frame_container);
          //above part is to determine which fragment is in your frame_container
          setFragment(fragmentDemo);
                       (OR)
          setFragment(new TestFragment1());
        }

        // This could be moved into an abstract BaseActivity 
        // class for being re-used by several instances
        protected void setFragment(Fragment fragment) {
            FragmentManager fragmentManager = getSupportFragmentManager();
            FragmentTransaction fragmentTransaction = 
                fragmentManager.beginTransaction();
            fragmentTransaction.replace(android.R.id.content, fragment);
            fragmentTransaction.commit();
        }
    }

Untuk menambahkan sebuah fragmen ke dalam Activity atau FramentActivity, ia membutuhkan sebuah Container. Wadah itu harus berupa " Framelayout", yang dapat dimasukkan dalam xml atau Anda dapat menggunakan wadah default untuk " android.R.id.content" seperti itu untuk menghapus atau mengganti sebuah fragmen dalam Activity.

main.xml

<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="match_parent" >
 <!-- Framelayout to display Fragments -->
   <FrameLayout
        android:id="@+id/frame_container"
        android:layout_width="match_parent"
        android:layout_height="match_parent" />

    <ImageView
        android:id="@+id/imagenext"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_alignParentBottom="true"
        android:layout_alignParentRight="true"
        android:layout_margin="16dp"
        android:src="@drawable/next" />
</RelativeLayout>
anand krish
sumber
29

Setelah membaca semua Jawaban saya datang dengan cara yang elegan:

public class MyActivity extends ActionBarActivity {

 Fragment fragment ;

@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);

    FragmentManager fm = getSupportFragmentManager();
    fragment = fm.findFragmentByTag("myFragmentTag");
    if (fragment == null) {
        FragmentTransaction ft = fm.beginTransaction();
        fragment =new MyFragment();
        ft.add(android.R.id.content,fragment,"myFragmentTag");
        ft.commit();
    }

}

pada dasarnya Anda tidak perlu menambahkan frameLayout sebagai wadah dari fragmen Anda, alih-alih Anda dapat menambahkan lurus fragmen ke dalam wadah Android root View

PENTING: jangan gunakan menggantikan fragmen karena kebanyakan dari pendekatan ditampilkan di sini, kecuali jika Anda tidak keberatan kehilangan fragmen variabel contoh negara selama onrecreation proses.

Xenione
sumber
terima kasih atas jawabannya, ini menambahkan tab fragmen ke seluruh layar? tetapi bagaimana Anda menambahkan satu tata letak bingkai atau tampilan pager?
flankechen
6
public abstract class SingleFragmentActivity extends Activity {

    public static final String FRAGMENT_TAG = "single";
    private Fragment fragment;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
        if (savedInstanceState == null) {
            fragment = onCreateFragment();
           getFragmentManager().beginTransaction()
                   .add(android.R.id.content, fragment, FRAGMENT_TAG)
                   .commit();
       } else {
           fragment = getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
       }
   }

   public abstract Fragment onCreateFragment();

   public Fragment getFragment() {
       return fragment;
   }

}

menggunakan

public class ViewCatalogItemActivity extends SingleFragmentActivity {
    @Override
    public Fragment onCreateFragment() {
        return new FragmentWorkShops();
    }

}
pengguna2212515
sumber
6

Untuk API level 17 atau lebih tinggi, View.generateViewId()akan menyelesaikan masalah ini. Metode utilitas memberikan id unik yang tidak digunakan dalam waktu pembuatan.

Sfseyhan
sumber
3
Selamat Datang di Stack Overflow! Sementara ini secara teoritis dapat menjawab pertanyaan, akan lebih baik untuk memasukkan bagian-bagian penting dari jawaban di sini, dan menyediakan tautan untuk referensi.
SuperBiasedMan
3

Untuk melampirkan fragmen ke aktivitas yang terprogram di Kotlin, Anda dapat melihat kode berikut:

MainActivity.kt

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

            // create fragment instance
            val fragment : FragmentName = FragmentName.newInstance()

            // for passing data to fragment
            val bundle = Bundle()
            bundle.putString("data_to_be_passed", DATA)
            fragment.arguments = bundle

            // check is important to prevent activity from attaching the fragment if already its attached
            if (savedInstanceState == null) {
                supportFragmentManager
                    .beginTransaction()
                    .add(R.id.fragment_container, fragment, "fragment_name")
                    .commit()
            }
        }

    }
}

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=".ui.MainActivity">

    <FrameLayout
        android:id="@+id/fragment_container"
        android:layout_width="0dp"
        android:layout_height="0dp"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintEnd_toEndOf="parent"
        app:layout_constraintStart_toStartOf="parent"
        app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>

FragmentName.kt

class FragmentName : Fragment() {

    companion object {
        fun newInstance() = FragmentName()
    }

    override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {

        // receiving the data passed from activity here
        val data = arguments!!.getString("data_to_be_passed")
        return view
    }

    override fun onActivityCreated(savedInstanceState: Bundle?) {
        super.onActivityCreated(savedInstanceState)
    }

}

Jika Anda terbiasa dengan Ekstensi di Kotlin maka Anda bisa lebih baik kode ini dengan mengikuti artikel ini .

bhavya_karia
sumber