Sunteți pe pagina 1din 35

Robert Győrödi

Profesor

Universitatea din Oradea, Departamentul de Calculatoare și Tehnologia Informației


Sistemul de permisiuni în Android
Înainte și după Marshmallow
Prezentare aplicație suport pentru curs
Bazat pe cartea ”Mastering Android Application Development”
Tipare de navigare
Dashboard
Sliding Panel
Tab-uri
Fragmente
Implementarea unor tab-uri glisante
https://developer.android.com/guide/topics/permissions/overview
https://developer.android.com/training/permissions/requesting
Acțiuni care pot dăuna dispozitivului sau confidențialității
datelor utilizatorului
Declararea acestor permisiuni în manifestul aplicației
Conectare la Internet, acces la GPS, la camera foto, la lista de contacte, …
Înainte de Marshmallow
La instalare Android atenționează utilizatorul, prezentând lista de permisiuni
cerute de aplicație – utilizatorul poate accepta sau refuza instalarea
După Marshmallow
La instalare nu se mai cere acceptul
În execuție Android va cere acceptul la fiecare permisiune
Aplicația trebuie să trateze cazurile în care utilizatorul nu acordă acea
permisiune
Există două categorii de permisiuni: periculoase și normale
https://developer.android.com/reference/android/Manifest.permission

Permisiunile cerute se declară în AndroidManifest.xml


...
<uses-permission android:name="android.permission.INTERNET"></uses-permission>
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"></uses-permission>
<uses-permission android:name="android.permission.CAMERA"></uses-permission>
<uses-permission android:name="android.permission.CAPTURE_AUDIO_OUTPUT"></uses-permission>
<uses-permission android:name="android.permission.CAPTURE_VIDEO_OUTPUT"></uses-permission>
<application
android:allowBackup="true“
...

android.permission.INTERNET
Se cere accesul la internet
android.permission.ACCESS_FINE_LOCATION
Se cere acces la o sursă de localizare cât mai exactă – GPS
android.permission.CAMERA
Se cere accesul la camera
Grupa de permisiuni Pemisiuni
android.permission-group.CALENDAR android.permission.READ_CALENDAR

Permisiunile normale sunt android.permission-group.CAMERA


android.permission.WRITE_CALENDAR
android.permission.CAMERA
acceptate la instalare și nu pot fi android.permission-group.CONTACTS android.permission.READ_CONTACTS

revocate (ex. android.permission.WRITE_CONTACTS


android.permission.READ_PROFILE
android.permission.WRITE_PROFILE
android.permission.INTERNET) android.permission-group.LOCATION android.permission.ACCESS_FINE_LOCATION
android.permission.ACCESS_COARSE_LOCATION
Permisiunile periculoase sunt android.permission-group.MICROPHONE android.permission.RECORD_AUDIO

grupate pe categorii pentru a le android.permission-group.PHONE android.permission.READ_PHONE_STATE


android.permission.CALL_PHONE
face mai ușor de înțeles pentru android.permission.READ_CALL_LOG
android.permission.WRITE_CALL_LOG
utilizatori. com.android.voicemail.permission.ADD_VOICEMAIL
android.permission.USE_SIP
android.permission.PROCESS_OUTGOING_CALLS
Dacă utilizatorul acceptă o android.permission-group.SENSORS android.permission.BODY_SENSORS
permisiune dintr-un grup/categorie, android.permission.USE_FIGERPRINT

el de fapt acceptă tot android.permission-group.SMS android.permission.SEND_SMS


android.permission.RECEIVE_SMS
grupul/categoria android.permission.READ_SMS
android.permission.RECEIVE_WAP_PUSH
android.permission.RECEIVE_MMS
android.permission.READ_CELL_BROADCASTS
android.permission-group.STORAGE android.permission.READ_EXTERNAL_STORAGE
android.permission.WRITE_EXTERNAL_STORAGE
Caracteristica
necesită
permisiune

Continuă cu Da Avem deja Nu


caracteristica permisiunea?

Afișează Da
permisiune Am întrebat deja?
respinsă

Nu

Cere utilizatorului Utilizatorul


permisiunea acceptă/refuză
private boolean hasPermission(String permission) {
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
return(checkSelfPermission(permission)== PackageManager.PERMISSION_GRANTED);
}

