web-dev-qa-db-fra.com

Obtenir un élément central visible de RecycleView lors du défilement

C'est ce que je veux:

 enter image description here

En tant qu'image ci-dessus, je veux tracer une ligne centrale sur RecycleView, puis obtenir l'élément central lors du défilement (ainsi que pour le déplacement à gauche ou à droite).
Voici mon tentative de dessiner une RecycleView horizontale:

    HorizontalAdapter adapter = new HorizontalAdapter(data);
    LinearLayoutManager layoutManager
            = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false);
    recycleView.setLayoutManager(layoutManager);
    recycleView.setAdapter(adapter);

Est-il possible de savoir quel élément est déplacé vers le centre de RecycleView? Et comment puis-je faire défiler RecycleView à gauche ou à droite d’une position?

Update: J'ai essayé d'utiliser un écouteur à défilement pour obtenir la position médiane, mais cela ne fonctionne pas comme un aspect. 

  @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
            super.onScrolled(recyclerView, dx, dy);
            int firstPos = layoutManager.findFirstVisibleItemPosition();
            int lastPos = layoutManager.findLastVisibleItemPosition();
            int middle = Math.abs(lastPos - firstPos) / 2 + firstPos;

            int selectedPos = -1;
            for (int i = 0; i < adapter.getItemCount(); i++) {
                if (i == middle) {
                    adapter.getItem(i).setSelected(true);
                    selectedPos = i;
                } else {
                    adapter.getItem(i).setSelected(false);
                }
            }

            adapter.notifyDataSetChanged();
        }

Et obtenez le résultat:

 enter image description here

Je veux seulement changer l'item selecte (faire du texte en blanc) quand il est sur le bleu Rect

43
R4j

J'ai fait quelque chose comme ça. Je peux faire exactement ce dont vous avez besoin ... Tout d'abord, voici comment se passe mon travail sur l'algorithme enter image description here

Ceci est mon adaptateur recyclerView

public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
private ArrayList<LabelerDate> dateDataList;


private static final int VIEW_TYPE_PADDING = 1;
private static final int VIEW_TYPE_ITEM = 2;
private int paddingWidthDate = 0;

private int selectedItem = -1;

public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
    this.dateDataList = dateData;
    this.paddingWidthDate = paddingWidthDate;

}


@Override
public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
    if (viewType == VIEW_TYPE_ITEM) {
        final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_date,
                parent, false);
        return new DateViewHolder(view);
    } else {
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.list_item_padding,
                parent, false);

        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
        layoutParams.width = paddingWidthDate;
        view.setLayoutParams(layoutParams);
        return new DateViewHolder(view);
    }
}

@Override
public void onBindViewHolder(DateViewHolder holder, int position) {
    LabelerDate labelerDate = dateDataList.get(position);
    if (getItemViewType(position) == VIEW_TYPE_ITEM) {
        if(labelerDate.dateType.equals(BirthDayActivity.DateType.C31))
                holder.tvDate.setText(String.valueOf(labelerDate.valueDate));
                holder.tvDate.setVisibility(View.VISIBLE);
                holder.imgSmall.setVisibility(View.VISIBLE);

        if (position == selectedItem) {
            holder.tvDate.setTextColor(Color.parseColor("#094673"));
            holder.tvDate.setTextSize(35);
            holder.imgSmall.setBackgroundResource(R.color.textviewbold);

        } else {
            holder.tvDate.setTextColor(Color.GRAY);
            holder.tvDate.setTextSize(35);
            holder.imgSmall.setBackgroundResource(R.color.gray);
        }
    }
}

public void setSelecteditem(int selecteditem) {
    this.selectedItem = selecteditem;
    notifyDataSetChanged();
}

@Override
public int getItemCount() {
    return dateDataList.size();
}

@Override
public int getItemViewType(int position) {
    LabelerDate labelerDate = dateDataList.get(position);
    if (labelerDate.dateType.equals(BirthDayActivity.DateType.NONE)) {
        return VIEW_TYPE_PADDING;
    }
    return VIEW_TYPE_ITEM;
}


public class DateViewHolder extends RecyclerView.ViewHolder {
    public TextView tvDate;
    public ImageView imgSmall;

