RecyclerView SnapHelper намира точната позиция на прихванатия елемент

Как мога да намеря индекса на прихванат елемент? Опитах да използвам:

recyclerView.setOnFlingListener(new RecyclerView.OnFlingListener() {
        @Override
        public boolean onFling(int velocityX, int velocityY) {
            Log.i(TAG, "initRecyclerView: SNAPPING TO:" +snapHelper.findTargetSnapPosition(recyclerView.getLayoutManager(),velocityX,velocityY)%songs.size());
            return false;
        }
    });

но наистина не е точно и понякога връща -1.

Опитах :

snapHelper.findSnapView(recyclerView.getLayoutManager());

но връща изглед и не мисля, че мога да получа индекса на изглед вътре в recyclerview. (Греша ли?)

Нямам предпочитание кога методът да връща индекса (преди хвърлянето да приключи или след това).

ps: Моят recyclerview има персонализиран ZoomingLayoutManager (за ефект на въртележка). Може би променя размера на изгледа и това прави snapHelper дефектен? Знам, че snapHelper работи със средната височина и ширина на видимите childViews.

public class ZoomingLayoutManager extends LinearLayoutManager {
    private final float mShrinkAmount = 0.5f;
    private final float mShrinkDistance = 0.9f;

    public ZoomingLayoutManager(Context context, int orientation, boolean reverseLayout) {
        super(context, orientation, reverseLayout);
    }

    @Override
    public int scrollHorizontallyBy(int dx, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == HORIZONTAL) {
            int scrolled = super.scrollHorizontallyBy(dx, recycler, state);

            float midpoint = getWidth() / 2.f;
            float d0 = 0.f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.f;
            float s1 = 1.f - mShrinkAmount;
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint =
                        (getDecoratedRight(child) + getDecoratedLeft(child)) / 2.f;
                float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
                float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
                child.setScaleX(scale);
                child.setScaleY(scale);
            }
            return scrolled;
        } else {
            return 0;
        }

    }

    @Override
    public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, RecyclerView.State state) {
        int orientation = getOrientation();
        if (orientation == VERTICAL) {
            int scrolled = super.scrollVerticallyBy(dy, recycler, state);
            float midpoint = getHeight() / 2.f;
            float d0 = 0.f;
            float d1 = mShrinkDistance * midpoint;
            float s0 = 1.f;
            float s1 = 1.f - mShrinkAmount;
            for (int i = 0; i < getChildCount(); i++) {
                View child = getChildAt(i);
                float childMidpoint =
                        (getDecoratedBottom(child) + getDecoratedTop(child)) / 2.f;
                float d = Math.min(d1, Math.abs(midpoint - childMidpoint));
                float scale = s0 + (s1 - s0) * (d - d0) / (d1 - d0);
                child.setScaleX(scale);
                child.setScaleY(scale);
            }
            return scrolled;
        } else {
            return 0;
        }
    }
}

Ако е вярно, как мога да отменя методите на snapHelper, за да намеря по-точната позиция? Или просто пропускам нещо очевидно!?


person Apptic    schedule 05.08.2018    source източник


Отговори (2)


Можете да опитате нещо подобно; в моя случай само един елемент щракна в центъра на екрана, левите и десните елементи се виждат само частично и работи перфектно.

Добавете onScrollListener и изчакайте състоянието SCROLL_STATE_SETTLING, след което вземете FirstCompletelyVisibleItemPosition.

ЗАБЕЛЕЖКА: Използвах Handler с 1 секунда закъснение, както виждате по-долу, можете да премахнете тази част и да използвате само findFirstCompletelyVisibleItemPosition(). Използва се за сигурност на състоянието на превъртане.

    recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);

            if (newState == RecyclerView.SCROLL_STATE_SETTLING) {
                new Handler().postDelayed(() -> {
                    int pos = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
                    if (pos >= 0) {
                        // do your thing, the pos value must be your first fully visible or snapped view
                    }
                }, 1000);

            }
        }

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

        }
    });
person Oğuzhan Döngül    schedule 05.08.2018
comment
Ако потребителят превърти само 1 елемент, това отнема около половин секунда, за да работи и мога да задам времето на 500 ms. но ако потребителят превърти три или повече елемента, това отнема - да речем - 2 секунди! и просто връща позицията на следващия елемент. Въпреки това, ако задам времето да бъде например 2000 ms, това се забавя, ако потребителят превърти само един елемент - person Apptic; 05.08.2018

И така, благодарение на съвета на Oğuzhan Döngül разбрах позицията с помощта на findFirstCompletelyVisibleItemPosition(), но там "кога" извиквате метода е важно. Когато превъртането приключи, състоянието на recyclerview се променя на „неактивен“, така че благодаря, когато можете да използвате предоставения метод, за да получите точната позиция на първия напълно видим изглед. но какво ще стане, ако позицията на първия видим изглед не е нещо, което искате, а искате позицията на изгледа в средата на екрана?

добре, можете да добавите отместване, като изчислите колко изгледа са напълно видими на екрана между средния елемент и границата на екрана (всъщност е доста просто)

така че кодът:

 recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
        @Override
        public void onScrollStateChanged(@NonNull RecyclerView recyclerView, int newState) {
            super.onScrollStateChanged(recyclerView, newState);
            if(newState==RecyclerView.SCROLL_STATE_IDLE){
                int pos = ((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstCompletelyVisibleItemPosition();
                if (pos >= 0) {
                    int position=pos+ bigScreenOffset;
                    // do your thing, the position value is the exact index of the middle item
                }
            }
        }
    });

където bigScreenOffset се изчислява по:

        bigScreenOffset= ( Utility.getDisplaySize(this,true,true)/200)/2;

и моят персонализиран метод в моя помощен клас, който обработва ширина|височина в dp|px е:

 public static int getDisplaySize(Context context, boolean width, boolean dp) {
    DisplayMetrics displayMetrics = new DisplayMetrics();
    ((Activity) context).getWindowManager().getDefaultDisplay().getMetrics(displayMetrics);
    if (width) {
        if (dp)
            return (int) convertPixelsToDp(displayMetrics.widthPixels, context);
        else
            return displayMetrics.widthPixels;
    } else {
        if (dp)
            return (int) convertPixelsToDp(displayMetrics.heightPixels, context);
        else
            return displayMetrics.heightPixels;
    }
}
person Apptic    schedule 10.08.2018