Pregătirea cererii }
return true;

SharedPreferences sharedPreferences = PreferenceManager.getDefaultSharedPreferences(this);


ArrayList<String> permissionsRejected = new ArrayList<>();
ArrayList<String> permissionsToRequest = new ArrayList<>();
permissionsToRequest.add(ACCESS_FINE_LOCATION);
permissionsToRequest.add(ACCESS_COARSE_LOCATION);
permissionsToRequest.add(CAMERA);
int resultCode = REQUEST_PERMS;
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.LOLLIPOP_MR1) {
requestPermissions(permissionsToRequest.toArray(new String[permissionsToRequest.size()]), resultCode);
for (String perm : permissionsToRequest) {
markAsAsked(perm);
}
}

Tratarea răspunsului private boolean shouldWeAsk(String permission) {


return(sharedPreferences.getBoolean(permission, true));
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { }
switch (requestCode) {
case REQUEST_PERMS: private void markAsAsked(String permission) {
boolean someAccepted = false; sharedPreferences.edit().putBoolean(permission, false).apply();
boolean someRejected = false; }
for(String perms : permissionsToRequest){
if(hasPermission(perms)){
someAccepted = true;
}else{
someRejected = true;
permissionsRejected.add(perms);
}
}
if(permissionsRejected.size()>0){
someRejected = true;
}
break;
}
}
MasteringAndroidApp
Va avea o parte pe server, unde se vor stoca datele, iar aplicația va comunica prin internet cu
server-ul
Aplicația va prezenta o listă de oferte de locuri de muncă
Această listă va fi întreținută pe parte de server
Utilizatorii aplicației vor putea:
citi ofertele
primi notificări
Aplicația va demonstra:
Tiparele de programare cele mai folosite
Tehnici concurente și diferite metode de conectare la API-uri REST
Crearea unui UI eficient
Folosind liste, grid-uri, material design, descărcare în background
Folosirea unei baze de date pentru funcționare offline
Folosirea de notificări push, rapoarte de blocare și analitice
Fazele publicării
Transformarea unei idei într-o aplicație reală
Crearea unei structuri de bază care să fie afișată pe ecran
Alegerea unui tipar de navigare potrivit

În următoarele vom discuta despre:


O privire de ansamblu asupra tiparelor de navigare
Conceptul de fragment și gestionarea fragmentelor
Implementarea tab-urilor și a ViewPager
Tranziții animate între ecrane
Numărul de aplicații disponibile în magazinele online este
impresionant
99% din ideile pe care le-ați putea avea sunt deja realizate
Aveți două posibilități
Îmbunătățirea unei aplicații deja existente
Continuarea brainstorming-ului până ajungeți la ceva original
Pentru a realiza o aplicație, primul pas ar fi să o vizualizăm
Trebuie să identificăm componentele de bază
Trebuie să simplificăm idea pe ecran
Trebuie să gândim mișcările dintre ecrane
Pentru a crea ecranele, trebuie să identificăm
punctele cheie ale ideii noastre
Trebuie să stabilim use case-urile
Pentru aplicația exemplu vom avea:
Ecran de prezentare – va prezenta un logo pe parcursul
inițializării (descărcare date de pe internet, dacă este cazul)
Ecran care va prezenta o listă de informații provenite de pe
internet
Elementele vor putea fi selectate de utilizator pentru a primi
detalii
Ca și ecran principal se va afișa un ecran de contact cu o
hartă cu locația curentă și datele de contact
Va mai exista și un ecran de preferințe/setări
Va permite activarea notificărilor și a altor setări
https://material.io/design/navigation/understanding-navigation.html
Acest tipar este unul dintre primele folosite în Android
Tipar bine cunoscut datorită aplicațiilor:
Gmail, Facebook
Prezintă ecrane care vin din dreapta sau stânga, printr-
un gest de swipe sau click pe meniul ”Hamburger”
Tiparul este perfect dacă avem un număr mare de
opțiuni
Se poate combina cu alte tipare – cum ar fi tiparul cu tab-uri
Poate avea elemente pe mai multe nivele
Implementarea se poate face cu clasa DrawerLayout
FrameLayout – conținutul navigării
NavigationView – conținând opțiunile (poate fi orice layout)
// activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.widget.DrawerLayout
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:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true">
După ce se selectează un element din meniu, va apărea un <LinearLayout
view în FrameLayout android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
Navigația din interiorul FrameLayout-ului la un sub-view se va tools:context=".MainActivity">

