Come imitare le voci di listView stickey come sull'app Contatti di Lollipop?

background

Google ha recentemente pubblicato una nuova versione di Android, che ha un'applicazione di contatto che somiglia a questo:

immettere qui la descrizione dell'immagine

  • Comunicazione tra Android Java e Phonegap Javascript?
  • Come posso creare plugin / estensioni di applicazione Android che possono essere installati come APK o dal mercato?
  • Qual è la relazione tra APP_PLATFORM, android: minSdkVersion e android: targetSdkVersion?
  • Android-Blackberry GCM: Imansible registrare l'applicazione Android portta ai servizi GCM
  • Può la cartella Layout Android contenere sottocartelle?
  • Perché il mio BroadcastReceiver non funziona?
  • Il problema

    Ho bisogno di imitare questo tipo di elenco, ma non riesco a capire come farlo bene.

    Questo è costituito da 3 componenti principali:

    1. i titoli di stickey, che rimangono fino a quando il contatto superiore ha il nome che inizia con questa lettera.

    2. Le foto circolari o cerchio + lettera (per i contatti che non dispongono di foto)

    3. il PagerTitleStrip, che ha spaziatura uniforms tra gli elementi e cerca di mostrare tutti sullo schermo (o permettere di scorrere se necessario).

    Quello che ho trovato

    1. Ci sono molte librerie di terze parti, ma che gestiscono le intestazioni di stickey, che fanno parte degli elementi della list stessa. Non hanno il titolo a sinistra, ma in cima agli articoli.

      Qui, il lato sinistro si muove in modo diverso dal lato destro. Può bloccarsi, e se non c'è bisogno di attaccare (per esempio, la sezione ha una voce, ad esempio) scorre.

    2. Ho notato che per le foto circolari, posso usare la nuova class (?) Denominata " RoundedBitmapDrawableFactory " (che crea " RoundedBitmapDrawable "). Questo mi permetterà probabilmente di mettere una foto in forma circolare bene. Tuttavia, non funziona per le lettere (utilizzate per i contatti che non dispongono di una foto), ma penso di poter mettere un textView in cima e impostare lo background in un colore.

      Inoltre, ho notato che per usare bene il "RoundedBitmapDrawable" (per renderlo veramente circolare) devo fornirlo con una bitmap quadrata. Altrimenti avrà una forma strana.

    3. Ho provato a utilizzare " setTextSpacing " per minimizzare lo spazio tra gli elementi, ma non sembra funzionare. Non ho anche potuto trovare alcun modo per personalizzare / personalizzare il PagerTitleStrip per essere come nell'app contact.

      Ho anche provato a usare " PagerTabStrip ", ma anche non ho aiutato.

    La domanda

    Come puoi imitare il modo in cui Google ha implementato questa schermata?

    Più specificamente:

    1. Come posso fare il lato sinistro comportrsi come nell'app kontakt?

    2. È questo il modo migliore per implementare la foto circolare? Devo veramente ritagliare la bitmap in un quadrato prima di utilizzare il drawable speciale? Ci sono delle linee guida di progettazione per i colors da utilizzare? C'è un modo più ufficiale di utilizzare la cella di text cerchio?

    3. Come si stile il PagerTitleStrip per avere lo stesso aspetto e l'impressione dell'app contact?


    Progetto Github

    EDIT: per # 1 e # 2, ho fatto un progetto su Github, qui . Purtroppo ho anche trovato un bug importnte, quindi ho anche pubblicato qui .

    3 Solutions collect form web for “Come imitare le voci di listView stickey come sull'app Contatti di Lollipop?”

    ok, sono riuscito a risolvere tutti i problemi che ho scritto su:

    1. Ho cambiato il modo in cui funziona la libreria di terze parti (non ricordo where ho ottenuto la libreria, ma questo è molto simile), cambiando il layout di each row, in modo che l'intestazione sia sulla sinistra del contenuto stesso. È solo una questione di un file XML di layout e sei piuttosto fatto. Forse pubblicherò una bella biblioteca per entrambe le soluzioni.

    2.This è la vista che ho fatto. Non è un'attuazione ufficiale (non ho trovato alcuna), quindi ho fatto qualcosa da solo. Può essere più efficiente, ma alless è abbastanza facile da capire e anche abbastanza flessibile:

    public class CircularView extends ViewSwitcher { private ImageView mImageView; private TextView mTextView; private Bitmap mBitmap; private CharSequence mText; private int mBackgroundColor = 0; private int mImageResId = 0; public CircularView(final Context context) { this(context, null); } public CircularView(final Context context, final AttributeSet attrs) { super(context, attrs); addView(mImageView = new ImageView(context), new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER)); addView(mTextView = new TextView(context), new FrameLayout.LayoutParams(LayoutParams.MATCH_PARENT, LayoutParams.MATCH_PARENT, Gravity.CENTER)); mTextView.setGravity(Gravity.CENTER); if (isInEditMode()) setTextAndBackgroundColor("", 0xFFff0000); } @Override protected void onMeasure(final int widthMeasureSpec, final int heightMeasureSpec) { super.onMeasure(widthMeasureSpec, heightMeasureSpec); final int measuredWidth = getMeasuredWidth(); final int measuredHeight = getMeasuredHeight(); if (measuredWidth != 0 && measuredHeight != 0) drawContent(measuredWidth, measuredHeight); } @SuppressWarnings("deprecation") private void drawContent(final int measuredWidth, final int measuredHeight) { ShapeDrawable roundedBackgroundDrawable = null; if (mBackgroundColor != 0) { roundedBackgroundDrawable = new ShapeDrawable(new OvalShape()); roundedBackgroundDrawable.getPaint().setColor(mBackgroundColor); roundedBackgroundDrawable.setIntrinsicHeight(measuredHeight); roundedBackgroundDrawable.setIntrinsicWidth(measuredWidth); roundedBackgroundDrawable.setBounds(new Rect(0, 0, measuredWidth, measuredHeight)); } if (mImageResId != 0) { mImageView.setBackgroundDrawable(roundedBackgroundDrawable); mImageView.setImageResource(mImageResId); mImageView.setScaleType(ScaleType.CENTER_INSIDE); } else if (mText != null) { mTextView.setText(mText); mTextView.setBackgroundDrawable(roundedBackgroundDrawable); // mTextView.setPadding(0, measuredHeight / 4, 0, measuredHeight / 4); mTextView.setTextSize(measuredHeight / 5); } else if (mBitmap != null) { mImageView.setScaleType(ScaleType.FIT_CENTER); mImageView.setBackgroundDrawable(roundedBackgroundDrawable); mBitmap = ThumbnailUtils.extractThumbnail(mBitmap, measuredWidth, measuredHeight); final RoundedBitmapDrawable roundedBitmapDrawable = RoundedBitmapDrawableFactory.create(getResources(), mBitmap); roundedBitmapDrawable.setCornerRadius((measuredHeight + measuredWidth) / 4); mImageView.setImageDrawable(roundedBitmapDrawable); } resetValuesState(false); } public void setTextAndBackgroundColor(final CharSequence text, final int backgroundColor) { resetValuesState(true); while (getCurrentView() != mTextView) showNext(); this.mBackgroundColor = backgroundColor; mText = text; final int height = getHeight(), width = getWidth(); if (height != 0 && width != 0) drawContent(width, height); } public void setImageResource(final int imageResId, final int backgroundColor) { resetValuesState(true); while (getCurrentView() != mImageView) showNext(); mImageResId = imageResId; this.mBackgroundColor = backgroundColor; final int height = getHeight(), width = getWidth(); if (height != 0 && width != 0) drawContent(width, height); } public void setImageBitmap(final Bitmap bitmap) { setImageBitmapAndBackgroundColor(bitmap, 0); } public void setImageBitmapAndBackgroundColor(final Bitmap bitmap, final int backgroundColor) { resetValuesState(true); while (getCurrentView() != mImageView) showNext(); this.mBackgroundColor = backgroundColor; mBitmap = bitmap; final int height = getHeight(), width = getWidth(); if (height != 0 && width != 0) drawContent(width, height); } private void resetValuesState(final boolean alsoResetViews) { mBackgroundColor = mImageResId = 0; mBitmap = null; mText = null; if (alsoResetViews) { mTextView.setText(null); mTextView.setBackgroundDrawable(null); mImageView.setImageBitmap(null); mImageView.setBackgroundDrawable(null); } } public ImageView getImageView() { return mImageView; } public TextView getTextView() { return mTextView; } } 

    3. Ho trovato una bella libreria che lo fa, chiamata PagerSlidingTabStrip . Non ha trovato un modo ufficiale di stilare il nativo, però.

    Un altro modo è guardare il campione di Google che è disponibile proprio all'interno di Android-Studio e viene chiamato "SlidingTabLayout". Mostra come è fatto.

    EDIT: una biblioteca migliore per # 3 è qui , chiamata anche "PagerSlidingTabStrip".

    È ansible effettuare le seguenti operazioni:

    1. Sul lato sinistro del tuo RecyclerView, crea un TextView che contiene l'indice di lettera;
    2. Nella parte superiore della vista Recycler (nel layout che lo avvolge) metta un TextView per coprire quello creato nel passaggio 1, questo sarà quello appiccicoso;
    3. Aggiungere un OnScrollListener nel tuo RecyclerView. Su metodo onScrolled (), impostare il TextView creato nel passaggio 2 per il text di riferimento prelevato da firstVisibleRow. Fin qui si avrà un indice stiky, senza gli effetti della transizione;
    4. Per aggiungere l'effetto di transizione in output / in output, sviluppare una logica che controlla se l'elemento precedente del currentFirstVisibleItem è l'ultimo della list di lettere precedente o se il secondVisibleItem è la prima della nuova lettera. Sulla base di queste informazioni, rendere l'indice appiccicoso visibile / invisibile e l'indice di row l'opposto, aggiungendo in questo ultimo l'effetto alfa.

        if (recyclerView != null) { View firstVisibleView = recyclerView.getChildAt(0); View secondVisibleView = recyclerView.getChildAt(1); TextView firstRowIndex = (TextView) firstVisibleView.findViewById(R.id.sticky_row_index); TextView secondRowIndex = (TextView) secondVisibleView.findViewById(R.id.sticky_row_index); int visibleRange = recyclerView.getChildCount(); int actual = recyclerView.getChildPosition(firstVisibleView); int next = actual + 1; int previous = actual - 1; int last = actual + visibleRange; // RESET STICKY LETTER INDEX stickyIndex.setText(String.valueOf(getIndexContext(firstRowIndex)).toUpperCase()); stickyIndex.setVisibility(TextView.VISIBLE); if (dy > 0) { // USER SCROLLING DOWN THE RecyclerView if (next <= last) { if (isHeader(firstRowIndex, secondRowIndex)) { stickyIndex.setVisibility(TextView.INVISIBLE); firstRowIndex.setVisibility(TextView.VISIBLE); firstRowIndex.setAlpha(1 - (Math.abs(firstVisibleView.getY()) / firstRowIndex.getHeight())); secondRowIndex.setVisibility(TextView.VISIBLE); } else { firstRowIndex.setVisibility(TextView.INVISIBLE); stickyIndex.setVisibility(TextView.VISIBLE); } } } else { // USER IS SCROLLING UP THE RecyclerVIew if (next <= last) { // RESET FIRST ROW STATE firstRowIndex.setVisibility(TextView.INVISIBLE); if ((isHeader(firstRowIndex, secondRowIndex) || (getIndexContext(firstRowIndex) != getIndexContext(secondRowIndex))) && isHeader(firstRowIndex, secondRowIndex)) { stickyIndex.setVisibility(TextView.INVISIBLE); firstRowIndex.setVisibility(TextView.VISIBLE); firstRowIndex.setAlpha(1 - (Math.abs(firstVisibleView.getY()) / firstRowIndex.getHeight())); secondRowIndex.setVisibility(TextView.VISIBLE); } else { secondRowIndex.setVisibility(TextView.INVISIBLE); } } } if (stickyIndex.getVisibility() == TextView.VISIBLE) { firstRowIndex.setVisibility(TextView.INVISIBLE); } } 

    Ho sviluppato un componente che fa la logica di cui sopra, può essere trovato qui: https://github.com/edsilfer/sticky-index

    L'Android è un fan Android di Google, tutto su telefoni Android, Android Wear, Android Dev e applicazioni Android Games e così via.