13 Şubat 2018 Salı

Android Dersleri 7.2 - Fragment örnek uygulama

7.2 - Android resmi sitesindeki Fragment örneği

Official Android Fragments derslerinde (https://developer.android.com/guide/components/fragments.html) öğrendiğimiz her şeyi biraraya getirip aşağıdaki örneği inceleyelim. Bu örneğin source kodunun tamamına şuradan erişilebilir :
https://github.com/android/platform_development/blob/master/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
       Bu örnekte, 2 tane fragment içeren bir activity yaratacağız, bu fragment'lardan birisinde Sheakspeare şarkı isimlerinin listesi gösterilecek, bu fragment'daki bir şarkı seçildiğinde ise diğer fragment'da bu şarkının detayları gösterilecek. Ayrıca ekranı portrait modda ve landscape modda kullanırken farklı fragment configuration'lar gerçekleştirilecektir, portrait modda activity'de yalnızca bir fragment yani şekspir'in şarkılarının listesi gösterilecek listedeki bir şarkıya tıklayınca yeni bir activity'de şarkının detayları gösterilecektir. Landscape modda ise her iki fragment aynı activity'de yan yana gösterilecektir.
The main activity applies a layout in the usual way, during onCreate()( Main activity'nin onCreate() method'unda, fragment_layout.xml dosyasında belirtilen layout'u kullanacağımızı söyleriz. )
@Override

protected void onCreate(Bundle savedInstanceState) {

    super.onCreate(savedInstanceState);



    setContentView(R.layout.fragment_layout);

}
The layout applied is fragment_layout.xml fragment element'in class attribute'üne com.example.android.apis.app package'ındaki, FragmentLayout isimli class'daki TitlesFragment isimli inner class'ının path'ini assign ettik. Yani layout'daki fragment element'in olduğu kısımda TitlesFragment isimli fragment class'ının layout'u gösterilecek. FrameLayout element'i hatırla. Üst üste çakışık view element koyabiliyorduk, sonra runtime'da istediğimiz view element'i en üste çıkartıp visible yapabiliyorduk, bu örnekte ise FrameLayout'a runtime'da DetailsFragment'ı koyacağız. )
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:orientation="horizontal"

    android:layout_width="match_parent" android:layout_height="match_parent">



    <fragment class= "com.example.android.apis.app.FragmentLayout$TitlesFragment"

            android:id="@+id/titles" android:layout_weight="1"

            android:layout_width="0px" android:layout_height="match_parent" />



    <FrameLayout android:id="@+id/details" android:layout_weight="1"

            android:layout_width="0px" android:layout_height="match_parent"

            android:background="?android:attr/detailsElementBackground" />



</LinearLayout>
       Using this layout, the system instantiates the TitlesFragment (which lists the play titles) as soon as the activity loads the layout, while the FrameLayout (where the fragment for showing the play summary will go) consumes space on the right side of the screen, but remains empty at first. As you'll see below, it's not until the user selects an item from the list that a fragment is placed into the FrameLayoutfragment_layout.xml dosyasında fragment element kullanırız, class attribute'üne TitlesFragment class'ının path'ini veririz , activity'ye static olarak TitlesFragment'ı eklemiş oluruz. TitlesFragment, şarkı başlıklarını listeler. fragment_layout.xml dosyasında FrameLayout element'i şarkının çalarkenki detaylarını gösterecektir, ekranın sağında gösterilir, başlangıçta boş bir alandır. Kullanıcı listeden bir item seçince Framelayout'un ekranda kapladığı boş alan dolar, bu alana DetailsFragment koyulur, seçilen şarkının detayları gösterilir.  )
       However, not all screen configurations are wide enough to show both the list of plays and the summary, side by side. So, the layout above is used only for the landscape screen configuration, by saving it at res/layout-land/fragment_layout.xml( Ancak bazı screen configuration'ları, hem şarkı listesini gösteren fragment'ı, hem de şarkı listesinden seçilen şarkının detaylarını gösteren fragment'ı yan yana gösterebilecek kadar geniş değildir. Dolayısıyla yukarıda tanımladığımız fragment_layout.xml dosyası sadece landscape screen configuration için kullanılmalıdır, dolayısıyla fragment_layout.xml dosyası res/layout-land/ klasörü altında olmalıdır.)
       Thus, when the screen is in portrait orientation, the system applies the following layout, which is saved at res/layout/fragment_layout.xml( Ekran portrait orientation'da iken yani telefon dik tutuluyorken kullanılacak olan fragment_layout.xml dosyasının içeriği aşağıdaki gibidir, bu dosya res/layout klasörü altında olmalıdır.  Yani fragment_layout.xml isimli 2 dosya tanımladık, bunların içerikleri farklıdır, bu dosyalardan birini res/layout klasörü altına diğerini res/layout-land klasörü altına koyduk. Portrait modda kullanılan layout xml dosyasını inceleyelim, root element olarak bir FrameLayout element kullanılmıştır, bu element'in içinde bir fragment element vardır, fragment element'in class attribute'üne TitlesFragment class'ının path'i assign edilmiştir. )
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent" android:layout_height="match_parent">

    <fragment class="com.example.android.apis.app.FragmentLayout$TitlesFragment"

            android:id="@+id/titles"

            android:layout_width="match_parent" android:layout_height="match_parent" />

</FrameLayout>
       This layout includes only TitlesFragment. This means that, when the device is in portrait orientation, only the list of play titles is visible. So, when the user clicks a list item in this configuration, the application will start a new activity to show the summary, instead of loading a second fragment. ( portrait modda kullanılan fragment_layout.xml dosyası sadece TitlesFragment  isimli fragment'ı içerir. Dolayısıyla sadece şarkı listesi gösterilecektir portrait orientation'da. Kullanıcı listedeki bir şarkı ismine tıklayınca uygulama yeni bir activity başlatacak ve bu activity'de şarkının detaylarını gösterecektir. Hatırlayalım, Landscape modda ise sağda boş bir framelayout vardı ve soldaki listedeki bir şarkı ismine tıklayınca framelayout element'e DetailFragment yükleniyordu. )
       Next, you can see how this is accomplished in the fragment classes. First is TitlesFragment, which shows the list of Shakespeare play titles. This fragment extends ListFragment and relies on it to handle most of the list view work. ( Fragment class'larının implementation'larını inceleyelim. TitlesFragment class'da şekspir'in şarkılarının listesi gösterilir. TitlesFragment, ListFragment class'ını extend eder, bu sayede listview ile ilgili bir çok işi handle eder.  )
       As you inspect this code, notice that there are two possible behaviors when the user clicks a list item: depending on which of the two layouts is active, it can either create and display a new fragment to show the details in the same activity (adding the fragment to the FrameLayout), or start a new activity (where the fragment can be shown). TitlesFragment class'ını inceleyelim. Kullanıcının telefonu dikey mi yatay mı tuttuğuna bağlı olarak 2 farklı senaryo vardır.
       Telefon yatay tutuluyorsa, bir activity'de 2 fragment gösterilecektir, activity'ye statik olarak TitlesFragment eklenir, runtime'da kullanıcı listedeki bir şarkıya tıklayınca, DetailsFragment class'dan bir object yaratılıp sağdaki frameLayout element'e dinamik olarak eklenir.
       Telefon dikey tutuluyorsa, başlangıçtaki activity'de sadece TitlesFragment gösterilir, burada şarkı listesi gösterilir, buradaki bir şarkıya tıklayınca yeni bir activity açılıp bu activity'de DetailsFragment yani seçili şarkının detayları gösterilir. )

getListView().setItemChecked(index, true);
A list view where the last item the user clicked is placed in the "activated" state, causing its background to highlight. ( Listedeki tıklanılan bir item'ın seçili hale gelmesini, yani tıklanılan item'ın highlight edilmesini, (arka planının farklı bir renkle boyanmasını) sağlamak için getListView().setItemChecked(index, true); çağırılır. setItemChecked() method'unun aldığı 1.parametre, listedeki tıklanılan item'ın index'idir. )

getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); ( Listede aynı anda sadece 1 tane item'ın veya birden fazla item'ın seçili olmasını sağlamak için getListView().setChoiceMode method'u çağırılır. )

getArguments(), setArguments, Bundle
http://stackoverflow.com/questions/5425568/how-to-use-setarguments-and-getarguments-methods-in-fragments
Eg: Add data:- ( Bir frame'e argument eklemek için frame class'ının içinde bir Bundle object yaratırız, sonra bu object'in putString() method'unu çağırarak Bundle object'de variable'lar saklayabiliriz. Sonra Fragment object'in setArguments() method'unu çağırırız, setArguments()  method'una argument olarak bundle object'i veririz. Böylece Fragment object'de variable'lar tutabiliriz.
Fragment class içerisinde herhangi bir yerde getArguments().getString("latitude") ve getArguments().getInt("some_number") gibi method'ları çağırarak fragment object'de saklanan argument'ler olan latitude ve some_number isimli variable'lara erişebiliriz. getArguments(), Bundle object return eder.)
   Bundle bundle = new Bundle();
   bundle.putString("latitude", latitude);
   bundle.putString("longitude", longitude);
   bundle.putString("board_id", board_id);
Bundle.putInt(YEAR, mDatePicker.getYear());
Bundle.putInt(MONTH, mDatePicker.getMonth());
   MapFragment mapFragment = new MapFragment();
   mapFragment.setArguments(bundle);
Eg: Get data :-
String latitude =  getArguments().getString("latitude")

Örneğin, Fragment class'ının onCreateView() method'unda, yani fragment'ın view'i yaratılırken, getArguments() metod'unu çağırabiliriz. getArguments()Bundle object return eder :
public class Frag2 extends Fragment {
     public View onCreateView(LayoutInflater inflater,
         ViewGroup containerObject,
         Bundle savedInstanceState){
         //here is your arguments
         Bundle bundle getArguments();

        //here is your list array
        String[] myStrings=bundle.getStringArray("elist");  
     }
}

       Bundle class'ının putInt() method'unu çağırarak bir variable'da bir değer tutabiliriz, sonra bu variable'ın tuttuğu değeri elde edebiliriz.
putInt() method'unun aldığı 1. parametre'deki variable, 2.parametresindeki değeri tutar. Yani curChoice isimli variable'a değer olarak mCurCheckPosition'ı assign ederiz:
Bundle outState;       // method's parameter
outState.putInt("curChoice", mCurCheckPosition);

        Aşağıda TitlesFragment isimli bir class tanımlanmıştır. Bu class ListFragment class'ını extend eder.
setListAdapternew ArrayAdapter<String>( getActivity(),android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES ) )
row'ların layout'u olarak built-in layout xml olan simple_list_item_activated_1 kullanılacaktır. Yani herbir row bu layout ile oluşturulup Shakespeare.TITLES array'indeki bir eleman ile doldurulacaktır. setListAdapter method'unun 1.parametresi Context object alır, bu örnekte getActivity() verilmiştir 1 .parametre olarak çünkü activity object de bir context object'dir.

Dikkat ettiysen, TitlesFragment class'ı, static bir class'dır. Aslında bu class bir inner class'dır. Outer class'ımız aşağıdaki şekildedir, 3 tane class içerir, bir activity ve iki fragment class içerir :
        Source kod bu şekilde, ancak bu şekilde geliştirme yapmak gerçekten mümkün mü bilmiyorum. Bu konu araştırılmalı.
        FragmentLayout isimli Activity'de fragment_layout.xml set edilir setContentView() method'u çağırılarak.
public class FragmentLayout extends Activity {

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);       
        setContentView(R.layout.fragment_layout);
    }

    /**
     * This is a secondary activity, to show what the user has selected
     * when the screen is not large enough to show it all in one activity.
     */
    public static class DetailsActivity extends Activity {

        @Override
        protected void onCreate(Bundle savedInstanceState) {
            ...
        }
    }

    /**
     * This is the "top-level" fragment, showing a list of items that the
     * user can pick.  Upon picking an item, it takes care of displaying the
     * data to the user as appropriate based on the currrent UI layout.
     */
    public static class TitlesFragment extends ListFragment {
        boolean mDualPane;
        int mCurCheckPosition = 0;
        @Override
        public void onActivityCreated(Bundle savedInstanceState) {
            ...
        }

        @Override
        public void onSaveInstanceState(Bundle outState) {
            super.onSaveInstanceState(outState);
            outState.putInt("curChoice", mCurCheckPosition);
        }

        @Override
        public void onListItemClick(ListView l, View v, int position, long id) {
            showDetails(position);
        }

        /**
         * Helper function to show the details of a selected item, either by
         * displaying a fragment in-place in the current UI, or starting a
         * whole new activity in which it is displayed.
         */
        void showDetails(int index) {
                                   ...
        }
    }

    /**
     * This is the secondary fragment, displaying the details of a particular
     * item.
     */
    public static class DetailsFragment extends Fragment {
        /**
         * Create a new instance of DetailsFragment, initialized to
         * show the text at 'index'.
         */
        public static DetailsFragment newInstance(int index) {
            ...
        }

        public int getShownIndex() {
            return getArguments().getInt("index", 0);
        }

        @Override
        public View onCreateView(LayoutInflater inflater, ViewGroup container,
                Bundle savedInstanceState) {
            ...
        }
    }
}

