web-dev-qa-db-ja.com

スクロール時にごみ箱の中央に見えるアイテムを取得

これは私が欲しいものです:

enter image description here

上の画像のように、RecycleViewに中心線を描画し、スクロールするときに中央の項目を取得します(左右に移動します)。
水平RecycleViewを描画しようとしています。

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

どのアイテムがRecycleViewの中心に移動したかを知る方法はありますか?そして、どのようにしてRecycleViewを1つだけ左または右にスクロールできますか?

Update:スクロールリスナーを使用して中間位置を取得しようとしましたが、アスペクトとしては機能しません。

  @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();
        }

そして結果を得る:

enter image description here

青いRect上にある場合にのみ、選択項目を変更(テキストを白色に変更)したい

48
R4j

私はこのようなものを作りました。私はあなたが必要とするものを正確に行うことができます。まず第一に、これは私のalogrithmの仕事です enter image description here

これは私の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);
    }
}}

これは最も重要なアルゴリズムです:

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);
}

そして、これは私の結果です: enter image description here

このビデオをご覧ください リンク 、これは私のアプリのデモです

49
TranHieu

何かを見逃す可能性があるため、サンプルコードブ​​ロック全体を一緒に必要とする場合があります。ここに私が持っているものがあります。どこかで小さな間違いをしている可能性があるので、何でもお気軽に修正してください。はい、この回答は@tranhieuの回答の延長です。ありがとう@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>
11
silentsudo

私はここでSnapHelperを使用しています:

    // 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);

TranHieuが言ったように、(開始位置と終了位置で)パディングのために2つのアイテムを挿入するソリューションは良いです。

コードの可読性が低いため、ViewTreeObserverの使用が好きではありません。この手法では、アイテムがリサイクルされている場合、アイテムの再描画も管理する必要があります。

Customviewクラスを使用している場合、これらのクラスに幅を直接設定できます。

たとえば、これは私のパディングクラスです

/**
 * 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));
    }

}

私のアダプターには、(displayWidth/2)-(realItemWidth/2)に等しい必要なパディング項目の幅を保存します

これは私のアダプターです。RecyclerView.Adapterに一致しないメソッドを見てはいけません。initAdapterメソッドと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;
    }
}

AndroidAnnotationsライブラリを使用していますが、必須ではありません

役立つことを願っています

5
firegloves

他の回答で述べたように、これを行う直接的な方法はありません。

これはおそらく、質問で説明したことを達成する方法です。

  1. 画面に表示されるアイテムの数を把握します。
  2. ビューがスクロールされるたびに、プログラムで中央のアイテムを選択します。
  3. Recyclerviewの中央のアイテムにオーバーレイとして部分的に透明な画像を保持します。 (リサイクラビューの幅または画面の幅と、配置するように選択したオーバーレイイメージの幅に基づいて座標を計算する必要があります。
  4. スクロールがあるたびに、リサイクラビューの下のテキストビューで選択した値を更新します。

画像オーバーレイは、接続されているように、1つのコントロールとして配置する必要があります。

ああ少年。私はこの答えをほぼ1週間探していましたが、解決策を見つけました。カスタムLayoutManagers?いいえ。ItemDecorator?いや。

最も簡単な方法は次のとおりです。

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

重要な部分は次のとおりです。

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

そして、SnapHelperRecylcerViewに割り当てるだけです:

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

これです。問題に対する最も簡単で完璧な解決策

3
egorikem

SNAPHELPERの使用-スムーズなソリューション

これは、SnapHelperを使用した別のソリューションです。ここの@TranHieuの答えから始めます:

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

ここで@ sector11によって圧縮されます:

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

上記の両方の答えにも基づいている次のコードを書きましたが、よりシンプルで、Androidサポートライブラリ24.2.0で提供されているSnapHelperを使用してよりスムーズなソリューションを提供します。

ここにMainActivityクラスがあります。残りは@ 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;
        }
    }
}
2
Dionis Beqiraj

この機能には、EcoGalleryライブラリを使用します。 https://github.com/falnatsheh/EcoGallery

1
user3579059

私の場合、別のアプローチを使用しました。

詳細はここで見つけることができます: RecyclerView-スクロール中に中央に見えるアイテムを強調表示する方法1

私の意見では、私のソリューションは他のソリューションよりも簡単です。

0
eldivino87

誰かがより一般的な実装を探しているなら、このスレッドの答えに基づいた私のコードは次のとおりです:

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);
    }

}

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);
});

アダプタで:

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

最初は、これではなく似たようなものが必要でした。しかし、@ TranHieuソリューションを自分のニーズに適合させることができたので、彼のソリューションに賛成票を投じました。

ユーザーがスクロールした後にscrollPositionをmostVisibleItemに更新するフルスクリーンの水平リサイクラビューを作成したかった

セットアップ:

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;
        }
    });
}

リスナー:

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