Ho qualche problema nel tentativo di controllare la propagazione dell'evento di tocco all'interno del mio RecycleView. Così ho una RecyclerView che popola una serie di CardViews che imitano una pila di carte (quindi si sovrappongono a vicenda con un certo spostamento, sebbene abbiano un diverso valore di elevazione). Il mio problema attuale è che ogni carta ha un pulsante e poiché lo spostamento relativo della carta è inferiore all'altezza del pulsante, si verifica la situazione in cui i pulsanti si sovrappongono e ogni volta che viene inviato un evento tattile inizia a propagarsi dalla gerarchia inferiore della vista (dai bambini con il numero bambino più alto).Controllo della propagazione dell'evento tattile in caso di visualizzazioni sovrapposte
Secondo articoli che ho letto (this, this e anche this video) tocco di propagazione dipende da l'ordine di opinioni in vista primaria, in modo da toccare per prima consegnato al bambino con l'indice più alto, mentre io voglio l'evento di tocco per essere elaborati solo da touchables della vista più in alto e RecyclerView (deve anche elaborare i gesti di trascinamento e rilascio). Attualmente sto usando un fallback con le carte che sono consapevoli della loro posizione all'interno della gerarchia delle viste dei genitori per impedire ai bambini sbagliati di elaborare tocchi, ma questo è davvero un modo brutto per farlo. La mia ipotesi è che devo scavalcare il metodo dispatchTouchEvent di RecyclerView e inviare correttamente un evento touch solo al bambino più in alto. Tuttavia, quando ho provato questo modo di fare che (che è anche un po 'goffo):
@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
View topChild = getChildAt(0);
for (View touchable : topChild.getTouchables()) {
int[] location = new int[2];
touchable.getLocationOnScreen(location);
RectF touchableRect = new RectF(location[0],
location[1],
location[0] + touchable.getWidth(),
location[1] + touchable.getHeight());
if (touchableRect.contains(ev.getRawX(), ev.getRawY())) {
return touchable.dispatchTouchEvent(ev);
}
}
return onTouchEvent(ev);
}
L'unico evento è stato consegnato al pulsante all'interno di una scheda (nessun evento click attivato). Apprezzerò qualsiasi consiglio sul modo di invertire l'ordine di propagazione degli eventi tattili o sulla delega di un evento tattile a una specifica vista. Grazie mille in anticipo.
EDIT: Questa è la schermata di come la pila esempio carta sta cercando come
esempio di codice adattatore:
public class TestAdapter extends RecyclerView.Adapter {
List<Integer> items;
public TestAdapter(List<Integer> items) {
this.items = items;
}
@Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.test_layout, parent, false);
return new TestHolder(v);
}
@Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
TestHolder currentHolder = (TestHolder) holder;
currentHolder.number.setText(Integer.toString(position));
currentHolder.tv.setTag(position);
currentHolder.tv.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
int pos = (int) v.getTag();
Toast.makeText(v.getContext(), Integer.toString(pos) + "clicked", Toast.LENGTH_SHORT).show();
}
});
}
@Override
public int getItemCount() {
return items.size();
}
private class TestHolder extends RecyclerView.ViewHolder {
private TextView tv;
private TextView number;
public TestHolder(View itemView) {
super(itemView);
tv = (TextView) itemView.findViewById(R.id.click);
number = (TextView) itemView.findViewById(R.id.number);
}
}
}
e un esempio di layout carta:
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:layout_margin="16dp"
android:textSize="24sp"
android:id="@+id/number"/>
<TextView android:layout_width="wrap_content"
android:layout_height="64dp"
android:gravity="center"
android:layout_alignParentBottom="true"
android:layout_alignParentLeft="true"
android:layout_margin="16dp"
android:textSize="24sp"
android:text="CLICK ME"
android:id="@+id/click"/>
</RelativeLayout>
</android.support.v7.widget.CardView>
E ecco il codice che sto usando ora per risolvere il problema (questo approccio non mi piace, voglio trovare il modo migliore)
public class PositionAwareCardView extends CardView {
private int mChildPosition;
public PositionAwareCardView(Context context) {
super(context);
}
public PositionAwareCardView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public PositionAwareCardView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
}
public void setChildPosition(int pos) {
mChildPosition = pos;
}
@Override
public boolean onInterceptTouchEvent(MotionEvent ev) {
// if it's not the topmost view in view hierarchy then block touch events
return mChildPosition != 0 || super.onInterceptTouchEvent(ev);
}
@Override
public boolean onTouchEvent(MotionEvent event) {
return false;
}
}
EDIT 2: ho dimenticato di menzionare, questo problema è presente solo su dispositivi pre-Lolipop, sembra che a partire dal Lolipop, ViewGroups anche prendere in considerazione l'elevazione mentre dispacciamento eventi touch
EDIT 3: Ecco il mio attuale ordine di disegno infantile:
@Override
protected int getChildDrawingOrder(int childCount, int i) {
return childCount - i - 1;
}
EDIT 4: Finalmente sono riuscito a risolvere il problema grazie a utenti casuali, e this answer, la soluzione era estremamente semplice:
01.235.164,106 mila@Override
public boolean dispatchTouchEvent(MotionEvent ev) {
if (!onInterceptTouchEvent(ev)) {
if (getChildCount() > 0) {
final float offsetX = -getChildAt(0).getLeft();
final float offsetY = -getChildAt(0).getTop();
ev.offsetLocation(offsetX, offsetY);
if (getChildAt(0).dispatchTouchEvent(ev)) {
// if touch event is consumed by the child - then just record it's coordinates
x = ev.getRawX();
y = ev.getRawY();
return true;
}
ev.offsetLocation(-offsetX, -offsetY);
}
}
return super.dispatchTouchEvent(ev);
}
puoi pubblicare il tuo xml e l'adattatore –
@war_Hero Ho aggiunto il codice per un'app di prova che ha lo stesso problema descritto in una domanda – Chaosit