TitlesFragment class'ının onActivityCreated() method'Unda fragment_layout.xml'deki details id'li view elde edilir.
View detailsFrame = getActivity().findViewById(R.id.details);
Eğer böyle bir id'ye sahip bir view gerçekten varsa detailFrame artık null değildir. Biz aslında telefonun yatay mı dikey mi tutulduğunu anlamak için details id'li bir view'in olup olmadığını check ediyoruz. Telefon yatay tutulduğunda res/layout-land/fragment_layout.xml dikey tutulduğunda ise res/layout/fragment_layout.xml layout dosyası kullanılacaktır demiştik daha önce hatırla. Telefon dikey tutuluyorsa layout olarak kullanılacak olan res/layout/fragment_layout.xml dosyasında details id'li bir view yoktur bir activity'de sadece şarkı başlıkları gösterilir. Dolayısıyla detailsFrame null'dır, dolayısıyla mDualPane null'dır. Fragment'ımız için bir argument set etme işlemi onSaveInstanceState() method'u aracılığıyla yapılır, bu argument'lere onActivityCreated() method'u içerisinden erişebiliriz, bu method'un aldığı Bundle object parametre'a dikkat edelim. Bu Bundle object, Fragment'ın argument'lerini taşır. Dolayısıyla Fragment'ımızın curChoice isimli argument'ine daha önceden bir değer verilmişse bunu alırız, class'ımızda tanımladığımız mCurCheckPosition isimli variable'a koyarız şöyle : mCurCheckPosition=savedInstanceState.getInt("curChoice", 0);
Sonra mDualPane'in null olup olmamasına bakarak telefonun dik mi yan mı tutulduğunu anlarız. Null değilse telefon yan tutuluyordur, çünkü details id'li bir view vardır ve bu view landscape mode'da kullanılan layout'da vardır.  Yan tutulma case'inde activity'de yan yana 2 fragment vardır, soldaki fragment'da haber başlıkları listesi vardır, sağdaki fragment başlangıçta boş gözükür. Yan tutuluyorsa if(true) olur, listedeki şarkı başlıklarından aynı anda sadece 1 tanesinin seçilebilir olmasını sağlarız : if (mDualPane) { getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE); .. }
Sonra showDetails(mCurCheckPosition) method'unu çağırarak sağdaki fragment'a seçilen haber başlığının detaylarını yükleriz. Soldaki haber başlıklarından kaçıncısı seçildiyse mCurCheckPosition onu gösterir. showDetails'a bunu argument olarak veririz. Uygulamayı ilk kez başlattığımızda mCurCheckPosition 0 olduğu için soldaki haber başlıklarından hiçbirinin seçilmediğini anlarız, dolayısıyla sağdaki fragment boş olur, hiçbir haberin detayını göstermez.
        Haber başlıkları listesindeki bir item'a tıklayınca onListItemClick() method'u çağırılır. Bu method'da showDetails(tıklanılan haberin pozisyonu) method'u çağırılır.
        Şimdi de showDetails() method'unu inceleyelim. Bu method, seçilen haber başlığının detaylarını aynı activity'deki bir fragment'da göstermek için veya yeni bir activity içindeki bir fragment'da göstermeyi sağlar. showDetails() method'u, listedeki tıklanılan element'in index'ini parametre olarak alır. Bunu mCurCheckPosition variable'a assign eder.
