【问题标题】:Nested RecyclerView handling onClick嵌套 RecyclerView 处理 onClick
【发布时间】:2017-12-21 02:38:31
【问题描述】:

我有一个片段,它有一个嵌套的 RecyclerView 来显示产品列表。我按照 mmlooloo 的解决方案实现了这一点:How to create scrollable page of carousels in Android?

这是我的片段,我称之为MainRecyclerProductAdapter

public class CashSaleFragment extends Fragment {
// TODO: Rename parameter arguments, choose names that match
// the fragment initialization parameters, e.g. ARG_ITEM_NUMBER
private static final String ARG_PARAM1 = "param1";
private static final String ARG_PARAM2 = "param2";
private static final String TAG = "MainActivity";
private UserPreference userPreference = new UserPreference();
int numOfRows = 11;
int numOfColumns = 8;
public int layout = 1; // 0-list_icon 1-list 2-grid_icon 3-grid
public List<Product> listProduct, listProductFilter;
private DatabaseHelper db;
private RecyclerView mRecyclerView;
private LinearLayoutManager mLayoutManager;
View view;
Activity activity;

// TODO: Rename and change types of parameters
private String mParam1;
private String mParam2;

private OnFragmentInteractionListener mListener;

public CashSaleFragment() {
    // Required empty public constructor
}

/**
 * Use this factory method to create a new instance of
 * this fragment using the provided parameters.
 *
 * @param param1 Parameter 1.
 * @param param2 Parameter 2.
 * @return A new instance of fragment CashSaleFragment_old.
 */
// TODO: Rename and change types and number of parameters
public static CashSaleFragment newInstance(String param1, String param2) {
    CashSaleFragment fragment = new CashSaleFragment();
    Bundle args = new Bundle();
    args.putString(ARG_PARAM1, param1);
    args.putString(ARG_PARAM2, param2);
    fragment.setArguments(args);
    return fragment;
}

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    if (getArguments() != null) {
        mParam1 = getArguments().getString(ARG_PARAM1);
        mParam2 = getArguments().getString(ARG_PARAM2);
    }
}

@Override
public View onCreateView(LayoutInflater inflater, final ViewGroup container,
                         Bundle savedInstanceState) {
    // Inflate the layout for this fragment
    view =  inflater.inflate(R.layout.fragment_cash_sale, container, false);

    activity = getActivity();
    userPreference.init(activity);
    db = DatabaseHelper.getInstance(activity);

    // Get layout setting value
    layout = Integer.parseInt(userPreference.getStringShared(SysPara.POS_LAYOUT));

    changeViewProductAll();

    return view;
}

public void onButtonPressed(Uri uri) {
    if (mListener != null) {
        mListener.onFragmentInteraction(uri);
    }
}

@Override
public void onAttach(Context context) {
    super.onAttach(context);
}

@Override
public void onDetach() {
    super.onDetach();
    mListener = null;
}

@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
    inflater.inflate(R.menu.cash_sale, menu);
    super.onCreateOptionsMenu(menu,inflater);
}

@Override
public boolean onOptionsItemSelected(MenuItem item) {
    return super.onOptionsItemSelected(item);
}

/**
 * This interface must be implemented by activities that contain this
 * fragment to allow an interaction in this fragment to be communicated
 * to the activity and potentially other fragments contained in that
 * activity.
 * <p>
 * See the Android Training lesson <a href=
 * "http://developer.android.com/training/basics/fragments/communicating.html"
 * >Communicating with Other Fragments</a> for more information.
 */
public interface OnFragmentInteractionListener {
    // TODO: Update argument type and name
    void onFragmentInteraction(Uri uri);
}

// Get all products
private List<Product> getProducts(String tms_id, String user_co_id) {
    return db.getAllProduct(tms_id, user_co_id);
}

