I'm currently new to android development and I'm in the process of building my first app. I'm stuck on a particular tricky bit in which I'm trying to implement and Endless Recycler View.
I'm using a recycler view (currently) just to get the current dates and display them in a calendar output. There is two things that are really holding me up. One there is not much documentation out there on using an endless recycler view without a database (I do plan to but again not right now), and not knowing where/how to make it endless. The recyclerView I have Implemented is already working I just need it to load endlessly. The only examples I could find essentially required me to be using a database, or were to create one gigantic method in the onCreate method. Again I have also tried to implement my own setOnScrollListener inside the home class but alas I couldn't get it working.
Below are the desired results that I've got pictured in my head.
Link to UI picture
Here is my code so far. Ideally I would like to keep the on scroll Listener in the RecyclerAdapter as I will probably reuse it later on in other aspects of the app. Thank you in advanced.
HOME CLASS
public class Home extends AppCompatActivity implements AdapterView.OnItemSelectedListener {
private static final String TAG = "Home Class" ;
//Recycler View Variables and myCalendarClass Variables
private myCalendarClass myCalendarObj;
private int weeksInView = 1; //will need to make this update dynamicallly based on when a user toggles the view
private ArrayList<Calendar> calendarArrayList = new ArrayList<>();
private ArrayList<Integer> iconList = new ArrayList<>();
private ArrayList<Integer> eventCounterList = new ArrayList<>();
//recycler view dynamic loading variables
private RecyclerView calendarRecyclerView;
private boolean isLoading = true;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_home);
initializeNavMenuButtons();
setViewSpinner();
this.myCalendarObj = new myCalendarClass();
setAllCalendarFields(this.myCalendarObj);
getImagesAndEventCounters();
//Example of the order to call the methods when a user switches the view
//setCalendarItrToCurrentSunday();
//setCalendarArrayList();
//getImagesAndEventCounters();
}
//Recycler view code
//gets images and events to feed into the recycler view
private void getImagesAndEventCounters() {
Log.d(TAG, "initImageBitMaps: called");
ArrayList<Calendar> weekView = getCalendarArrayList();
int daysInView = this.weeksInView * 7;
System.out.println("Days in view " + daysInView + " Weeks In View " + this.weeksInView);
for (int i = 0; i < daysInView; i++) {
calendarArrayList.add(weekView.get(i));
iconList.add(R.id.fitnessIcon);
eventCounterList.add(R.string.XEventsDefault);
iconList.add(R.id.educationIcon);
eventCounterList.add(R.string.XEventsDefault);
iconList.add(R.id.workIcon);
eventCounterList.add(R.string.XEventsDefault);
iconList.add(R.id.personalIcon);
eventCounterList.add(R.string.XEventsDefault);
initRecyclerView();
}
}
//passes data to the parent recycler view and sets the view
private void initRecyclerView() {
//For one recyclerView
this.parentLayoutManager = new GridLayoutManager(this, 7);
//GridLayoutManager layoutManager = parentLayoutManager;
RecyclerView recyclerView = findViewById(R.id.calendarRecyclerView);
recyclerView.setLayoutManager(parentLayoutManager);
RecyclerViewAdapter adapter = new RecyclerViewAdapter(this,iconList,eventCounterList,weeksInView,calendarArrayList);
recyclerView.setAdapter(adapter);
//ViewSpinner(Drop Down Menu)
private void setViewSpinner(){
Spinner viewSpinner = findViewById(R.id.ViewDropDownButton);
ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(this,R.array.Calendar_View_List, android.R.layout.simple_spinner_item);
viewSpinner.setAdapter(adapter);
viewSpinner.setOnItemSelectedListener(this);
}
#Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String text = parent.getItemAtPosition(position).toString();
myCalendarClass myCalendarObj;
if(text.equals("Week View")){
myCalendarObj = new myCalendarClass("Week View");
setAllCalendarFields(myCalendarObj);
getImagesAndEventCounters();
this.myCalendarObj = myCalendarObj;
}
else if (text.equals("Biweekly View")){
myCalendarObj = new myCalendarClass("Biweekly View");
setAllCalendarFields(myCalendarObj);
getImagesAndEventCounters();
this.myCalendarObj = myCalendarObj;
}
else if (text.equals("Month View")){
myCalendarObj = new myCalendarClass("Month View");
setAllCalendarFields(myCalendarObj);
getImagesAndEventCounters();
this.myCalendarObj = myCalendarObj;
}
}
#Override
public void onNothingSelected(AdapterView<?> parent) {
}
//setters
//sets all the local variables needed from myCalendarClass
private void setAllCalendarFields(myCalendarClass c){
this.myCalendarObj = c;
this.calendarArrayList = c.getCalendarArrayList();
this.weeksInView = c.getWeeksInView();
}
//getters
//returns the calendar arraylist
private ArrayList<Calendar> getCalendarArrayList(){
return this.calendarArrayList;
}
RECYCLER ADAPTER
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ViewHolder> {
private static final String TAG = "RecyclerViewAdapter";
//variables for creating the recyclerView cards
private ArrayList<Integer> iconList;
private ArrayList<Integer> eventCounterList;
private Context mContext;
private int itemCount;
private ArrayList<Calendar> calendarArrayList;
private ArrayList<String> dayStr;
private ArrayList<String> dayNum;
public RecyclerViewAdapter(Context context, ArrayList<Integer> imageList, ArrayList<Integer> eventArray, int weeksInView,ArrayList<Calendar> calendarArrayList){
this.iconList = imageList;
this.eventCounterList = eventArray;
this.mContext = context;
this.itemCount = weeksInView*7;
this.calendarArrayList = calendarArrayList;
setDayStrNDayNum();
}
#NonNull
#Override
//this is the method that actually inflates each individual layout
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
Log.d(TAG, "onCreateViewHolder: called.");
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout_listitem,parent,false);
return new ViewHolder(view);
}
#Override
//this is where we bind the data to each individual list items
//essentially all the data and stuff is actually attached in this method to each indiviual list item
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Log.d(TAG, "onBindViewHolder: called");
holder.fitnessIcon.getDrawable();
holder.educationIcon.getDrawable();
holder.personalIcon.getDrawable();
holder.workIcon.getDrawable();
holder.fitnessEventCounter.setText(eventCounterList.get(position));
holder.educationEventCounter.setText(eventCounterList.get(position));
holder.workEventCounter.setText(eventCounterList.get(position));
holder.personalEventCounter.setText(eventCounterList.get(position));
holder.dayString.setText(dayStr.get(position));
holder.dayNum.setText(dayNum.get(position));
}
//returns the amount of items we wish to include in the recycler view (not the individual items within a card layout
// but instead how many cards we wish to include...probably
#Override
public int getItemCount() {
return itemCount;
}
public class ViewHolder extends RecyclerView.ViewHolder{
ImageView fitnessIcon;
ImageView educationIcon;
ImageView workIcon;
ImageView personalIcon;
TextView dayString;
TextView dayNum;
TextView fitnessEventCounter;
TextView educationEventCounter;
TextView workEventCounter;
TextView personalEventCounter;
public ViewHolder(View itemView){
super(itemView);
//icons within the layout_listitem
fitnessIcon = itemView.findViewById(R.id.fitnessIcon);
educationIcon = itemView.findViewById(R.id.educationIcon);
workIcon = itemView.findViewById(R.id.workIcon);
personalIcon = itemView.findViewById(R.id.personalIcon);
//text fields within the layout_listitem
dayNum = itemView.findViewById(R.id.dayNum);
dayString = itemView.findViewById(R.id.dayStr);
fitnessEventCounter = itemView.findViewById(R.id.fitnessEventCounter);
educationEventCounter = itemView.findViewById(R.id.educationEventCounter);
workEventCounter = itemView.findViewById(R.id.workEventCounter);
personalEventCounter = itemView.findViewById(R.id.personalEventCounter);
}
}
public void setItemCount(int newItemCount){
itemCount = newItemCount;
}
//sets the dayStr arrays and the dayNum array using CalendarArrayList
// to pull the day in the month and the string value(monday,tuesday,etc.)
private void setDayStrNDayNum(){
Iterator<Calendar> itr = this.calendarArrayList.iterator();
ArrayList<String> tempDayNum = new ArrayList<>();
ArrayList<String> tempDayStr = new ArrayList<>();
int i = 0;
while(itr.hasNext()){
String dayInMonth = getDayInMonth(calendarArrayList.get(i));
String weekDayToString = weekDayToString(calendarArrayList.get(i));
tempDayNum.add(dayInMonth);
tempDayStr.add(weekDayToString);
i++;
itr.next();
}//while
this.dayNum = tempDayNum;
this.dayStr = tempDayStr;
}
//takes in a Calendar Obj, gets the int, and returns it in a String Format
private String getDayInMonth (Calendar c){
Integer temp = c.get(Calendar.DAY_OF_MONTH);
String answer = Integer.toString(temp);
return answer;
}
//takes in a Calendar Obj, gets the weekday digit from 1 to 7, and returns a String
// 1 being Su for Sunday, 2 Mo for Monday, and etc.
private String weekDayToString (Calendar x){
int temp = x.get(Calendar.DAY_OF_WEEK);
switch(temp){
case 1:
return "Su";
case 2:
return "Mo";
case 3:
return "Tu";
case 4:
return "We";
case 5:
return "Th";
case 6:
return "Fr";
case 7:
return "Sa";
}
return "null";
}//weekDayToString
}
here is an idea: in onCreate create some base list containing the next 1000 dates surrounding the current date for your recyclerview and use the following link to understand how to find out after any scrollevent at which position you are on the recyclerview. once you get close to the edges simply add an additional 1000 dates on that end of the spectrum.
Look at the following link to see how to set an onscrolllistener on a recyclerview inside of the adapter (basically you need to pass the recyclerview to the adapters constructor)
hope this helps
Related
i created a custom listView of all installed apps but i don't know how to add a search functionality because it's a little complicated(..my app) can anyone help me with that?
(picture of the app )
App.java - app constructor
public class App {
private int number;
private String name;
private String version;
private Drawable drawable;
public App(int number, String name, String version, Drawable drawable){
this.number = number;
this.name = name;
this.version = version;
this.drawable = drawable;
}
//Getters & Setters...
}
AppAdapter.java - listView Adapter
public class AppAdapter extends ArrayAdapter<App> {
Context context;
List<App> objects;
public AppAdapter(Context context, int resources, int textViewResources, List<App> objects){
super(context, resources, textViewResources, objects);
this.context = context;
this.objects = objects;
}
public View getView(int position, View convertView, ViewGroup parent){
LayoutInflater layoutInflater = ((Activity)context).getLayoutInflater();
View view = layoutInflater.inflate(R.layout.custom_card,parent,false);
TextView tvName = (TextView)view.findViewById(R.id.tvName);
TextView tvVersion = (TextView)view.findViewById(R.id.tvVersion);
TextView tvNumber = (TextView)view.findViewById(R.id.tvNumber);
ImageView ivImage = (ImageView)view.findViewById(R.id.ivImage);
App current = objects.get(position);
tvName.setText(String.valueOf(current.getName()));
tvVersion.setText(String.valueOf(current.getVersion()));
tvNumber.setText(String.valueOf(current.getNumber()));
ivImage.setImageDrawable(current.getDrawable());
return view;
}
}
MainActivity.java
public class MainActivity extends AppCompatActivity {
ArrayList<App> appList;
ListView lv;
AppAdapter appAdapter;
App lastSelected;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
EditText etSearch = findViewById(R.id.etSearch);
PackageManager packageManager = getPackageManager();
List<PackageInfo> mApps = packageManager.getInstalledPackages(0);
//array strings to all packages, names and version
final String[] arrPackages = new String[mApps.size()];
final String[] arrVersion = new String[mApps.size()];
String[] arrName = new String[mApps.size()];
//array of Drawables for icons...
Drawable[] arrIcons = new Drawable[mApps.size()];
App[] arrApps = new App[mApps.size()];
appList = new ArrayList<>();
//reading all app's packages and version to the arrays
for (int i = 0; i < mApps.size(); i++){
arrVersion[i] = mApps.get(i).versionName;
arrPackages[i] = mApps.get(i).packageName;
}
for (int i = 0; i < mApps.size(); i++){
try {//getting app's names from theres packages
arrName[i] = (String) packageManager.getApplicationLabel(packageManager.getApplicationInfo(arrPackages[i], PackageManager.GET_META_DATA));
} catch (PackageManager.NameNotFoundException e) {
arrName[i] = "Unknown";
}
try {//same as names for icons
arrIcons[i] = packageManager.getApplicationIcon(arrPackages[i]);
} catch (PackageManager.NameNotFoundException e) {
arrIcons[i] = getDrawable(R.drawable.placeholder);
}
arrApps[i] = new App(i + 1, "Name: "+arrName[i], "Version: "+arrVersion[i], arrIcons[i]);
appList.add(arrApps[i]);
}
//on item click open app
appAdapter = new AppAdapter(this,0,0,appList);
lv = findViewById(R.id.lv);
lv.setAdapter(appAdapter);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
lastSelected = appAdapter.getItem(position);
Intent launchIntent = getPackageManager().getLaunchIntentForPackage(arrPackages[position]);
if (launchIntent != null) {
startActivity(launchIntent);//null pointer check in case package name was not found
}
}
});
//(trying to..) Add Text Change Listener to EditText
etSearch.addTextChangedListener(new TextWatcher() {
#Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Call back the Adapter with current character to Filter
MainActivity.this.appAdapter.getFilter().filter(s.toString());
}
#Override
public void beforeTextChanged(CharSequence s, int start, int count,int after) {
}
#Override
public void afterTextChanged(Editable s) {
}
});
}
}
(when i try to search something it's gives nothing...)
exchange this line
App current = objects.get(position);
to this
App current = (App) getItem(position);
you don't need to keep List<App> objects; inside your adapter, it is passed in constructors super call (last param) and kept under the hood for you. check out ArrayAdapter source - passed ArrayList is kept as mObjects and further used in some methods, e.g. getPosition, getCount and getItem
ArrayAdapter already implements Filterable, so there is an overriden method getFilter, which returns ArrayFilter - this inner class is declared on the bottom. when you call getFilter().filter( then performFiltering gets called (in a separated thread) and iterates through local copy of your data (line 588). It is using values.get(i).toString().toLowerCase() to compare objects from array with passed String (CharSequence in fact). so in your custom App class override toString method and return in there some searchable value, e.g.
#Override
public String toString(){
return name;
}
this is not best approach, because toString may be used in a lot of mechanisms (its base method of Object) and with above toString implementation two Apps with same name, but different version or number are threated as same object, which isn't true... maybe better way would be to return name+version+number;, but still you have also Drawable in there. thats why I've suggested (in this answer before edit) to make own class extends BaseAdapter and implement own filtering or at least use ArrayAdapter, but override getFilter method and return your own Filter implementation comparing variables instead of using toString. Then won't be needed to override this class, leave it as it is. By default it returns kind-of memory address, so it is unique for every new App instance, even when created with exacly same variables
also in THIS answer you can find nice example how to implement that Filter
very new to Android App development here. I'm not sure if I'm asking the right question, but here's my situation. I have a simple gradebook app that looks like the following:
My goal is to display multiple courses on the app. I am using the RecyclerView to implement this, using the following onCreate function in my activity to pass the course information to the adapter:
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grade);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
for(int i = 0; i<2; i++)
{
Course course = Course.generateRandomCourse();
recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
mAdapter = new RecyclerViewAdapter(course);
recyclerView.setAdapter(mAdapter);
}
My goal for the for loop was to have two courses show up, but this is not the case. I'm not sure whether I need to rework my Recycling Adapter to take a list of courses, or if there is a simple way to display multiple courses.
Any help is much appreciated! Thank you for your time :)
EDIT: After adding 2 recyclers in XML and trying to invoke both, this is the code I used:
Course course = Course.generateRandomCourse();
Course course1 = Course.generateRandomCourse();
recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
recyclerView1 = (RecyclerView) findViewById(R.id.togglegrades1);
layoutManager = new LinearLayoutManager(this);
layoutManager1 = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
recyclerView1.setLayoutManager(layoutManager1);
mAdapter = new RecyclerViewAdapter(course);
mAdapter1 = new RecyclerViewAdapter(course1);
recyclerView.setAdapter(mAdapter);
recyclerView1.setAdapter(mAdapter1);
Now the output is just overlapped.
EDIT 2: Including full adapter code below.
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>
{
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private List<Course> courses = new ArrayList<>();
private ArrayList<Assignment> assignments;
private String course;
private String average;
public RecyclerViewAdapter(List<Course> courses)
{
for(Course c : courses)
{
course = c.getCourseTitle();
assignments = c.getAssignments();
if(assignments.size()==0)
{
average = "0";
}
else
{
Integer grade_total = new Integer(0);
Integer assignment_total = new Integer(0);
for (Assignment i : assignments)
{
grade_total += i.getAssignmentGrade();
assignment_total++;
}
average = Integer.toString(grade_total /assignment_total);
}
}
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType)
{
if(viewType == TYPE_HEADER)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
return new ViewHolderHeader(v);
}
else if(viewType == TYPE_ITEM)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_course, parent, false);
return new ViewHolderItem(v);
}
else return null;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
if(holder instanceof ViewHolderHeader)
{
ViewHolderHeader VHHeader = (ViewHolderHeader)holder;
VHHeader.title.setText(course);
VHHeader.average.setText("Average: " + average + "%");
}
else if(holder instanceof ViewHolderItem)
{
String assignemnt_name = assignments.get(position-1).getAssignmentTitle();
String assignment_grade = Integer.toString(assignments.get(position-1).getAssignmentGrade());
ViewHolderItem VHItem = (ViewHolderItem)holder;
VHItem.name.setText(assignemnt_name);
VHItem.grade.setText(assignment_grade + "%");
}
}
#Override
public int getItemCount()
{
return assignments.size() + 1;
}
private boolean isPositionHeader(int position)
{
if(position == 0)
return true;
else
return false;
}
#Override
public int getItemViewType(int position)
{
if(isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}
public class ViewHolderHeader extends RecyclerView.ViewHolder
{
TextView title;
TextView average;
public ViewHolderHeader(View view)
{
super(view);
this.title = (TextView)itemView.findViewById(R.id.course);
this.average = (TextView)itemView.findViewById(R.id.average);
}
}
public class ViewHolderItem extends RecyclerView.ViewHolder
{
TextView name;
TextView grade;
public ViewHolderItem(View view)
{
super(view);
this.name = (TextView)itemView.findViewById(R.id.assignment);
this.grade = (TextView)itemView.findViewById(R.id.grade);
}
}
}
You do not need to use multiple RecyclerView.
Just look at your code and you will find the answer why your code is not working.
RecyclerView is similar like listview where you have to pass a list. But in your code you are passing just 1 object of course.
You must not add recyclerview inside for loop, as recyclerview purpose is to store list,not 1 object.
So you should pass List to the recyclerview.
#Override
protected void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_grade);
getSupportActionBar().setDisplayShowHomeEnabled(true);
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
getSupportActionBar().setDisplayShowTitleEnabled(false);
recyclerView = (RecyclerView) findViewById(R.id.togglegrades);
layoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(layoutManager);
List<Course> listCourse=new ArrayList();
for(int i = 0; i<2; i++)
{
listCourse.add(Course.generateRandomCourse());
}
mAdapter = new RecyclerViewAdapter(listCourse);
recyclerView.setAdapter(mAdapter);
}
And inside the adapter you can use this list of course.
For more reference you can check this link, on how to use RecyclerView with Adapter
EDIT: In your adapter look at this code:
private boolean isPositionHeader(int position)
{
if(position == 0)
return true;
else
return false;
}
Here position == 0 is static code, which will not work for dynamic code.If your list contains 1 Header and 1 Item, than you can change simply position%2 == 0 condition. else you have to create other recyclerview inside adapter.
Check out below code and comments I have added , at the end of onBindViewHolder you can add new recyclerview, for that you have to add recyclerview in your layout also.
Checkout how you can use List<Course> courses in your constructor and onBindViewHolder.
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEM = 1;
private List<Course> courses = new ArrayList<>();
public RecyclerViewAdapter(List<Course> courses)
{
this.courses = courses;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent,int viewType)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
return new ViewHolderHeader(v);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position)
{
Course c=courses.get(position);
String course = c.getCourseTitle();
String assignments = c.getAssignments();
if(assignments.size()==0)
{
average = "0";
}
else
{
Integer grade_total = new Integer(0);
Integer assignment_total = new Integer(0);
for (Assignment i : assignments)
{
grade_total += i.getAssignmentGrade();
assignment_total++;
}
average = Integer.toString(grade_total /assignment_total);
}
ViewHolderHeader VHHeader = (ViewHolderHeader)holder;
VHHeader.title.setText(course);
VHHeader.average.setText("Average: " + average + "%");
//Here you can set new recyclerview in your adapter for assignments
}
#Override
public int getItemCount()
{
return courses.size();
}
/*private boolean isPositionHeader(int position)
{
//Change logic here as per your list, because you have list not single object.
//if(position == 0)
// return true;
//else
// return false;
}*/
//Remove this code as you have not different views in your list
/*#Override
public int getItemViewType(int position)
{
if(isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEM;
}*/
public class ViewHolderHeader extends RecyclerView.ViewHolder
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.header_course,parent,false);
return new ViewHolderHeader(v);
}
}
You need to have 2 RecycleViews in your activity_grade xml layout, and do what you're doing inside the for loops for each.
What your for loop is currently doing, is creating a random course, and then setting that course to the RecyclerView called togglegrades, then on the second iteration, it does the same thing again, on the same RecyclerView
So what I would do after adding a second RecyclerView in the xml layout, is replace the for loop with something like this:
recyclerView1 = (RecyclerView) findViewById(R.id.togglegrades);
recyclerView2 = (RecyclerView) findViewById(R.id.togglegrades2);
layoutManager = new LinearLayoutManager(this);
recyclerView1.setLayoutManager(layoutManager);
recyclerView2.setLayoutManager(layoutManager);
mAdapter = new RecyclerViewAdapter(course);
// Maybe create a different course here and set it to a new adapter variable
recyclerView.setAdapter(mAdapter);
recyclerView2.setAdapter(mAdapter);
You can do it in a very optimal way. Please see the tutorial titled Recyclerview multiple view type. Here you don't need to create multiple recycler views and you can use a single recycler view for multiple types of data.
Why do you use two RecyclerView?
Your Layout looks simple, just use one recyclerView with two viewType.
This is a sample
I made an GridView to organize TextView and Edit views, like a table , programatically, using an CustomAdapter. In getView() override method i create the views, add it to a arrayList and let the abstraction do its job. The problem arises when i try to acess the views outside the adapter class, in this case, in OnCreate of the activity. I was trying to get the array through getItems() method which returns the arrayList of views. But it returns am empty array (i check with a log that prints 0). And i'm wondering why is that happening? maybe the getView() methods runs only after i try to get the array in onCreate, somehow the onCreate finishes before the adapter is fully ready?
and if that's the case, where/when should i get the views?
Here's my activity and adapter class:
public class MatrizMultiplicationActivity extends AppCompatActivity {
MatrizGridAdapter gridAdapter;
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_matriz_mult);
GridView gridView = (GridView) findViewById(R.id.grid_view);
gridView.setNumColumns(5);
gridAdapter = new MatrizGridAdapter(this,5,5);
gridView.setAdapter(gridAdapter);
ArrayList<EditText> matrix = gridAdapter.getItems();
Log.d("mSize", String.valueOf(matrix.size()));
}
public class MatrizGridAdapter extends BaseAdapter {
Context ctx;
int rows;
int coluns;
int size;
ArrayList<EditText> views;
public MatrizGridAdapter(Context ctx, int rows, int coluns)
{
this.ctx = ctx;
this.rows= rows;
this.coluns = coluns;
this.size = rows*coluns;
this.views = new ArrayList<EditText>();
}
// .. some methods
public ArrayList<EditText> getItems()
{
return views;
}
#Override
public int getCount() {
return size;
}
#Override
public EditText getItem(int position) {
if((views.size()<position))
{
return views.get(position);
}
return null;
}
#SuppressLint("ResourceAsColor")
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if(convertView==null)
{
CardView cardview = new CardView(ctx);
//code for setting CardView attributes
//...
//..
//layout logic, doesn't really matter
if(position==0)
{
TextView textView = new TextView(ctx);
//code for setting textView attributes
cardview.addView(textView);
cardview.setBackgroundColor(R.color.cinzarrom);
}
//layout logic, doesn't really matter
//it is the first row, so put the numbers of coluns above
else if(position<coluns)
{
//code for setting textView attributes
//...
//..
cardview.addView(textView);
cardview.setBackgroundColor(R.color.cinzarrom);
setMarginLeft(textView, MyMath.convertDpToPixels(20,ctx));
}
//it is the first colun in any row, so put the number of rows on the left
else if(position%coluns==0)
{
//code for setting textView attributes
cardview.addView(textView);
cardview.setBackgroundColor(R.color.cinzarrom);
setMarginLeft(textView, MyMath.convertDpToPixels(20,ctx));
}
//ARE THE VIEWS BEING ADDED BEFORE THE CALL TO ARRAY.SIZE?()
else {
EditText editText = new EditText(ctx);
//add it to the array who stores the text views
views.add(editText);
//code for setting textView attributes
cardview.setBackgroundColor(Color.BLUE);
cardview.addView(editText);
}
return cardview;
}
return convertView;
}
}
Currently had a listview where I showed:
moves
That works fine.
The problem is that now I would like to add pagination, so I show 10 "moves", and then, with an arrow or something, link to the next 10 "moves".
This is my ListView:
RecyclweView Java:
RecyclerView lstMovsWallet = (RecyclerView) findViewById(R.id.lstMovsWallet);
lstMovsWallet.setLayoutManager(new
LinearLayoutManager(MovsMobileWallet.this));
AdapterCobrosPendientesListado adapter = new
AdapterCobrosPendientesListado(MovsMobileWallet.this, items);
lstMovsWallet.setAdapter(adapter);
Adapter for de RecyclerView :
public class AdapterCobrosPendientesListado extends RecyclerView.Adapter<AdapterCobrosPendientesListado.ViewHolder> {
private LayoutInflater mInflater;
protected List<MovimientoCuenta> items;
public AdapterCobrosPendientesListado(Context context, List<MovimientoCuenta> data) {
this.mInflater = LayoutInflater.from(context);
this.items = data;
}
#Override
public AdapterCobrosPendientesListado.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.activity_adapter_billings_listhistory, parent, false);
ViewHolder viewHolder = new ViewHolder(view);
return viewHolder;
}
#Override
public void onBindViewHolder(AdapterCobrosPendientesListado.ViewHolder holder, int position) {
DecimalFormat formater = new DecimalFormat("###.00");
String numero = items.get(position).getNumber();
String cantidad = items.get(position).getMonto();
String fecha = items.get(position).getFecha();
String referencia = items.get(position).getReferencia();
String debitoCredito = items.get(position).getDebitoCredito();
holder.number.setText(numero);
holder.mount.setText(cantidad);
holder.date.setText(fecha);
holder.ref.setText(referencia);
if(debitoCredito.compareTo("DBT")==0){
holder.title.setText("Pago");
holder.auxBilling.setImageResource(R.mipmap.signonegativo);
}
else {
holder.title.setText("Cobro");
holder.auxBilling.setImageResource(R.mipmap.signomas);
}
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
public TextView number;
public TextView mount;
public TextView date;
public ImageView auxBilling;
public TextView ref;
public TextView title ;
public ViewHolder(View itemView) {
super(itemView);
number = itemView.findViewById(R.id.txtNumberPhoneBilling);
mount = itemView.findViewById(R.id.txtMountBillingNotifications);
date = itemView.findViewById(R.id.txtDateBillingNotifications);
auxBilling = itemView.findViewById(R.id.btnCancelBillingNotifications);
ref = itemView.findViewById(R.id.txtDateBillingRef);
title = itemView.findViewById(R.id.TitleMovs);
itemView.setOnClickListener(this);
}
#Override
public void onClick(View view) {
// if (mClickListener != null) mClickListener.onItemClick(view, getAdapterPosition());
}
}
/* // convenience method for getting data at click position
public String getItem(int id) {
return mData.get(id);
}
// allows clicks events to be caught
public void setClickListener(ItemClickListener itemClickListener) {
this.mClickListener = itemClickListener;
}
// parent activity will implement this method to respond to click events
public interface ItemClickListener {
void onItemClick(View view, int position);
}*/
}
Here I leave the class of movements to replicate:
public class MovimientoCuenta {
private String number;
private String monto;
private String moneda;
private String fecha;
private String ID;
private String referencia ;
private String filtro ;
private String debitoCredito ;
private String nombreMov;
public MovimientoCuenta(String number, String monto, String moneda, String fecha, String ID, String referencia, String filtro, String debitoCredito,String nombreMov) {
this.number = number;
this.monto = monto;
this.moneda = moneda;
this.fecha = fecha;
this.ID = ID ;
this.filtro =filtro;
this.referencia=referencia;
this.debitoCredito =debitoCredito;
this.nombreMov =nombreMov;
}
Any help will be welcome from now thanks.
You can try adding another data model in your list at the end of the current list. Have the view type of this model be a button. On clicking this, you can fetch the next set of moves from your server using offset and limit in your query.
Make the list of a generic type say Parcelable and have all the elements in the list be child via either extending or implementing the parent type.
Eg the list can be of type class A
ArrayList<A>
And the elements in it can be of types
Class B extends A
Class C extends A
Now in your getView , check the type of data using instance of operator and inflate the correct view layout
If(yourlist.get(positionpassedingetview) instanceof B){
//Inflate the view for B item if not already inflated
}else{
//Inflate view of type C
}
For every click on the above said load more item button, use the offset as the current length of list minus one and the limit will always be 10. For the first ever query, use offset as 0 or the sql will throw an exception, rest subsequent queries will have the offset = list size minus one (minus one because the last element of the list is a load more button and not the actual data)
Eg in mysql, this could be:
Select * from yourtable where yourcondition order by yourorder limit youroffset,10;
If there's no more data, just update the model of the load more saying no more items and disabling the button. If you find more items, insert them at the poisition above the last element which is the load more button and notify the list adapter.
You will have to find a tutorial for such a heterogenous list view with more than one viewtypes and viewholders. This is a general idea of how to do it, i myself use this approach with a recyclerview with a similar logic
I'm having this issue with my android custom listview such that everytime i exit the activity (i.e. click the "back" button on emulator), then return back to the same activity that contains this custom listview, the listview adds an additional row to itself.
For example, originally it is:
item a
When I leave that activity and come back to it, the row doubles:
item a
item a
However, when i restart the emulator again, the custom listview goes back to the original number of data retrieved from sqlite.
How do I stop the rows from doubling themselves?
Here are my codes.
list.java:
//DATABASE
MyItems mi;
//For Items display - ArrayList
private ArrayList<SalesItemInformationLV> displayiteminfo;
/* new ArrayList<SalesItemInformationLV>(); */
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list_sale_item);
final float sellingpvalue = 13.5f;
final float costpvalue = 19.0f;
final String datesoldvalue = "9/9/1995";
final String staffdiscountvalue = "true";
mi = MyItems.getInstance();
displayiteminfo = mi.retrieveAllForlist(getApplicationContext());
//New array adapter for customised ArrayAdapter
final ArrayAdapter<SalesItemInformationLV> adapter = new itemArrayAdapter(this, 0, displayiteminfo);
//displayiteminfo - the ArrayList of item objects to display.
//Find the list view, bind it with custom adapter
final ListView listView = (ListView)findViewById(R.id.customListview);
listView.setAdapter(adapter);
// listView.setLayoutParams(new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 9));
//LONG PRESS CONTEXT MENU
registerForContextMenu(listView);
//Selecting the listview item!
listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
#Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
SalesItemInformationLV saleitem = displayiteminfo.get(position);
String namevalue = saleitem.getItemname();
int qtyvalue = saleitem.getItemquantity();
Intent myintent = new Intent(ListSaleItemActivity.this, ViewSaleDetails.class);
myintent.putExtra("itemname", namevalue);
myintent.putExtra("itemqty", qtyvalue);
myintent.putExtra("itemcp", costpvalue);
myintent.putExtra("itemsp", sellingpvalue);
myintent.putExtra("itemds", datesoldvalue);
myintent.putExtra("itemsstaffdis", staffdiscountvalue);
startActivity(myintent);
}
});
}
//custom Arrayadapter
class itemArrayAdapter extends ArrayAdapter<SalesItemInformationLV>
{
private Context context;
private List<SalesItemInformationLV> item;
//constructor, call on creation
public itemArrayAdapter(Context context, int resource, ArrayList<SalesItemInformationLV> objects) {
//chaining to "default constructor" of ArrayAdapter manually
super(context, resource, objects);
this.context = context;
this.item = objects;
}
//called to render the list
public View getView(int position, View convertView, ViewGroup parent)
{
//get the item we are displaying
SalesItemInformationLV iteminfo = item.get(position);
//get the inflater and inflate the xml layout for each item
LayoutInflater inflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
View view = inflater.inflate(R.layout.item_layout, null);
//Each component of the custom item_layout
TextView name = (TextView) view.findViewById(R.id.ItemNameSales);
TextView qty = (TextView)view.findViewById(R.id.ItemNameQty);
//set the name of item - access using an object!
name.setText(String.valueOf(iteminfo.getItemname()));
//set the quantity of item - access using an object!
qty.setText(String.valueOf(iteminfo.getItemquantity()));
return view;
//Now return to onCreate to use this cuztomized ArrayAdapter
}
}
Myitems.java:
public class MyItems extends Application {
//ID and contact information
private List<String> contactList;
private List<Integer> contactIdList;
private static MyItems ourInstance = new MyItems();
//Populate SaleItemInformationLV
private ArrayList<SalesItemInformationLV> displayiteminfo2 =
new ArrayList<SalesItemInformationLV>();
public MyItems()
{
contactList = new ArrayList<String>();
contactIdList = new ArrayList<Integer>();
}
public static MyItems getInstance(){
return ourInstance;
}
//RETRIEVE ALL ENTRIES
//LISTVIEW
public ArrayList<SalesItemInformationLV> retrieveAllForlist(Context c)
{
Cursor myCursor;
String mystring = "";
MyDbAdapter db = new MyDbAdapter(c);
db.open();
//contactIdList.clear();
//contactList.clear();
myCursor = db.retrieveAllEntriesCursor();
if (myCursor !=null && myCursor.getCount()>0)
{
myCursor.moveToFirst();
do {
displayiteminfo2.add(new SalesItemInformationLV(myCursor.getString(db.COLUMN_NAME_ID), db.COLUMN_QTYSOLD_ID));
} while (myCursor.moveToNext());
}
db.close();
return displayiteminfo2;
}
MyItems is a (java-)singleton. Each times that you call public ArrayList<SalesItemInformationLV> retrieveAllForlist(Context), you add objects in displayiteminfo2 et return this list.
If you call a second times retrieveAllForlist, you keep the same list with objects already in it and add more to it.
It's a bad pattern to return a private instance object in a function. Anything outside of your class can modify the list. Just create one for returning it.
public ArrayList<SalesItemInformationLV> retrieveAllForlist(Context c)
{
ArrayList<SalesItemInformationLV> items = new ArrayList<SalesItemInformationLV>();
Cursor myCursor;
String mystring = "";
MyDbAdapter db = new MyDbAdapter(c);
db.open();
//contactIdList.clear();
//contactList.clear();
myCursor = db.retrieveAllEntriesCursor();
if (myCursor != null && myCursor.getCount() > 0)
{
myCursor.moveToFirst();
do {
items.add(new SalesItemInformationLV(myCursor.getString(db.COLUMN_NAME_ID), db.COLUMN_QTYSOLD_ID));
} while (myCursor.moveToNext());
}
db.close();
return items;
}
It looks like MyItems is a singleton. Are you clearing the values before calling
mi.retrieveAllForlist(getApplicationContext())? If not, you may be doubling up the values when onCreate() is called after returning to the activity.