if(mDualPane)
{      
        Telefon yatay pozisyondadır.
        Aynı activity içerisindeki sağdaki fragment'da haber detayı gösterilecektir.

}
else
{
        Telefon dikey pozisyondadır.
        Yeni bir fragment object yarat.
        Yeni bir activity başlat ve bu yeni activity'ye fragment object'i göm.
}
        Önce getListView().setItemChecked(index, true); diyerek listedeki seçili item highlight edilir.
        Sonra  DetailsFragment details = (DetailsFragment)getFragmentManager().findFragmentById(R.id.details);  diyerek details id'li view yani FrameLayout view elde edilir. Daha önceki incelediğimiz derslerimizde getFragmentManager.findFragmentById() method'unu Activity içerisinde çağırmıştık ve ilgili id'ye sahip olan view'i elde etmiştik. Bu örnekte ise Fragment class'ında getFragmentManager.findFragmentById()  çağırarak FrameLayout view'i elde ettik, FrameLayout'a Fragment object yükleyeceğiz, dolayısıyla FrameLayout'u bir Fragment gibi düşün burada, FrameLayout'a Fragment'a erişir gibi eriştiğimize dikkat et.
         Soru : FrameLayout'a getActivity().findViewById() diye de erişmiştik daha önce. Bu sefer neden bu şekilde erişmedik, bu şekilde erişsek uygulama düzgün çalışır mıydı?  Bence cevap şu olmalı: details id'li framelayout'da fragment tutulacağı için, diğer  bir deyişle details id'li framelayout ile bir fragment replace edileceği için details id'li view'e bir fragment gibi yaklaşmalıyız. details id'li fragment'ı al dediğimizde eğer daha önce details id'li framelayout ile bir fragment replace edilmemişse, bu null return eder, dolayısıyla if block'a girer, DetailsFragment object yaratılıp details id'li view ile replace edilir. Eğer details id'li framelayout ile bir fragment object daha önceden replace edilmişse, details==null false return eder, dolayısıyla (details.getShownIndex != index) expression'ına bakılır, daha önceden tıklanılan haber başlığından farklı bir haber başlığına tıklanılıp tıklanılmadığını check eder bu expression.
        Farklı bir haber başlığına tıklanılmışsabu true return eder, dolayısıyla if block'a girilir. If block'da DetailsFragment class'ının static bir method'u olan newInstance(index) method'u çağırılır, bu method'da yeni bir DetailsFragment ve Bundle object yaratılır. Bundle object'e index argument koyulur. Fragment'a Bundle object koyulur. Fragment object return edilir.