// Convert to ListofListofProduct
private List<List<Product>> toListofListProduct(List<Product> product, int listSize, int row, int col) {
    List<List<Product>> listOfListOfProducts = new ArrayList<List<Product>>();
    int index = 0;
    // if listing, change layout to contain only 1 column in each row
    if (layout == 0 || layout == 1) {
        row = product.size();
        col = 1;
    }

    for(int i = 0 ; i<row ; i++){
        List<Product> listOfProducts = new ArrayList<Product>();
        for(int j = 0 ; j<col ; j++){
            if (listSize == index) {
                listOfListOfProducts.add(listOfProducts);
                return listOfListOfProducts;
            }
            listOfProducts.add(product.get(index));
            index++;
        }
        listOfListOfProducts.add(listOfProducts);
    }
    return listOfListOfProducts;
}

// Change the viewing display of product: list, listIcon, grid, gridIcon
public void changeViewProductAll() {
    mRecyclerView = (RecyclerView) view.findViewById(R.id.rv_product);
    mRecyclerView.addItemDecoration(new SimpleDividerItemDecoration(activity));
    mLayoutManager = new LinearLayoutManager(activity,LinearLayoutManager.VERTICAL,false);
    mRecyclerView.setLayoutManager(mLayoutManager);
    mRecyclerView.setHasFixedSize(true);

    //creation of the products for the grid/list
    listProduct = new ArrayList<Product>();
    listProductFilter = new ArrayList<Product>();

    listProduct = getProducts(userPreference.getStringShared(SysPara.TMS_ID), userPreference.getStringShared(SysPara.USER_CO_ID));
    List<List<Product>> ListofListofProducts = toListofListProduct(listProduct, listProduct.size(), numOfRows, numOfColumns);

    Fragment fragment = new CashSaleFragment();
    MainRecyclerProductAdapter mainRecyclerProductAdapter = new MainRecyclerProductAdapter(ListofListofProducts,activity,fragment);
    mRecyclerView.setAdapter(mainRecyclerProductAdapter);
}

这是MainRecyclerProductAdapter

public class MainRecyclerProductAdapter extends RecyclerView.Adapter<MainRecyclerProductAdapter.ViewHolder> {

public static final String LOGTAG ="MainRecyclerProductAdapter";
private List<List<Product>> mRows;
Context mContext;
Fragment mFragment = new CashSaleFragment();
public MainRecyclerProductAdapter(List<List<Product>> objects, Context context, Fragment fragment) {
    mContext = context;
    mRows = objects;
    mFragment = fragment;
}

static class ViewHolder extends RecyclerView.ViewHolder{
    public  RecyclerView mRecyclerViewRow;
    public ViewHolder(View itemView) {
        super(itemView);
        mRecyclerViewRow =(RecyclerView)itemView.findViewById(R.id.recyclerView_row);
    }
}

@Override
public int getItemCount() {

    return mRows.size();
}

@Override
public void onBindViewHolder(ViewHolder holder, int position) {

    List<Product> RowItems = mRows.get(position);
    LinearLayoutManager layoutManager = new LinearLayoutManager(mContext,LinearLayoutManager.HORIZONTAL,false);     
    holder.mRecyclerViewRow.setLayoutManager(layoutManager);
    holder.mRecyclerViewRow.setHasFixedSize(true);
    RowRecyclerProductAdapter rowsRecyclerAdapter = new RowRecyclerProductAdapter(mContext,RowItems,mFragment);
    holder.mRecyclerViewRow.setAdapter(rowsRecyclerAdapter);
}


@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {
    LayoutInflater inflater =    
            (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    View convertView = inflater.inflate(R.layout.recyclerview_row, parent, false);
    return new ViewHolder(convertView);
}

RowRecyclerProductAdapter

public class RowRecyclerProductAdapter extends RecyclerView.Adapter<RowRecyclerProductAdapter.ViewHolder> {

private OnItemClickListener onItemClickListener;
private List<Product>  mItems, productClickList;
private UserPreference userPreference = new UserPreference();
private int layout;
Context mContext;
com.m3online.i3twpos.util.OnItemClickListener onItemClickListener;
Fragment mFragment = new CashSaleFragment();
final Handler handler = new Handler();
public RowRecyclerProductAdapter(Context context, List<Product> objects, Fragment fragment) {
    mContext = context;
    mItems = objects;
    mFragment = fragment;

    userPreference.init(mContext);
    layout = Integer.parseInt(userPreference.getStringShared(SysPara.POS_LAYOUT));


}

static class ViewHolder extends RecyclerView.ViewHolder{
    public ImageView mImageView;
    public TextView mTextView, mSKU;
    public View rootView;
    public ViewHolder(View itemView) {
        super(itemView);
        rootView = itemView;
        mImageView =(ImageView)itemView.findViewById(R.id.productIcon);
        mTextView =(TextView)itemView.findViewById(R.id.productTitle);
        mSKU =(TextView)itemView.findViewById(R.id.sku);
    }
}

@Override
public int getItemCount() {

    return mItems.size();
}

public RowRecyclerProductAdapter (com.m3online.i3twpos.util.OnItemClickListener onItemClickListener) {
    this.onItemClickListener = onItemClickListener;
}

@Override
public void onBindViewHolder(final ViewHolder holder, int position) {
    final ViewHolder vh = (ViewHolder) holder;

    final Product product = mItems.get(position);

    holder.mTextView.setText(product.getProduct_name());
    holder.mSKU.setText(product.getProduct_sku());

    vh.itemView.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            Toast.makeText(mContext, "Click "+holder.mTextView.getText().toString(), Toast.LENGTH_SHORT).show();

            onItemClickListener.onItemClick(product);
        }
    });
}

