かつて、Android開発者としてのキャリアの夜明けに、既存のアプリケーションの例を見て、春の太陽のように美しい
U2020で、非常に便利なアダプターの例を見つけました。 彼の名前は
BindableAdapterです。 長い間、それをListViewとGridViewのアダプターの基礎として使用していましたが、それらの時代は終わりに近づき、RecyclerViewの時代が始まります。 この時代には新しい便利なアダプターが必要で、私はそれを作ろうとしました。
まず、Googleに
「私の最愛の人はどこですか?」 「RecyclerBindableAdapter」と尋ねることにしました
が、彼は答えをくれませんでした。 私は正直なところ、私が快適に作業できるアダプターを見つけようとしましたが、それはすべて無駄でした。 私が見つけたすべての例は、特定の機能のみを実装し、抽象的であろうとしませんでした。 そして、率直に言って、それらの多くの瞬間は私を混乱させました。例えば、それらの多くでは、ContextとLayoutManagerが何らかの理由でコンストラクターに転送されました。 RecyclerView.Adapterには優れたonAttachedToRecyclerView(RecyclerView recyclerView)メソッドがありますが、結果のrecyclerViewからContextとLayoutManagerの両方を取得できます。
これらすべてにより、私はそれらが思慮深くよりも急いで書かれたと考えるようになりました。 私はどこでもRecyclerViewを使用し、毎回似たようなコードを大量に生成するのは、どういうわけかうまくいかないので、状況を修正して便利な抽象アダプターを書くことにしました。
そして、実際に何が必要ですか?
RecyclerViewのすべての新機能を最大限に活用しようとしながら、RecyclerViewに同じBindableAdapterを移植します。 いつものように、1つの「しかし」があります。RecyclerViewにはデフォルトでヘッダーとフッターがありません。 したがって、将来のユーザーが新しいタイプの要素を作成してこの問題を修正しようとすると、このため、位置番号はヘッダーの数だけシフトします。 これは悪いです。 そのため、事前にヘッダーとフッターを操作できるようにする必要があります。また、データを操作する方法でヘッダーとフッターを使用できるようにする必要があります。
それから、私がよく使う他のRecyclerView機能、すなわち要素フィルタリングとヘッダー視差を実装するのがいいと思いました。 ここでも、すべてがそれほどスムーズではありません。
フィルタリングを提供するアダプターは、オブジェクトの2つのリスト(一般リストと現在表示されているそれらのアイテムのリスト)を保存することを強制されます。 したがって、より多くのメモリを消費し、要素を操作するメソッドの動作は遅くなります。 メモリに追加のデータを事前に入力する必要はなく、フィルタリングはそれほど必要ありません。 そのため、メインアダプタを拡張する別のクラスにすることにしました。 したがって、フィルタリングが必要な場合にのみこのアダプターを使用し、その他の場合はメインアダプターを使用します。
ヘッダーの視差でも同様の状況が発生しています。 メインアダプタには、多くのヘッダーとフッターを含めることができます。 同時に複数の要素の視差を実装することは問題になる可能性が高く、見苦しいため、試しても意味がありません。 同様に別のクラスにします。
この時点で、SimpleAdapterの類似物があればいいと思いました。 レイアウトIDとViewHolderを挿入するだけで、1行として宣言できます。 結局のところ、単純なリストのために大量のコードを作成するのはなぜでしょう。
その結果、タスクは4つのアダプターを作成することでした。
- RecyclerBindableAdapter-メインアダプター
- FilterBindableAdapter-フィルター可能なアダプター
- ParallaxBindableAdapter-ヘッダー視差機能を備えたアダプター
- SimpleBindableAdapter-シンプルなアダプター
降りる
RecyclerBindableAdapter
まず、ヘッダーとフッターを追加できるようにします。これは、かつてStackOverflowの広大さで出会い、アダプターのニーズに応じて調整した類似の実装です。 Androidを使用しているほとんどの人が同様の実装を見たり、行ったりしていると思うので、詳しくは説明しません。
次に、データを操作するためのメソッドを作成する必要があります。それらの名前は、ListView / GridViewのBindableAdapterから取得します。
基本的に、RecyclerBindableAdapterは完成しました。 全文は、
ここまたはスポイラーの下で見ることができます。
RecyclerBindableAdapter public abstract class RecyclerBindableAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> { public static final int TYPE_HEADER = 7898; public static final int TYPE_FOOTER = 7899; private List<View> headers = new ArrayList<>(); private List<View> footers = new ArrayList<>(); private List<T> items = new ArrayList<>(); private RecyclerView.LayoutManager manager; private LayoutInflater inflater; private GridLayoutManager.SpanSizeLookup spanSizeLookup = new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { return getGridSpan(position); } }; public int getRealItemCount() { return items.size(); } public T getItem(int position) { return items.get(position); } public void add(int position, T item) { items.add(position, item); notifyItemInserted(position); int positionStart = position + getHeadersCount(); int itemCount = items.size() - position; notifyItemRangeChanged(positionStart, itemCount); } public void add(T item) { items.add(item); notifyItemInserted(items.size() - 1 + getHeadersCount()); } public void addAll(List<? extends T> items) { final int size = this.items.size(); this.items.addAll(items); notifyItemRangeInserted(size + getHeadersCount(), items.size()); } public void set(int position, T item) { items.set(position, item); int positionStart = position + getHeadersCount(); int itemCount = items.size() - position; notifyItemRangeChanged(positionStart, itemCount); } public void removeChild(int position) { items.remove(position); notifyItemRemoved(position + getHeadersCount()); int positionStart = position + getHeadersCount(); int itemCount = items.size() - position; notifyItemRangeChanged(positionStart, itemCount); } public void clear() { final int size = items.size(); items.clear(); notifyItemRangeRemoved(getHeadersCount(), size); } public void moveChildTo(int fromPosition, int toPosition) { if (toPosition != -1 && toPosition < items.size()) { final T item = items.remove(fromPosition); items.add(toPosition, item); notifyItemMoved(getHeadersCount() + fromPosition, getHeadersCount() + toPosition); int positionStart = fromPosition < toPosition ? fromPosition : toPosition; int itemCount = Math.abs(fromPosition - toPosition) + 1; notifyItemRangeChanged(positionStart + getHeadersCount(), itemCount); } }
次に、いくつかの例を作成します。
public class LinearExampleAdapter extends RecyclerBindableAdapter<Integer, LinearViewHolder> { private LinearViewHolder.ActionListener actionListener;
ViewHolder(注意バターナイフ7) public class LinearViewHolder extends RecyclerView.ViewHolder { @Bind(R.id.linear_example_item_text) TextView text; private int position; private ActionListener actionListener; public LinearViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } public void bindView(Integer item, int position, ActionListener listener) { actionListener = listener; this.position = position; text.setText(String.valueOf(item)); } @OnClick(R.id.linear_example_item_move_to_top) protected void OnMoveToTopClick() { if (actionListener != null) { actionListener.onMoveToTop(position); } } @OnClick(R.id.linear_example_item_remove) protected void OnRemoveClick() { if (actionListener != null) { actionListener.OnRemove(position); } } @OnClick(R.id.linear_example_item_up) protected void OnUpClick() { if (actionListener != null) { actionListener.OnUp(position); } } @OnClick(R.id.linear_example_item_down) protected void OnDownClick() { if (actionListener != null) { actionListener.OnDown(position); } } public interface ActionListener { void onMoveToTop(int position); void OnRemove(int position); void OnUp(int position); void OnDown(int position); } }
だから、今ではすべてがシンプルです。 アダプターの操作は、時々簡素化されています。
ここにこの例を見つけることができます、そして、ここにいくつかのタイプの例があります。
FilterBindableAdapter
フィルター可能なアダプターを作成しましょう。 RecyclerBindableAdapterを展開し、まずすべてのオブジェクトと現在表示されているオブジェクトの2つのリストを作成します。 メソッドの一部を再定義して、2つのリストで機能するようにしました。
次に、フィルタリングを行いましょう。
アダプターの例は、1つのメソッドを除いて、RecyclerBindableAdapterの例と完全に似ています。 このメソッドは、要素を文字列に変換して、要素をフィルタリングするためのものです。
@Override protected String itemToString(String item) { return item; }
全文は、
ここまたはスポイラーの下で見ることができます。
FilterBindableAdapter public abstract class FilterBindableAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerBindableAdapter<T, VH> { private final Object lock = new Object(); private List<T> originalValues; private List<T> objects; private ArrayFilter filter; private OnFilterObjectCallback onFilterObjectCallback; protected FilterBindableAdapter() { objects = new ArrayList<T>(); originalValues = new ArrayList<T>(); } @Override public void addAll(List<? extends T> data) { if (objects.containsAll(data)) { return; } objects.clear(); objects.addAll(data); originalValues.clear(); originalValues.addAll(data); notifyItemRangeInserted(getHeadersCount(), data.size()); }
例は
ここにあります 。
ParallaxBindableAdapter
それでは、視差効果の作成を始めましょう。 最初は、ヘッダーのみの視差を作成する予定でしたが、フッターの視差を作成することは非常に興味深い経験になると考えました。 最初に、ヘッダーとフッターを追加するメソッドを再定義して、アダプターがそれぞれ1つのインスタンスのみを持つようにします。
addHeaderとaddFooterのオーバーライド @Override public void addHeader(View header) { if (getHeadersCount() == 0) { super.addHeader(header); } else { removeHeader(getHeader(0)); super.addHeader(header); } } @Override public void addFooter(View footer) { if (getFootersCount() == 0) { super.addFooter(footer); } else { removeFooter(getFooter(0)); super.addFooter(footer); } }
次に、ヘッダーが他の要素の下でクロールされないように、レンダリング領域を制限するコンテナを作成します。 すでに視差を行っている場合は、おそらく同様の実装にすでに遭遇している可能性があります。 この実装の違いは、フッターもサポートすることです。 onCreateViewHolder()でFrameLayoutの代わりにこのコンテナを使用します。
視差コンテナ public class ParallaxContainer extends FrameLayout { private final boolean isParallax; private final boolean isFooter; private int offset; public ParallaxContainer(Context context, boolean shouldClick, boolean isFooter) { super(context); isParallax = shouldClick; this.isFooter = isFooter; } @Override protected void dispatchDraw(@NonNull Canvas canvas) { if (isParallax) { int top = isFooter ? -offset : 0; int bottom = isFooter ? getBottom() : getBottom() + offset; Rect rect = new Rect(getLeft(), top, getRight(), bottom); canvas.clipRect(rect); } super.dispatchDraw(canvas); } public void setClipY(int offset) { this.offset = offset; invalidate(); } }
スクロール中にコンテナを移動するメソッドを作成しましょう。 これを行うには、onAttachedToRecyclerView()メソッドで、RecyclerViewでOnScrollListenerをハングさせます。 その中で、シフトのためのメソッドを呼び出します。 もちろん、視差効果を有効/無効にするためのメソッドを作成する必要があります。
全文は、
ここまたはスポイラーの下で見ることができます。
ParallaxBindableAdapter public abstract class ParallaxBindableAdapter<T, VH extends RecyclerView.ViewHolder> extends RecyclerBindableAdapter<T, VH> { private static final float SCROLL_MULTIPLIER = 0.5f; private ParallaxContainer header; private ParallaxContainer footer; private OnParallaxScroll parallaxScroll; private boolean isParallaxHeader = true; private boolean isParallaxFooter = true;
RecyclerBindableAdapter, .
.
SimpleBindableAdapter
, , ViewHolder. , . .
非表示のテキスト public abstract class BindableViewHolder<T> extends RecyclerView.ViewHolder { public BindableViewHolder(View itemView) { super(itemView); }
. RecyclerBindableAdapter ViewHolder Java Reflection.
, . .
private SimpleBindableAdapter<Integer, SimpleViewHolder> simpleExampleAdapter; @Override protected void onCreate(Bundle savedInstanceState) { ............................................................................................. simpleExampleAdapter = new SimpleBindableAdapter<>(R.layout.simple_example_item, SimpleViewHolder.class); simpleExampleAdapter.setActionListener(new SimpleViewHolder.SimpleActionListener() { ............................................................................................. } simpleExampleRecycler.setAdapter(simpleExampleAdapter); .............................................................................................. }
, , . ViewHolder.
非表示のテキスト public class SimpleViewHolder extends BindableViewHolder<Integer> { @Bind(R.id.simple_example_item_tittle) TextView tittle; private int position; private SimpleActionListener simpleActionListener; public SimpleViewHolder(View itemView) { super(itemView); ButterKnife.bind(this, itemView); } @Override public void bindView(int position, Integer item, ActionListener actionListener) { super.bindView(position, item, actionListener); this.position = position; simpleActionListener = (SimpleActionListener) actionListener; tittle.setText(String.valueOf(item)); } @OnClick(R.id.simple_example_item_move_to_top) protected void OnMoveToTopClick() { if (simpleActionListener != null) { simpleActionListener.onMoveToTop(position); } } @OnClick(R.id.simple_example_item_remove) protected void OnRemoveClick() { if (simpleActionListener != null) { simpleActionListener.OnRemove(position); } } @OnClick(R.id.simple_example_item_up) protected void OnUpClick() { if (simpleActionListener != null) { simpleActionListener.OnUp(position); } } @OnClick(R.id.simple_example_item_down) protected void OnDownClick() { if (simpleActionListener != null) { simpleActionListener.OnDown(position); } } public interface SimpleActionListener extends ActionListener { void onMoveToTop(int position); void OnRemove(int position); void OnUp(int position); void OnDown(int position); } }
.
.
, , .
PS。 100% , . , - .
PSS. , BindableAdapter RecyclerView, , . , , .