public static DetailsFragment newInstance(int index) {
        
DetailsFragment f = new DetailsFragment();
        
Bundle args = new Bundle();
        args
.putInt("index", index);
        f
.setArguments(args);
        
return f;
    
}
Sonra getFragmentManager().beginTransaction() method'u çağırılarak FragmentTransaction object elde edilir. Fragment Transaction object'in replace(neyin yerine,neyi koyalım) method'u çağırılarak details id'li view'in yerine details isimli reference variable'ının refer ettiği object yani az önce yarattığımız DetailsFragment object koyulur.
Sonra FragmentTransaction object'in setTransition() method'u çağırılarak replace sırasında gösterilecek animasyon seçilir. En son FragmentTransaction object'in commit() method'u çağırılır.
        Telefon dikey tutuluyorsa, showDetails() method'u çağırıldığında if block'a girilmez, else block'a girilir. else block'u inceleyelim:

Burada bir Intent object yaratılır. intent object'İn setClass() method'u çağırılır, setClass() method'unun aldığı ilk argument context object olmalıdır dolayısıyla getActivity() verilir ilk argument olarak, 2.argument olarak DetailsActivity.class verilir. Yani DetailsActivity isimli activity'ye geçiş yapılacaktır. Ayrıca intent object'in putExtra("index",index) method'u çağırılarak intent object'de index isimli bir argument tutulur. En son startActivity(intent) method'u invoke edilir.

Source code from : https://github.com/android/platform_development/blob/master/samples/ApiDemos/src/com/example/android/apis/app/FragmentLayout.java
public static class TitlesFragment extends ListFragment {

    boolean mDualPane;

    int mCurCheckPosition = 0;



    @Override

