Programlı olarak oluşturulmuş bir içerik görünümü olan bir Etkinliğe nasıl Parça eklerim


240

Düzenini programlı olarak uygulayan bir Etkinliğe bir Parça eklemek istiyorum. Fragment belgelerine baktım ama neye ihtiyacım olduğunu açıklayan pek çok örnek yok. İşte yazmaya çalıştığım kod türü:

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;
    }
}

Bu kod derlenir, ancak başlangıçta kilitlenir, muhtemelen benim FragmentTransaction.add()yanlış olduğundan. Bunu yapmanın doğru yolu nedir?

Yanıtlar:


198

Bu kodda birden fazla sorun olduğu ortaya çıktı. Bir parça, etkinlikle aynı java dosyasının içinde bu şekilde ilan edilemez, ancak genel bir iç sınıf olarak ilan edilemez. Çerçeve, parçanın yapıcısının (parametresiz) genel ve görünür olmasını bekler. Parçayı bir iç sınıf olarak Etkinliğe taşımak veya parça için yeni bir java dosyası oluşturmak bunu düzeltir.

İkinci sorun, bu şekilde bir parça eklediğinizde, parçanın içeren görünümüne bir referans iletmeniz ve bu görünümün özel bir kimliğe sahip olması gerektiğidir. Varsayılan kimliği kullanmak uygulamayı kilitler. Güncellenmiş kod:

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;
        }
    }
}

115
Parçayı yalnızca etkinliğin en üst düzey içerik görünümü olarak kullanmak istiyorsanız kullanabilirsiniz ft.add(android.R.id.content, newFragment). Yalnızca parçanın kapsayıcısı etkinliğin içerik görünümü değilse özel bir düzen oluşturmak ve kimliğini ayarlamak gerekir.
Tony Wong

25
Kimliği sabit kodlamak yerine, XML'de tanımlayabilir ve normal olarak başvurabilirsiniz (R.id.myid).
Jason Hanley

1
Bunu nasıl yapacağımı bilmiyorum, ancak bir kimliğin yalnızca kullanmanız gereken kapsamda benzersiz olması gerektiğini unutmayın.
Jason Hanley

2
kimliğin yalnızca içerme düzeninin mevcut heirarşisi içindeki düzeyinde benzersiz olması gerekir. Diyelim ki doğrusal bir düzene sarılmış, sadece bu doğrusal düzen içindeki diğer görünümler arasında benzersiz olması gerekiyor.
Shaun

1
SetId (View.NO_ID) kullanarak dinamik olarak bir kimlik oluşturabilir ve ardından ne olduğunu görmek için getId () yöntemini kullanabilirsiniz.
enl8enmentnow

71

İşte Tony Wong'un yorumunu okuduktan sonra bulduğum şey :

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

Kotlin kullanıyorsanız Google'ın Kotlin uzantılarının neler sunduğuna bir göz atın veya sadece kendinizinkini yazın.


Bunu yapma! if (savedInstanceState == null)Parça oluşturulmadan önce kontrol edin veya bir ekranı döndürdükten sonra iki parça veya parça yeniden siparişiniz olacaktır. addYöntemi hiç kullanmayın ! Sadece replace. Yoksa tuhaf davranışlara sahip olacaksınız.
CoolMind

"BackStackStateName" değerini nereden edinebilirsiniz? (Değiştirme işlevini kullanırken)
vikzilla

@vikzilla Burada ve belgelerde oldukça iyi cevaplar bulabilirsiniz . Kısacası: backStackStateNamedize sizin tarafınızdan tanımlanan bir şeydir.
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();
        }
    }

Etkinlik veya FramentActivity'ye bir parça eklemek için bir Kapsayıcı gerekir. Bu kapsayıcı Framelayoutxml içine eklenebilecek bir " " olmalıdır , yoksa " android.R.id.content" gibi varsayılan kapsayıcıyı Faaliyetteki bir parçayı kaldırmak veya değiştirmek için kullanabilirsiniz.

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>

29

Tüm Cevapları okuduktan sonra zarif bir şekilde geldim:

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();
    }

}

temelde parçanızın kabı olarak bir frameLayout eklemenize gerek yoktur, bunun yerine parçayı doğrudan android root'a ekleyebilirsiniz.

ÖNEMLİ: yeniden oluşturma işlemi sırasında parça değişkeni örnek durumunu kaybetmeyi düşünmüyorsanız, burada gösterilen yaklaşımın çoğunu değiştirme parçasını kullanmayın .


cevap için teşekkürler, bu tüm ekrana parça sekmesini ekler? ancak bir kare düzenine veya görüntüleme çağrı cihazına nasıl eklersiniz?
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;
   }

}

kullanım

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

}

6

İçin API düzeyinde 17 veya daha yüksek, View.generateViewId()bu sorunu çözecektir. Yardımcı program yöntemi, derleme zamanında kullanılmayan benzersiz bir kimlik sağlar.


3
Stack Overflow'a hoş geldiniz! Bu teorik olarak soruyu cevaplayabilse de , cevabın temel kısımlarını buraya dahil etmek ve referans için bağlantı sağlamak tercih edilir.
SuperBiasedMan

3

Kotlin'deki bir etkinliğe parça eklemek için aşağıdaki koda bakabilirsiniz:

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)
    }

}

Kotlin'de Uzantılar hakkında bilgi sahibi iseniz, bu makaleyi izleyerek bu kodu daha da iyi hale getirebilirsiniz .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.