    public DateViewHolder(View itemView) {
        super(itemView);
        tvDate = (TextView) itemView.findViewById(R.id.tvNumberDate);
        imgSmall = (ImageView) itemView.findViewById(R.id.small_marked_dob);
    }
}}

C'est l'algorithme le plus important:

public void getRecyclerviewDate() {
    recyclerViewDate = (RecyclerView) findViewById(R.id.recyclerViewDay);
    ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
    vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
            finalWidthDate = recyclerViewDate.getMeasuredWidth();
            itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
            paddingDate = (finalWidthDate - itemWidthDate) / 2;
            firstItemWidthDate = paddingDate ;
            allPixelsDate = 0;

            final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
            dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
            recyclerViewDate.setLayoutManager(dateLayoutManager);
            recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
                @Override
                public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                    super.onScrollStateChanged(recyclerView, newState);
                    synchronized (this) {
                         if(newState == RecyclerView.SCROLL_STATE_IDLE){           
                            calculatePositionAndScrollDate(recyclerView);
                        }
                    }

                }

                @Override
                public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                    super.onScrolled(recyclerView, dx, dy);
                    allPixelsDate += dx;
                }
            });
            if (labelerDates == null)
                labelerDates = new ArrayList<>();
            labelerDates.addAll(genLabelerDate(currentMonth, currentYear));
            dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
            recyclerViewDate.setAdapter(dateAdapter);
            return true;
        }
    });
}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/

private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
    int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);

    if (expectedPositionDate == -1) {
        expectedPositionDate = 0;
    } else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
        expectedPositionDate--;
    }
    scrollListToPositionDate(recyclerView, expectedPositionDate);

}
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
    float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
    float missingPxDate = targetScrollPosDate - allPixelsDate;
    if (missingPxDate != 0) {
        recyclerView.smoothScrollBy((int) missingPxDate, 0);
    }
}
private void setDateValue() {
    int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
    setColorDate = expectedPositionDateColor + 1;
    //set color here
    dateAdapter.setSelecteditem(setColorDate);
}
 @Override
protected void onRestoreInstanceState(@NonNull Bundle savedInstanceState) {
    super.onRestoreInstanceState(savedInstanceState);   
    allPixelsDate = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE);
    allPixelsDateChanged = savedInstanceState.getFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED);
}

@Override
protected void onSaveInstanceState(@NonNull Bundle outState) {
    super.onSaveInstanceState(outState);
    outState.putFloat(BUNDLE_LIST_PIXELS_DATE, allPixelsDate);
    outState.putFloat(BUNDLE_LIST_PIXELS_DATE_CHANGED, allPixelsDateChanged);
}

Et voici mon résultat: enter image description here

Regardez cette vidéo link , ceci est ma démo d'application

44
TranHieu

Il est parfois nécessaire d’exploiter l’ensemble du bloc de code exemple, car il se peut que nous manquions quelque chose. Voici ce que j'ai, n'hésitez pas à corriger n'importe quoi puisque je peux faire une petite erreur quelque part. Et oui, cette réponse est une extension de la réponse @tranhieu. Merci @tranhieu.

MainActivity.Java

package com.test;

import Android.app.Activity;
import Android.graphics.Color;
import Android.support.annotation.Nullable;
import Android.support.v7.app.AppCompatActivity;
import Android.os.Bundle;
import Android.support.v7.widget.LinearLayoutManager;
import Android.support.v7.widget.RecyclerView;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
import Android.view.ViewTreeObserver;
import Android.widget.TextView;

import Java.util.ArrayList;

public class MainActivity extends Activity {

    private static final String TAG = MainActivity.class.getSimpleName();