    public void onActivityCreated(Bundle savedInstanceState) {

        super.onActivityCreated(savedInstanceState);



        // Populate list with our static array of titles.

        setListAdapter(new ArrayAdapter<String>(getActivity(),

                android.R.layout.simple_list_item_activated_1, Shakespeare.TITLES));



        // Check to see if we have a frame in which to embed the details

        // fragment directly in the containing UI.

        View detailsFrame= getActivity().findViewById(R.id.details);

        mDualPane = detailsFrame != null && detailsFrame.getVisibility() == View.VISIBLE;



     if (savedInstanceState != null) {

        // Restore last state for checked position.

        mCurCheckPosition=savedInstanceState.getInt("curChoice", 0);

     }



     if (mDualPane) {

   // In dual-pane mode, the list view highlights the selected item.

           getListView().setChoiceMode(ListView.CHOICE_MODE_SINGLE);

            // Make sure our UI is in the correct state.

            showDetails(mCurCheckPosition);

        }

    }



    @Override

    public void onSaveInstanceState(Bundle outState) {

        super.onSaveInstanceState(outState);

        outState.putInt("curChoice", mCurCheckPosition);


 
  
  
Haber başlıkları listesindeki bir item'a tıklayınca onListItemClick() method'u çağırılır. Bu method'da showDetails(tıklanılan haberin pozisyonu) method'u çağırılır.
    }     @Override     public void onListItemClick(ListView l, View v, int position, long id) {         showDetails(position);     }     /* Helper function to show the details of a selected item, either by displaying a fragment in-place in the current UI, or starting a whole new activity in which it is displayed. */     void showDetails(int index) {        mCurCheckPosition = index;        if (mDualPane) {             // We can display everything in-place with fragments, so update               // the list to highlight the selected item and show the data.             getListView().setItemChecked(index, true);             // Check what fragment is currently shown, replace if needed.             DetailsFragment details = (DetailsFragment)getFragmentManager().findFragmentById(R.id.details);            if (details == null || details.getShownIndex() != index) {                // Make new fragment to show this selection.                details = DetailsFragment.newInstance(index);                // Execute a transaction, replacing any existing fragment                // with this one inside the frame.                FragmentTransaction ft=getFragmentManager().beginTransaction();                if (index == 0) {                    ft..details, details);                 } else {                     ft.replace(R.id.a_item, details);                 }                 ft.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE);                 ft.commit();             }         } else {             // Otherwise we need to launch a new activity to display             // the dialog fragment with selected text.             Intent intent = new Intent();             intent.setClass(getActivity(), DetailsActivity.class);             intent.putExtra("index", index);             startActivity(intent);         }     } }
When you replace a fragment in the code then you can attach a name to it.    ft.replace(R.id.frameLayoutId, tf, “visible_fragment”);
The second fragment, DetailsFragment shows the play summary for the item selected from the list from TitlesFragment:( TitlesFragment 'daki listeden bir haber başlığı seçilir, haber detayları DetailsFragment'da gösterilir.
        newInstance(index) method'unda yeni bir DetailsFragment ve Bundle object yaratılır. Bundle object'e index argument koyulur. Fragment'a Bundle object koyulur. Fragment object return edilir.
         getShownIndex() method'u şunu return eder : getArguments().getInt("index", 0. Yani DetailsFragment'da tutulan Bundle object'in tuttuğu index isimli argument'in değeri return edilir.
         onCreateView() method'unu inceleyelim.
Bu method da container null ise null return edilir, bunun nedenini daha detaylı araştır. Container null değilse, yani bu fragment'ı içeren bir framelayout varsa şunlar yapılır, ScrollView ve TextView class'larından view object yaratılır, constructor'lara context object'in argument olarak verildiğine dikkat et. Fragment class'ında çağırılan getAactivity() method'u, activity object'i return eder, activity object de bir context object'dir hatırla. Sonra bir padding tanımlanır, TextView object'in setPadding() method'u çağırılarak text'in etrafına bir miktar boşluk bırakılır. Sonra ScrollView view'e textview view'i koyulur. Sonra TextView'deki text set edilir. En son ScrollView view object return edilir.  )

public static class DetailsFragment extends Fragment {

    /** Create a new instance of DetailsFragment, initialized to show the text at 'index'.   */

    public static DetailsFragment newInstance(int index) 
    {

        DetailsFragment f = new DetailsFragment();

        Bundle args = new Bundle();

        args.putInt("index", index);

        f.setArguments(args);

        return f;

    }



    public int getShownIndex() {

        return getArguments().getInt("index", 0);

    }



    @Override

    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {

        if (container == null) {

     /* We have different layouts, and in one of them this fragment's containing frame doesn't exist.  The fragment may still be created from its saved state, but there is no reason to try to create its view hierarchy because it won't be displayed.  Note this is not needed -- we could just run the code below, where we would create and return the view hierarchy; it would just never be used.( Telefon dikey tutulduğunda kullanılan layout, framelayout view içermez, yani fragment'ı içerecek  bir framelayout view yoktur. DetailsFragment isimli fragment object'i içeren bir framelayout yoksa, container null'dır, fragment'ı kaydedilen state'den yaratmak mümkün olmasına karşın buna gerek yoktur, çünkü bu fragment gösterilmeyecektir, kullanılmayacaktır. ) */

            return null;

        }



      ScrollView scroller=new ScrollView(getActivity());

      TextView text = new TextView(getActivity());

      int padding = (int)TypedValue.applyDimension (TypedValue.COMPLEX_UNIT_DIP, 4, getActivity().getResources().getDisplayMetrics());

      text.setPadding(padding, padding, padding, padding);

      scroller.addView(text);

            text.setText(Shakespeare.DIALOGUE[getShownIndex()]);

      return scroller;

    }

}
       Recall from the TitlesFragment class, that, if the user clicks a list item and the current layout does not include the R.id.details view (which is where the DetailsFragment belongs), then the application starts the DetailsActivity activity to display the content of the item. ( TitlesFragment class'ını hatırlayalım, eğer kullanıcı listedeki bir item'a tıklarsa ve layout details id'li bir view içermiyorsa yani telefon dik tutuluyorsa uygulamamız DetailsActivity'yi başlatır ve haber detaylarını bu activity'de gösterir.  )
       Here is the DetailsActivity, which simply embeds the DetailsFragment to display the selected play summary when the screen is in portrait orientation:( DetailsActivity isimli activity class aşağıda gösterilmiştir. Seçilen haber detayını içeren DetailsFragment, DetailsActivity'de gömülüdür telefon dikey kullanılınca. )
public static class DetailsActivity extends Activity 
{



 @Override

 protected void onCreate(Bundle savedInstanceState) {

  super.onCreate(savedInstanceState);



  if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
      // If the screen is now in landscape mode, we can show the dialog in-line with the list so we don't need this activity.

  finish();

  return;

}



 if (savedInstanceState == null) 
 {

  // During initial setup, plug in the details fragment.

   DetailsFragment details = new DetailsFragment();

   details.setArguments(getIntent().getExtras());

   getFragmentManager().beginTransaction().add (android.R.id.content, details).commit();

  }

 }

}
Notice that this activity finishes itself if the configuration is landscape, so that the main activity can take over and display the DetailsFragment alongside the TitlesFragment. This can happen if the user begins the DetailsActivity while in portrait orientation, but then rotates to landscape (which restarts the current activity). (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE diyerek telefonun yatay tutulup tutulmadığını check ederiz, yatay tutuluyorsa finish() method'unu ve sonra return statement çağırıp activity'yi sonlandırırız hemencecik.Yani olur da activity landscape modda çalıştırılısa hemen sonlandırılır ve main activity tekrar gösterilir, main activity'de TitlesFragment ve DetailsFragment yan yana gösterilir. Bu senaryo hangi durumda gerçekleşir? Kullanıcı portrait modda DetailsActivity'yi başlattıktan sonra orientation moda geçerse aynı acitivty restart edilir ve bu sırada bu if block'a girilip activity sonlandırılır. )

Adding listener to back stack - addOnBackStackChangedListener , onBackStackChanged(), OnBackStackChangedListener

 
          Activity class'ının onCreate() method'unda, FragmentManager object yaratılır. Sonra FragmentManager object'in addOnBackStackChangedListener() method'u çağırılır. Bu method'a parameter olarak this keyword verilirse, aynı class OnBackStackChangedListener interface'ini implement etmelidir ve bu class içerisinde onBackStackChanged() method'u implement edilmelidir.
 
public class A extends Activity implements FragmentManager.OnBackStackChangedListener { 
 
    protected void onCreate(Bundle savedInstanceState) 
    {
        FragmentManager fm = getSupportFragmentManager(); 
        fm.addOnBackStackChangedListener(this);
    }
    public void onBackStackChanged() 
    {
        ...
    }
}
 
Veya en güzeli kısa yoldan sadece şunu yazmak yeterlidir Activity class'ının onCreate() method'unda :
getFragmentManager().addOnBackStackChangedListener( new OnBackStackChangedListener()
{
        @Override
        public void onBackStackChanged()
        {
                make something.
                Back stack'a bir şey eklenince örneğin back tuşuna
                basılınca onBackStackChanged() method'u invoke edilir.
        }
} );
Şurada farklı örnekler çok complex source kodlar var bunları incele, bilmediklerini yeni öğrnemen gereken şeyleri burdan bul araştır incele: http://www.programcreek.com/java-api-examples/index.php?class=android.support.v4.app.FragmentManager&method=addOnBackStackChangedListener

Understanding the FragmentManager
The FragmentManager is responsible for all runtime management of fragments including adding, removing, hiding, showing, or otherwise navigating between fragments. As shown above, the fragment manager is also responsible for finding fragments within an activity. Important available methods are outlined below:
Method    Description
addOnBackStackChangedListener             -> Add a new listener for changes to the fragment back stack.
beginTransaction()                -> Creates a new transaction to change fragments at runtime.
findFragmentById(int id)               -> Finds a fragment by id usually inflated from activity XML layout.
findFragmentByTag(String tag)   -> Finds a fragment by tag usually for a runtime added fragment.
popBackStack()     -> Remove a fragment from the backstack.
executePendingTransactions()   -> Forces committed transactions to be applied.

Managing Fragment Backstack, getBackStackEntryCount(), popBackStack(), addToBackStack()

      A record of all Fragment transactions is kept for each Activity by the FragmentManager. When used properly, this allows the user to hit the device’s back button to remove previously added Fragments (not unlike how the back button removes an Activity). Simply call addToBackstack on each FragmentTransaction that should be recorded: ( Bir activity için olan fragment transaction'ları fragmentManager tutar. Kullanıcı geri tıuşuna bastığında FragmentManager kullanılarak fragment backstack'a koyulabilir ve backstack'dan çıkartılabilir. Aşağıdaki örnekte FragmentTransaction object'in addToBackStack() method'u çağırılarak back stack'a ilgili fragment koyulur.  )
// Create the transaction
FragmentTransaction fts = getSupportFragmentManager().beginTransaction();
// Replace the content of the container
fts.replace(R.id.flContainer, new FirstFragment()); 
// Append this transaction to the backstack
fts.addToBackStack("optional tag");
// Commit the changes
fts.commit();
Programmatically, you can also pop from the back stack at any time through the manager: ( FragmentManager object'in popBackStack() method'u çağırılarak back stack'dan bir fragment pop edilebilir. FragmentManager object'in getBackStackEntryCount() method'u çağırılarak back stack'daki fragment sayısını öğrenebiliriz.   )
FragmentManager fragmentManager = getSupportFragmentManager();
if (fragmentManager.getBackStackEntryCount() > 0) {
    fragmentManager.popBackStack();
}
      With this approach, we can easily keep the history of which fragments have appeared dynamically on screen and allow the user to easily navigate to previous fragments. ( Bu yaklaşım sayesinde kullanıcı fragment'lar arasında ileri geri gidebilir. )

Fragment Hiding vs Replace

      In many of the examples above, we call transaction.replace(...) to load a dynamic fragment which first removes the existing fragment from the activity invoking onStop and onDestroy for that fragment before adding the new fragment to the container. This can be good because this will release memory and make the UI snappier. However, in many cases, we may want to keep both fragments around in the container and simply toggle their visibility. This allows all fragments to maintain their state because they are never removed from the container. To do this, we might modify this code: ( Yukarıdaki örneklerin çoğunda FragmentTransaction object'in replace() method'unu çağırarak activity'den varolan bir fragment'ın onStop() ve onDestroy() method'larını çağırarak yok edip sonrasında ise bunun yerine yeni bir fragment koyulur. Bu iyi bir seçimdir, çünkü varolan fragment'ı silerek memory'yi release ederiz. )
// Within an activity
 
private FragmentA fragmentA;
private FragmentB fragmentB;
private FragmentC fragmentC;
 
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState == null) {
        fragmentA = FragmentA.newInstance("foo");
        fragmentB = FragmentB.newInstance("bar");
        fragmentC = FragmentC.newInstance("baz");
    }
}
 
protected void displayFragmentA() {
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    // removes the existing fragment calling onDestroy
    ft.replace(R.id.flContainer, fragmentA); 
    ft.commit();
}
to this approach instead leveraging add, show, and hide in the FragmentTransaction( Ancak bir fragment'ın yerine başka bir fragment koymak için varolan fragment'ı silmek zorunda değiliz, varolan fragment'ı container'dan silmeyip görünmez yapabilir başka fragment'ları görünür yapabiliriz. Aşağıdaki gibi :  )
// ...onCreate stays the same
@Override
protected void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);
    if (savedInstanceState == null) {
        fragmentA = FragmentA.newInstance("foo");
        fragmentB = FragmentB.newInstance("bar");
        fragmentC = FragmentC.newInstance("baz");
    }
}
 
// Replace the switch method
protected void displayFragmentA() {
    FragmentTransaction ft = getSupportFragmentManager().beginTransaction();
    if (fragmentA.isAdded()) { // if the fragment is already in container
        ft.show(fragmentA);
    } else { // fragment needs to be added to frame container
        ft.add(R.id.flContainer, fragmentA, "A");
    }
    // Hide fragment B
    if (fragmentB.isAdded()) { ft.hide(fragmentB); }
    // Hide fragment C
    if (fragmentC.isAdded()) { ft.hide(fragmentC); }
    // Commit changes
    ft.commit();
}
Using this approach, all three fragments will remain in the container once added initially and then we are simply revealing the desired fragment and hiding the others within the container. ( Bu yaklaşımı kullanarak container'a 3 fragment'ı da aynı anda ekleyebilir sonra istediğimiz fragment'ı görünür yapabilir istediğimizi görünmez yapabiliriz. Fragment'ın object'in isAdded() method'u, fragment'ın container'a eklenip eklenmediğini check eder.
      FragmentTransaction object'in show(fragmentA) method'unu çağırarak fragmentA'yı görünür yaparız.
      FragmentTransaction object'in hide(fragmentA) method'unu çağırarak fragmentA'yı görünmez yaparız.
      FragmentTransaction object'in add(fragmentA) method'unu çağırarak fragment'ı container'a ekleriz.  )

FragmentList :
      ListFragment, Android 3.0'dan önce de kullanılabilirdi hala da kullanılmaktadır. Item listesi oluşturmak için kullanılır ListFragment. Örneğin, aşağıdaki fragment'da her satırda bir resim ve text olan bir item listesi görülüyor. İşte bunu ListFragment ile yapabiliriz.
Bu örnekte arrayAdapter'e dayalı bir list fragment yaratacağız.
SimpleListFragment isimli bir uygulama yaratalım. Otomatik olarak bir main activity class'ı ve layout'u oluşturulacaktır böylece. main layout'un içerisinde fragment element yazalım.
- res/layout  klasörü altında list_fragment.xml isimli bir layout dosyası yaratalım.
- myListFragment.java isimli bir java dosyası yaratalım.  Bu class şu method'ları içerir : onCreateView()onActivityCreated() and OnItemClickListener()
src/main/java/myListFragment.java dosyasının içeriği aşağıda gösterilmiştir. Bu class ListFragment class'ını extend eder.
- Bu class'daki onCreateView() method'u içerisinde fragment'da gösterilecek olan layout belirtilir, bu method inflater.inflate() method'unu return eder. inflater.inflate() method'unun aldığı ilk parameter fragment'ın layout'u olarak kullanılacaktır.
- onActivityCreated() method'u, activity yaratılırken çağırılır otomatik olarak. Bu method'da, R.array.planet isimli string array kullanılarak bir arrayadapter yaratılır. Bu arrayadapter Listview'e bağlanır.
- onItemClickListener() method'u, listview'deki bir item'a tıklanıldığında çağırılır. Bu method çağırılınca bir tıklanılan item'ın pozisyonunu söyleyen bir toast mesajı gösterilir. onItemClickListener() method'u OnItemClickListener interface'inde tanımlı bir abstract method'dur, dolayısıyla onItemClickListener() method'unu implement eden class OnItemClickListener interface'ini implement ediyor olmalıdır.
package com.example.tutorialspoint7.myapplication;
import ...

public class MyListFragment extends ListFragment implements OnItemClickListener {
   @Override
   public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState)
  {
      View view = inflater.inflate(R.layout.list_fragment, container, false);
      return view;
   }

   @Override
   public void onActivityCreated(Bundle savedInstanceState)
  {
      super.onActivityCreated(savedInstanceState);
      ArrayAdapter adapter = ArrayAdapter.createFromResourcegetActivity(), R.array.Planets, android.R.layout.simple_list_item_1);
      setListAdapter(adapter);
      getListView().setOnItemClickListener(this);
   }

   @Override
   public void onItemClick(AdapterView<?> parent, View view, int position,long id) {
      Toast.makeText(getActivity(), "Item: " + position, Toast.LENGTH_SHORT).show();
   }
}
MainActivity.java dosyasının içeriği aşağıda gösterilmiştir. Bu activity'de main layout set edilir sadece. :
package com.example.tutorialspoint7.myapplication;

import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;

public class MainActivity extends AppCompatActivity {

   @Override
   protected void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
      setContentView(R.layout.activity_main);
   }
}

res/values klasörünün altındaki string.xml dosyasının içeriği aşağıda gösterilmiştir. Bu dosyada string constant'lara initial değerleri verilmiştir:
<?xml version="1.0" encoding="utf-8"?>
<resources>
   <string name="app_name">ListFragmentDemo</string>
   <string name="action_settings">Settings</string>
   <string name="hello_world">Hello world!</string>
   <string name="imgdesc">imgdesc</string>
  
