RecyclerView詳細説明
45890 ワード
RecyclerViewの簡単な紹介
RecyclerViewは高度なデカップリングであり、非常に柔軟なViewであり、それが提供する異なるLayoutManager、ItemDecoration、ItemAnimatorを設定することで目を見張る効果を実現しています.
RecyclerViewと先輩ListViewの違いは、主に以下の特性にあります.
AdapterでのViewHolderモード ListViewでは、ViewHolderを作成することでパフォーマンスを向上させる必要はありません.ListViewには厳密なViewHolder設計モデルがないからです.しかし、RecyclerViewを使用する場合、Adapterは少なくとも1つのView Holderを実現し、View Holder設計モードに従う必要があります.
Itemエントリのカスタマイズ ListViewは、垂直に直線的に並ぶリストビューしか実現できないが、これとは異なり、RecyclerViewは、RecyclerViewを設定することで実現できる.LayoutManagerは、水平スクロールリストや不規則な滝のリストなど、異なるスタイルのビューをカスタマイズします.
temアニメーション ListViewにはメソッドやインタフェースが一切提供されておらず,開発者がItemの削除アニメーションを実現するのに便利である.逆に、RecyclerViewのRecyclerViewを設定ことにより、ItemAnimatorは、エントリにアニメーション効果を追加します.
データ・ソースの設定 配列を処理するArrayAdapterやDatabaseの結果を示すCursorAdapterなど、LisViewでは異なるデータに対して様々なタイプのAdapterがカプセル化されている.逆に、RecyclerViewでは、RecyclerViewの実装をカスタマイズする必要がある.Adapterは、データセットを提供します.
エントリ分割線の設定 ListViewでは、android:dividerプロパティを設定することで、2つのItem間に分割線を設定できます.この効果をRecyclerViewに追加するには、RecyclerViewを使用する必要があります.ItemDecorationでは、この実装方法はより柔軟であるだけでなく、スタイルも豊富です.
クリックイベントの設定 ListViewにはAdapterViewが存在する.OnItemClickListenerインタフェースで、エントリのクリックイベントをバインドします.しかし、残念ながらRecyclerViewでは、このようなインタフェースは提供するが、別のインタフェースRcyclerViewが提供されている.OnItemTouchListenerは、エントリのタッチイベントに応答するために使用されます.
RecyclerViewの簡単な使用
1つはGradleに直接追加することです.
compile 'com.android.support:recyclerview-v7:24.2.1'
バージョン番号は探さないで、上のappcompatをまねて直接使って、appcompatをrecyclerviewに変えるだけでいいです
compile 'com.android.support:appcompat-v7:24.2.1'
2つ目はDesignパネルを直接ドラッグすると、追加を求めるメッセージが表示されます
コアクラスの紹介
RecyclerViewのタスクは、画面上のViewの回収と位置決めに限られます.Viewは、AdapterサブクラスとViewHolderサブクラスの2つのクラスのサポートからデータを表示できます.
RecyclerView.Adapter データセットマージの処理ビューのバインド
ViewHolder データのバインドまたは操作が必要なすべてのViewを保持
LayoutManager ビューの配置などの操作を担当する
ItemDecoration Item付近の分割線の描画を担当
ItemAnimator エントリの削除など、Itemの一般的な操作にアニメーション効果を追加します.
ViewHolder
ViewHolderはただ一つのことをします:Viewビューを収容します
RecyclerView自体はビューを作成しません.ビューホストを作成します.ビューホストはitemViewを参照しています.次の図に示します.
ViewHolderを印刷することができます.より多くの有効な情報を取得するためにtoString
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("ViewHolder{" +
Integer.toHexString(hashCode()) + " position=" + mPosition + " id=" + mItemId +
", oldPos=" + mOldPosition + ", pLpos:" + mPreLayoutPosition);
if (isScrap()) {
sb.append(" scrap ")
.append(mInChangeScrap ? "[changeScrap]" : "[attachedScrap]");
}
if (isInvalid()) sb.append(" invalid");
if (!isBound()) sb.append(" unbound");
if (needsUpdate()) sb.append(" update");
if (isRemoved()) sb.append(" removed");
if (shouldIgnore()) sb.append(" ignored");
if (isTmpDetached()) sb.append(" tmpDetached");
if (!isRecyclable()) sb.append(" not recyclable(" + mIsRecyclableCount + ")");
if (isAdapterPositionUnknown()) sb.append(" undefined adapter position");
if (itemView.getParent() == null) sb.append(" no parent");
sb.append("}");
return sb.toString();
}
Adapter
Adapterはコントローラオブジェクトで、モデル層からデータを取得し、RecyclerViewに提供し、コミュニケーションの橋渡しの役割を果たしています.adapterは責任を負います:1、必要なView Holderを作成します.2、ViewHolderをモデル層データにバインドする.
public VH onCreateViewHolder(ViewGroup parent,int viewType)は、Itemビューを作成し、対応するViewHolderを返します.
public void onBindViewHolder(VH holder,int position)は、正しいItemビューにデータをバインドします.
public int getItemCount()は、Adapterが保有するItemの数を返します.
adapterを作成するには、まずRecyclerViewを定義します.Adapterサブクラス、RecyclerViewがビューオブジェクトを表示する必要がある場合、次の図に示すadapterを探します.
まず、AdapterのgetItemCount()メソッドを呼び出すことで、RecyclerViewは配列リストに何個のオブジェクトが含まれているかを尋ねます.次に、RecyclerViewがadapterのcreateViewHolder(View Group,int)メソッドを呼び出して、View HolderおよびView Holderが表示するビューを作成します.最後に、RecyclerViewはViewHolderとその場所に転送され、onBindViewHolder(ViewHolder,int)メソッドが呼び出されます.adapterは、ターゲットの場所のデータを見つけ、ViewHolderのビューにバインドします.バインドとは、モデルデータを使用してビューを塗りつぶすことです.プロセス全体が実行されると、RecyclerViewでcrimeリスト項目が画面に表示されます.なお、createViewHolder(ViewGroup,int)メソッドの呼び出しは、onBindViewHolder(ViewHolder,int)メソッドに対して頻繁ではない.十分なViewHolderが作成されると、RecyclerViewはcreateViewHolder()メソッドの呼び出しを停止します.その後、古いViewHolderをリサイクルすることで時間とメモリを節約します.
使用開始
まず、JavaBeanがシミュレーションデータを作成します.
public class JavaBean {
String name;
int age;
public JavaBean(String name, int age) {
this.name = name;
this.age = age;
}
}
次に、上記の2つの方法で依存を追加した後、コントローラを設定します.
/**
* @author HaoTianYi [email protected]
* @version v1.0
* @des UI、
* @time 2016-11-10 18:54
*/
public class MyHolder extends RecyclerView.ViewHolder {
private TextView mTvAge;
private TextView mTvName;
public MyHolder(View itemView) {
super(itemView);
// View
mTvAge = (TextView) itemView.findViewById(R.id.age);
mTvName = (TextView) itemView.findViewById(R.id.name);
}
/**
* UI
* @param bean
* @call MyAdapter onBindViewHolder
*/
public void bindData(JavaBean bean) {
mTvAge.setText(" "+bean.age);
mTvName.setText(bean.name);
}
}
コンテンツ入力者を設定するには、次の手順に従います.
/**
* @author HaoTianYi [email protected]
* @version v1.0
* @des RecyclerView( )
* @time 2016-11-10 18:54
*/
public class MyAdapter extends RecyclerView.Adapter<MyHolder> {
private List mBeen;
public MyAdapter(List been) {
mBeen = been;
}
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// parent context, context
// null
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null);
return new MyHolder(itemView);
}
/**
* item , item
* onCreateViewHolder onBindViewHolder
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(MyHolder holder, int position) {
//
holder.bindData(mBeen.get(position));
}
@Override
public int getItemCount() {
return mBeen.size();
}
}
Activityの使用コード:
public class MainActivity extends AppCompatActivity {
private List mBeen = new ArrayList();
private final String NAME = " ";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
init();
}
private void init() {
//
for (int i = 0; i < 50; i++) {
JavaBean javaBean = new JavaBean(NAME + i, i);
mBeen.add(javaBean);
}
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);
recyclerView.setAdapter(new MyAdapter(mBeen));
// ,
recyclerView.setLayoutManager(new LinearLayoutManager(MainActivity.this));
}
}
ListViewに比べて、RecyclerViewの各クラスは本当に職責が単一ですね!!!
最後にitemのコードを入れます.
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="60dp"
android:orientation="horizontal">
<TextView
android:id="@+id/name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="nihao"
android:textSize="20sp"/>
<TextView
android:id="@+id/age"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:text="llll"
android:textSize="20sp"/>
LinearLayout>
レイアウト管理LayoutManager
レイアウト管理のベースクラスはRecyclerViewである.LayoutManager(抽象クラス)
LinearLayoutManagerリニアマネージャで、横、縦をサポートします.GridLayoutManagerグリッドレイアウトマネージャStaggerdGridLayoutManager滝レイアウトマネージャ
上のコードを変更し、瞬時に水平スクロールを実現します.
LinearLayoutManager manager = new LinearLayoutManager(MainActivity.this);
manager.setOrientation(LinearLayoutManager.HORIZONTAL);
recyclerView.setLayoutManager(manager);
行ごとに4列のメッシュマネージャの実装
recyclerView.setLayoutManager(new GridLayoutManager(MainActivity.this,4));
滝フローマネージャは、2番目のパラメータがVerticalに設定されている場合、上記と同じです.
recyclerView.setLayoutManager(new StaggeredGridLayoutManager(4,StaggeredGridLayoutManager.VERTICAL));
HORIZONTALが水平に4行になるように設定すると、水平にスライドし、itemの幅は設定に注意する必要があります.横の幅には制約がありませんので、コントロールの横にスクロールすることができます.ここに深刻な変形がないのは、フォントがサイズを設定しているからです.
共通API
findFirstVisibleItem Position()現在の最初の可視Itemを返すposition findFirstCompletelyVisibleItem Position()現在の最初の完全可視Itemを返すposition findLastVisibleItem Position()現在の最後の可視Itemを返すposition findLastCompletelyVisibleItem Position()現在の最後の完全可視Itemを返すposition
滝流のレイアウトを設定する
各itemの高さを固定しないで実現するには、まずAdapterのonCreateViewHolderメソッドを変更します.
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// parent context, context
// null
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null);
//
int height = (int) (Math.random() * 100);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(954/4,300+height);
params.setMargins(10,10,10,10);
itemView.setLayoutParams(params);
return new MyHolder(itemView);
}
Activityの参照コードは変更されません:
StaggeredGridLayoutManager manager = new StaggeredGridLayoutManager(3, StaggeredGridLayoutManager.VERTICAL);
recyclerView.setLayoutManager(manager);
この場合は実現できますが、itemごとに変更され、アニメーション効果があります.
スライド時にレイアウトの変化が見られ、AdapterのgetItemViewTypeを書き換えることで各itemを固定できます
@Override
public int getItemViewType(int position) {
return position;
}
今はレイアウトが変わっていません
セパレータItemDecoration
recyclerViewを設定します.addItemDecoration(new DividerDecoration(this)); を使用して、Item間のオフセット量を変更したり、Itemを装飾したりします.
RecyclerView.ItemDecorationは抽象クラスであり、以下の3つの方法を書き換えることでItem間のオフセット量または装飾効果を実現することができる:public void onDraw(Canvas c,RecyclerView parent)装飾の描画はItemエントリの描画前に呼び出され、したがって、Itemのコンテンツによってブロックされる可能性があるpublic void onDrawOver(Canvas c,RecyclerView parent)装飾の描画はItemエントリの描画後に呼び出されるため、装飾はItemの上に浮くpublic void getItemOffsets(Rect outRect,int itemPosition,RecyclerView parent)はpaddingまたはmarginと同様に、LayoutManagerは測定段階でこの方法を呼び出す.各Itemの正確な寸法を算出し、オフセット量を設定します.
具体的な使用
public class DividerItemDecoration extends RecyclerView.ItemDecoration {
/**
* , attrs
*/
private static final int[] ATTRS = new int[]{
android.R.attr.listDivider
};
public static final int HORIZONTAL_LIST = LinearLayoutManager.HORIZONTAL;
public static final int VERTICAL_LIST = LinearLayoutManager.VERTICAL;
/**
* Drawable
*/
private Drawable mDivider;
private int mOrientation;
public DividerItemDecoration(Context context, int orientation) {
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
setOrientation(orientation);
}
public void setOrientation(int orientation) {
if (orientation != HORIZONTAL_LIST && orientation != VERTICAL_LIST) {
throw new IllegalArgumentException("invalid orientation");
}
mOrientation = orientation;
}
@Override
public void onDraw(Canvas c, RecyclerView parent) {
System.out.println("recyclerview - itemdecoration onDraw()");
if (mOrientation == VERTICAL_LIST) {
drawVertical(c, parent);
} else {
drawHorizontal(c, parent);
}
}
/**
* ( )
* @param c
* @param parent
*/
public void drawVertical(Canvas c, RecyclerView parent) {
final int left = parent.getPaddingLeft();
final int right = parent.getWidth() - parent.getPaddingRight();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
android.support.v7.widget.RecyclerView v = new android.support.v7.widget.RecyclerView(parent.getContext());
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
// ,
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
/**
* ( )
* @param c
* @param parent
*/
public void drawHorizontal(Canvas c, RecyclerView parent) {
final int top = parent.getPaddingTop();
final int bottom = parent.getHeight() - parent.getPaddingBottom();
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition, RecyclerView parent) {
if (mOrientation == VERTICAL_LIST) {
//
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else {
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
}
}
}
上のコードでActivityのコードを変更します.
recyclerView.addItemDecoration(new DividerItemDecoration(MainActivity.this,DividerItemDecoration.VERTICAL_LIST));
カスタム分割子
attrsにandroid:listDividerプロパティを追加すれば、残りは自動的に生成されます.
-- Base application theme. -->
対応するカスタムdivider_item.xml
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<size android:width="3dp" android:height="3dp"/>
<solid android:color="@color/colorPrimaryDark"/>
shape>
最も単純な分割子
空きスペースを設定したい場合は、Adapterでmarginと少ないコード量を設定し、adapterのonCreateViewHolderメソッドを変更します.
@Override
public MyHolder onCreateViewHolder(ViewGroup parent, int viewType) {
// parent context, context
// null
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.item, null);
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(parent.getWidth(),150);
params.setMargins(10,10,10,10);
itemView.setLayoutParams(params);
return new MyHolder(itemView);
}
グリッドレイアウトの区切り文字の例
参照:csdn
public class DividerGridItemDecoration extends RecyclerView.ItemDecoration
{
private static final int[] ATTRS = new int[] { android.R.attr.listDivider };
private Drawable mDivider;
public DividerGridItemDecoration(Context context)
{
final TypedArray a = context.obtainStyledAttributes(ATTRS);
mDivider = a.getDrawable(0);
a.recycle();
}
@Override
public void onDraw(Canvas c, RecyclerView parent)
{
drawHorizontal(c, parent);
drawVertical(c, parent);
}
private int getSpanCount(RecyclerView parent)
{
//
int spanCount = -1;
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
spanCount = ((StaggeredGridLayoutManager) layoutManager)
.getSpanCount();
}
return spanCount;
}
public void drawHorizontal(Canvas c, RecyclerView parent)
{
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int left = child.getLeft() - params.leftMargin;
final int right = child.getRight() + params.rightMargin
+ mDivider.getIntrinsicWidth();
final int top = child.getBottom() + params.bottomMargin;
final int bottom = top + mDivider.getIntrinsicHeight();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
public void drawVertical(Canvas c, RecyclerView parent)
{
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++)
{
final View child = parent.getChildAt(i);
final RecyclerView.LayoutParams params = (RecyclerView.LayoutParams) child
.getLayoutParams();
final int top = child.getTop() - params.topMargin;
final int bottom = child.getBottom() + params.bottomMargin;
final int left = child.getRight() + params.rightMargin;
final int right = left + mDivider.getIntrinsicWidth();
mDivider.setBounds(left, top, right, bottom);
mDivider.draw(c);
}
}
private boolean isLastColum(RecyclerView parent, int pos, int spanCount,
int childCount)
{
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
if ((pos + 1) % spanCount == 0)// ,
{
return true;
}
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
if ((pos + 1) % spanCount == 0)// ,
{
return true;
}
} else
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// ,
return true;
}
}
return false;
}
private boolean isLastRaw(RecyclerView parent, int pos, int spanCount,
int childCount)
{
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager)
{
childCount = childCount - childCount % spanCount;
if (pos >= childCount)// ,
return true;
} else if (layoutManager instanceof StaggeredGridLayoutManager)
{
int orientation = ((StaggeredGridLayoutManager) layoutManager)
.getOrientation();
// StaggeredGridLayoutManager
if (orientation == StaggeredGridLayoutManager.VERTICAL)
{
childCount = childCount - childCount % spanCount;
// ,
if (pos >= childCount)
return true;
} else
// StaggeredGridLayoutManager
{
// ,
if ((pos + 1) % spanCount == 0)
{
return true;
}
}
}
return false;
}
@Override
public void getItemOffsets(Rect outRect, int itemPosition,
RecyclerView parent)
{
int spanCount = getSpanCount(parent);
int childCount = parent.getAdapter().getItemCount();
if (isLastRaw(parent, itemPosition, spanCount, childCount))// ,
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(), 0);
} else if (isLastColum(parent, itemPosition, spanCount, childCount))// ,
{
outRect.set(0, 0, 0, mDivider.getIntrinsicHeight());
} else
{
outRect.set(0, 0, mDivider.getIntrinsicWidth(),
mDivider.getIntrinsicHeight());
}
}
動画リストItemAnimator
AndroidでDefaultItemAnimatorがデフォルトで実装されています
ItemAnimatorは、Itemが独立したアニメーションを実現するのに役立ちます.ItemAnimatorは、次の3つのイベントにトリガーされます.
イベントリスニングlistener
public void setOnItemClickListener(@N u l l a ble OnItemClickListener listener)Itemクリックイベントリスニングpublic void setOnItemLongClickListener(OnItemLongClickListener listener)Itemイベントリスニング
まずAdapterでコールバックインタフェースを設定し、OnItemClickListenerインタフェースを追加し、onBindViewHolderメソッドを変更します.
private OnItemClickListener mOnItemClickListener;
interface OnItemClickListener {
//
void onItemClick(View view, int position);
//
void onItemLongClick(View view, int position);
}
public void setOnItemClickLitener(OnItemClickListener mLitener) {
mOnItemClickListener = mLitener;
}
/**
* item , item
* onCreateViewHolder onBindViewHolder
*
* @param holder
* @param position
*/
@Override
public void onBindViewHolder(final MyHolder holder, final int position) {
//
System.out.println(holder.toString());
holder.bindData(mBeen.get(position));
if (mOnItemClickListener != null) {
holder.itemView.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mOnItemClickListener.onItemClick(holder.itemView, position);
}
});
holder.itemView.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
mOnItemClickListener.onItemLongClick(holder.itemView, position);
return true;
}
});
}
}
Activityでのリスニングの設定:
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.rv);
mAdapter = new MyAdapter(mBeen);
mAdapter.setOnItemClickLitener(new MyAdapter.OnItemClickListener() {
@Override
public void onItemClick(View view, int position) {
System.out.println("-----"+"onItemClick"+"-----");
}
@Override
public void onItemLongClick(View view, int position) {
System.out.println("-----"+"onItemLongClick"+"-----");
}
});
recyclerView.setAdapter(mAdapter);
イベントがトリガーされ、結果が表示されます.
RecyclerView参照
作者の公式ブログ
原文アドレスRecyclerView詳細説明