    public float firstItemWidthDate;
    public float paddingDate;
    public float itemWidthDate;
    public int allPixelsDate;
    public int finalWidthDate;
    private DateAdapter dateAdapter;
    private ArrayList<LabelerDate> labelerDates = new ArrayList<>();

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

    }


    public void getRecyclerviewDate() {
        final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
        if (recyclerViewDate != null) {
            recyclerViewDate.postDelayed(new Runnable() {
                @Override
                public void run() {
                    setDateValue();
                }
            }, 300);
            recyclerViewDate.postDelayed(new Runnable() {
                @Override
                public void run() {
                    recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
                    setDateValue();
                }
            }, 5000);
        }
        ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
        vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {


            @Override
            public boolean onPreDraw() {
                recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
                finalWidthDate = recyclerViewDate.getMeasuredWidth();
                itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
                paddingDate = (finalWidthDate - itemWidthDate) / 2;
                firstItemWidthDate = paddingDate;
                allPixelsDate = 0;

                final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
                dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
                recyclerViewDate.setLayoutManager(dateLayoutManager);
                recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
                        super.onScrollStateChanged(recyclerView, newState);
                        synchronized (this) {
                            if (newState == RecyclerView.SCROLL_STATE_IDLE) {
                                calculatePositionAndScrollDate(recyclerView);
                            }
                        }

                    }

                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        super.onScrolled(recyclerView, dx, dy);
                        allPixelsDate += dx;
                    }
                });
                if (labelerDates == null) {
                    labelerDates = new ArrayList<>();
                }
                genLabelerDate();
                dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
                recyclerViewDate.setAdapter(dateAdapter);
                dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
                return true;
            }
        });
    }

    private void genLabelerDate() {
        for (int i = 0; i < 32; i++) {
            LabelerDate labelerDate = new LabelerDate();
            labelerDate.setNumber(Integer.toString(i));
            labelerDates.add(labelerDate);

            if (i == 0 || i == 31) {
                labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
            } else {
                labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
            }
        }
    }
/* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/

    private void calculatePositionAndScrollDate(RecyclerView recyclerView) {
        int expectedPositionDate = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);

        if (expectedPositionDate == -1) {
            expectedPositionDate = 0;
        } else if (expectedPositionDate >= recyclerView.getAdapter().getItemCount() - 2) {
            expectedPositionDate--;
        }
        scrollListToPositionDate(recyclerView, expectedPositionDate);

    }

    /* this if most important, if expectedPositionDate < 0 recyclerView will return to nearest item*/
    private void scrollListToPositionDate(RecyclerView recyclerView, int expectedPositionDate) {
        float targetScrollPosDate = expectedPositionDate * itemWidthDate + firstItemWidthDate - paddingDate;
        float missingPxDate = targetScrollPosDate - allPixelsDate;
        if (missingPxDate != 0) {
            recyclerView.smoothScrollBy((int) missingPxDate, 0);
        }
        setDateValue();
    }

    //
    private void setDateValue() {
        int expectedPositionDateColor = Math.round((allPixelsDate + paddingDate - firstItemWidthDate) / itemWidthDate);
        int setColorDate = expectedPositionDateColor + 1;
//        set color here
        dateAdapter.setSelecteditem(setColorDate);
    }


    public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
        private ArrayList<LabelerDate> dateDataList;


        private static final int VIEW_TYPE_PADDING = 1;
        private static final int VIEW_TYPE_ITEM = 2;
        private int paddingWidthDate = 0;

        private int selectedItem = -1;

        public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
            this.dateDataList = dateData;
            this.paddingWidthDate = paddingWidthDate;

        }


        @Override
        public DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            if (viewType == VIEW_TYPE_ITEM) {
                final View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
                    parent, false);
                return new DateViewHolder(view);
            } else {
                View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item,
                    parent, false);

                RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
                layoutParams.width = paddingWidthDate;
                view.setLayoutParams(layoutParams);
                return new DateViewHolder(view);
            }
        }

        @Override
        public void onBindViewHolder(DateViewHolder holder, int position) {
            LabelerDate labelerDate = dateDataList.get(position);
            if (getItemViewType(position) == VIEW_TYPE_ITEM) {
                holder.tvDate.setText(labelerDate.getNumber());
                holder.tvDate.setVisibility(View.VISIBLE);

                Log.d(TAG, "default " + position + ", selected " + selectedItem);
                if (position == selectedItem) {
                    Log.d(TAG, "center" + position);
                    holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
                    holder.tvDate.setTextSize(35);

                } else {
                    holder.tvDate.setTextColor(Color.WHITE);
                    holder.tvDate.setTextSize(18);
                }
            } else {
                holder.tvDate.setVisibility(View.INVISIBLE);
            }
        }

        public void setSelecteditem(int selecteditem) {
            this.selectedItem = selecteditem;
            notifyDataSetChanged();
        }

        @Override
        public int getItemCount() {
            return dateDataList.size();
        }

        @Override
        public int getItemViewType(int position) {
            LabelerDate labelerDate = dateDataList.get(position);
            if (labelerDate.getType() == VIEW_TYPE_PADDING) {
                return VIEW_TYPE_PADDING;
            } else {
                return VIEW_TYPE_ITEM;
            }

        }


        public class DateViewHolder extends RecyclerView.ViewHolder {
            public TextView tvDate;

            public DateViewHolder(View itemView) {
                super(itemView);
                tvDate = (TextView) itemView.findViewById(R.id.txt_date);
            }
        }
    }

    private class LabelerDate {
        private int type;
        private String number;

        public String getNumber() {
            return number;
        }

        public void setNumber(String number) {
            this.number = number;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }
    }
}

