I create a RecyclerView containing list of items with vertical orientation.
I added another RecyclerView inside a single node in the parent RecyclerView to show a grid of items inside each cell.
I added a click listener for the grid item in the cell as like : mRvHomeMosaic.setOnClickListener(this); inside view holder, but it's not getting triggered by a click.
Please help me, if anyone has the same problem. Or anyone who use Nested RecyclerView.
My Holder class and onBindView() is given below
class HomeViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
RecyclerView mRvHomeMosaic;
public HomeViewHolder(View itemView) {
super(itemView);
this.mRvHomeMosaic = (RecyclerView) itemView.findViewById(R.id.rv_home_mosaic_image);
mRvHomeMosaic.setEnabled(true);
mRvHomeMosaic.setOnClickListener(this);
}
#Override
public void onClick(View view) {
if (clickListener != null) {
clickListener.itemClickListner(view, getPosition());
}
}
}
#Override
public void onBindViewHolder(final HomeViewHolder holder, int position) {
if (mDataList.get(position).getmMosaicModelList() != null &&
mDataList.get(position).getmMosaicModelList().size() > 0) {
holder.mRvHomeMosaic.setEnabled(true);
HomeSubAdapter adapter = new HomeSubAdapter(mContext, mDataList.get(position).
getmMosaicModelList());
holder.mRvHomeMosaic.setAdapter(adapter);
StaggeredGridLayoutManager mLayoutManager = new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL);
holder.mRvHomeMosaic.setLayoutManager(mLayoutManager);
} else {
holder.mRvHomeMosaic.setEnabled(true);
List<MosaicTileModel> modelList = new ArrayList<>();
MosaicTileAdapter adapter = new MosaicTileAdapter(mContext, modelList);
holder.mRvHomeMosaic.setAdapter(adapter);
StaggeredGridLayoutManager mLayoutManager = new StaggeredGridLayoutManager(3,
StaggeredGridLayoutManager.VERTICAL);
holder.mRvHomeMosaic.setLayoutManager(mLayoutManager);
}
}`
The single node has some other items also. Click listener is properly triggered that items. But in the case of Inner Recylcerview object it's not getting triggered.
Related
I have the problem that I want to update a nested RecyclerView with dynamically loading data. The outer recyclerView is vertical and the inner recyclerView is horizontal. So, I have created 2 adapters.
The main activity:
public class GroupScreenActivity extends AppCompatActivity {
private RecyclerView recyclerView;
private OuterRecyclerViewAdapter adapter;
// the service connection
private ServiceConnection connection = new ServiceConnection() {
... // code that handles the service connection (not relevant for my question)
}
};
#Override
protected void onStart(){
// code that bind the service to the activity (not really relevant for my question)
}
#Override
protected void onStop(){
// code that unbinds the service from the activity (not really relevant for my question)
}
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_group_screen);
recyclerView = (RecyclerView) findViewById(R.id.recycler_View);
recyclerView.setLayoutManager(new LinearLayoutManager(this));
updateUI();
}
// a Handler that calls a method of a bound service to retrieve the data of interest
private void updateUI(final String token){
final Handler handler = new Handler();
handler.post(new Runnable() {
#Override
public void run() {
if(bound && (mDownloadCliquesService != null)){
// holds the list of the statement lists
ArrayList<ArrayList<Statement>> myList = mDownloadCliquesService.getDataOfUser();
if(adapter == null){
// data is passed to the outer recyclerView adapter
adapter = new OuterRecyclerViewAdapter(this, myList);
recyclerView.setAdapter(adapter);
}
else{
// notify that the data is changed
adapter.notifyDataSetChanged();
}
}
// repeat the whole after 5 seconds
handler.postDelayed(this, 5000);
}
});
}
}
As you can see: The main activity just retrieves some data from a bound service and passes it to the outer recycler view. The data is a list of lists of type Statement. The number of lists in myList gives the rows of the outer recyclerview and the items in each list will define the number of columns of each inner recyclerview.
The outer recycler view looks like the following:
public class OuterRecyclerViewAdapter extends RecyclerView.Adapter<OuterRecyclerViewAdapter.InnerRecyclerViewHolder> {
// some instance variables
public OuterRecyclerViewAdapter( ... ) {
...
}
#Override
public InnerRecyclerViewHolder onCreateViewHolder(final ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(context);
View view = inflater.inflate(R.layout.inner_recyclerview_layout, parent, false);
return new InnerRecyclerViewHolder(view);
}
#Override
public void onBindViewHolder(final InnerRecyclerViewHolder holder, int position) {
// here, I create the inner recycler view adapter. Do I need to update it too???
adapter = new InnerRecyclerViewAdapter(context, items, position);
holder.recyclerView.setLayoutManager(new LinearLayoutManager(context, LinearLayoutManager.HORIZONTAL, false));
holder.recyclerView.setAdapter(adapter);
}
#Override
public int getItemCount() {
return items.size();
}
public class InnerRecyclerViewHolder extends RecyclerView.ViewHolder {
private RecyclerView recyclerView;
private Button mAddButton;
private Button mSendButton;
private TextView tvCliqueName;
private ArrayList<Object> mList;
public InnerRecyclerViewHolder(View itemView) {
super(itemView);
// using 'itemView', get a reference to the inner recycler view.
recyclerView = (RecyclerView) itemView.findViewById(R.id.inner_recyclerView);
// get a reference to the clique name
tvCliqueName = (TextView) itemView.findViewById(R.id.cliqueName);
// get a reference to the send button
mSendButton = (Button) itemView.findViewById(R.id.send);
// get a reference to the add button
mAddButton = (Button) itemView.findViewById(R.id.add);
}
}
}
For the sake of brevity, I do not post the code for the inner recyclerview adapter because there is no adapter reference to update.
So, after every 5 second the main activity gets fresh data from my bound service and passes it to the outer recyclerview adapter which looks how many lists exist in the nested array list. Each list is then passed to the inner recycler view adapter which then shows the elements of each list.
So, my problem is the following: After each update the list is scrolling to the beginning. Let's say I have 5 elements in the first list, and I scroll to the 3rd one, after the update inner recycler view goes to the 1st automatically. Here is a short GIF how the output looks like:
I have checked out the following StackOverflow posts:
How to save RecyclerView's scroll position using RecyclerView.State?
Nested Recyclerview scrolls by itself
How to save scroll position of recyclerview in fragment
How to save scroll position of RecyclerView in Android?
But without success. How do I need to update so that the scroll position is not affected ?
Thanks and best regards
Save your horizontal scroll value:
outerRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() {
#Override
public void onScrollStateChanged(#NonNull RecyclerView recyclerView, int newState) {
super.onScrollStateChanged(recyclerView, newState);
}
#Override
public void onScrolled(#NonNull RecyclerView recyclerView, int dx, int dy) {
//save dx
}
});
and restore after update:
outerRecyclerView.setScrollX(dx)
It is basically a recyclerview that I fill with some textviews to show some categories. I have access to the position of clicked item within recyclerview, but how can I get a reference to the actual textview to set the background color?
here is my code
RecyclerView CategoriesRecyclerView;
RecyclerView.LayoutManager CategoriesLayoutManager;
CategoriesAdapter CategoriesAdapter;
List<Category> categories;
protected override void OnCreate(Bundle bundle)
{
base.OnCreate(bundle);
// Get our RecyclerView layout:
CategoriesRecyclerView = FindViewById<RecyclerView>(Resource.Id.CategoriesRecyclerView);
//............................................................
// Layout Manager Setup:
// Use the built-in linear layout manager:
CategoriesLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.Horizontal, true);
// Plug the layout manager into the RecyclerView:
CategoriesRecyclerView.SetLayoutManager(CategoriesLayoutManager);
//............................................................
// Adapter Setup:
CategoriesAdapter = new CategoriesAdapter(categories);
// Register the item click handler (below) with the adapter:
CategoriesAdapter.ItemClick += CategoriesOnItemClick;
// Plug the adapter into the RecyclerView:
CategoriesRecyclerView.SetAdapter(CategoriesAdapter);
}
void CategoriesOnItemClick(object sender, int position)
{
//here I want the reference to the textview
// ((TextView).SetBackgroundColor(Color.Aqua);
Toast.MakeText(this, "This is category " + categories[position].Id + categories[position].Name, ToastLength.Short).Show();
}
I found the answer to my question in case it would help someone. you use OnBindViewHolder method of the RecyclerView.Adapter and make a reference list of all the textviews. and then use the position to get the one clicked.
// ADAPTER
// Adapter to connect the data set to the RecyclerView:
public class CategoriesAdapter : RecyclerView.Adapter
{
// Event handler for item clicks:
public event EventHandler<int> ItemClick;
// Underlying data set
public List<Category> Categories;
public List<TextView> TextViews = new List<TextView>();
// Load the adapter with the data set at construction time:
public CategoriesAdapter(List<Category> categories)
{
this.Categories = categories;
}
// Create a new CardView (invoked by the layout manager):
public override RecyclerView.ViewHolder OnCreateViewHolder(ViewGroup parent, int viewType)
{
// Inflate the CardView for the photo:
View itemView = LayoutInflater.From(parent.Context).
Inflate(Resource.Layout.CategoryTextView, parent, false);
// Create a ViewHolder to find and hold these view references, and
// register OnClick with the view holder:
CategoryViewHolder vh = new CategoryViewHolder(itemView, OnClick);
return vh;
}
// Fill in the contents (invoked by the layout manager):
public override void OnBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
var vh = holder as CategoryViewHolder;
vh.CategoryTextView.Text = Categories[position].Name;
TextViews.Add(vh.CategoryTextView);
if (position == 0)
{
vh.CategoryTextView.SetBackgroundColor(Color.Aqua);
}
vh.CategoryTextView.Click += (sender, e) =>
{
foreach (var textView in TextViews)
{
textView.SetBackgroundColor(Color.White);
}
((TextView)sender).SetBackgroundColor(Color.Aqua);
};
}
// Return the number of photos available in the photo album:
public override int ItemCount
{
get { return Categories.Count; }
}
// Raise an event when the item-click takes place:
void OnClick(int position)
{
if (ItemClick != null)
ItemClick(this, position);
}
}
I can change properties of selected item in RecyclerView but I want to remove selection for older selections.
Here is how I create RecyclerView :
fragmentViewPagerAdapter.addFragmentView((arg1, arg2, arg3) -> {
View view = arg1.inflate(R.layout.recyclerview_layout, arg2, false);
RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recyclerView);
selectDateRecyclerViewAdapter = new SelectDateRecyclerViewAdapter(dayList,this,(v,position) ->
{
AppCompatButton appCompatButton = (AppCompatButton)v.findViewById(R.id.selectHourButton);
AppCompatImageView appCompatImageView = (AppCompatImageView)v.findViewById(R.id.calendarDot);
highlightButton(appCompatButton,appCompatImageView);
});
recyclerView.setHasFixedSize(false);
recyclerView.addItemDecoration(selectDateRecyclerViewAdapter. new CalendarItemDecoration(10,dayList.size()));
GridLayoutManager gridLayoutManager = new GridLayoutManager(getApplicationContext(),4,GridLayoutManager.VERTICAL,false);
recyclerView.setLayoutManager(gridLayoutManager);
recyclerView.setAdapter(selectDateRecyclerViewAdapter);
selectDateRecyclerViewAdapter.notifyDataSetChanged();
return view;
});
highlightButton method changes background of Button etc.
Thanks.
You may need to hold flags to record which buttons are selected, when you select a new item, clear the flags first and reset it to the position of your new selected item. Then notifyDataSetChanged() or notifyItemChanged().done.
The main code of this function may be placed in highlightButton method. so It's better If you post the highlightButton code.
Since its a single selection, you can track the selected position using an external variable, say int selectedIndex;
In your adapter code :
public class ViewHolder extends RecyclerView.ViewHolder {
View itemView;
public ViewHolder(View v) {
super(v);
itemView = v;
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
selectedPostion = getAdapterPosition();
if( selectedPosition == RecyclerView.NO_POSITION) return;
recyclerViewOnItemClickListener.onItemSelect(itemView, getAdapterPosition()); //Custom listener - in turn calls your highlightButton method
//call notifyDataSetChanged(); or notifyItemRangeChanged();
}
});
}
}
#Override
public void onBindViewHolder(ViewHolder holder, final int position) {
holder.itemView.setSelected(position == selectedPostion);
}
i want to show list as per image for that i am using recycler view and showing row its easy .but inside each row i want to showing many rows
say
i have 10 rows and each row has different row inside
so 1 row have 3 rows where as 2nd have 2 as on
so what is best way to do this
is it possible we can have one more listview inside that row ?
or inside onBindViewHolder i have to manually loop
and inflate layout
Edit :-
when i am trying this is always shuffles
#Override
public void onBindViewHolder(final RecyclerViewHolder holder, int position) {
for (int i = 0; i < position; i++) {
View c = ((Activity) mContext).getLayoutInflater().inflate(R.layout.row2, null);
// ((TextView) c.findViewById(R.id.mis)).setText(data.get(position) + "");
holder.inner.addView(c);
}
holder.n.setText(position+"");
holder.itemView.setTag(position);
}
image as follows
Yes you can use recyclerview inside recycler view just need to maintain separate adapter for that.
Or in this case you can also use expandable list view which will be much easier to use in this case.
If in your case, you don't have many rows, you can apply this:
Use NestedScrollview and add 2 RecyclerViews inside of it.
If you have specific number of rows like 2-3, it will be easy to implement.
Add layout_behavior to your RecyclerViews like below:
<android.support.v7.widget.RecyclerView
android:id="#+id/myRecyclerView"
app:layout_behavior="#string/appbar_scrolling_view_behavior"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
And wrap content for layout height is important.
android:layout_height="wrap_content"
And last, you should add this, so scroll works only for NestedScrollView
myRecyclerView.setNestedScrollingEnabled(false);
If you have many items use Single RecyclerView with multiple types of viewholders.
public class MyAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final int TYPE_MAIN = 0;
private final int TYPE_SUB = 1;
private ArrayList<Object> dataSet;
class ViewHolderMain extends RecyclerView.ViewHolder {
...
}
class ViewHolderSub extends RecyclerView.ViewHolder {
...
}
#Override
public int getItemViewType(int position) {
if(dataSet.get(position) instance of MainRowObject){
return TYPE_MAIN;
}else{
return TYPE_SUB;
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_MAIN: return new ViewHolderMain(...);
case TYPE_SUB: return new ViewHolderSub(...);
...
}
}
}
With the library SectionedRecyclerViewAdapter you can group your items in sections:
class MySection extends StatelessSection {
List<String> list;
public MySection(List<String> list) {
// call constructor with layout resource for this Section items
super(R.layout.section_item);
this.list = list;
}
#Override
public int getContentItemsTotal() {
return list.size(); // number of items of this section
}
#Override
public RecyclerView.ViewHolder getItemViewHolder(View view) {
// return a custom instance of ViewHolder for the items of this section
return new MyItemViewHolder(view);
}
#Override
public void onBindItemViewHolder(RecyclerView.ViewHolder holder, int position) {
MyItemViewHolder itemHolder = (MyItemViewHolder) holder;
// bind your view here
itemHolder.tvItem.setText(list.get(position));
}
}
Then you set up the RecyclerView with your sections:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Create your sections with the list of data per row
MySection row1Section = new MySection(data1List);
MySection row2Section = new MySection(data2List);
// Add your Sections to the adapter
sectionAdapter.addSection(row1Section);
sectionAdapter.addSection(row2Section);
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);
I'm trying to display the listview from sqlite. At the end of Listview I want to load next 10 data from sqlite and this process will continue till data ends in SQLite. But the issue is data load at first time successfully and when i scroll 3rd time page/data does not load and after LocalListPageIndex= 2 then progress bar is continuously running.
Here is my Code of ListView Scrolling.
listView.removeFooterView(progressBar);
listView.setOnScrollListener(new AbsListView.OnScrollListener() {
#Override
public void onScrollStateChanged(AbsListView view, int scrollState) {
}
#Override
public void onScroll(AbsListView view, int firstVisibleItem, int visibleItemCount, final int totalItemCount) {
lastInScreen = firstVisibleItem + visibleItemCount;
if (X != firstVisibleItem) {
if (LocalListPageIndex <= 5) {
Toast.makeText(getApplicationContext(), "Your Last Item." + lastInScreen, Toast.LENGTH_SHORT).show();
if (lastInScreen == totalItemCount) {
listView.addFooterView(progressBar);
// Execute some code after 15 seconds have passed
Handler handler = new Handler();
handler.postDelayed(new Runnable()
{
public void run()
{
LocalListPageIndex += 1;
int OFFSET_SCROLL = 10
List<All_Post> allDesc = dbhelper.getAllDescriptions(OFFSET_SCROLL);
for (All_Post all_Post : allDesc) {
descArray.add(all_Post);
}
if (adapter != null) {
adapter.notifyDataSetChanged();
// adapter = new AllPostAdapter(getApplicationContext(), R.layout.allpostlist, descArray);
listView.setAdapter(adapter);
//listView.invalidateViews();
listView.setSelection(totalItemCount);
}
}
}, 15000);
}
} if (LocalListPageIndex == 5) {
Log.e("hide footer", "footer hide");
listView.removeFooterView(progressBar);
}
}
X = firstVisibleItem;
}
});
You could try using RecyclerView:
The RecyclerView widget is a more advanced and flexible version of ListView. This widget is a container for displaying large data sets that can be scrolled very efficiently by maintaining a limited number of views. Use the RecyclerViewwidget when you have data collections whose elements change at runtime based on user action or network events.
A layout manager positions item views inside a RecyclerView and determines when to reuse item views that are no longer visible to the user. To reuse (or recycle) a view, a layout manager may ask the adapter to replace the contents of the view with a different element from the dataset. Recycling views in this manner improves performance by avoiding the creation of unnecessary views or performing expensivefindViewById() lookups
Example:
Add this view to your layout:
android.support.v7.widget.RecyclerView
android:id="#+id/my_recycler_view"
android:scrollbars="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent"
Once you have added a RecyclerView widget to your layout, obtain a handle to the object, connect it to a layout manager, and attach an adapter for the data to be displayed:
public class MyActivity extends Activity {
private RecyclerView mRecyclerView;
private RecyclerView.Adapter mAdapter;
private RecyclerView.LayoutManager mLayoutManager;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.my_activity);
mRecyclerView = (RecyclerView) findViewById(R.id.my_recycler_view);
// use this setting to improve performance if you know that changes
// in content do not change the layout size of the RecyclerView
mRecyclerView.setHasFixedSize(true);
// use a linear layout manager
mLayoutManager = new LinearLayoutManager(this);
mRecyclerView.setLayoutManager(mLayoutManager);
// specify an adapter (see also next example)
// Set your myDataSet with the url of your images.
mAdapter = new MyAdapter(myDataset);
mRecyclerView.setAdapter(mAdapter);
}
...
}
Create an adapter to manage the recycler view:
public class MyAdapter extends RecyclerView.Adapter<MyAdapter.ViewHolder> {
private MySQLClass mDataset;
// Provide a reference to the views for each data item
// Complex data items may need more than one view per item, and
// you provide access to all the views for a data item in a view holder
public static class ViewHolder extends RecyclerView.ViewHolder {
// each data item is just a string in this case
public ViewHolder(TextView v) {
super(v);
textView = v;
}
}
// Provide a suitable constructor (depends on the kind of dataset)
public MyAdapter(MySQLClass myDataset) {
mDataset = myDataset;
}
// Create new views (invoked by the layout manager)
#Override
public MyAdapter.ViewHolder onCreateViewHolder(ViewGroup parent,
int viewType) {
// create a new view
View v = LayoutInflater.from(parent.getContext())
.inflate(R.layout.my_image_view, parent, false);
// set the view's size, margins, paddings and layout parameters
...
ViewHolder vh = new ViewHolder(v);
return vh;
}
// Replace the contents of a view (invoked by the layout manager)
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
textView.setText(mDataset.get(i));
}
// Return the size of your dataset (invoked by the layout manager)
#Override
public int getItemCount() {
return mDataset.length;
}
}
info from:
http://developer.android.com/training/material/lists-cards.html
When you are working with retrieving the data from the database its better to use loaders.A loader will load all the details depending upon your query and give it to you in the form of a Cursor.Now you can simply give that cursor to CursorAdapter(if you are using Cursor adapter)or you can extract the data from cursor to a list and give that list to normal adapter.