   <string-array name="Planets">
      <item>Sun</item>
      <item>Mercury</item>
      <item>Venus</item>
      <item>Earth</item>
      <item>Mars</item>
      <item>Jupiter</item>
      <item>Saturn</item>
      <item>Uranus</item>
      <item>Neptune</item>
   </string-array>

</resources>
         res/layout/activity_main.xml dosyasının içeriği aşağıda gösterilmiştir. Root element bir LinearLayout'dur. LinearLayout element içerisinde fragment element vardır.
<?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" >
  
   <fragment  android:id="@+id/fragment1"
              android:name="com...MyListFragment"
              android:layout_width="match_parent"
              android:layout_height="match_parent" />

</LinearLayout>
res/layout/list_fragment.xml dosyasının içeriği aşağıda gösterilmiştir. Root element bir LinearLayout'dur. LinearLayout element içerisinde ListView element ve TextView element vardır.
<?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" >

   <ListView
      android:id="@android:id/list"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" >
   </ListView>

   <TextView
      android:id="@android:id/empty"
      android:layout_width="match_parent"
      android:layout_height="wrap_content" >
   </TextView>
</LinearLayout>

Manifest.xml dosyasının içeriği aşağıda gösterilmiştir :
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
   package="com.example.tutorialspoint7.myapplication">