activity_main.xml

<?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">

    <FrameLayout
        Android:layout_width="match_parent"
        Android:layout_height="wrap_content">

        <Android.support.v7.widget.RecyclerView
            Android:id="@+id/rv_tasks_date"
            Android:layout_width="match_parent"
            Android:layout_height="48dp" />

        <ImageView
            Android:layout_width="48dp"
            Android:layout_height="48dp"
            Android:layout_gravity="center"
            Android:layout_marginTop="48dp"
            Android:src="@Android:drawable/ic_dialog_info" />
    </FrameLayout>

</LinearLayout>

item.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:Android="http://schemas.Android.com/apk/res/Android"
              Android:orientation="vertical" Android:layout_width="wrap_content"
              Android:layout_height="match_parent">

    <TextView
        Android:id="@+id/txt_date"
        Android:layout_width="@dimen/item_dob_width"
        Android:layout_height="48dp"
        Android:text="32"
        Android:textColor="@Android:color/white"
        Android:background="@Android:color/darker_gray"
        Android:textSize="28sp"
        Android:gravity="center"/>

</LinearLayout>

dimens.xml

<resources>
    <dimen name="item_dob_width">100dp</dimen>
</resources>
10
silentsudo

J'ai utilisé le SnapHelper juste ici:

    // init snaphelper        
    SnapHelper snapHelper = new LinearSnapHelper();
    snapHelper.attachToRecyclerView(recyclerView)

    // init layout manager
    LinearLayoutManager layoutManager = new LinearLayoutManager(mainActivity);
    layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
    recyclerView.setLayoutManager(layoutManager);

    // init adapter
    adatper.setSnapHelper(snapHelper);
    adatper.setLayoutManager(layoutManager);

    adatper.initAdapter(new Float((DisplayHelper.getDisplayWidth(mainActivity) / 2) - (fooViewWidth / 2)).intValue());
    recyclerView.setAdapter(adatper);

Comme l'a dit TranHieu, la solution consistant à insérer 2 éléments pour le rembourrage (aux positions de départ et d'arrivée) est bonne.

Je n'aime pas l'utilisation de ViewTreeObserver en raison de la mauvaise lisibilité du code. Avec cette technique, vous devez également gérer le redessinage des éléments s'ils sont recyclés.

Si vous utilisez des classes customview, vous pouvez définir sa largeur directement dans ces classes.

Par exemple c'est ma classe de rembourrage

/**
 * Created by firegloves on 25/09/15.
 */
@EViewGroup(R.layout.view_padding)
public class PaddingView extends FooView {

    Context mCtx;

    public PaddingView(Context context) {
        super(context);
        mCtx = context;
    }


    public void setWidth(int width) {
        setLayoutParams(new LayoutParams(width, ViewGroup.LayoutParams.WRAP_CONTENT));
    }

}

Dans mon adaptateur, je stocke la largeur d'élément de remplissage souhaitée, qui est égale à (displayWidth/2) - (realItemWidth/2)

Ceci est mon adaptateur, ne regardez pas les méthodes ne correspondant pas à RecyclerView.Adapter, faites attention à la méthode initAdapter et à la méthode onCreateItemView

@EBean
public class FooAdapterRecycler extends RecyclerViewAdapterBase<Foo, FooView> {

    private final int TYPE_PADDING_VIEW = 0;
    private final int TYPE_REAL_VIEW = 1;

    @RootContext
    Context ctx;
    @Bean(Finder.class)
    IFinder Finder;

    SnapHelper snapHelper;
    RecyclerView.LayoutManager layoutManager;

    private int paddingWidth = 0;