putea face prin view-ul respectiv <android.support.design.widget.AppBarLayout


android:layout_width="match_parent"
Din view-ul respectiv nu se va putea naviga niciodată la un android:layout_height="wrap_content"
android:theme="@style/AppTheme.AppBarOverlay">
element din meniul principal <android.support.v7.widget.Toolbar
android:id="@+id/my_toolbar"
Navigația dintre view și sub-view poate fi gestionată prin android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
butonul back sau navigarea în sus de pe bara de acțiuni android:background="?attr/colorPrimary"
app:popupTheme="@style/AppTheme.PopupOverlay" />
Pentru tratarea evenimentelor de închidere/deschidere a </android.support.design.widget.AppBarLayout>
panelului trebuie să setăm un listener de tipul <FrameLayout
ActionBarDrawerToggle android:id="@+id/content_frame"
android:layout_width="match_parent"
onDrawerClosed android:layout_height="match_parent" >
</FrameLayout>
onDrawerOpened
</LinearLayout>
Trebuie folosită componenta din android.support.v7.app <ListView
Adăugarea librăriei este explicată la link-urile: android:id="@+id/drawer_list"
android:layout_width="240dp"
android:choiceMode="singleChoice"
https://developer.android.com/topic/libraries/support-library/setup android:background="@drawable/bg_key"
android:layout_height="match_parent"
android:layout_gravity="start" />
https://developer.android.com/studio/build/build-variants
</android.support.v4.widget.DrawerLayout>
drawerToggle = new ActionBarDrawerToggle(this, drawerLayout, myToolbar,
public class MainActivity extends AppCompatActivity {
R.string.open_drawer, R.string.close_drawer) {
private DrawerLayout drawerLayout;
/** Called when a drawer has settled in a completely closed state. */
private FrameLayout frameLayout;
public void onDrawerClosed(View view) {
private ListView drawerList;
super.onDrawerClosed(view);
private ActionBarDrawerToggle drawerToggle;
getSupportActionBar().setTitle(mTitle);
private String[] drawerListText;
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
private CharSequence mDrawerTitle;
}
private CharSequence mTitle;
/** Called when a drawer has settled in a completely open state. */
@Override
public void onDrawerOpened(View drawerView) {
protected void onCreate(Bundle savedInstanceState) {
super.onDrawerOpened(drawerView);
super.onCreate(savedInstanceState);
getSupportActionBar().setTitle(mDrawerTitle);
setContentView(R.layout.activity_main);
invalidateOptionsMenu(); // creates call to onPrepareOptionsMenu()
}
mTitle = mDrawerTitle = getTitle();
};
Toolbar myToolbar = (Toolbar) findViewById(R.id.my_toolbar);
drawerToggle.setDrawerIndicatorEnabled(true);
myToolbar.setTitle(mTitle);
drawerToggle.syncState();
setSupportActionBar(myToolbar);
drawerLayout.addDrawerListener(drawerToggle);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
}
@Override
drawerLayout = (DrawerLayout)findViewById(R.id.drawer_layout);
public void setTitle(CharSequence title) {
frameLayout = (FrameLayout)findViewById(R.id.content_frame);
mTitle = title;
drawerList = (ListView)findViewById(R.id.drawer_list);
getSupportActionBar().setTitle(mTitle);
}
drawerListText = getResources().getStringArray(R.array.drawer_labels);
}
View headerView = getLayoutInflater().inflate(R.layout.drawer_header, null);

drawerList.addHeaderView(headerView, null, false);


