2016-03-21 17 views
1

ネストされたReyclerView(垂直方向の水平)を実装しましたが、CardView要素でラップされた行全体のクリックリスナーを追加することはできません。ネストされたRecyclerView onClickListenerが機能しない

問題は、内部RecyclerViewがすべてのタッチイベントをキャプチャし、ルートCardViewがonClickイベントに応答しないことです。

私はまた、CardViewがタッチイベントを傍受させようとしましたが、このアプローチではリップル効果(実際にはフィードバック)は機能しませんでした。

RecyclerViewを入れ子にした状態で行にクリックリスナーを実装する方法をお勧めしますか?

ありがとうございます。

--LE--

この現在の実装である:

断片レイアウト

<android.support.v7.widget.RecyclerView 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     xmlns:app="http://schemas.android.com/apk/res-auto" 
     xmlns:tools="http://schemas.android.com/tools" 
     android:id="@+id/list" 
     android:name=".NestedRecyclerViewsFragment" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     app:layoutManager="LinearLayoutManager" 
     tools:context=".NestedRecyclerViewsFragment" 
     tools:listitem="@layout/fragment_nested_recyclerview_item" 
     /> 

フラグメントonCreateView()実装

@Override 
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { 
     View view = inflater.inflate(R.layout.fragment_nested_recyclerview_list, container, false); 

     // Set the adapter 
     if (view instanceof RecyclerView) { 
      Context context = view.getContext(); 
      RecyclerView recyclerView = (RecyclerView) view; 
      recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)); 
      recyclerView.setHasFixedSize(true); // Improves performance - as we know the size doesn't change 

      //Initialize and set the adapter 
      mAdapter = new RootAdapter(context, mListener); 
      recyclerView.setAdapter(mAdapter); 

      final GestureDetector mGestureDetector = 
        new GestureDetector(view.getContext(), new GestureDetector.SimpleOnGestureListener() { 

         @Override 
         public boolean onSingleTapUp(MotionEvent e) { 
          return true; 
         } 

        }); 
      recyclerView.addOnItemTouchListener(new RecyclerView.SimpleOnItemTouchListener() { 
       //TODO: intercept simple gestures like onClick and/or onLongClick 

       @Override 
       public boolean onInterceptTouchEvent(RecyclerView rv, MotionEvent e) { 
        View child = rv.findChildViewUnder(e.getX(), e.getY()); 

        if (child != null && mGestureDetector.onTouchEvent(e)) { 
         //TODO: handle the intercept?? 
         child.callOnClick(); 
         return true; 
        } 

        return super.onInterceptTouchEvent(rv, e); 
       } 
      }); 
      recyclerView.setNestedScrollingEnabled(true); 
     } 
     return view; 
} 

**ルートアダプタレイアウト**

<?xml version="1.0" encoding="utf-8"?> 
<android.support.v7.widget.CardView 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     xmlns:app="http://schemas.android.com/apk/res-auto" 
     xmlns:tools="http://schemas.android.com/tools" 
     android:layout_width="match_parent" 
     android:layout_height="@dimen/row_height" 
     android:layout_gravity="center_horizontal" 
     android:layout_margin="@dimen/none" 
     android:padding="@dimen/none" 
     app:cardCornerRadius="@dimen/none" 
     tools:context=".NestedRecyclerViewActivity" 
     <!-- Simple selector for API < 21 and ripple effect for APi >= 21 --> 
     android:foreground="@drawable/selector_default" 
     android:clickable="true" 
     android:focusable="true" 
     > 

    <!-- Horizontal image gallery inside row item --> 
    <android.support.v7.widget.RecyclerView 
      android:id="@+id/child_recycler_view" 
      android:layout_width="match_parent" 
      android:layout_height="match_parent" 
      /> 

    ... 

</android.support.v7.widget.CardView> 

ルートアダプタonBindView()の実装:

... 

holder.childRecyclerView.setLayoutManager(new LinearLayoutManager(holder.mView.getContext(), 
                 LinearLayoutManager.HORIZONTAL, false)); 
holder.childRecyclerView.setHasFixedSize(true); // We know the image don't change size 
holder.childRecyclerView.setAdapter(new ChildAdapter(items)); 

... 

ルートアダプタViewHolder実装:

public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener { 

     private ItemClickListener mClickListener; 

     /** 
     * The whole view - useful if you need to place some touch listener on the entire row. 
     */ 
     public final View mView; 
     /** 
     * The model associated with this view. - will be updated on bind method 
     */ 
     public Object mItem; 

     @Bind(R.id.child_recycler_view) 
     RecyclerView childRecyclerView; 

     ... 

     public ViewHolder(View view) { 
      super(view); 
      ButterKnife.bind(this, view); 
      mView = view; 

      mView.setOnClickListener(this); 
      // Set the click listener bound to the fragment or activity 
      mClickListener = RootAdapter.this; 
     } 

     @Override 
     public void onClick(View v) { 
      if (null != mClickListener) { 
       mClickListener.onClick(v, getAdapterPosition()); 
      } 
     } 
} 

