ItemTouchHelper学習
7589 ワード
demoコードのテスト
これによりrecyclerviewを長押しするとサブitemの上下左右をドラッグできますが、順序は交換されませんので、デフォルトのItemTouchHelperでは、itemを上下左右にドラッグしても自動的に交換されず、交換をコールバックする関数が実現されます.次の分析関数呼び出しプロセス
helper.attachToRecyclerView(recyclerView); 内部コール多くのリスナーと初期化を設定
Down: RecyclerView.onInterceptTouchEvent RecyclerView.listener.onInterceptTouchEvent RecyclerView.OnItemTouchListener.onInterceptTouchEvent(demo:this.mRecyclerView.addOnItemTouchListener(this.mOnItemTouchListener);) ItemTouchHelper.this.mGestureDetector.onTouchEvent(event); 実はItemTouchHelperがRecyclerViewイベントリスニングを登録してmGestureDetector処理に渡し、mGestureDetectorが長押イベント通知を検出すると、ItemTouchHelperも長押を知って を呼び出す.
ジェスチャーに基づいて、どのViewとView Holderに属するかを見つけ、flagドラッグサポートを設定した場合は続行します.ItemTouchHelper.this.select(vh, 2); 主にthisを初期化する.mSelected = selected; 後味アニメーションオブジェクトを設定しますRecoverAnimationこれでItemTouchHelperは現在選択されているView Holderがあることを知っています
Move: RecyclerView.onTouchEvent(MotionEvent e) RecyclerView.dispatchOnItemTouch(e) RecyclerView.OnItemTouchListener.onTouchEvent() ViewHolder viewHolder = ItemTouchHelper.this.mSelected; switch(action){case 2:recyclerview.invalidate();このいくつかのステップはMoveを分析する時、実はRecyclerViewから事件を伝えて最終的にRecyclerViewがRecyclerViewを描いて見るのは少し気絶して、実は更に を見ます
Up: case MotionEvent.ACTION_UP: select(null, ACTION_STATE_IDLE); タグがnullとして選択すると、thisもトリガーされます.mRecyclerView.invalidate();
ItemTouchHelper extends ItemDecoration onDrawメソッドはItemTouchUIUtilImplをトリガーします.JAvaのonChildDrawとonChildDrawOver onChildDrawOverは空の方法onChildDrawはコアがviewです.setTranslationX(dX); view.setTranslationY(dY); 実はx,yのオフセットであり,いわば手の移動に応じてx,yのオフセットを設定する.
パラメータmDxは、現在の手と前に落ちる距離の差です.
パラメータmSelectedStartXは初期位置が選択されています.これを参照してください.mSelectedStartX = (float)selected.itemView.getLeft(); selectメソッドでは、mInitialTouchYはonLongPress設定ジェスチャードロップポイントで、2つのパラメータが異なることに注意して設定されています
ItemTouchHelperがスライドをリスニングしている間に、現在選択されている空のドラッグモードではないことがわかりました.
その中にはthis.moveIfNecessary(viewHolder);コールバック交換シーケンスをトリガーします
Build.VERSION.SDK_INT<21ドラッグビュー位置を書き換えるgetChildDrawingOrder
Build.VERSION.SDK_INT>=21 ItemTouchUIUtilImplを参照
デフォルトの長押しの場合、ドラッグモードに入り、手が動くにつれてx,yのオフセット量を計算し、ジェスチャートリガ描画はviewにsetTranslateXメソッドを直接呼び出すのではなくrecyclerviewである.invalidate、ItemDecorationペイント呼び出しonDrawメソッドをトリガーし、ドラッグビュー設定setTranlateXおよびsetTranlateYを設定します.
RecyclerView recyclerView = new RecyclerView(context);
recyclerView.setLayoutManager(new LinearLayoutManager(context));
final TouchAdapter adapter = new TouchAdapter();
recyclerView.setAdapter(adapter);
ItemTouchHelper helper = new ItemTouchHelper(new ItemTouchHelper.Callback() {
@Override
public int getMovementFlags(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder) {
return makeMovementFlags(ItemTouchHelper.LEFT | ItemTouchHelper.RIGHT | ItemTouchHelper.UP | ItemTouchHelper.DOWN, 0);
}
@Override
public boolean onMove(@NonNull RecyclerView recyclerView, @NonNull RecyclerView.ViewHolder viewHolder, @NonNull RecyclerView.ViewHolder viewHolder1) {
// adapter.swap(viewHolder,viewHolder1);
return true;
}
@Override
public void onSwiped(@NonNull RecyclerView.ViewHolder viewHolder, int i) {
}
});
helper.attachToRecyclerView(recyclerView);
これによりrecyclerviewを長押しするとサブitemの上下左右をドラッグできますが、順序は交換されませんので、デフォルトのItemTouchHelperでは、itemを上下左右にドラッグしても自動的に交換されず、交換をコールバックする関数が実現されます.次の分析関数呼び出しプロセス
リスナーの登録
helper.attachToRecyclerView(recyclerView); 内部コール多くのリスナーと初期化を設定
private void setupCallbacks() {
ViewConfiguration vc = ViewConfiguration.get(this.mRecyclerView.getContext());
this.mSlop = vc.getScaledTouchSlop();
this.mRecyclerView.addItemDecoration(this);
// , ,
this.mRecyclerView.addOnItemTouchListener(this.mOnItemTouchListener);
// Item , RecyclerView
this.mRecyclerView.addOnChildAttachStateChangeListener(this);
this.startGestureDetection();
// ,
}
イベント転送プロセス
Down:
View child = ItemTouchHelper.this.findChildView(e);
if (child != null) {
ViewHolder vh = ItemTouchHelper.this.mRecyclerView.getChildViewHolder(child);
if (vh != null) {
if (!ItemTouchHelper.this.mCallback.hasDragFlag(ItemTouchHelper.this.mRecyclerView, vh)) {
return;
}
ジェスチャーに基づいて、どのViewとView Holderに属するかを見つけ、flagドラッグサポートを設定した場合は続行します.ItemTouchHelper.this.select(vh, 2); 主にthisを初期化する.mSelected = selected; 後味アニメーションオブジェクトを設定しますRecoverAnimationこれでItemTouchHelperは現在選択されているView Holderがあることを知っています
Move:
public void onDraw(Canvas c) {
super.onDraw(c);
int count = this.mItemDecorations.size();
for(int i = 0; i < count; ++i) {
((RecyclerView.ItemDecoration)this.mItemDecorations.get(i)).onDraw(c, this, this.mState);
}
}
RecyclerView ItemDecoration
Up: case MotionEvent.ACTION_UP: select(null, ACTION_STATE_IDLE); タグがnullとして選択すると、thisもトリガーされます.mRecyclerView.invalidate();
分析
ItemTouchHelper extends ItemDecoration onDrawメソッドはItemTouchUIUtilImplをトリガーします.JAvaのonChildDrawとonChildDrawOver onChildDrawOverは空の方法onChildDrawはコアがviewです.setTranslationX(dX); view.setTranslationY(dY); 実はx,yのオフセットであり,いわば手の移動に応じてx,yのオフセットを設定する.
オフセット計算規則
private void getSelectedDxDy(float[] outPosition) {
if ((mSelectedFlags & (LEFT | RIGHT)) != 0) {
outPosition[0] = mSelectedStartX + mDx - mSelected.itemView.getLeft();
} else {
outPosition[0] = mSelected.itemView.getTranslationX();
}
if ((mSelectedFlags & (UP | DOWN)) != 0) {
outPosition[1] = mSelectedStartY + mDy - mSelected.itemView.getTop();
} else {
outPosition[1] = mSelected.itemView.getTranslationY();
}
}
パラメータmDxは、現在の手と前に落ちる距離の差です.
void updateDxDy(MotionEvent ev, int directionFlags, int pointerIndex) {
float x = ev.getX(pointerIndex);
float y = ev.getY(pointerIndex);
this.mDx = x - this.mInitialTouchX;
this.mDy = y - this.mInitialTouchY;
パラメータmSelectedStartXは初期位置が選択されています.これを参照してください.mSelectedStartX = (float)selected.itemView.getLeft(); selectメソッドでは、mInitialTouchYはonLongPress設定ジェスチャードロップポイントで、2つのパラメータが異なることに注意して設定されています
通知交換ViewHolderコールバック
ItemTouchHelperがスライドをリスニングしている間に、現在選択されている空のドラッグモードではないことがわかりました.
ItemTouchHelper.this.updateDxDy(event, ItemTouchHelper.this.mSelectedFlags, activePointerIndex);
ItemTouchHelper.this.moveIfNecessary(viewHolder);
ItemTouchHelper.this.mRecyclerView.removeCallbacks(ItemTouchHelper.this.mScrollRunnable);
ItemTouchHelper.this.mScrollRunnable.run();
ItemTouchHelper.this.mRecyclerView.invalidate();
その中にはthis.moveIfNecessary(viewHolder);コールバック交換シーケンスをトリガーします
RecyclerViewの一番上に浮かぶ実現原理を分析する
Build.VERSION.SDK_INT<21ドラッグビュー位置を書き換えるgetChildDrawingOrder
mChildDrawingOrderCallback = new RecyclerView.ChildDrawingOrderCallback() {
@Override
public int onGetChildDrawingOrder(int childCount, int i) {
if (mOverdrawChild == null) {
return i;
}
int childPosition = mOverdrawChildPosition;
if (childPosition == -1) {
childPosition = mRecyclerView.indexOfChild(mOverdrawChild);
mOverdrawChildPosition = childPosition;
}
if (i == childCount - 1) {
return childPosition;
}
return i < childPosition ? i : i + 1;
}
Build.VERSION.SDK_INT>=21 ItemTouchUIUtilImplを参照
if (Build.VERSION.SDK_INT >= 21) {
if (isCurrentlyActive) {
Object originalElevation = view.getTag(R.id.item_touch_helper_previous_elevation);
if (originalElevation == null) {
originalElevation = ViewCompat.getElevation(view);
float newElevation = 1f + findMaxElevation(recyclerView, view);
ViewCompat.setElevation(view, newElevation);
view.setTag(R.id.item_touch_helper_previous_elevation, originalElevation);
}
}
}
分析のまとめ
デフォルトの長押しの場合、ドラッグモードに入り、手が動くにつれてx,yのオフセット量を計算し、ジェスチャートリガ描画はviewにsetTranslateXメソッドを直接呼び出すのではなくrecyclerviewである.invalidate、ItemDecorationペイント呼び出しonDrawメソッドをトリガーし、ドラッグビュー設定setTranlateXおよびsetTranlateYを設定します.