What am i trying to do?
I have a RecyclerView which keeps rows and looks like a ListView. Rows of these list are defined at feed_listview_row.xml. I have a RecyclerAdapter for this RecyclerView and it is called Feed_Recycler_Adapter.java which you can see down below.
I want to show an AlertDialog when user Long clicked one of these rows. There is a question about if user want to delete this row or keep it. After user accept to delete the row. There will be SnackBar to notify user and give her/him a last chance to take it back.
Problem
I can't view the SnackBar because I am not able to show the CoordinatorLayout.
Code
Feed_Recycler_Adapter.java
holder.layout.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
cl = (CoordinatorLayout) v.findViewById(R.id.FeedCoordinatorLayout);
/*this row above is failing to fill the cl variable.
This cl variable returns null! I need to get the coordinatorlayout for snackbar*/
String selectedListId = mDataset.get(position).getListId();
String selectedPostId = mDataset.get(position).getPostId();
Push_Options.ownerOrfollower(v.getContext(), selectedListId, selectedPostId, cl);
return true;
}
});
I tried
I tried to inflate the content_feed at the Feed_Recycler_Adapter.ViewHolder onCreateViewHolder and using it as a view to get FeedCoordinatorLayout, did not solve the problem but I didn't get any error simply because cl wasn't null. But didn't show the SnackBar either.
I read some part of your huge question and find that you need to rootView (coordinatorLayout)
this maybe help you :
add constructor to YourAdapter with Context param:
public yourAdapter(Context activity){
context = activity // context is local object
}
now in your activity where you create an instance of YourAdapter do this :
adapter = new YourAdapter(YourActivity.this);
and when you want to show snakBar :
Snackbar.make(((YourActivity) context).coordinatorLayout, "Post deleted.", Snackbar.LENGTH_LONG).show()
sure to define coordinatorLayout in YourActivity class .
I found the solution with a little bit of help from dariush f's answer.
I modified my existing constructor to have a CoordinatorLayout field.
class Feed_Recycler_Adapter extends RecyclerView.Adapter<Feed_Recycler_Adapter.ViewHolder> {
public static String TAG = "Feed Recycler Adapter";
private CoordinatorLayout cl;
private ArrayList<Feed_List_Class> mDataset;
//CoordinatorLayout cl;
// Provide a suitable constructor (depends on the kind of dataset)
Feed_Recycler_Adapter(ArrayList<Feed_List_Class> myDataset,CoordinatorLayout coordinatorLayout) {
cl=coordinatorLayout;
mDataset = myDataset;
}
And i am sending the coordinator layout when i instantiate this adapter. Like this:
CoordinatorLayout cl = (CoordinatorLayout) findViewById(R.id.FeedCoordinatorLayout) ;
mAdapter = new Feed_Recycler_Adapter(posts,cl);
Since my onLongClick method is already inside my Adapter class i can create a snackbar with my cl variable which is set by my constructor.
Push_Options.ownerOrfollower(v.getContext(), selectedListId, selectedPostId, cl);
Related
I've a vertical RecyclerView and each element of it, is a nested horizontal RecyclerView. Both have their Adapter and ViewHolder. When I change a flag, I want to be able to refresh the drawing of all items in each inner horizontal RecyclerView. I've written a method in the outer adapter that consequently call the inner one:
OuterAdapter:
public void setEditEnabled(boolean enabled) {
innerAdapter.setEditEnabled(enabled);
notifyDataSetChanged();
}
InnerAdapter:
public void setEditEnabled(boolean enabled) {
this.editable = enabled;
notifyDataSetChanged();
}
Then in the activity I call:
outerAdapter.setEditEnabled(editable);
outerRecyclerView.invalidate();
But only some "rows" are correctly updated...How can I solve this?
EDIT: so the flow is:
Outer setEditEnabled -> inner setEditEnabled -> inner notify -> outer notify
First of all, why are you calling invalidate on the RecyclerView. when you call notifyDataSetChanged() on the adapter, it automatically updates the RecyclerView items. And why is there only one inner Adapter. I think there should be a different adapter for each horizontal RecyclerView.
I don't know if I'm doing it right, but it seems to work fine.
Previously I was instantiating a new innerAdapter in the ViewHolder constructor:
class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView innerRecyclerView;
ViewHolder(View view) {
super(view);
[...] // init things
innerRecyclerView.setAdapter(new InnerAdapter(context, onItemClickListener));
}
}
Now I'm doing the new in the OuterAdapter constructor and saving the reference as private field of the class:
public DailyMenusAdapter(Context context, OnItemClickListener onItemClickListener) {
innerAdapter = new InnerAdapter(context, onItemClickListener);
}
Then I pass the reference to:
innerRecyclerView.setAdapter(innerAdapter);
Is that ok in your opinion? So reusing the same adapter over and over again.
You know, I think if you will create annonimous Adapter class instead of notifyDataSetChanged (somethink like mRecyclerView.setAdapter(new YourAdapter(this, listOfDataYouPass))) it will work fine.
EDIT:Usually we do something like this to set adapter to RecyclerView:
List mLogs = new ArrayList<>;
mLogs.addAll (logs);
mLogAdapter = new LogAdapter(this, mLogs);
mRecyclerView = findViewById(R.id.log_list);
mRecyclerView.setLayoutManager(new LinearLayoutManager(this));
mRecyclerView.setAdapter(mLogAdapter);
and then, when we need to modify something we call mLogAdapter.notifyDataSetChanged. Or just call notifyDataSetChanged inside Adapter class. But in many cases it works wrong, hard to say why in your case.
So here is what I advise:
instead of call notifyDataSetChanged make this method and call it when you need to update or change something:
private void updateRecyclerView (){
mRecyclerView.setAdapter(new LogAdapter(this, mLogs));
}
Note: Do not pay attention to the names, I took code from the first project I got
I am trying to get viewholder of a particular position from recycle view.
I had found to do so by doing this in MainActivity :
viewHolderRec = recyclerViewInstance.findViewHolderForAdapterPosition(vHID);
It works well inside my MainActivity.
But, when I tried to implement the same inside my recycle view cursor adapter like this :
I had changed the cursor to take recycleview :
In my MainActivity :
final RecyclerView recyclerView = findViewById(R.id.recycle_view);
LinearLayoutManager manager = new LinearLayoutManager(getApplicationContext());
recyclerView.setLayoutManager(manager);
recyclerView.setNestedScrollingEnabled(true);
mCursorAdapter = new RecycleCursorAdapter(this, null, recyclerView);
recyclerView.setAdapter(mCursorAdapter);
In RecycleCursorAdapter :
RecyclerView recyclerViewInstance;
public RecycleCursorAdapter(Context context, Cursor cursor, RecyclerView recyclerView) {
super(context, cursor);
recyclerViewInstance = recyclerView;
}
#Override
public void onBindViewHolder(ChatCursorAdapter3.ViewHolder viewHolder, Cursor cursor) {
ChatListItem myListItem = ChatListItem.fromCursor(cursor);
int vHID = viewHolder.getAdapterPosition();
viewHolderRec = (ViewHolder) recyclerViewInstance.findViewHolderForAdapterPosition(vHID - 1);
}
But viewHolderRec is null.
I want to get the viewholder of the view which is above the current viewHolder.
I just want to know how can I get it work.
What I want is to use findViewHolderForAdapterPosition() method inside my recycle view cursor adapter.
Yes, I know that I can use
viewHolder.getOldPosition()
But It does not fulfill my needs at the time of scrolling the recycle view.
Thanks In Advance :-)
I had searched a lot and find that, Instead of using recyclerViewInstance.findViewHolderForAdapterPosition(vHID - 1);
I can find the viewholder of any position by storing the viewholders in a List<> like
List<ViewHolder> vList = new ArrayList<>();
and whenever i want the viewholder of any posstion than i can find by
int vHID = viewHolder.getAdapterPosition();
viewHolderRec = vList.get(vHID - 1);
As what i need to do.
But, I know this effects lots of memory when the list is very large.
So what I did is removing the viewholders from memory which are not visible right now.
For this, I get the height in display pixels(dp) and get the number of visible items by it. and remove all the other viewholders.
This trick works for me. May Be this will be helpful for someone so I posting this answer.
I have a list of data to be displayed in a recycler view and that is working fine. Now I have another dynamic list of data to be displayed inside the parent recycler-view. So I tried using another recycler-view inside the parent recycler-view, that is not working fine. It will be good if I get some idea of using recycler-view inside another one. Thanks in advance..!
I have illustrated my problem with an example:
For eg: I have a parent recyclerView with five linearLayout and I have created a child recyclerView inside the Linearlayout with visibility GONE. Now when I click the first Linearlayout I am changing the visibility of child recyclerView for the first Linearlayout to VISIBLE and attaching a separate view to it and same concept for all the other Linearlayouts. What happens is when I click first, second, third and fourth linearLayout the child recyclerView is not displaying date which I pass to it, all those first, sec, third and fourth data are accumulated and displayed in the last (i.e) inside fifth linearLayout.
Here is my parent recyclerview code:
class CardAdapter extends RecyclerView.Adapter<CardAdapter.MyViewHolder>
{
RecyclerView insideCardRecyclerView,recyclerView;
List<String> monthsWeek = new ArrayList<>();
List<String> dealers = new ArrayList<>();
List<String> dealersList = new ArrayList<>();
List<String> date = new ArrayList<>();
HashSet<String> dealersListHash = new HashSet<>();
public CardAdapter(List<String> monthsWeek,List<String> dealers,List<String> date)
{
this.monthsWeek = monthsWeek;
this.dealers = dealers;
this.date = date;
}
public class MyViewHolder extends RecyclerView.ViewHolder
{
ProgressBar progressBar;
RecyclerView recyclerView;
TextView period;
LinearLayout linearLayoutParent,linearLayoutCardDetails;
public MyViewHolder(View view)
{
super(view);
linearLayoutParent = (LinearLayout) view.findViewById(R.id.card_view_linear_parent_layout);
linearLayoutCardDetails = (LinearLayout) view.findViewById(R.id.linear_card_layout_details);
period = (TextView) view.findViewById(R.id.period_summary_graph_card);
insideCardRecyclerView = (RecyclerView) view.findViewById(R.id.summary_graph_card_view_recycler_view);
}
}
#Override
public CardAdapter.MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType)
{
View itemView = LayoutInflater.from(getActivity())
.inflate(R.layout.summary_card_view,parent,false);
recyclerView = (RecyclerView) parent;
return new CardAdapter.MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(final MyViewHolder holder, final int position)
{
holder.period.setText(monthsWeek.get(position));
holder.linearLayoutParent.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view)
{
if(searchClick)
{
for (String date1 : date)
{
if(Objects.equals(date1,monthsWeek.get(position)))
{
Log.e("Summary123 date..///", date1);
dealersList.add(dealers.get(date.indexOf(date1)));
}
}
searchClick = false;
holder.linearLayoutCardDetails.setVisibility(View.VISIBLE);
dealersListHash.addAll(dealersList);
dealersList.clear();
dealersList.addAll(dealersListHash);
//if the condition is true i am attaching another recyclerview inside this.
cardAdapterList = new CardAdapterList(dealersList);
LinearLayoutManager mLayoutManager1 = new LinearLayoutManager(getActivity(), LinearLayoutManager.VERTICAL,true);
mLayoutManager1.setReverseLayout(false);
insideCardRecyclerView.setLayoutManager(mLayoutManager1);
insideCardRecyclerView.setItemAnimator(new DefaultItemAnimator());
insideCardRecyclerView.setAdapter(cardAdapterList);
}
else
{
searchClick = true;
holder.linearLayoutCardDetails.setVisibility(View.GONE);
}
}
});
}
#Override
public int getItemCount() {
return monthsWeek.size();
}
}
I've been in trouble sometimes with RecyclerView when multiple lists are needed to be shown in a single page of the application. Its not a very good idea actually to have multiple lists in a single layout but however, the idea of having a ScrollView and the lists inside that ScrollView is even worse.
I had to implement a ListView inside a ScrollView once and yes it was not a very good experience. Firstly, my list was not scrolling at all. Then I had to add some code to disable the scrolling when the touch is detected inside the list. It was not a very good idea of solving the actual problem. I had another problem of having a fixed height of the ListView. In case of list items with dynamic heights, the solution failed.
Having two lists in the layout, one after one is not a good idea either. As the first list need to have a fixed height.
So, after searching for suggestions about how can I implement two lists in a single layout file, I found most of the developers suggests of having a single list with a header and footer if necessary. Later, I could manage to show two lists in a single RecyclerView using my custom Adapter. I thought I should save some of my code for future use and hence, you see this note.
You can refer this sample code.
I am trying to wrap my head around implementing adapters properly and would like to know the ff:
Is it okay to pass an activity context to an adapter? I've read somewhere it is bad practice to pass around activity context and find it confusing.
In getting the parent view e.g to be used in SnackBar etc., is instantiating it via constructor like this
this.mRootView =
((Activity)mContext).getWindow().getDecorView().findViewById(R.id.activity_country_search);`
the right way or should it be instantiated in onCreateViewHolder and utilize the View parent parameter like this:
```
#Override
public CountryIssuingViewHolder onCreateViewHolder(ViewGroup parent) {
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_country_search_issuing_row, parent, false);
this.mRootView = parent.findViewById(R.id.activity_country_search);
return new CountryIssuingViewHolder(v, true);
}
Is it okay to implement snackbar in the adapter or should I use eventbus and show it via activity?
Current adapter implementation:
Activity
adapter = new CountryIssuingListAdapter(this, countryIssuingList, new
CountryPhoneCodeAdapter.OnItemClickListener() {
#Override
public void onItemClick(Country item, View v) {
Gson gson = new Gson();
Intent intent = new Intent();
intent.putExtra("COUNTRY", gson.toJson(item));
setResult(RESULT_OK, intent);
finish();
}
});
Adapter
public CountryIssuingListAdapter(Context mContext, ArrayList<Country> itemArrayList, CountryPhoneCodeAdapter.OnItemClickListener listener) {
this.countryArrayList = itemArrayList;
this.countryArrayListForSearch = new ArrayList<>();
this.countryArrayListForSearch.addAll(itemArrayList);
this.mContext = mContext;
this.TAG = mContext.getClass().getSimpleName();
this.mRootView = ((Activity)mContext).getWindow().getDecorView().findViewById(R.id.activity_country_search);
this.listener = listener;
}
Thanks a lot.
I don't see anything bad here. But in most cases you don't need a Context inside an adapter
2-3. Do you use a SnackBar to diplay an event (e.g. click, swipe)? If so, then you should pass a listener to the adapter and implement it in an activity like you did for OnItemClick. This way the adapter's work is only to display items and notify about User's actions but do not handle these actions
P.S. I would argue about using an EventBus instead of listeners but that's not the topic
I am having adapter class, In that, I need to pass invoiceId to an Activity Class. I have seen some example like pass-through interface, but I lost track on following the code procedure.
Here Is My Adapter Class extends BaseAdapter
#Override
public View getView(final int position, View convertView, ViewGroup parent) {
companyName = ct.getSharedPreferences("prefs", 0);
Log.d("test", "" + deliveryListBeans.size());
LayoutInflater inflater = (LayoutInflater) ct.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = inflater.inflate(R.layout.list_vew_for_delivery_order, null);
TextView invoice = (TextView) v.findViewById(R.id.invoice);
final TextView delivery = (TextView) v.findViewById(R.id.do_delivery);
final DeliveryListBean dlb = deliveryListBeans.get(position);
invoice.setText(dlb.getInvoiceNo());
}
delivery.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
ct.startActivity(new Intent(ct, EmployeesListForPopUp.class));
DeliveryOrdersListAdapter deliveryOrdersListAdapter=new DeliveryOrdersListAdapter(EmployeesListForPopUp.this);
}
});
}
Here is My Activity Class
public class EmployeesListForPopUp extends Activity {
private List<EmployeeIdNameBean> employeeIdNameBeans = new ArrayList<EmployeeIdNameBean>();
ListView listView;
SharedPreferences companyName;
EmployeePopUpAdapter employeePopUpAdapter;
private ImageView img1;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_employees_list_for_pop_up);
I need to get invoiceId from Adapter Class. How?
You need to pass context of the activity in adapters constructor.
Then set activity.invoiceid value in clickevents of adapter.
One simple way is that you write a method in MainActivity
public void setInvoiceId(int invoiceId) {
// do what you want with invoiceId
}
and pass the instance of your activity to adapter
DeliveryOrdersListAdapter adapter = new DeliveryOrdersListAdapter(EmployeesListForPopUp.this);
and get it in your adapter and keep it
EmployeesListForPopUp myActivity;
public MyAdapter(EmployeesListForPopUp activity) {
myActivity = activity;
}
and where you need to pass invoiceId just call the method of main activity
myActivity.setInvoiceId(invoiceId);
General way of implementing it:
In the adapter class, where you set text to invoice TextView, you also can add a tag to it. Put attention - despite every item in the list is build from the same prototype, the tag (as well as text) will be uniq. The best way is to use "position" as value of the tag: invoice.setText(dlb.getInvoiceNo());
invoice.setTag(Integer.valueOf(position).toString());
You need to make your items in the list clickable (this is out of the scope of this question). So, when you click on some item - you can retrieve any data it has, and specifically tag - getTag();.
Then you send Intent to other activity, providing the tag as extra message. So that activity will "know" which item in the array list it is related to (i.e. tag == position, right?). And continue from there.
I implemented simple project that illustrates it. This project is simple demo and illustration of working with ArrayList adapter,
displaying the item in the ListView, clicking on some item and display relevant data in separated activity. Please download it and try (min API 21). Basic description is available in README file.
The project is here on the GitHub:
(corrected path)
https://github.com/everall77/ArrayListSimpleExmpl