1

I have a fragment which has a nested RecyclerView to display list of products. I implement this as per this solution by mmlooloo : How to create scrollable page of carousels in Android?

This is my fragment where I called my 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);
}

This is the 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);
}

With this implementation, I could click and have access to each ViewHolder, but I could not pass the data to my fragment. This is because I would like to add the clicked item to another list(productOrderList), and display it in a ListView. As a workaround now, I put the clicked Product into SharedPreferences and then load my fragment again.

This way, I can get the list from SharedPreferences and display my ListView, but I want to do a proper way of handling this type of situation. I'm really new to programming and still learning a lot of things on a daily basis. Really hope you guys could help me out on this.

Sorry if my explanation is not clear enough, please let me know if I need to add other details as well.

EDIT :

Screenshot of my current app

The orange box is my MainRecyclerProductAdapter while the box with green color is the RowRecyclerProductAdapter for each row. What I want to achieve is, once I clicked on any of the Product listed in my RecyclerView, I want it to be added into a productOrderList and generate a ListView in the box color purple.

Mike
  • 697
  • 1
  • 9
  • 21

2 Answers2

2

This will solve your problem: but I could not pass the data to my fragment

You can add an interface with the object as parameter, and by using this, you can now pass the data from your adapter to the fragment and use it for your: and display it in a ListView problem.

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
}
Tenten Ponce
  • 2,436
  • 1
  • 13
  • 40
  • Thanks, I will try your solution but I has 1 question, currently `myFragment` only create `MainRecyclerAdapter` and then from `MainRecyclerAdapter` it will create the `RowRecyclerAdapter`. So, how exactly I implement this "YourAdapter yourAdapter = new YourAdapter(new OnProductClickListener()" on `myFragment`? – Mike Dec 21 '17 at 04:44
  • Add a constructor on your MainRecyclerAdapter that accepts the interface, public MainRecyclerAdapter(OnProductClickListener onProductClickListener) { .... }, if you can show your code both fragment and adapter, that would be great. – Tenten Ponce Dec 21 '17 at 05:52
  • I've included my fragment and both main and row adapter as well. The code might be a bit messed up due to all the testing that I was doing. Hope you could understand it. – Mike Dec 21 '17 at 07:08
  • I've update your my code to match with your code. I've modified it directly so you can find just copy the code exactly to the code that I've switched. Tell me if you encountered some problems. – Tenten Ponce Dec 21 '17 at 07:20
  • Just tested your solution and it's working like a charm. Thanks so much Ponce. I really need to learn how to pass data between class/adapter/fragment. Sigh. *Sorry out of topic, any advise on what topic or site I could learn on this? – Mike Dec 21 '17 at 07:57
  • This method is by using callbacks. I've just encounter this solution during my experience on java programming. I don't know specific site for this, I just browse everything on google, changing my question format to look for any other workarounds if I encounter a problem. – Tenten Ponce Dec 21 '17 at 08:06
  • Oh, callbacks. At least now I know the keyword to search should I encounter these kind of situation. Thanks again Tenten Ponce. – Mike Dec 21 '17 at 08:39
0

First, you need an interface which will intercept the touch events inside the inner recyclerview so create a class inside like this. It will pass back any touch intercepted back to the parent calling class.

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

    }
}

}

now when you create your outer recyclerview adapter just initialize this one.

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

by this time you will have all the methods need to intercept the inner touch event which you have passed as this in outer adapter class.

but we also want these events to pass from the outer adapter class to the calling fragment set a listener like this.

rootAdapter.setOnItemClickListener(this);

now in the root adapter, when binding the items and creating the inner adapter, pass on the item click support listener to the inner adapter, like 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 is what you have set above through fragment.

In the inner adapter, when creating the views,set an click listener on the root of the layout inflated and pass that event back to the custom click listener:

    // 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 is actually the item click support which was passed by adapter described above.

vikas kumar
  • 10,447
  • 2
  • 46
  • 52
  • Thanks Vikas. Will try to implement your solution and I'll update once tested. – Mike Dec 21 '17 at 04:45
  • Hi, thanks for the answer Vikas. But I've accepted the Tenten answer because of the fact that he had answered first and was helping me applying it into my existing code. Nonetheless, I really appreciate you taking your time to answer my question. – Mike Dec 21 '17 at 08:03
  • no problem @Dzulhafiz glad you got your problem solved. – vikas kumar Dec 21 '17 at 16:42