Documente Academic
Documente Profesional
Documente Cultură
Profesor
Tratarea imaginilor
Gestionarea memoriei
Îmbunătățirea UI
Folosire CardView
Atribute layout în timpul design-ului
Folosirea de fonturi personalizate
Concepte Material Design cu librăria suport pentru design
TabLayout
Toolbar, action bar și app bar
CoordinatorLayout
Navigarea în sus
Pentru a simula mai bine un panou publicitar cu notițe putem
folosi widget-ul CardView
<TextView
android:id="@+id/rowJobOfferTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
tools:text="Title of the job"
android:textColor="#555"
android:textSize="18sp"
android:layout_marginBottom="20dp"
/>
Pentru folosirea unor fonturi personalizate acestea trebuie
incluse în aplicație
Copiate în directorul /src/main/assets/fonts
Trebuie creat un obiect Typeface și setat pe componenta
dorită
Acest lucru ar trebui repetat pentru fiecare componentă
Duce la o degradare a performanțelor
Typeface type = Typeface.createFromAsset(getAssets(), "fonts/Roboto-Regular.ttf");
myTextView.setTypeface(type);
În fișierul xml de layout trebuie specificat font-ul dorit folosind atributul fontPath
(fără prefix). Pentru a nu primi atenționări se poate specifica atributul ignore
<TextView
android:text="@string/hello_world"
android:layout_width=“wrap_content“ android:layout_height="wrap_content“
...
fontPath="fonts/Roboto-Bold.ttf"
tools:ignore="MissingPrefix"
/>
În fiecare activitate unde dorim afișarea fontului implicit trebuie inclus următorul cod:
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}
<?xml version="1.0" encoding="utf-8"?> <TextView
<android.support.v7.widget.CardView android:id="@+id/rowJobOfferDesc"
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_marginTop="5dp"
xmlns:card_view="http://schemas.android.com/apk/res-auto" android:layout_width="fill_parent"
xmlns:tools="http://schemas.android.com/tools" android:layout_height="wrap_content"
android:orientation="vertical" android:layout_width="match_parent" tools:text="Description of the job"
android:layout_height="170dp" android:textColor="#999"
android:layout_margin="10dp" android:textSize="16sp"
card_view:cardElevation="4dp" android:ellipsize="marquee"
card_view:cardCornerRadius="4dp" fontPath=“fonts/daniel.ttf"
> tools:ignore=“MissingPrefix"
/>
<LinearLayout
android:orientation="vertical" </LinearLayout>
android:layout_width="wrap_content"
android:padding="15dp" <ImageView
android:layout_height="wrap_content"> android:layout_gravity="right"
android:layout_marginRight="20dp"
<TextView android:layout_width="100dp"
android:id="@+id/rowJobOfferTitle" android:layout_height="100dp"
android:layout_width="fill_parent" android:src="@drawable/pin"/>
android:layout_height="wrap_content"
tools:text="Title of the job" </android.support.v7.widget.CardView>
android:textColor="#555"
android:textSize="18sp"
android:layout_marginBottom="20dp“
fontPath=“fonts/danielbd.ttf"
tools:ignore=“MissingPrefix"
/>
https://developer.android.com/topic/libraries/support-library/packages#design
<android.support.design.widget.TabLayout
android:id="@+id/tab_layout"
android:layout_width="match_parent"
android:layout_height="50dp"/>
<android.support.v4.view.ViewPager
android:id="@+id/pager"
android:layout_width="match_parent"
android:layout_height="wrap_content">
</android.support.v4.view.ViewPager>
</LinearLayout>
@Override
protected void attachBaseContext(Context newBase) {
super.attachBaseContext(CalligraphyContextWrapper.wrap(newBase));
}
}
Action și app bar sunt una și aceeași componentă
App bar este doar un nume nou pentru action bar în material design
În aplicația exemplu, detaliile unei oferte vor fi afișate într-o activitate separată când
se dă click pe un card
Dacă descrierea este lungă, atunci va trebui să putem derula în jos pentru a citit tot
În acest moment logo-ul va fi redus, deoarece nu mai este relevant.
Când se revine la început, logo-ul va fi readus la dimensiunea originală
Pentru acest lucru avem nevoie de CollapsingToolbarLayout
<android.support.design.widget.AppBarLayout <TextView
android:id="@+id/appbar" android:id="@+id/rowJobOfferDesc"
android:layout_height="250dp" android:layout_width="fill_parent"
android:layout_width="match_parent" android:layout_height="fill_parent"
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar" android:text=“Long scrollable text ..."
android:fitsSystemWindows="true"> android:textColor="#999"
android:textSize="18sp"
<android.support.design.widget.CollapsingToolbarLayout />
android:id="@+id/collapsingtoolbar"
android:layout_width="match_parent" </android.support.v4.widget.NestedScrollView>
android:layout_height="match_parent"
android:fitsSystemWindows="true" </android.support.design.widget.CoordinatorLayout>
app:layout_scrollFlags="scroll|exitUntilCollapsed"
app:expandedTitleMarginStart="48dp"
app:expandedTitleMarginEnd="64dp"
app:contentScrim="?attr/colorPrimary">
<ImageView
android:id="@+id/logo"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scaleType="centerCrop"
android:src="@drawable/top_wooden_texture"
android:fitsSystemWindows="true"
app:layout_collapseMode="parallax" />
<android.support.v7.widget.Toolbar
app:popupTheme="@style/ThemeOverlay.AppCompat.Light"
android:id="@+id/toolbar"
android:layout_height="?attr/actionBarSize"
android:layout_width="match_parent"
app:layout_collapseMode="pin"/>
</android.support.design.widget.CollapsingToolbarLayout>
</android.support.design.widget.AppBarLayout>
Pentru a afișa detaliile trebuie să navigăm la OfferDetailActivity,
și să transmitem informațiile despre ofertă
public class MyViewHolder extends RecyclerView.ViewHolder public class OfferDetailActivity extends AppCompatActivity {
implements View.OnClickListener, View.OnLongClickListener{
@Override
public TextView textViewName; protected void onCreate(Bundle savedInstanceState) {
public TextView textViewDescription; super.onCreate(savedInstanceState);
setContentView(R.layout.activity_offer_detail);
public MyViewHolder(View v){
super(v); final Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
textViewName = (TextView)v.findViewById(R.id.rowJobOfferTitle); setSupportActionBar(toolbar);
textViewDescription = (TextView)v.findViewById(R.id.rowJobOfferDesc); getSupportActionBar().setDisplayHomeAsUpEnabled(true);
v.setOnClickListener(this);
v.setOnLongClickListener(this); String job_title = getIntent().getStringExtra("job_title");
}
CollapsingToolbarLayout collapsingToolbar =
@Override (CollapsingToolbarLayout) findViewById(R.id.collapsingtoolbar);
public void onClick(View view) { collapsingToolbar.setTitle(job_title);
Intent intent = new Intent(view.getContext(), OfferDetailActivity.class);
JobOffer selectedJobOffer = mOfferList.get(getPosition()); }
intent.putExtra("job_title", selectedJobOffer.getTitle());
intent.putExtra("job_description",selectedJobOffer.getDescription());
...
view.getContext().startActivity(intent);
}
new AsyncTask<Object,Void,Bitmap>(){
ImageView imageView;
String link;
Trebuie să ținem cont de faptul că nu @Override
putem să apelăm direct de pe firul protected Bitmap doInBackground(Object... params) {
imageView = (ImageView) params[0];
principal o metodă care face o link = (String) params[1];
}.execute(imageView,link);
}
Volley oferă două mecanisme pentru a prelua imagini
ImageRequest
ImageLoader
Tratează mai multe cereri, potrivit pentru listview
Poate folosi un mecanism pentru cache personalizat (memorie, disc)
Folosește un tip special de ImageView: NetworkImageView
public void displayImageWithVolley(final ImageView imageView, String url){ myNetworkImageView.setImageUrl(url, imageLoader);
myNetworkImageView.setDefaultImageResId(R.id.default_image);
ImageRequest request = new ImageRequest(url, myNetworkImageView.setErrorImageResId(R.id.image_not_found);
new Response.Listener<Bitmap>() {
@Override @Override
public void onResponse(Bitmap bitmap) { public void onCreate() {
imageView.setImageBitmap(bitmap); super.onCreate();
} sInstance = this;
}, 0, 0, null, mRequestQueue = Volley.newRequestQueue(this);
new Response.ErrorListener() { mImageLoader = new ImageLoader(mRequestQueue, new MyImageCache());
public void onErrorResponse(VolleyError error) {
//imageView.setImageResource(R.drawable.image_load_error);
}
});
MAApplication.getInstance().getRequestQueue().add(request);
}
public class LruBitmapCache extends LruCache<String, Bitmap> implements ImageLoader.ImageCache {
public LruBitmapCache(int maxSize) {
super(maxSize);
Alegerea }
cache este un }
@Override
proces manual și protected int sizeOf(String key, Bitmap value) {
return value.getRowBytes() * value.getHeight();
trebuie comunicat }
constructorului @Override
public Bitmap getBitmap(String url) {
return get(url);
ImageLoader }
@Override
public void putBitmap(String url, Bitmap bitmap) {
put(url, bitmap);
}
Modul de folosire:
Picasso.with(context)
.load(url)
.placeholder(R.drawable.user_placeholder)
.error(R.drawable.user_placeholder_error)
.into(imageView);
https://github.com/bumptech/glide
Modul de folosire
Utilizare simplă
Utilizare printr-un API fluent – necesită implementarea unui modul
care extinde AppGlideModule
În configurația de bază se poate apela Glide direct
Glide.with(fragment)
.load(myUrl)
.into(imageView);
Glide.with(fragment)
.clear(imageView);
Trebuie implementat un modul specific
package com.example.glidesample;
import com.bumptech.glide.annotation.GlideModule;
import com.bumptech.glide.module.AppGlideModule;
@GlideModule
public final class MyAppGlideModule extends AppGlideModule {}
https://bumptech.github.io/glide/doc/getting-started.html
Imaginile pot fi plasate în mai multe directoare, în funcție de
densitatea ecranului
ldpi, mdpi, hdpi, xxxhdpi, …
Această duplicare a imaginilor duce la creșterea dimensiunii APK
Cu introducerea graficelor vectoriale în Android 5.0 acest
neajuns poate fi eliminat
Acestea se pot scala fără pierdere de calitate
Necesită doar o singură copie
Definite la fel cum se definesc formele (shape) într-un xml
Exemplu:
<?xml version="1.0" encoding="utf-8"?>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:height="64dp" android:width="64dp"
android:viewportHeight="600" android:viewportWidth="600">
<group>
<path android:fillColor="@color/black_primary"
android:pathData="M12 36l17-12-17-12v24zm20-24v24h4V12h-4z" />
</group>
</vector>
https://developer.android.com/reference/android/graphics/drawable/AnimatedVectorDrawable
Este o imagine care poate fi redimensionată pe baza conținutului
Implică păstrarea unor zone fără expandare
Poate fi creat dintr-o imagine PNG
Este același PNG doar că i se adaugă câte un pixel pe fiecare parte
Conține informații despre care zone se pot extinde și care nu
Se salvează cu extensia .9.png folosind unealta draw9patch
Android Studio oferă unelte pentru profilarea aplicațiilor și măsurarea performanțelor,
respectiv a consumului de resurse:
CPU
Memorie
Trafic rețea
https://developer.android.com/studio/profile/memory-profiler
Întotdeauna este mai bine să se prevină decât să se caute
cauzele după aceea
Referințe la activitate și context
Aceste referințe sunt una dintre principalele cauze ale problemei
Este destul de frecvent cazul de a trimite o referință a activității la un listener
Dacă această referință este deținută într-un alt obiect, acest lucru va preveni
eliberarea activității de către GC
De exemplu la schimbarea orientării activitatea este creată din nou, iar
vechea activitate ar trebui distrusă
Deci țineți minte să vă ”dezabonați” de la listeneri în metoda onDestroy și să
urmăriți obiectele la care trimiteți contextul, căci acesta este o rerefință
puternică la activitate
Folosirea WeakReference
Implicit în Java când se creează un obiect, acesta este creat cu o referință
puternică
Obiectele diferite de null cu referințe puternice nu vor fi eliberate de GC
Un obiect care conține doar referințe slabe va fi eliberat în următorul ciclu
Același obiect poate avea mai multe referințe
Dacă avem nevoie de un obiect doar temporar, putem crea un referință slabă
Când toate referințele puternice la acel obiect sunt eliminate, acel obiect va fi
eliberat de GC
Exemplu, un popup are nevoie de o referință la un view ancoră,
dar odată afișat nu mai este nevoie de acesta:
private final WeakReference<View> mAnchorViewRef;