Uvod
Adapter je most između izvora podataka (Array objekat, List objekat…) i korisničkog interfejsa. Uloga adapter objekta (implementira Adapter interfejs) je da čita podatke iz različitih izvora podataka, i na osnovu njih popunjava View objekte članove nekog ViewGroup-a.
Do sada smo koristili ListView ali preporuka je da se umesto listView-a koristi RecyclerView, a naručito kada god imate kolekcije podataka čiji se elementi menjaju tokom izvršavanja aplikacije kao reakcija na aktivnost korisnika ili mrežnih dogadjaja. RecyclerView je naslednik ListView i GridView-a, i namenjen je da efikasno renderuje adapter-based view.
RecyclerView ima svoj adapter “RecyclerView.adapter” koji implementira ViewHolder patern, integriše “convertView” i ima pripremljene metode koji su zamena za sve što smo uradili kod “CustomArrayAdapter-a” a poboljšava efikasnost prikaza pri skrolovanju liste.
Postupak kreiranja RecyclerView.Adapter-a
a) Podaci za listu
U ovome primeru jedan član liste prihvata više podataka, stoga je potrebno napraviti klasu koja će da bude “modla” za kreiranje objekata koji čuvaju podatke jednog člana nekog AdapterView-a:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
public class ExampleItem { private int mImageResource; private String mText1; private String mText2; public ExampleItem(int imageResource, String text1, String text2) { mImageResource = imageResource; mText1 = text1; mText2 = text2; } // geteri public int getImageResource() {return mImageResource;} public String getText1() {return mText1;} public String getText2() {return mText2;} } |
Definisali smo da se kroz konstruktor ubacuju podaci za svaki član liste, te stoga možemo da generišemo pocetnu listu podataka u okviru Aktivnosti (Fragmenta):
1 2 3 4 5 6 7 8 9 10 |
@Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayList<ExampleItem> exampleList = new ArrayList<>(); exampleList.add(new ExampleItem(R.drawable.nasmejan, "Pera", "Pancevo")); exampleList.add(new ExampleItem(R.drawable.ravnodusan, "Mika", "Beograd")); exampleList.add(new ExampleItem(R.drawable.tuzan, "Steva", "Nis")); } |
b) Kreiranje AdapterView-a
Potrebno je u sklopu layout-a Aktivnosti (fragmenta) definisati mesto gde će biti smeštena lista podataka. Ovaj deo je sličan kao kod običnog adaptera, stim što se kreira se umesto ListView koristi RecyclerView.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
<?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"> <android.support.v7.widget.RecyclerView android:id="@+id/recyclerView" android:layout_width="0dp" android:layout_height="0dp" android:scrollbars="vertical" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent"/> </android.support.constraint.ConstraintLayout> |
c) Kreiranje custom layout-a za jedan elementa liste
Sada je potrebno napraviti custom layout, koji će da ih prikaže više podataka nemenjenih za jedan element liste. Ovaj deo nije bio potreban kod “običnog” adaptera ali je sada potreban zbog viška podataka, jer ne možemo da koristimo već predefinisane androidove layout-e koji prihvataju samo jedan podatak. U ovome primeru imamo za svaki row po tri podatka: slika i dva teksta.
NAPOMENA:
Klikom na row element RecyclerView-a se ne dešava očekivani “ripple” efekat. Da bi se takav efekat aktivirao potrebno je root View-u row layout-a dodati atribut: android:background=”?android:attr/selectableItemBackground”
Pogledajte ceo primer koda ovde.
d) Kreiranje Custom Adapter klase
Za kreiranja klase customAdaptera je potrebno da naša klasa ekstenduje RecyclerView.Adapter klasu. Da bi smo definisali koga tipa su podaci u adapter-u, potrebno je pre svega da unutar adaptera napravimo unutašnju statičnu ViewHolder klasu koja ekstenduje RecyclerView.ViewHolder klasu. Kada smo kreirali statičnu ViewHolder potrebnoje da unutar nje definišemo njenu spostvenu konstruktor metodu.
1 2 3 4 5 |
public static class ExampleViewHolder extends RecyclerView.ViewHolder { public ExampleViewHolder(@NonNull View itemView) { super(itemView); } } |
Sada možemo da okviru uglasitih zagrada ubacimo naziv našeg ViewHolder-a i tako definišemo koji tip prihvata adapter.
1 |
public class ExampleAdapter extends RecyclerView.Adapter <ExampleAdapter.ExampleViewHolder> |
Tek nakon ovoga možemo da implementiramo sve metode koje zahteva RecyclerView.Adapter klasa, i da definišemo konstruktor koji prihvata ArrayList-u podataka kao parametar. Posle svega klasa bi trebalo ovako da izgleda:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class ExampleAdapter extends RecyclerView.Adapter <ExampleAdapter.ExampleViewHolder>{ ArrayList<ExampleItem> mExampleList; public ExampleAdapter(ArrayList<ExampleItem> exampleList) { mExampleList = exampleList; } public static class ExampleViewHolder extends RecyclerView.ViewHolder { public ExampleViewHolder(@NonNull View itemView) { super(itemView); } } @NonNull @Override public ExampleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { return null; } @Override public void onBindViewHolder(@NonNull ExampleViewHolder exampleViewHolder, int i) { } @Override public int getItemCount() { return 0; } } |
Sada kada smo napravili kostur potrebno je definišemo i detalje.
Definisanje ViewHolder klase
Kao prvo potrebno je da u okviru ViewHolder klase targetiramo sve sub elemente iz example_item.XML layout-a:
1 2 3 4 5 6 7 8 9 10 11 12 |
public static class ExampleViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public TextView mTextView1; public TextView mTextView2; // konstruktor ViewHolder klase public ExampleViewHolder(@NonNull View itemView) { super(itemView); mImageView = itemView.findViewById(R.id.imageView); mTextView1 = itemView.findViewById(R.id.textView); mTextView2 = itemView.findViewById(R.id.textView2); } } |
Kreiranje instance ViewHolder-a
Prvo je potrebno izvršimo parsiranje row layout-a u View objekat, a to se vrši u okviru onCreateViewHolder() metode, pa se zatim taj objekat prosledi kao parametar konstruktoru nove ViewHolder instance:
1 2 3 4 5 6 7 |
@NonNull @Override public ExampleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.example_item, viewGroup, false); ExampleViewHolder evh = new ExampleViewHolder(v); return evh; } |
Ubacivanje podataka u listu se vrši u sklopu onBindViewHolder() metode:
1 2 3 4 5 6 7 8 |
@Override public void onBindViewHolder(@NonNull ExampleViewHolder exampleViewHolder, int i) { ExampleItem currentItem = mExampleList.get(i); exampleViewHolder.mImageView.setImageResource(currentItem.getImageResource()); exampleViewHolder.mTextView1.setText(currentItem.getText1()); exampleViewHolder.mTextView2.setText(currentItem.getText2()); } |
Definisanje veličine liste
Veličina liste se definiš u okviru getItemCount()
1 2 3 4 |
@Override public int getItemCount() { return mExampleList.size(); } |
Cela Custom Adapter klasa sada izgleda ovako.
e) Ubacivanje RecyclerView-a u Aktivnost
Neophodne stvari koji definišu RecyclerView su:
- View koji predstavlja RecylerView
- RecyclerView.LayoutManager koji se koristi da definiše raspored elemenata u okviru RecyclerView-a (LinearLayoutManager, GridLayoutManager, StaggeredGridLayoutManager)
- Adapter koji se koristi za popunjavanje RecyclerView-a
NAPOMENA:
RecyclerView se uglavnom definiše tako da ne zavisi od child elemenata nego uglavnom od parent View-a u kome je smešten, te se njegova visina i šira generalno ne menje tokom vremena. Ali taj podatak moramo da naglasimo da android to ne bi svaki put proveravo kada ubacujemo novi ili izbacujemo već postojeći element. Iz tog razloga da bi poboljšali efiasnost RecyclerVie-a je dobro da definišemo naš RecyclerView kao da je fiksne veličine sa setHasFixedSize(true).
Pre svega je potrebno da definišemo polja koja će biti kasnije dostupna drugim delovima koda:
1 2 3 |
private RecyclerView mRecyclerView; private RecyclerView.LayoutManager mLayoutManager; private ExampleAdapter mAdapter; |
U sklopu onCreate() metode ćemo setovati napravljena polja:
1 2 3 4 5 6 7 8 |
mRecyclerView = findViewById(R.id.recyclerView); mRecyclerView.setHasFixedSize(true); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mAdapter = new ExampleAdapter(exampleList); mRecyclerView.setAdapter(mAdapter); |
Ceo kod koji se nalazi u Aktivnosti možete da pogledate ovde.
NAPOMENA
Ukoliko dodajemo ili uklanjamo element iz RecyclerView-a potrebno je posle svakog dodavanja obavestiti sistem da je došlo do promene pozivajući metodu notifyItemInserted(position):
1 |
mAdapter.notifyItemInserted(position); |
Takodje treba i posle svakog uklanjanja postojećeg elelementa obavestiti sistem da je došlo do promene pozivaju’i metodu notifyItemRemoved(position):
1 |
mAdapter.notifyItemRemoved(position); |
Pa bi metode za ubacivanje i izbacivanje elemenata ovako izgledale:
1 2 3 4 5 6 7 8 9 |
public void insertItem(int position) { mExampleList.add(position, new ExampleItem(R.drawable.ic_android, "New Item At Position" + position, "This is Line 2")); mAdapter.notifyItemInserted(position); } public void removeItem(int position) { mExampleList.remove(position); mAdapter.notifyItemRemoved(position); } |
Sve metode koje mogu da se korisite za obaveštavanje sisteme o premenama pogledajte ovde.
f) Definisanje click listener-a
Zajedno sa standardnim ListView-om stiže i onItemClick interfejs, medjutim toga nema kod RecyclerView-a stoga moramo da kreiramo naš interfejs i customClickListener (pogledaj kako se kreiraju customListener-i ovde).
Kreiranje novog interfejsa
Prvo je potrebno da u sklopu našeg Custom Adapter-a definišemo novi interfejs i jednu callback metodu:
1 2 3 |
public interface OnItemClickListener { void onItemClick(int position); } |
Setter metoda
A zatim je potrebno da definišemo polje i setter metodu, čijim pozivanjem u Aktivnosti definišemo objekat koji osluškuje event:
1 2 3 4 5 |
private OnItemClickListener mListener; public void setOnItemClickListener(OnItemClickListener listener) { mListener = listener; } |
Okidanje dogadjaja
Potrebno je “okinuti” dogadjaj klikom na neki element RecyclerView liste koji je definisan u ViewHolder klasi, a to ćemo uraditi tako što ćemo pozovati callback metodu našeg interfejsa:
1 |
listener.onItemClick(position); |
Ovu metodu ćemo da pozovemo u trenutku kada se klikne na element “itemView” (predstavljen kao parametar konstruktorske metode). Iz tog razloga poziv metode će biti smešten u okviru onClick() metode:
1 2 3 4 5 6 7 8 9 10 11 |
itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (listener != null) { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { listener.onItemClick(position); } } } }); |
Sigurno ste primetili da android studio prijavljuje grešku da ne može da nadje promenjivu listener pošto je naša klasa statična. Da bi smo napravili ovu promenjivu dostupnom, prosledićemo je kao parametar konstruktorskoj funkciji Custom ViewHolder klase. Ubacivanje listenera kao parametra klase je najlakše postići ako se u AndroidStudiu označi listener koji je problem i sa ALT + ENTER pozove pomoćni meni, gde se izabere opcija “Create parameter listener”, nakon čega će Android Studio odraditi sve sam.
Pa bi cela ViewHolder klasa sada ovako izgledala
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
public static class ExampleViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public TextView mTextView1; public TextView mTextView2; // konstruktor ViewHolder klase public ExampleViewHolder(@NonNull View itemView, final OnItemClickListener listener) { super(itemView); mImageView = itemView.findViewById(R.id.imageView); mTextView1 = itemView.findViewById(R.id.textView); mTextView2 = itemView.findViewById(R.id.textView2); itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { if (listener != null) { int position = getAdapterPosition(); if (position != RecyclerView.NO_POSITION) { listener.onItemClick(position); } } } }); } } |
NAPOMENA:
Pri svakom kliku na neki element liste je potrebno da znamo na koji element je kliknuto, to se jednostavno dobija koristeći metodu getAdapterPosition()
Definisanje objekta koji osluškuje i sadržaja callback metode
U okviru Aktivnosti se nadje objekat koji implementira interfejs a to je u našem slučaju instanca samog adaptera “mAdapter”. Zatim taj objekat poziva setter metodu setOnItemClickListener() da bi kroz prosledjeni parametar definisao objekat koji osluškuje event. U našem slučaju će to da bude anonimni objekat koji implementira interfejs “on the fly”. Akciju koja treba da se izvede nakon okidanja dogadjaja ćemo definisati tako što “pregazimo” (override) njegovu callback metodu onItemClick:
1 2 3 4 5 6 7 8 |
mAdapter.setOnItemClickListener(new ExampleAdapter.OnItemClickListener() { @Override public void onItemClick(int position) { // Ovde se definiše rakcija na klik ExampleItem item = mAdapter.mExampleList.get(position); Toast.makeText(MainActivity.this, "Kliknut element " + item.getText1(), Toast.LENGTH_SHORT).show(); } }); |
Pogledajte ceo kod Aktivnosti ovde.
Ovo je jednostavniji način i sve se definiše u okviru adapter klase. Prvo je potrebno da naša ViewHolder klasa implementira View.OnClickListener:
1 |
public static class ExampleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener |
A nakon toga će AndroidStudio prijaviti grešku i zahtevati da se implementira i njegova callback metoda onClick(). U okviru ove metode se definiše akcija koja se dešava kada se okine dogadjaj tj. kada se klikne na neki član liste.
1 2 3 4 5 6 7 |
@Override public void onClick(View view) { // Ovde ide reakcija na klik int position = getAdapterPosition(); ExampleItem item = mExampleList.get(position); Toast.makeText(view.getContext(), "Kliknut element je " + item.getText1(), Toast.LENGTH_SHORT).show(); } |
Da bi se definisao objekat koji osluškuje potrebno je da ga prosledimo kao parametar setOnClickListener() metodi u okviru ViewHolder konstruktora. U našem slučaju to je sam ViewHolder pa ćemo da prosledimo “this”:
1 |
itemView.setOnClickListener(this); |
Pa bi ceo ViewHolder ovako izgledao:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
public class ExampleViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener{ public ImageView mImageView; public TextView mTextView1; public TextView mTextView2; // Konstruktor ViewHolder klase public ExampleViewHolder(@NonNull View itemView) { super(itemView); mImageView = itemView.findViewById(R.id.imageView); mTextView1 = itemView.findViewById(R.id.textView); mTextView2 = itemView.findViewById(R.id.textView2); // Definišemo osluškivač itemView.setOnClickListener(this); } @Override public void onClick(View view) { // Ovde ide reakcija na klik int position = getAdapterPosition(); ExampleItem item = mExampleList.get(position); Toast.makeText(view.getContext(), "Kliknut element je " + item.getText1(), Toast.LENGTH_SHORT).show(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
<?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="wrap_content" android:background="?android:attr/selectableItemBackground"> <ImageView android:id="@+id/imageView" android:layout_width="60dp" android:layout_height="60dp" android:layout_marginStart="16dp" android:layout_marginTop="16dp" android:layout_marginBottom="16dp" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintStart_toStartOf="parent" app:layout_constraintTop_toTopOf="parent" app:srcCompat="@mipmap/ic_launcher" /> <TextView android:id="@+id/textView" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="16dp" android:layout_marginEnd="8dp" android:layout_toEndOf="@+id/imageView" android:text="Line 1" android:textColor="@android:color/black" android:textSize="20sp" android:textStyle="bold" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.0" app:layout_constraintStart_toEndOf="@+id/imageView" app:layout_constraintTop_toTopOf="parent" /> <TextView android:id="@+id/textView2" android:layout_width="0dp" android:layout_height="wrap_content" android:layout_marginStart="8dp" android:layout_marginTop="12dp" android:layout_marginEnd="8dp" android:layout_toEndOf="@+id/imageView" android:text="Line 2" android:textSize="15sp" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintHorizontal_bias="0.003" app:layout_constraintStart_toEndOf="@+id/imageView" app:layout_constraintTop_toBottomOf="@+id/textView" /> </android.support.constraint.ConstraintLayout> |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public class ExampleAdapter extends RecyclerView.Adapter <ExampleAdapter.ExampleViewHolder>{ ArrayList<ExampleItem> mExampleList; public ExampleAdapter(ArrayList<ExampleItem> exampleList) { mExampleList = exampleList; } // ViewHolder public static class ExampleViewHolder extends RecyclerView.ViewHolder { public ImageView mImageView; public TextView mTextView1; public TextView mTextView2; // konstruktor ViewHolder klase public ExampleViewHolder(@NonNull View itemView) { super(itemView); mImageView = itemView.findViewById(R.id.imageView); mTextView1 = itemView.findViewById(R.id.textView); mTextView2 = itemView.findViewById(R.id.textView2); } } @NonNull @Override public ExampleViewHolder onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { View v = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.example_item, viewGroup, false); ExampleViewHolder evh = new ExampleViewHolder(v); return evh; } @Override public void onBindViewHolder(@NonNull ExampleViewHolder exampleViewHolder, int i) { ExampleItem currentItem = mExampleList.get(i); exampleViewHolder.mImageView.setImageResource(currentItem.getImageResource()); exampleViewHolder.mTextView1.setText(currentItem.getText1()); exampleViewHolder.mTextView2.setText(currentItem.getText2()); } @Override public int getItemCount() { return mExampleList.size(); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private RecyclerView.LayoutManager mLayoutManager; private ExampleAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayList<ExampleItem> exampleList = new ArrayList<>(); exampleList.add(new ExampleItem(R.drawable.nasmejan, "Pera", "Pancevo")); exampleList.add(new ExampleItem(R.drawable.ravnodusan, "Mika", "Beograd")); exampleList.add(new ExampleItem(R.drawable.tuzan, "Steva", "Nis")); mRecyclerView = findViewById(R.id.recyclerView); mRecyclerView.setHasFixedSize(true); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mAdapter = new ExampleAdapter(exampleList); mRecyclerView.setAdapter(mAdapter); } } |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
public class MainActivity extends AppCompatActivity { private RecyclerView mRecyclerView; private RecyclerView.LayoutManager mLayoutManager; private ExampleAdapter mAdapter; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); ArrayList<ExampleItem> exampleList = new ArrayList<>(); exampleList.add(new ExampleItem(R.drawable.nasmejan, "Pera", "Pancevo")); exampleList.add(new ExampleItem(R.drawable.ravnodusan, "Mika", "Beograd")); exampleList.add(new ExampleItem(R.drawable.tuzan, "Steva", "Nis")); mRecyclerView = findViewById(R.id.recyclerView); mRecyclerView.setHasFixedSize(true); mLayoutManager = new LinearLayoutManager(this); mRecyclerView.setLayoutManager(mLayoutManager); mAdapter = new ExampleAdapter(exampleList); mRecyclerView.setAdapter(mAdapter); mAdapter.setOnItemClickListener(new ExampleAdapter.OnItemClickListener() { @Override public void onItemClick(int position) { ExampleItem item = mAdapter.mExampleList.get(position); Toast.makeText(MainActivity.this, "Kliknut element " + item.getText1(), Toast.LENGTH_SHORT).show(); } }); } } |
Method | Description |
---|---|
notifyItemChanged(int pos) | Notify that item at position has changed. |
notifyItemInserted(int pos) | Notify that item reflected at position has been newly inserted. |
notifyItemRemoved(int pos) | Notify that items previously located at position has been removed from the data set. |
notifyDataSetChanged() | Notify that the dataset has changed. Use only as last resort. |
Pocetnik sam u Android programiranju. Zanima me kako se menjaju svojstva view-a koji je prosledjen kao argument callback metodi onClick klase ExampleViewHolder? Npr, ovako mogu da promenim boju pozadine view.setBackgroundColor(0x4D00E640); ali npr ne mogu da pocesim text da bude bold? Npr view.setTypeface(null, Typeface.BOLD); ovo ne funkcionise, jer nad view objektom ne mogu da pozovem metodu setTypeFace. Hvala unapred.
Da li si targtirao TextView pa tek onda probao programirano da promeniš neko svojstvo?
(kao npr. mTextView1 = itemView.findViewById(R.id.textView))
Da, upravo tako sam i uspeo. Pravio sam gresku jer sam findViewById methodu pozivao nad v objektom onClick method-e, umesto nad itemView objektom. Hvala.
Da bi ukapirao sta pise u ovom “uputstvu” moram da odgledam brdo tutrijala na netu, onda kada se vratim na tekst.. Postavlja se pitanje, cemu kad sam vec sve naucio.
Ovo sve me podseca na neki veliki VIC iz delova namenjen samo za internu upotrebu u nekoj programerskoj firmi.
Ne vidim smisao postojanja ovog teksta? Programeri koji to vec znaju jedini mogu da prate tekst, ostali, koji tek treba da nauce izgube nit.
Deluje kao blog a nije, cini mi se da je “preslisavanje” programera na 3 godini studija koji je dobro spremio ispit.
Sajt je napravljen sa najboljim namerama (totalno neprofitabilan), i zamišljen kao pomoć programerima u vidu brzog podsetnka. Mogli ste da pročitate na početnoj strani da sadržaj sajta čine moje lične skripte koje su “malo” sredjene da bi dobile formu tutorijala, medjutim ja sam i za to “malo” sredjivanja (svakog od ovih stotinjak članaka) utrošio popriličnu količinu svog slobodnog vremena. Voleo bih da sada kada ste već naučili ovu oblast, i vi pomognete programersku zajednicu i napravite bolje “uputstvo”.
Veliki pozdrav.