    /**
     * preleva i dati dal Finder
     */
    public void initAdapter(int paddingWidth) {

        /*******************************
         * THIS CODE IS THE IMPORTANT ONE
         ******************************/

        this.paddingWidth = paddingWidth;

        // add 1 item for initial space
        mItems = new ArrayList<>();
        Foo foo = new Foo();
        mItems.add(foo);

        // get real items from Finder
        mItems.addAll(Finder.findAll());

        // add 1 item for final space
        mItems = new ArrayList<>();
        Foo foo2 = new Foo();
        mItems.add(foo2);

    }


    @Override
    public int getItemViewType(int position) {
        if (position == 0 || position == getItemCount()-1) {
            return TYPE_PADDING_VIEW;
        } else {
            return TYPE_REAL_VIEW;
        }
    }

    @Override
    protected FooView onCreateItemView(ViewGroup parent, int viewType) {

        /*******************************
         * THIS CODE IS THE IMPORTANT ONE
         ******************************/

        if (viewType == TYPE_PADDING_VIEW) {
            PaddingView view = PaddingView_.build(ctx);
            view.setWidth(paddingWidth);
            return view;
        } else {
            return FooView_.build(ctx);
        }
    }

    public void setSnapHelper(SnapHelper snapHelper) {
        this.snapHelper = snapHelper;
    }

    public void setLayoutManager(RecyclerView.LayoutManager layoutManager) {
        this.layoutManager = layoutManager;
    }
}

J'utilise la bibliothèque AndroidAnnotations mais ce n'est pas obligatoire

J'espère que cela pourra aider

5
firegloves

Comme mentionné dans l’autre réponse, il n’existe aucun moyen direct de procéder.

C'est probablement ainsi que vous pourrez réaliser ce que vous avez décrit dans la question.

  1. Connaître le nombre d'éléments visibles à l'écran.
  2. Sélectionnez l'élément du milieu par programmation à chaque défilement de la vue.
  3. Conservez une image partiellement transparente en superposition sur l'élément central de la vue de recyclage. (Vous aurez besoin de calculer les coordonnées en fonction de la largeur de la vue du recycleur ou de la largeur de l'écran et de la largeur de l'image de surimpression que vous choisissez de placer.
  4. Actualisez la valeur sélectionnée dans une vue de texte sous la vue du recycleur chaque fois qu'il y a un défilement.

Les superpositions d'image doivent être placées de manière à ce qu'elles apparaissent connectées et sous la forme d'un contrôle unique.

Pour utiliser cette fonction, utilisez la bibliothèque EcoGallery: https://github.com/falnatsheh/EcoGallery

1
user3579059

UTILISATION DE SNAPHELPER - UNE SOLUTION PLUS DOUCE

Voici une autre solution utilisant SnapHelper. À partir de la réponse de @TranHieu ici:

https://stackoverflow.com/a/34647005/3944251

et le compressé par @ sector11 ici:

https://stackoverflow.com/a/38411582/3944251

J'ai écrit le code suivant qui est également basé sur les deux réponses ci-dessus, mais il est plus simple et offre une solution plus fluide à l'aide de SnapHelper présentée dans bibliothèque de support Android 24.2.0

Ici vous avez la classe MainActivity. Le reste est le même avec la réponse de @ sector11.

import Android.graphics.Color;
import Android.os.Bundle;
import Android.support.annotation.Nullable;
import Android.support.v7.app.AppCompatActivity;
import Android.support.v7.widget.LinearLayoutManager;
import Android.support.v7.widget.LinearSnapHelper;
import Android.support.v7.widget.RecyclerView;
import Android.util.Log;
import Android.view.LayoutInflater;
import Android.view.View;
import Android.view.ViewGroup;
import Android.view.ViewTreeObserver;
import Android.widget.TextView;

import Java.util.ArrayList;

public class MainActivity extends AppCompatActivity {

    private static final String TAG = MainActivity.class.getSimpleName();

    public float firstItemWidthDate;
    public float itemWidthDate;
    public int allPixelsDate;
    public int finalWidthDate;
    private DateAdapter dateAdapter;
    private ArrayList<LabelerDate> labelerDates;

    @Override
    protected void onCreate(@Nullable Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        labelerDates = new ArrayList<>();
        getRecyclerviewDate();

    }