drawerList.setAdapter(new ArrayAdapter<String>(this,
android.R.layout.simple_list_item_activated_1, drawerListText));
drawerList.setOnItemClickListener(new ListView.OnItemClickListener() {
@Override
// build.gradle (app)
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
dependencies {
// Highlight the selected item, update the title, and close the drawer
...
if (id != -1) {
implementation 'com.android.support:appcompat-v7:27.1.1'
view.setSelected(true);
implementation 'com.android.support:design:27.1.1'
setTitle(drawerListText[position-1]);
...
drawerLayout.closeDrawer(drawerList);
}
}
}
});
Acest tipar este un tipar folosit deseori
Arată un meniu fix cu componente la același nivel
În acest caz ”meniul” este vizibil tot timpul
Sunt cazuri în care nu toate tab-urile încap pe ecran, caz în care
se poate folosi o variațiune a acestui tipar și anume:
Sliding Tabs
Implementarea celor două variante este diferită, deci trebuie să
decidem care variantă o vom folosi
https://material.io/design/components/tabs.html

Câteva specificații de formatare din ghidul de material design:


Afișați tab-urile pe o singură linie
Textul tab-urilor poate fi formatat pe două linii, dar dacă nu încape, trebuie tăiat
Nu includeți un alt set de conținut cu tab-uri în interiorul unui tab
Scoateți în evidență tab-ul corespunzător conținutului vizibil
Grupați tab-urile împreună ierarhic
Conectați un grup de tab-uri cu conținutul acestora
Țineți tab-urile lângă conținutul lor
Exemplu de tab-uri cu scroll/slide având un submeniu
Componentele principale sunt:
Activități
Fragmente
https://developer.android.com/guide/components/fragments

Un fragment reprezintă un comportament sau o


porțiune din interfața utilizator a unei activități
Putem combina mai multe fragmente într-o singură
activitate pentru a construi un UI multi-panou
Putem refolosi un fragment în mai multe activități
Un fragment poate fi privit ca și o secțiune
modulară a unei activități, care are propriul ciclu de
viață și primește propriile evenimente
Aceste fragmente se pot adăuga și elimina pe parcursul
rulării activității
https://developer.android.com/guide/components/fragments#Lifecycle

Ciclul de viață a fragmentelor este puțin diferit de cel al activităților


onAttach – conectează fragmentul la activitate
onDetach – deconectează fragmentul de la activitate
De obicei va trebui să implementați cel puțin următoarele metode:
onCreate – sistemul apelează aceasta când creează fragmentul
Ar trebui inițializate componentele esențiale ale fragmentului, care se doresc a fi reținute când fragmentul
este pus pe pauză sau oprit și reluat
onCreateView – sistemul apelează aceasta când fragmentul ar trebui să își deseneze interfața
pentru prima dată
Trebuie să se întoarcă un View, care este rădăcina layout-ului fragmentului
Se poate întoarce null dacă fragmentul nu oferă o interfață grafică
onPause – sistemul apelează această metodă ca și un prim indiciu că utilizatorul părăsește
fragmentul
Aici este locul unde ar trebui salvate datele care trebuie persistate
onActivityCreated informează fragmentul că activitatea sa și-a terminat
apelul Activity.onCreate
Există două modalități de afișare:
Includerea fragmentului în layout-ul XML
Aceasta va crea fragmentul în momentul în care vederea care conține
fragmentul este încărcat
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent"
android:layout_height="fill_parent">
<fragment android:name="com.example.android.MyFragment"
android:id="@+id/headlines_fragment"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>

Crearea fragmentului în mod programatic și informarea Fragment