内側RecyclerView (子供)は、リスナーが設定されていない非常にシンプルでスタンドアロンの実装です。

このソリューションは機能しますが、クリックイベントが発生し、内部のRecyclerViewスクロールが機能しますが、クリックのフィードバックが表示されないという問題が発生しました。

LE:私はこの問題を掲載するのでソリューション

私の要件はPagerAdapterで、内側RecyclerViewを交換すること、少し変更しましたが、私は実装ソリューションは、ネストされたRecyclerViewで動作するはずです。

基本的に私は、すぐに彼らが傍受されているとして、バック親にクリックイベントを渡しますカスタムItemClickSupportクラスを使用します。

/** 
* Utility class which adds the ability to add Click Support for RecyclerViews without the need to implement click 
* listeners into the adapter or in the ViewHolder's implementation. 
* <p> 
* Use it by simply binding an click listener to the desired RecyclerView. 
* <pre><code> 
* ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(new ItemClickSupport.OnItemClickListener() { 
*  {@[email protected]}Override 
*  public void onItemClicked(RecyclerView recyclerView, int position, View v) { 
*   // Handle the clicked item 
*  } 
* }); 
* </code></pre> 
* </p> 
* Based on <a href="http://www.littlerobots.nl/blog/Handle-Android-RecyclerView-Clicks/">Handle-Android-RecyclerView 
* -Clicks</a>, <br/><b>Hugo Visser</b>. Which is very similar with the implementation from <a 
* href="https://github.com/lucasr/twoway-view">TwoWay-View</a>. 
* <p/> 
* Created by ionut on 22.03.2016. 
*/ 
public class ItemClickSupport { 

    private final RecyclerView mRecyclerView; 
    private OnItemClickListener mOnItemClickListener; 
    private OnItemLongClickListener mOnItemLongClickListener; 
    private View.OnClickListener mOnClickListener = new View.OnClickListener() { 
     @Override 
     public void onClick(View v) { 
      if (mOnItemClickListener != null) { 
       RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v); 
       mOnItemClickListener.onItemClicked(mRecyclerView, holder.getAdapterPosition(), v); 
      } 
     } 
    }; 
    private View.OnLongClickListener mOnLongClickListener = new View.OnLongClickListener() { 
     @Override 
     public boolean onLongClick(View v) { 
      if (mOnItemLongClickListener != null) { 
       RecyclerView.ViewHolder holder = mRecyclerView.getChildViewHolder(v); 
       return mOnItemLongClickListener.onItemLongClicked(mRecyclerView, holder.getAdapterPosition(), v); 
      } 
      return false; 
     } 
    }; 
    private RecyclerView.OnChildAttachStateChangeListener mAttachListener = 
      new RecyclerView.OnChildAttachStateChangeListener() { 
       @Override 
       public void onChildViewAttachedToWindow(View view) { 
        if (mOnItemClickListener != null) { 
         view.setOnClickListener(mOnClickListener); 
        } 
        if (mOnItemLongClickListener != null) { 
         view.setOnLongClickListener(mOnLongClickListener); 
        } 
       } 

       @Override 
       public void onChildViewDetachedFromWindow(View view) { 

       } 
      }; 

    private ItemClickSupport(RecyclerView recyclerView) { 
     mRecyclerView = recyclerView; 
     mRecyclerView.setTag(R.id.item_click_support, this); 
     mRecyclerView.addOnChildAttachStateChangeListener(mAttachListener); 
    } 

    public static ItemClickSupport addTo(RecyclerView view) { 
     ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support); 
     if (support == null) { 
      support = new ItemClickSupport(view); 
     } 
     return support; 
    } 

    public static ItemClickSupport removeFrom(RecyclerView view) { 
     ItemClickSupport support = (ItemClickSupport) view.getTag(R.id.item_click_support); 
     if (support != null) { 
      support.detach(view); 
     } 
     return support; 
    } 

    public ItemClickSupport setOnItemClickListener(OnItemClickListener listener) { 
     mOnItemClickListener = listener; 
     return this; 
    } 

    public ItemClickSupport setOnItemLongClickListener(OnItemLongClickListener listener) { 
     mOnItemLongClickListener = listener; 
     return this; 
    } 

    private void detach(RecyclerView view) { 
     view.removeOnChildAttachStateChangeListener(mAttachListener); 
     view.setTag(R.id.item_click_support, null); 
    } 

    public interface OnItemClickListener { 

     void onItemClicked(RecyclerView recyclerView, int position, View v); 

     void onItemClicked(int position); 
    } 

    public interface OnItemLongClickListener { 

     boolean onItemLongClicked(RecyclerView recyclerView, int position, View v); 
    } 

    public static class SimpleOnItemClickListener implements OnItemClickListener { 

     @Override 
     public void onItemClicked(int position) { 

     } 

     @Override 
     public void onItemClicked(RecyclerView recyclerView, int position, View v) { 

     } 
    } 
} 