    public void getRecyclerviewDate() {
        final RecyclerView recyclerViewDate = (RecyclerView) findViewById(R.id.rv_tasks_date);
        recyclerViewDate.postDelayed(new Runnable() {
            @Override
            public void run() {
                //recyclerViewDate.smoothScrollToPosition(dateAdapter.getItemCount()-1);
                setDateValue();
            }
        }, 300);
        ViewTreeObserver vtoDate = recyclerViewDate.getViewTreeObserver();
        vtoDate.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {

            @Override
            public boolean onPreDraw() {
                recyclerViewDate.getViewTreeObserver().removeOnPreDrawListener(this);
                finalWidthDate = recyclerViewDate.getMeasuredWidth();
                itemWidthDate = getResources().getDimension(R.dimen.item_dob_width);
                firstItemWidthDate = (finalWidthDate - itemWidthDate) / 2;
                allPixelsDate = 0;

                final LinearLayoutManager dateLayoutManager = new LinearLayoutManager(getApplicationContext());
                dateLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL);
                recyclerViewDate.setLayoutManager(dateLayoutManager);

                /* Create a LinearSnapHelper and attach the recyclerView to it. */
                final LinearSnapHelper snapHelper = new LinearSnapHelper();
                snapHelper.attachToRecyclerView(recyclerViewDate);

                recyclerViewDate.addOnScrollListener(new RecyclerView.OnScrollListener() {
                    @Override
                    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
                        allPixelsDate += dx;
                        recyclerView.post(new Runnable() {
                            public void run() {
                                setDateValue();
                            }
                        });
                    }
                });

                genLabelerDate();
                dateAdapter = new DateAdapter(labelerDates, (int) firstItemWidthDate);
                recyclerViewDate.setAdapter(dateAdapter);
                dateAdapter.setSelecteditem(dateAdapter.getItemCount() - 1);
                return true;
            }
        });
    }

    private void genLabelerDate() {
        for (int i = 0; i < 32; i++) {
            LabelerDate labelerDate = new LabelerDate();
            labelerDate.setNumber(Integer.toString(i));
            labelerDates.add(labelerDate);

            if (i == 0 || i == 31) {
                labelerDate.setType(DateAdapter.VIEW_TYPE_PADDING);
            } else {
                labelerDate.setType(DateAdapter.VIEW_TYPE_ITEM);
            }
        }
    }

    //
    private void setDateValue() {
        int expectedPositionDateColor = Math.round(allPixelsDate / itemWidthDate);
        int setColorDate = expectedPositionDateColor + 1;
//        set color here
        dateAdapter.setSelecteditem(setColorDate);
    }


    public class DateAdapter extends RecyclerView.Adapter<DateAdapter.DateViewHolder> {
        private ArrayList<LabelerDate> dateDataList;


        private static final int VIEW_TYPE_PADDING = 1;
        private static final int VIEW_TYPE_ITEM = 2;
        private int paddingWidthDate = 0;

        private int selectedItem = -1;

        public DateAdapter(ArrayList<LabelerDate> dateData, int paddingWidthDate) {
            this.dateDataList = dateData;
            this.paddingWidthDate = paddingWidthDate;

        }


        @Override
        public DateAdapter.DateViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, parent, false);
            if (viewType == VIEW_TYPE_PADDING) {
                RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) view.getLayoutParams();
                layoutParams.width = paddingWidthDate;
                view.setLayoutParams(layoutParams);
            }
            return new DateViewHolder(view);
        }

        @Override
        public void onBindViewHolder(DateAdapter.DateViewHolder holder, int position) {
            LabelerDate labelerDate = dateDataList.get(position);
            if (getItemViewType(position) == VIEW_TYPE_ITEM) {
                holder.tvDate.setText(labelerDate.getNumber());
                holder.tvDate.setVisibility(View.VISIBLE);

                Log.d(TAG, "default " + position + ", selected " + selectedItem);
                if (position == selectedItem) {
                    Log.d(TAG, "center" + position);
                    holder.tvDate.setTextColor(Color.parseColor("#76FF03"));
                    holder.tvDate.setTextSize(35);

                } else {
                    holder.tvDate.setTextColor(Color.WHITE);
                    holder.tvDate.setTextSize(18);
                }
            } else {
                holder.tvDate.setVisibility(View.INVISIBLE);
            }
        }

        public void setSelecteditem(int selecteditem) {
            this.selectedItem = selecteditem;
            notifyDataSetChanged();
        }

        @Override
        public int getItemCount() {
            return dateDataList.size();
        }

        @Override
        public int getItemViewType(int position) {
            LabelerDate labelerDate = dateDataList.get(position);
            if (labelerDate.getType() == VIEW_TYPE_PADDING) {
                return VIEW_TYPE_PADDING;
            } else {
                return VIEW_TYPE_ITEM;
            }

        }


        public class DateViewHolder extends RecyclerView.ViewHolder {
            public TextView tvDate;

            public DateViewHolder(View itemView) {
                super(itemView);
                tvDate = (TextView) itemView.findViewById(R.id.txt_date);
            }
        }
    }

    private class LabelerDate {
        private int type;
        private String number;

        public String getNumber() {
            return number;
        }

        public void setNumber(String number) {
            this.number = number;
        }

        public int getType() {
            return type;
        }

        public void setType(int type) {
            this.type = type;
        }
    }
}
1
Dionis Beqiraj