Manager-ului pentru a îl afișa într-un container
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="horizontal"
android:layout_width="fill_parent" Myfragment fragment = MyFragment.newInstance();
android:layout_height="fill_parent"> getSupportFragmentManager().beginTransaction()
<FrameLayout android:id="@+id/fragment_container" .add(R.id.fragment_container, fragment).commit();
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Modul de creare recomandată a fragmentelor în cod este printr-o metodă
de tip factory și nu folosind constructorul
Motivul este că Android recreează fragmentele folosind constructorul
implicit
Deci dacă avem un constructor cu parametri, acesta va fi ignorat și parametri vor fi
pierduți
Așa, trimitem parametri ca și argument într-un bundle, astfel permițând
fragmentului să acceseze parametri dacă trebuie să se recreeze
public class MyFragment extends Fragment {
// Static factory method that returns a new fragment
// receiving a parameter and initializing the fragment's arguments
public static MyFragment newInstance(int param) {
MyFragment fragment = new MyFragment();
Bundle args = new Bundle();
args.putInt("param", param);
fragment.setArguments(args);
return fragment;
}
}
FragmentManager este o interfață folosită pentru
interacționarea cu fragmentele din cadrul unei activități
Orice operațiune, cum ar fi adăugare, înlocuire, eliminare sau căutare a
unui fragment, trebuie să fie făcută prin el
Pentru a obține un FragmentManager, activitatea trebuie să
derive din FragmentActivity
Acest lucru ne permite să apelăm:
getFragmentManager()
getSupportFragmentManager() – menține compatibilitatea cu versiunile vechi
folosind managerul de fragmente inclus în android.support.v4
Fragmentele încuibate pot fi gestionate prin
getChildFragmentManager()
Nu se poate încărca un layout într-un fragment dacă acest layout
conține <fragment>
Fragmentele încuibate sunt suportate doar dacă sunt adăugate la un
fragment în mod dinamic
Executarea unei metode a activității dintr-un fragment
Avem două opțiuni:
Implementarea unei metode publice în activitate:
((MyActivity)getActivity()).doSomething();
Implementarea în activitate a unei interfețe definite în fragment și instalarea instanței activității ca și un
listener pentru această interfață în fragment în timpul apelului metodei onAttach(Activity)
Pentru un alt scenariu, dacă nu avem instanțiat un fragment A într-o variabilă în
activitate, putem găsi fragmentul în manager
Folosind ID-ul container-ului sau a unui tag
FragmentManager fm = getSupportFragmentManager();
FragmentA fragmentA = fm.findFragmentById(R.id.fragment_container);
fragmentA.doSomething(params);
Un ultim scenariu ar fi cel în care din fragmentul B am dori să apelăm o metodă din
fragmentul A
FragmentManager fm = getActivity().getSupportFragmentManager();
FragmentA fragmentA = fm.findFragmentById(R.id.fragment_container);
fragmentA.doSomething(params);
Găsirea unui fragment prin FragmentManager este posibil
datorită unei stive de fragmente unde se pot adăuga și elimina
fragmente folosind tranzacții
Un fragment afișat dinamic se poate adăuga pe stivă sau nu
Având fragmentul pe stivă se permite navigarea înapoi la
fragmente precedente
Acest lucru se face apelând addToBackStack(String tag)
Tag-ul poate fi null sau un șir care ne va permite regăsirea acestui fragment

FragmentTransaction ft = getFragmentManager().beginTransaction();
ft.replace(R.id.simple_fragment, newFragment);
ft.addToBackStack(null);
ft.commit();
Aplicația va avea următoarele componente mari:
SplashActivity
MainActivity
4 fragmente:
ListFragment
ContactFragment
SettingsFragment
DetailsFragment
Pentru a implementa navigarea înapoi în DetailsFragment
Activitatea la apăsarea butonului Back înainte de a ieși din aplicație
(care este acțiunea implicită)
Dacă stiva de fragmente nu este goală, navigăm înapoi
Acest lucru se poate face suprascriind onKeyDown și tratarea navigării
între fragmente @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK && getSupportFragmentManager().
getBackStackEntryCount() > 1) {
getSupportFragmentManager().popBackStack();
return true;
SAU }
return super.onKeyDown(keyCode, event);
}

@Override