public interface OnItemClickListener {
    void onItemClick(Product product);
}

@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int arg1) {
    LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
    View convertView = inflater.inflate(R.layout.product_grid, parent, false);
    return new ViewHolder(convertView);
}

通过这个实现,我可以单击并访问每个 ViewHolder,但我无法将数据传递给我的片段。这是因为我想将单击的项目添加到另一个列表(productOrderList),并将其显示在 ListView 中。现在作为一种解决方法,我将点击的Product 放入 SharedPreferences 中,然后再次加载我的片段。

这样,我可以从SharedPreferences 获取列表并显示我的ListView,但我想做一个正确的方法来处理这种情况。我对编程真的很陌生,并且每天仍在学习很多东西。真的希望你们能帮我解决这个问题。

对不起,如果我的解释不够清楚,如果我还需要添加其他详细信息,请告诉我。

编辑:

Screenshot of my current app

橙色框是我的MainRecyclerProductAdapter,而绿色框是每行的RowRecyclerProductAdapter。我想要实现的是,一旦我点击了RecyclerView 中列出的任何Product,我希望它被添加到productOrderList 中并在紫色框中生成一个ListView。

【问题讨论】:

    标签: android listview android-fragments android-recyclerview nestedrecyclerview


    【解决方案1】:

    这将解决您的问题:但我无法将数据传递给我的片段

    您可以添加一个以对象为参数的接口,通过使用它,您现在可以将数据从适配器传递到片段并将其用于您的:并在 ListView 中显示问题。

    CashSaleFragment

    MainRecyclerProductAdapter mainRecyclerProductAdapter = new MainRecyclerProductAdapter(ListofListofProducts,activity,fragment, new com.m3online.i3twpos.util.OnItemClickListener() {
        @Override
        public void onItemClick(Product product) {
            //you can now handle the product that is clicked from here on fragment. yey!
        }
    });
    

    MainRecyclerProductAdapter

    public static final String LOGTAG ="MainRecyclerProductAdapter";
    private List<List<Product>> mRows;
    Context mContext;
    Fragment mFragment = new CashSaleFragment();
    om.m3online.i3twpos.util.OnItemClickListener onItemClickListener; //add this
    
    //update your constructor, add your custom interface
    public MainRecyclerProductAdapter(List<List<Product>> objects, Context context, Fragment fragment, com.m3online.i3twpos.util.OnItemClickListener onItemClickListener) {
        mContext = context;
        mRows = objects;
        mFragment = fragment;
        this.onItemClickListener = onItemClickListener; //add this
    }
    
    .....
    
    @Override
    public void onBindViewHolder(ViewHolder holder, int position) {
    
        List<Product> RowItems = mRows.get(position);
        LinearLayoutManager layoutManager = new LinearLayoutManager(mContext,LinearLayoutManager.HORIZONTAL,false);     
        holder.mRecyclerViewRow.setLayoutManager(layoutManager);
        holder.mRecyclerViewRow.setHasFixedSize(true);
        RowRecyclerProductAdapter rowsRecyclerAdapter = new RowRecyclerProductAdapter(mContext, RowItems, mFragment, onItemClickListener);
        holder.mRecyclerViewRow.setAdapter(rowsRecyclerAdapter);
    }
    

    RowRecyclerProductAdapter

    public RowRecyclerProductAdapter(Context context, List<Product> objects, Fragment fragment, com.m3online.i3twpos.util.OnItemClickListener onItemClickListener) {
        mContext = context;
        mItems = objects;
        mFragment = fragment;
    
        userPreference.init(mContext);
        layout = Integer.parseInt(userPreference.getStringShared(SysPara.POS_LAYOUT));
    
        this.onItemClickListener = onItemClickListener; //add this, to get the interface passed from fragment to mainrecyclerproductadapter to here
    }
    

    【讨论】:

    • 谢谢,我会尝试您的解决方案,但我有 1 个问题,目前 myFragment 仅创建 MainRecyclerAdapter,然后从 MainRecyclerAdapter 创建 RowRecyclerAdapter。那么,我究竟如何在myFragment 上实现这个“YourAdapter yourAdapter = new YourAdapter(new OnProductClickListener()”?
    • 在您的 MainRecyclerAdapter 上添加一个接受接口的构造函数 public MainRecyclerAdapter(OnProductClickListener onProductClickListener) { .... },如果您可以同时显示代码片段和适配器,那就太好了。跨度>
    • 我已经包含了我的片段以及主适配器和行适配器。由于我正在进行的所有测试,代码可能有点混乱。希望你能理解。
    • 我已更新您的代码以匹配您的代码。我已直接对其进行了修改,因此您只需将代码完全复制到我已切换的代码即可。如果您遇到了一些问题,请告诉我。
    • 刚刚测试了您的解决方案,它的工作就像一个魅力。非常感谢庞塞。我真的需要学习如何在类/适配器/片段之间传递数据。叹。 *对不起,题外话了,有什么建议我可以在这方面学习什么主题或网站?
    【解决方案2】:

    首先,您需要一个接口来拦截内部 recyclerview 中的触摸事件,因此在内部创建一个这样的类。它会将截获的任何触摸传回给父调用类。

    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) {
    
        }
    }
    

    }

    现在,当您创建外部 recyclerview 适配器时,只需初始化这个。

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

    此时,您将拥有拦截您在外部适配器类中作为this 传递的内部触摸事件所需的所有方法。

    但我们也希望这些事件从外部适配器类传递到调用片段,设置一个像这样的侦听器。

    rootAdapter.setOnItemClickListener(this);
    

    现在在根适配器中,当绑定项目并创建内部适配器时,将项目单击支持侦听器传递给内部适配器,如下所示:

        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是你上面通过fragment设置的。

    在内部适配器中,当创建视图时,在布局的根节点上设置一个点击监听器并将该事件传递回自定义点击监听器:

        // 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实际上是上面描述的适配器传递的item click support。

    【讨论】:

    • 谢谢维卡斯。将尝试实施您的解决方案,一旦测试,我会更新。
    • 您好,感谢 Vikas 的回答。但我接受了 Tenten 的答案,因为他首先回答并帮助我将其应用到我现有的代码中。尽管如此,我非常感谢您抽出宝贵时间回答我的问题。
    • 没问题@Dzulhafiz 很高兴你的问题得到了解决。
    猜你喜欢
    • 2019-08-23
    • 2020-03-18
    • 1970-01-01
    • 2015-05-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-17
    • 1970-01-01
    相关资源
    最近更新 更多