J'ai utilisé une autre approche dans mon cas.

vous pouvez trouver les deatils ici: RecyclerView - Comment mettre en évidence un élément central visible pendant le défilement1

À mon avis, ma solution est plus simple que les autres.

0
eldivino87

Oh mec. Je cherche cette réponse depuis presque une semaine et j'ai ensuite trouvé la solution. Personnalisé LayoutManagers? Non ItemDecorator? Nan. 

Voici le moyen le plus simple de le faire:

<Android.support.v7.widget.RecyclerView
    Android:layout_width="match_parent"
    Android:layout_height="wrap_content"
    Android:layout_gravity="center_horizontal"
    Android:paddingStart="150dp"
    Android:paddingEnd="150dp"
    Android:clipToPadding="false" />

La partie critique est:

    Android:paddingStart="150dp"
    Android:paddingEnd="150dp"
    Android:clipToPadding="false"

Et ensuite, assignez simplement SnapHelper à votre RecylcerView:

val snapHelper = LinearSnapHelper()
snapHelper.attachToRecyclerView(recyclerView)

Ça y est. La solution la plus simple et la plus parfaite au problème

0
egorikem

Si quelqu'un recherche une implémentation plus générique, voici mon code basé sur les réponses de ce fil de discussion:

Ajouter la CenterLinearSnapHelper

public class CenterLinearSnapHelper extends LinearSnapHelper {

    //Constants
    public static final String TAG = CenterLinearSnapHelper.class.getSimpleName();

    //Attributes
    private Context context;
    private float itemWidth;
    private OnPaddingComputationListener listener;

    //Constructors

    /**
     * A linear snap helper which helps centering the items in a recyclerview.
     *
     * @param itemWidth The (fixed) width of a child view in pixels.
     */
    public CenterLinearSnapHelper(float itemWidth) {
        this.itemWidth = itemWidth;
    }

    public void attachToRecyclerView(@Nullable RecyclerView recyclerView,
                                     @NonNull OnPaddingComputationListener listener) throws IllegalStateException {
        this.listener = listener;

        //Calculates the padding for the first and end item
        calculatePadding(recyclerView);

        //Create a LinearSnapHelper and attach the recyclerView to it.
        attachToRecyclerView(recyclerView);
    }

    public float getItemWidth() {
        return itemWidth;
    }

    private void calculatePadding(RecyclerView recyclerView) {
        if (recyclerView == null)
            return;

        ViewTreeObserver observer = recyclerView.getViewTreeObserver();
        observer.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {

            @Override
            public boolean onPreDraw() {
                recyclerView.getViewTreeObserver().removeOnPreDrawListener(this);
                int finalWidth = recyclerView.getMeasuredWidth();
                float padding = (finalWidth - itemWidth) / 2;
                listener.onPadding(padding, finalWidth);
                return true;
            }
        });
    }

    public interface OnPaddingComputationListener {
        void onPadding(float padding, int finalWidth);
    }

}

Dans votre activité/fragment où vous créez votre RecyclerView:

float itemWidth = getResources().getDimension(R.dimen.favorite_room_width);
CenterLinearSnapHelper snapHelper = new CenterLinearSnapHelper(itemWidth);
snapHelper.attachToRecyclerView(binding.listFavorites, (padding, finalWidth) -> {
    //Set the adapter
    roomAdapter = new RoomAdapter(requireContext(), rooms);
    roomAdapter.addPaddingItems((int) padding);
    roomAdapter.setOnToggleClickListener(FavoritesFragment.this);
    binding.listFavorites.setAdapter(roomAdapter);
});