Suprascriind onBackPressed public void onBackPressed() {


int count = getSupportFragmentManager().getBackStackEntryCount();
if (count == 0) {
și tratarea navigării între fragmente super.onBackPressed();
} else {
getSupportFragmentManager().popBackStack();
}
}
Pentru exemplul folosit avem două modalități de a naviga între
fragmentele din MainActivity
Apăsând pe tab-uri
Cu glisare (swipe) între fragmente (include sincronizare între swipe și tab-uri)
ViewPager poate fi folosit pentru a glisa orice tip de vedere (view)
O galerie de imagini
Un tutorial la pornirea aplicației
Se pot folosi diverse librării terțe pentru a îmbunătăți experiența vizuală
cu tab-uri sau se pot crea chiar și manual pentru o customizare mai mare
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
ViewPager lucrează cu un adaptor
Adaptorul este elementul care gestionează crearea fiecărei
pagini care glisează
Pentru glisarea de fragmente există extensii ale clasei Adapter:
FragmentStatePagerAdapter
Salvează starea paginii
Îl distruge când nu apare pe pagină
Îl recreează când este necesar
FragmentPagerAdapter
Păstrează toate paginile în memorie
Nu are costurile de performanță asociate cu salvarea/recreerea paginilor
Numărul de pagini depinde de memoria disponibilă
public class MyPagerAdapter extends FragmentPagerAdapter {
public MyPagerAdapter(FragmentManager fm) {
super(fm);

Avem trei tab-uri fixe }


@Override
public Fragment getItem(int i) {
switch (i) {
Alegem FragmentPagerAdapter case 0 :
return new ListFragment();
MyPagerAdapter case 1 :
return new ContactFragment();
case 2 :
getCount() return new SettingsFragment();
default:
getItem(int i) }
return null;

}
@Override
public int getCount() {
return 3;
}
}

public class MainActivity extends FragmentActivity {


@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
MyPagerAdapter adapter = new MyPagerAdapter(getSupportFragmentManager());
ViewPager viewPager = (ViewPager) findViewById(R.id.pager);
viewPager.setAdapter(adapter);
}
}
Până acum putem glisa între fragmente
În continuare vom adăuga tab-uri
PagerTabStrip
PagerTitleStrip
Există o modalitate elegantă pentru a include PagerTabStrip în tag-ul
XML al ViewPager
Trebuie să adăugăm metoda getPageTitle în MyPagerAdapter
<?xml version="1.0" encoding="utf-8"?>
<android.support.v4.view.ViewPager
xmlns:android="http://schemas.android.com/apk/res/android" @Override
android:id="@+id/pager" public CharSequence getPageTitle(int position) {
android:layout_width="match_parent" switch (position) {
android:layout_height="wrap_content"> case 0 :
<android.support.v4.view.PagerTabStrip return "LIST";
android:id="@+id/pager_title_strip" case 1 :
android:layout_width="match_parent" return "CONTACT";
android:layout_height="wrap_content" case 2 :
android:layout_gravity="top" return "SETTINGS";
android:background="#33b5e5" default:
android:textColor="#fff" return null;
android:textSize="20dp" }
android:paddingTop="10dp" }
android:paddingBottom="10dp" />
</android.support.v4.view.ViewPager>
Tab-urile în Android au o istorie lungă
TabActivity – depreciat în API 13
FragmentTabHost – nu permitea icoane în tab-uri
ActionBar.Tab – apărut în API 11, depreciat în API 21
Se poate crea o implementare personalizată folosind:
LinearLayout
Elemente de tip Button
Se pot folosi librării terțe
ViewPagerIndicator https://github.com/JakeWharton/ViewPagerIndicator

PagerSlidingTabStrip https://github.com/astuetz/PagerSlidingTabStrip
Mici animații pot aduce un plus la o aplicație
Exemplul folosit are două tipuri de tranziții de ecran:
Tranziție între activități
SplashActivity  MainActivity
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);

@Override
public void onBackPressed() {
super.onBackPressed();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
}

Tranziție între fragmente


ListFragment  DetailsFragment
Trebuie să folosim obiectul FragmentTransaction
Folosind object animator, putem defini tranzițiile în fișiere XML
FragmentTransaction transaction = getFragmentManager().
beginTransaction();
transaction.setCustomAnimations(R.animator.enter, R.animator.exit);
transaction.replace(R.id.container, new DetailsFragment());
transaction.commit();

<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>


<!-- enter.xml --> <!-- exit.xml -->
<set> <set>
<objectAnimator <objectAnimator
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
android:duration="1000" android:duration="1000"
android:propertyName="y" android:propertyName="y"
android:valueFrom="2000" android:valueFrom="0"
android:valueTo="0" android:valueTo="-2000"
android:valueType="floatType" /> android:valueType="floatType" />
</set> </set>

Pentru versiunea Lollipop sau mai noi, se pot seta tranzițiile


direct pe fragment
Fragment f = new MyFragment();
f.setEnterTransition(new Slide(Gravity.RIGHT));
f.setExitTransition(new Slide(Gravity.LEFT));

S-ar putea să vă placă și