   <application
      android:allowBackup="true"
      android:icon="@mipmap/ic_launcher"
      android:label="@string/app_name"
      android:supportsRtl="true"
      android:theme="@style/AppTheme">
     <activity android:name=".MainActivity">
       <intent-filter>
         <action android:name="android.intent.action.MAIN" />
         <category android:name="android.intent.category.LAUNCHER" />
       </intent-filter>
     </activity>
   </application>
</manifest>
     Uygulamamızı çalıştırdığımızda main activity aşağıdaki gibi görünür. Bu örnekteki main activity ListFragment içermektedir.


Additional References which i didn't study for Fragments:
https://guides.codepath.com/android/Creating-and-Using-Fragments
https://guides.codepath.com/android/Creating-and-Using-Fragments very nice guide
https://www.tutorialspoint.com/android/android_list_fragment.htm         anlamadım
https://www.tutorialspoint.com/android/android_fragment_transitions.htm
https://developer.android.com/reference/android/app/Fragment.html
https://www.raywenderlich.com/117838/introduction-to-android-fragments-tutorial
http://www.vogella.com/tutorials/AndroidFragments/article.html
https://www.airpair.com/android/fragments-android-studio

Accessing Activity layout inside fragment

        Since you want the Activity's views, you're going to want to do this:
ProgressBar progressBar = (ProgressBar) getActivity().findViewById(R.id.ctrlActivityIndicator);

        You call getActivity() to get the Activity instance. Then you use findViewById() as normal (provided that R.id.ctrlActivityIndicator is part of the Activity layout, you won't get NPEs).

Hiç yorum yok:

Yorum Gönder