Dans votre adaptateur:

public void addPaddingItems(int padding) {
    if (padding < 0)
        throw new IllegalStateException("Padding cannot be smaller than 0");

    this.padding = padding;
    //Add 2 new items as the first and last
    //NOTE: If you update your existing dataset (e.g add new items), you should redo the calculation!
    rooms.add(0, new Room("First"));
    rooms.add(rooms.size(), new Room("Last"));
}


@Override
public int getItemViewType(int position) {
    if (padding >= 0 && (position == 0 || position == rooms.size() - 1)) {
        return VIEW_TYPE_PADDING;
    }

    return VIEW_TYPE_ITEM;
}

@NonNull
@Override
public RoomViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
    ViewFavoriteRoomBinding binding = DataBindingUtil.inflate(inflater, R.layout.view_favorite_room, parent, false);

    if (viewType == VIEW_TYPE_PADDING) {
        RecyclerView.LayoutParams layoutParams = (RecyclerView.LayoutParams) binding.getRoot().getLayoutParams();
        layoutParams.width = padding;
        binding.getRoot().setLayoutParams(layoutParams);
    }

    RoomViewHolder viewHolder = new RoomViewHolder(context, binding, onToggleClickListener);
    viewHolder.getRecyclerView().setRecycledViewPool(viewPool);
    return viewHolder;
}
0
Verhelst

Je ne pense pas qu'il existe une approche directe pour atteindre cet objectif. Vous pouvez essayer ceci - 

listView.setOnScrollListener (écouteur);

@Passer outre

public void onScroll(AbsListView view, int firstVisibleItem,int visibleItemCount, int totalItemCount) {
    int firstVisibleRow = listView.getFirstVisiblePosition();
    int lastVisibleRow = listView.getLastVisiblePosition();
    int count - ((int*)count/2;
    int middle = listview.getChildAt(count - ((int)count/2));
    }
}

vous pouvez également consulter cette réponse https://stackoverflow.com/a/13134100/1320616https://stackoverflow.com/a/19205940/1320616

0
Ankit Aggarwal

Au début, j'avais besoin de quelque chose de similaire, pas de ça. Mais j'ai pu adapter la solution @TranHieu à mes besoins, j'ai donc voté pour sa solution.

Je voulais créer une vue de recyclage horizontale plein écran qui, une fois l'utilisateur sroll mis à jour scrollPosition en mostVisibleItem.

installer:

private void setUpScrolling() {
    mRecyclerVIew.getViewTreeObserver().addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
        @Override
        public boolean onPreDraw() {
            mRecyclerVIew.getViewTreeObserver().removeOnPreDrawListener(this);
            CustomScrollListener listener = (CustomScrollListener) mScrollListener;
            listener.width = mRecyclerVIew.getMeasuredWidth();
            listener.dx = 0;
            return true;
        }
    });
}

auditeur:

private class CustomScrollListener extends OnScrollListener {
    private int mLastDx = 0;
    int width = 0;
    int dx = 0;

    @Override
    public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
        if (newState == RecyclerView.SCROLL_STATE_IDLE) {
            if (mLastDx != dx) {
                scrollToMostVisibleItem();
            } else {
                dx = 0;
                mLastDx = 0;
            }
        }
        super.onScrollStateChanged(recyclerView, newState);
    }

    @Override
    public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
        super.onScrolled(recyclerView, dx, dy);
        this.dx += dx;
    }

    private void scrollToMostVisibleItem() {
        int direction = (dx > 0) ? 1 : -1;
        dx = Math.abs(dx);
        int shiftCount = Math.round(dx / width);
        int pixelShift = dx % width;
        if (pixelShift > width / 2) {
            shiftCount++;
        }

        float targetScrollPixels = shiftCount * width;
        float finalScrollPixels = (targetScrollPixels - dx) * direction;
        if (finalScrollPixels != 0) {
            mRecyclerVIew.smoothScrollBy((int) finalScrollPixels, 0);
            mLastDx = (int) finalScrollPixels;
            dx = 0;
        }
    }
}
0
ThinkDeep