ルートアダプタを作成するとき、私はそれのためのアイテムをクリックリスナーを設定し、このような:

ItemClickSupport.addTo(mRecyclerView).setOnItemClickListener(this); 

そして私はまた、このように、setterメソッドを介して内アダプタは、このリスナーを送信:

rootAdapter.setOnItemClickListener(this); 

rootAdapterはルートアダプタであり、thisは現在のフラグメントで、ItemClickSupportListenerを実装しています。ルートアダプタで

、アイテムを結合し、内部アダプタを作成するときに、私はこのように、内側のアダプタにアイテムのクリック支援リスナーに渡す:

innerAdapter.setOnItemClickListener(new ItemClickSupport.SimpleOnItemClickListener() { 
    @Override 
    public void onItemClicked(int position) { 
     // Here we pass the click to the parent provided click listener. 
     // We modify the position with the one of the ViewHolder so that we don't get the 
     // position of the horizontal RecyclerView adapter - we are interested on the 
     // vertical item actually. 
     if (null != mItemClickListener) { 
      mItemClickListener.onItemClicked(holder.getAdapterPosition()); 
     } 
    } 
}); 

mItemClickListenerが実際によって設定されたリスナーです上記のセッター法により断片化する。ビューを作成、私は私が膨張レイアウトのルートにクリックリスナを設定して、戻って私のカスタムクリックリスナーにそのイベントを渡す内側のアダプタでは、

// Detect the click events and pass them to any listeners 
itemView.setOnClickListener(new View.OnClickListener() { 
    @Override 
    public void onClick(View v) { 
     if (null != mOnItemClickListener) { 
      mOnItemClickListener.onItemClicked(position); 
     } 
    } 
}); 

mOnItemClickListenerが実際にあります上記のアダプタで渡されたアイテムクリックサポート。

もう一つ重要なことは、このように、アダプタ項目ビューの内容のためでframeLayoutを使用することです:

<FrameLayout 
    android:layout_width="match_parent" 
    android:layout_height="0dp" 
    android:layout_weight="1" 
    android:addStatesFromChildren="true" 
    android:foreground="@drawable/selector_default" 
    > 

ここで重要なのは、セレクタとフラグandroid:addStatesFromChildrenを使用します​​です。これはルートアダプター項目に設定されています。我々はまた、フォアグラウンド属性を使用して、それのセレクタを設定することができるように

内部アダプタ項目は、コンテンツビューとしてでframeLayoutを使用する必要があります。

<FrameLayout 
     xmlns:android="http://schemas.android.com/apk/res/android" 
     xmlns:tools="http://schemas.android.com/tools" 
     android:layout_width="match_parent" 
     android:layout_height="match_parent" 
     android:foreground="@drawable/selector_default" 
     > 

をこれが必要とされ、内側のアダプタ項目がキャプチャしますときので、クリックイベント、セレクタをアクティブにします。そうでない場合、親はclickイベントに反応し、セレクタをアクティブにする必要があります。

+0

試みをクリックリスナーを設定してみてください。 – Irfan

+0

子RecyclerViewはonBindViewHolder()メソッドで作成されるため、水平方向の各行項目に対してリスナーを作成する必要があります。 また、CardViewセレクタがもう動作しない問題を解決するとは思えません。 –

+0

ここでonClickListenerを設定しますか? – Irfan

答えて

0

は、アダプタ内のビューに表示するアダプタでクリックリスナーに設定する

public class NormalItem extends RecyclerView.ViewHolder { 

    CardView cardView; 
    TextView textView; 


    public NormalItem(View itemView) { 
     super(itemView); 


     textView = (TextView) itemView.findViewById(R.id.textView); 
     cardView = (CardView) itemView.findViewById(R.id.cardview); 

     itemView.setOnClickListener(new View.OnClickListener() { 
      @Override 
      public void onClick(View v) { 
      // to perform click on entire row do like this. 
      } 
     }); 
     textView.setOnClickListener(new View.OnClickListener(){ 
     @Override 
     public void onClick(View v){ 
     // perform clicks to views inside the items. 
    } 
} 
+0

Clickリスナーは、自分の例のように子RecyclerViewを削除すると、ルートRecyclerViewで動作します。この問題は、すべてのイベントをインターセプトする入れ子のRecyclerViewがある場合に表示されます。 –

関連する問題