I want to create the following UI with a single list of dataset.
I tried using multiple view type but could not achieve my requirement. I also implemented this blog Android Horizontal and Vertical RecyclerView Example.
But this uses two recyclerviews and there are two sets of data (horizontal data and vertical data).
I also have tried this one. RecyclerView with multiple views using custom adapter in Android
But this is using static card views in XML and loading them in adapter.
I'm beginner in Android development. Please help!
Thank you in advance.
Your desired layout can be achieved by using a GridLayoutManager along with two "item view types" inside your RecyclerView.Adapter.
Here are my layout XML files:
activity_main.xml:
-------------------------
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.RecyclerView
xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/recycler"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
horizontal.xml:
-------------------------
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_margin="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal">
<ImageView
android:id="#+id/image"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="#ccc"/>
<TextView
android:id="#+id/text"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center"
android:background="#fff"
android:textColor="#000"
android:textSize="18sp"
android:text="TEXT"/>
</LinearLayout>
</android.support.v7.widget.CardView>
vertical.xml:
-------------------------
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="120dp"
android:layout_margin="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/image"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:background="#ccc"/>
<TextView
android:id="#+id/text"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="1"
android:gravity="center"
android:background="#fff"
android:textColor="#000"
android:textSize="18sp"
android:text="TEXT"/>
</LinearLayout>
</android.support.v7.widget.CardView>
And here is my Java file:
public class MainActivity extends AppCompatActivity {
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
GridLayoutManager manager = new GridLayoutManager(this, 2);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
return (position % 4) < 2 ? 2 : 1;
}
});
RecyclerView recycler = (RecyclerView) findViewById(R.id.recycler);
recycler.setLayoutManager(manager);
recycler.setAdapter(new MyAdapter());
}
private static class MyAdapter extends RecyclerView.Adapter<MyViewHolder> {
#Override
public int getItemViewType(int position) {
return (position % 4) < 2
? R.layout.horizontal
: R.layout.vertical;
}
#Override
public MyViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(viewType, parent, false);
return new MyViewHolder(itemView);
}
#Override
public void onBindViewHolder(MyViewHolder holder, int position) {
holder.image.setImageResource(R.drawable.mouse);
holder.text.setText("" + position);
}
#Override
public int getItemCount() {
return Integer.MAX_VALUE;
}
}
private static class MyViewHolder extends RecyclerView.ViewHolder {
private final ImageView image;
private final TextView text;
public MyViewHolder(View itemView) {
super(itemView);
this.image = (ImageView) itemView.findViewById(R.id.image);
this.text = (TextView) itemView.findViewById(R.id.text);
}
}
}
Let's go over the important parts. First up is the combination of GridLayoutManager and SpanSizeLookup. We're creating the layout manager with this line:
GridLayoutManager manager = new GridLayoutManager(this, 2);
Which means that, by default, there will be two cards in each row of our grid. But then we apply the SpanSizeLookup, which says that half of our rows (found by the statement position % 4 < 2) should actually take up two columns. So we'll have one card, one card, two cards repeating in our "grid".
Then, in the RecyclerView.Adapter class, we override the getItemViewType() method. Here we again use the position % 4 < 2 statement to say that half of our views should be horizontal, and half should be vertical.
getItemViewType() just needs to return any unique int for each view type, so we use a nice trick of returning R.layout constants from this method. Since the view type will then be passed into onCreateViewHolder(), we can use the viewType argument to inflate the correct layout.
And that's it! Not too bad after all. Here's a screenshot of my code in action:
I had this issue not too long ago, though I may not be an expert I think my answer can help you as well. You can do this by creating two different types of layouts, similar to the article. You need to create an abstract class that both of the layout types can extend. Then, in your adapter, check what type of object should be shown.
Abstract class:
public abstract class ListItem {
public static final int TYPE_HORIZONTAL = 0;
public static final int TYPE_VERTICAL = 1;
abstract public int getType();
}
Horizontal Item:
public class HorizontalItem extends ListItem {
private String text;
private Bitmap image;
public HorizontalItem(String text, Bitmap image) {
this.text = text;
this.image = image;
}
/*
* Getter and setter methods here
*/
#Override
public int getType() {
return ListItem.TYPE_HORIZONTAL;
}
}
Vertical Item:
public class VerticalItem extends ListItem {
private String text1;
private Bitmap image1;
private String text2;
private Bitmap image2;
public VerticalItem(String text1, String text2, Bitmap image1, Bitmap image2) {
this.text1 = text1;
this.image1 = image1;
this.text2 = text2;
this.image2 = image2;
}
/*
* Getter and setter methods here
*/
#Override
public int getType() {
return ListItem.TYPE_HORIZONTAL;
}
}
Adapter:
public class ListAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private ArrayList<ListItem> listItems;
public static class HorizontalViewHolder extends RecyclerView.ViewHolder {
public TextView text;
public ImageView imageView;
public HorizontalHolder(View v) {
super(v);
text = (TextView) v.findViewById(R.id.text);
imageView = (ImageView) v.findViewById(R.id.imageView);
}
}
public static class VerticalViewHolder extends RecyclerView.ViewHolder {
public TextView text1;
public ImageView imageView1;
public TextView text2;
public ImageView imageView2;
public HorizontalHolder(View v) {
super(v);
text1 = (TextView) v.findViewById(R.id.text1);
imageView1 = (ImageView) v.findViewById(R.id.imageView1);
text2 = (TextView) v.findViewById(R.id.text2);
imageView2 = (ImageView) v.findViewById(R.id.imageView2);
}
}
// constructor
public ListAdapter(ArrayList<ListItem> listItems) {
this.listItems = listItems;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View v;
RecyclerView.ViewHolder holder = null;
switch (viewType) {
case ListItem.TYPE_HORIZONTAL:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.template_horizontal, parent, false);
holder = new HorizontalViewHolder(v);
break;
case ListItem.TYPE_VERTICAL:
v = LayoutInflater.from(parent.getContext()).inflate(R.layout.template_vertical, parent, false);
holder = new VerticalViewHolder(v);
break;
}
return holder;
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
switch (holder.getItemViewType()) {
case ListItem.TYPE_HORIZONTAL:
((HorizontalViewHolder) holder).text.setText(listItems.get(position).getText());
((HorizontalViewHolder) holder).imageView.setImageBitmap(listItems.get(position).getBitmap());
// the getText() and getBitmap() methods come from the getters of the HorizontalItems and VerticalItems that are stored in the ArrayList, listItems
break;
case ListItem.TYPE_DECK:
// Identical to above
break;
}
}
#Override
public int getItemCount() {
return listItems.size();
}
// This is extremely important, it is what lets the adapter know what type each listItem element is
#Override
public int getItemViewType(int position) {
return listItems.get(position).getType();
}
}
Activity:
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_list);
// Must be of abstract type ListItem
ArrayList<ListItem> items = new ArrayList<>();
// populate your ArrayList
items.add(new HorizontalItem("text", bitmap));
items.add(new VerticalItem("text1", "text2", bitmap1, bitmap2));
// ... and so on
ListAdapter adapter = new ListAdapter(items)
RecyclerView recycler = (RecyclerView) findViewById(R.id.recycler);
recycler.setAdapter(adapter);
recycler.setLayoutManager(new LinearLayoutManager(this));
// ... the rest of your code below
}
In your layout for the vertical list item, I would just create one file with two halves to it. You can use a LinearLayout to easily divide the subsections into perfect halves.
Related
Im trying to create a bunch of rows in a Listview to show from an ArrayList of object class
my object class Locations has
private String name;
private int currentCapacity;
private int maxCapacity;
private int id;
public Locations(String name, int currentCapacity, int maxCapacity, int id){
this.currentCapacity = currentCapacity;
this.id = id;
this.maxCapacity = maxCapacity;
this.name = name;
}
Locations loc1 = new Locations("foodplace", 10, 100, 1);
Locations loc2 = new Locations("area", 15, 25, 2);
Locations loc3 = new Locations("otherplace", 25, 25, 3);
locationsArrayList.add(loc1);
locationsArrayList.add(loc2);
locationsArrayList.add(loc3);
ListView list = (ListView) findViewById(R.id.theList);
ArrayAdapter adapter = new ArrayAdapter(this, android.R.layout.simple_list_item_1, locationsArrayList);
list.setAdapter(adapter);
how do i input my ArrayList such that i can change the color of the row based on the currentCapacity and show the name on the block?
You have to use RecyclerView
//Declare some items in your ArrayList
Locations loc1 = new Locations("foodplace", 10, 100, 1);
Locations loc2 = new Locations("area", 15, 25, 2);
Locations loc3 = new Locations("otherplace", 25, 25, 3);
locationsArrayList.add(loc1);
locationsArrayList.add(loc2);
locationsArrayList.add(loc3);
//make your own adapter class which extend RecyclerView.Adapter
MyAdapter myAdapter = new MyAdapter(locationsArrayList,this);
//setup your RecylerView
recyclerView.setAdapter(itemsListAdapter);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
recyclerView.setLayoutManager(linearLayoutManager);
Now in your Adapter class you can change each item color or font... based on ArrayList attribute.
public class MyAdapter extends RecyclerView.Adapter{
private ArrayList<Location> locationsArrayList;
private Context context;
public MyAdapter(ArrayList<Location> locationsArrayList, Context context) {
this.locationsArrayList= locationsArrayList;
this.context = context;
}
#Override
public int getItemViewType(int position) {
return super.getItemViewType(position);
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
return new ViewHolder(LayoutInflater.from(context)
.inflate(R.layout.item_layout,parent,false));
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
ViewHolder viewHolder = (ViewHolder) holder;
//here you can set your own conditions based on your arraylist using position parameter
viewHolder.itemNameTextView.setText(locationsArrayList.get(position).getName());
}
#Override
public int getItemCount() {
return locationsArrayList.size();
}
public Context getContext() {
return context;
}
class ViewHolder extends RecyclerView.ViewHolder {
//setup item layout attributes
TextView itemNameTextView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
itemNameTextView = itemView.findViewById(R.id.itemNameTextView);
}
}
}
I know it's complicated but RecyclerView make your list more flexible.
Hie i would suggest you use RecyclerView to achieve your list view
if you are using gradle for your dependencies you need to implement it in your app level gradle file
In your activity.xml file you implement like this
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/locations_list"
android:layout_width="0dp"
android:layout_height="0dp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
And then create a item file that will represent item in a list item
for example lets call it location_list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:paddingTop="10dp"
android:paddingBottom="10dp"
>
<TextView
android:id="#+id/location_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<TextView
android:id="#+id/max_capacity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
<TextView
android:id="#+id/current_capacity"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
/>
</LinearLayout>
Then after that you would need to implement an Adapter lets call it LocationAdapter.java
public class LocationAdapter extends RecyclerView.Adapter<LocationAdapter.ViewHolder> {
ArrayList<Location> locations;//list of locations
public LoacationAdapter(ArrayList<Location> locations) {
this.locations = locations //initialising list in constructor
}
#NonNull
#Override
public Locations.Adapter.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
//here this method inflates the row view
View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.location_list_item, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(#NonNull LocationAdapter.ViewHolder holder, int position) {
//this method sets values in the views
holder.locationName.settext(locations.get(position).getLocationName())
holder.maxCapacity.settext(locations.get(position).getMaxCapacity())
holder.currentCapacity.settext(locations.get(position).getCurrentCapacity())
}
#Override
public int getItemCount() {
return locatioins.size();// number of locations
}
//this class will repressent the list item
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {
// this are the views in the list item
TextView locationName, maxCapacity, currrentCapacity;
public ViewHolder(#NonNull View itemView) {
super(itemView);
locationName = itemView.findViewById(R.id.location_name);
maxCapacity= itemView.findViewById(R.id.max_capacity);
currentCapacity= itemView.findViewById(R.id.current_capacity);
itemView.setOnClickListener(this::onClick);
}
#Override
public void onClick(View v) {
//do what ever you want when an item is clicked
}
}
}
In in your activity file then
ArrayList<Location> locationsArraylist = new ArrayLists<> ();
//initializing recylcer view
RecyclerView locationList = findViewById(R.id.locations_list);
// setting layout manager
locationsList.setLayoutManager(new LinearLayoutManager(getContext, RecyclerView.Vertical, false));
//setting adapter adapter with the list of locations you want to display
locationList.setAdapter(new LocationsAdapter(locationsArraylist));
I am fairly new to Android and java. I am trying to make an application with multiple pages that you can swipe through. I started from a ViewPager2 example that is using a RecyclerView. It has 2 layout files. A main one and a viewPager one that is used for all the different pages, but with a different background color and title.
I have added a switch button on the viewpager xml and want to synchronize this button so it has the same state on all pages. But it does not do that out of the box. It seems the switch is created again for each of the different pages and I don't know how to access them on the other pages when the button on the current page is being changed.
It seems like a very simple thing to do, but I cannot find how to do it. Below is the code for my 2 java files.
Any help would be greatly appreciated.
public class MainActivity extends AppCompatActivity {
ViewPager2 viewPager2;
boolean continuous;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
viewPager2 = findViewById(R.id.viewPager2);
viewPager2.registerOnPageChangeCallback(new ViewPager2.OnPageChangeCallback() {
#Override
public void onPageSelected (int position) {
if (continuous == true) continuous = false;
else continuous = true;
int pos = position;
}
});
List<String> list = new ArrayList<>();
list.add("First Screen");
list.add("Second Screen");
list.add("Third Screen");
list.add("Fourth Screen");
viewPager2.setAdapter(new ViewPagerAdapter(this, list, viewPager2));
}
}
public class ViewPagerAdapter extends RecyclerView.Adapter<ViewPagerAdapter.ViewHolder> {
private List<String> mData;
private LayoutInflater mInflater;
private ViewPager2 viewPager2;
private int[] colorArray = new int[]{android.R.color.black, android.R.color.holo_blue_dark, android.R.color.holo_green_dark, android.R.color.holo_red_dark};
ViewPagerAdapter(Context context, List<String> data, ViewPager2 viewPager2) {
this.mInflater = LayoutInflater.from(context);
this.mData = data;
this.viewPager2 = viewPager2;
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = mInflater.inflate(R.layout.item_viewpager, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(ViewHolder holder, int position) {
String animal = mData.get(position);
holder.myTextView.setText(animal);
holder.relativeLayout.setBackgroundResource(colorArray[position]);
}
#Override
public int getItemCount() {
return mData.size();
}
// stores and recycles views as they are scrolled off screen
public class ViewHolder extends RecyclerView.ViewHolder {
TextView myTextView;
RelativeLayout relativeLayout;
Button button;
Switch switch2;
ViewHolder(View itemView) {
super(itemView);
myTextView = itemView.findViewById(R.id.tvTitle);
relativeLayout = itemView.findViewById(R.id.container);
button = itemView.findViewById(R.id.btnToggle);
switch2 = itemView.findViewById(R.id.switch2);
button.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(viewPager2.getOrientation() == ViewPager2.ORIENTATION_VERTICAL)
viewPager2.setOrientation(ViewPager2.ORIENTATION_HORIZONTAL);
else{
viewPager2.setOrientation(ViewPager2.ORIENTATION_VERTICAL);
}
}
});
switch2.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if (switch2.isChecked()) {
button.setEnabled(false);
} else {
button.setEnabled(true);
}
}
});
}
}
}
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/container"
android:layout_width="match_parent"
android:layout_height="match_parent">
<Button
android:id="#+id/login"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="top|left"
app:tint="#color/white"
android:layout_margin="#dimen/small"
android:layout_marginTop="#dimen/small"
android:src="#drawable/ic_account_circle_24"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintHorizontal_bias="0.0"/>
<androidx.viewpager2.widget.ViewPager2
android:id="#+id/screen_viewpager"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginBottom="8dp"
app:layout_constraintBottom_toTopOf="#+id/tab_indicator"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
This will help if you want to place the button on each page you should add it to your activity file and then place it on the position of the view pager
It seems like you are placing a different button on each page in the ViewPager but what you probably want is to place the button in the Activity layout?
I'm creating a calendar app, with each month being displayed using a RecylcerView. Inside each day of each month, there is another RecyclerView to display events on that day. I'm using the custuomLinearLayoutManager from https://stackoverflow.com/a/38513905/13199395, and I've looked at many examples. My code for the nesting is very similar to them but for some reason is not working. Any help as to why is not working would be much appreciated.
The month view:
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical"
tools:context=".MonthViewActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/calView"
android:layout_width="match_parent"
android:layout_height="581dp"
android:layout_gravity="bottom" />
</FrameLayout>
The view for each day:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/dayView"
android:layout_width="145px"
android:layout_height="265px"
android:orientation="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/eventDayView"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
</androidx.recyclerview.widget.RecyclerView>
</LinearLayout>
and the view for the inner recyclerView:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/showEvents"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<TextView
android:id="#+id/eventName"
android:layout_width="145px"
android:layout_height="wrap_content" />
</LinearLayout>
The adapter for the main recyclerview:
public class MonthViewAdapter extends RecyclerView.Adapter<MonthViewAdapter.ViewHolder> {
Context context;
ArrayList<Day> days;
CustomLinearLayoutManager l;
public MonthViewAdapter(Context context, ArrayList<Day> arrayList) {
this.context = context;
this.days = arrayList;
}
#NonNull
#Override
public MonthViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.content_events, parent, false);
return new ViewHolder(view, this.mOnDayClickListener);
}
#Override
public void onBindViewHolder(MonthViewAdapter.ViewHolder holder, int position) {
l = new CustomLinearLayoutManager(context, LinearLayout.VERTICAL, false);
ViewGroup.LayoutParams params=holder.r.getLayoutParams();
params.height=100;
holder.r.setLayoutParams(params);
holder.r.setLayoutManager(l);
EventViewAdapter adapter = new EventViewAdapter(context, days.get(position).getEvents(), this.mOnDayClickListener);
holder.r.setAdapter(adapter);
holder.t.setText(days.get(position).getMonthDayNumber());
}
#Override
public int getItemCount() {
return days.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView r;
TextView t;
FrameLayout f;
LinearLayout l;
public ViewHolder(View itemView) {
super(itemView);
l = itemView.findViewById(R.id.dayView);
t = itemView.findViewById(R.id.dayNum);
r = itemView.findViewById(R.id.eventDayView);
}
}
}
The adapter for the inner RecyclerView:
public class EventViewAdapter extends RecyclerView.Adapter<EventViewAdapter.ViewHolder> {
Context context;
ArrayList<Event> events;
public EventViewAdapter (Context context, ArrayList<Event> arrayList) {
this.context = context;
this.events = arrayList;
}
#Override
public EventViewAdapter.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.content_single_event, parent, false);
return new ViewHolder(view);
}
#Override
public void onBindViewHolder(EventViewAdapter.ViewHolder holder, int position) {
Log.d("event", Integer.toString(events.size()));
holder.eventName.setText(events.get(position).getName());
}
#Override
public int getItemCount() {
return events.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView eventName;
LinearLayout linearLayout;
public ViewHolder(View itemView) {
super(itemView);
eventName = itemView.findViewById(R.id.eventName);
linearLayout = itemView.findViewById(R.id.showEvents);
}
}
}
The custom linear layout manager: https://stackoverflow.com/a/38513905/13199395
And the activity
public class MonthViewActivity extends AppCompatActivity{
RecyclerView month;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_month_view);
month.setHasFixedSize(true);
month.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.HORIZONTAL));
month.addItemDecoration(new DividerItemDecoration(this,
DividerItemDecoration.VERTICAL));
GridLayoutManager g = new GridLayoutManager(this, 7);
month.setLayoutManager(g);
ArrayList<Day> day = new ArrayList<>();
LocalDate dd = LocalDate.now();
Day d1 = new Day(dd);
Day d2 = new Day(dd.plusDays(1));
Day d3 = new Day(dd.plusDays(2));
d1.addEvent(new Event("a"));
day.add(d1);
adapter = new MonthViewAdapter(this, day, this);
month.setAdapter(adapter);
}
}
Each element of the list that I pass into the month view adapter has a list of events that are then passed into the event adapter. However, each day in the grid only shows one of the events, it does not show all of the events in the event list on each day. Why is this happening and how can I fix it? I am getting an error (java.lang.IndexOutOfBoundsException: Invalid item position) on
View view = recycler.getViewForPosition(position);
in the custom layout manager, but the program does not crash.
I'm aware of a few ways to populate an Android ListView object with a title and a icon but I feel stuck when trying to simplify and improve my code.
This is the scenario:
1 - Creating two arrays
2 - Creating a RowItem class
public class IconRow {
private String title;
private int icon;
public IconRow(String title, int icon) {
this.title = title;
this.icon = icon;
}
public String getTitle() {
return title;
}
public int getIcon() {
return icon;
}
}
3 - Creating a ListAdapter
public class ListAdapter extends BaseAdapter {
Context context;
List<IconRow> rowItem;
String description;
long option;
public ListAdapter(Context context, List<IconRow> rowItem, String description, long option)
{
this.context = context;
this.rowItem = rowItem;
this.description = description;
this.option = option;
}
#Override
public int getCount() {
return rowItem.size();
}
#Override
public Object getItem(int position) {
return rowItem.get(position);
}
#Override
public long getItemId(int position) {
return rowItem.indexOf(getItem(position));
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
if (convertView == null) {
LayoutInflater mInflater = (LayoutInflater) context
.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
convertView = mInflater.inflate(R.layout.lv_arrow, null);
ImageView imgIcon = (ImageView) convertView.findViewById(R.id.imageView1);
TextView txtTitle = (TextView) convertView.findViewById(R.id.textView1);
IconRow row_pos = rowItem.get(position);
// setting the image resource and title
imgIcon.setImageResource(row_pos.getIcon());
txtTitle.setText(row_pos.getTitle());
}
return convertView;
}
4 - Creating a Row Layout
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:background="#android:color/white" >
<TextView
android:id="#+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:layout_marginLeft="20dp"
android:layout_marginRight="10dp"
android:fontFamily="sans-serif-light"
android:text="TextView"
android:textColor="#android:color/black"
android:textSize="24dp" />
<ImageView
android:id="#+id/imageView1"
android:layout_width="match_parent"
android:layout_height="48dp"
android:layout_gravity="right|center"
android:layout_marginLeft="20dp"
android:scaleType="fitEnd"
android:src="#drawable/ic_go"
android:layout_marginRight="0dp" />
</LinearLayout>
5 - Creating the List inside Fragment
#Override
public View onCreateView(LayoutInflater inflater, ViewGroup container,
Bundle savedInstanceState) {
View rootView = inflater.inflate(R.layout.fragment_parameters_settings, container, false);
lv_settings_1 = (ListView) rootView.findViewById(R.id.lv_settings_1);
menutitles = getResources().getStringArray(R.array.array_lv_settings_1);
menuIcons = getResources().obtainTypedArray(R.array.arrow_icons);
menu_iconRow = new ArrayList<>();
for (int i = 0; i < menutitles.length; i++) {
IconRow items = new IconRow(menutitles[i], menuIcons.getResourceId(
i, -1));
menu_iconRow.add(items);
}
adapter_settings_1 = new ListAdapter(getActivity(), menu_iconRow, "No Description", 0);
lv_settings_1.setAdapter(adapter_settings_1);
return rootView;
}
Am I doing it the wrong way? Is there anyway I can simplify this code?
I usually use your way for manage ListView. Probably you know this but give a look at this page http://developer.android.com/training/improving-layouts/smooth-scrolling.html
Your code might call findViewById() frequently during the scrolling of ListView, which can slow down performance. Even when the Adapter returns an inflated view for recycling, you still need to look up the elements and update them. A way around repeated use of findViewById() is to use the "view holder" design pattern.
static class ViewHolder {
TextView text;
TextView timestamp;
ImageView icon;
ProgressBar progress;
int position;
}
I have a problem with the GridView.
I would like to have a Grid with pictures and under each of them should be a timer. When I click one of the images, there should start the timer bellow of it.
How can I adapt TextViews and Images to a Gridview and let the TextView change every second.
I changed my code now to this
MainActivity
import android.app.Activity;
import android.os.Bundle;
import android.os.Handler;
import android.view.View;
import android.widget.AdapterView;
import android.widget.GridView;
import android.widget.Toast;
import java.util.ArrayList;
public class MainActivity extends Activity implements Runnable{
/** The Constant INTERVALL. */
private static final int INTERVALL = 1000;
private ImageAdapter mAdapter;
private ArrayList<String> listText;
private ArrayList<Integer> listImage;
private GridView gridView;
/** The handler. */
private Handler handler = new Handler();
public boolean timerRuns = false;
public int time ;
/**
* Called when the activity is first created.
*/
#Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.grid);
//GridView gridview = (GridView) findViewById(R.id.gridview);
//gridview.setAdapter(new ImageAdapter(this));
prepareList();
// prepared arraylist and passed it to the Adapter class
mAdapter = new ImageAdapter(this, listText, listImage);
// Set custom adapter to gridview
gridView = (GridView) findViewById(R.id.gridView);
gridView.setAdapter(mAdapter);
// Implement On Item click listener
gridView.setOnItemClickListener(new AdapterView.OnItemClickListener()
{
#Override
public void onItemClick(AdapterView<?> arg0, View arg1, int position,
long arg3) {
Toast.makeText(MainActivity.this, mAdapter.getItem(position), Toast.LENGTH_SHORT).show();
}
});
}
#Override
public final void run() {
chronometer();
}
public void startTimer(View v) {
timerRuns = true;
time = 5*60;
update();
this.handler.postDelayed(this, INTERVALL);
}
public void stopTimer(View v) {
timerRuns = false;
handler.removeCallbacks(this);
}
public void chronometer(){
time = time -1;
update();
handler.postDelayed(this, INTERVALL);
}
private void update(){
updateScreen();
}
private void updateScreen(){
//TextView tvChronometer = (TextView)findViewById(R.id.tvChronometer);
//tvChronometer.setText(Integer.toString(time));
}
public void prepareList()
{
listText = new ArrayList<String>();
listText.add("Sample");
listText.add("Brazil");
listText.add("Canada");
listText.add("China");
listText.add("France");
listText.add("Germany");
listText.add("Iran");
listText.add("Italy");
listText.add("Japan");
listText.add("Korea");
listText.add("Mexico");
listText.add("Netherlands");
listText.add("Portugal");
listText.add("Russia");
listText.add("Saudi Arabia");
listText.add("Spain");
listText.add("Turkey");
listText.add("United Kingdom");
listText.add("United States");
listImage = new ArrayList<Integer>();
listImage.add(R.drawable.sample_0);
listImage.add(R.drawable.sample_3);
listImage.add(R.drawable.sample_3);
listImage.add(R.drawable.sample_7);
listImage.add(R.drawable.sample_1);
listImage.add(R.drawable.sample_6);
listImage.add(R.drawable.sample_2);
listImage.add(R.drawable.sample_7);
listImage.add(R.drawable.sample_4);
listImage.add(R.drawable.sample_5);
listImage.add(R.drawable.sample_0);
listImage.add(R.drawable.sample_5);
listImage.add(R.drawable.sample_0);
listImage.add(R.drawable.sample_2);
listImage.add(R.drawable.sample_1);
listImage.add(R.drawable.sample_4);
listImage.add(R.drawable.sample_3);
listImage.add(R.drawable.sample_1);
listImage.add(R.drawable.sample_6);
}
}
The ImageAdapter
`public class ImageAdapter extends BaseAdapter {
private ArrayList<String> listText;
private ArrayList<Integer> listImage;
private Activity activity;
public ImageAdapter(Activity activity,ArrayList<String> listText, ArrayList<Integer> listImage) {
super();
this.listText = listText;
this.listImage = listImage;
this.activity = activity;
}
#Override
public int getCount() {
return listText.size();
}
#Override
public String getItem(int position) {
return listText.get(position);
}
#Override
public long getItemId(int position) {
return 0;
}
public static class ViewHolder
{
public ImageView imgView;
public TextView txtView;
}
#Override
public View getView(int position, View convertView, ViewGroup parent) {
ViewHolder view;
LayoutInflater inflator = activity.getLayoutInflater();
if(convertView==null)
{
view = new ViewHolder();
convertView = inflator.inflate(R.layout.element, null);
view.txtView = (TextView) convertView.findViewById(R.id.eText);
view.imgView = (ImageView) convertView.findViewById(R.id.eImage);
convertView.setTag(view);
}
else
{
view = (ViewHolder) convertView.getTag();
}
view.txtView.setText(listText.get(position));
view.imgView.setImageResource(listImage.get(position));
return convertView;
}
}`
my gridview
<?xml version="1.0" encoding="utf-8"?>
<GridView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/gridView"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:columnWidth="110dp"
android:numColumns="auto_fit"
android:verticalSpacing="10dp"
android:horizontalSpacing="10dp"
android:stretchMode="columnWidth"
android:gravity="center"
/>
and my element.xml
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="5dp" >
<ImageView
android:id="#+id/eImage"
android:layout_width="120dp"
android:layout_height="120dp"
android:src="#color/Bisque" >
</ImageView>
<TextView
android:id="#+id/eText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="5dp"
android:textSize="15sp" >
</TextView>
I hope you can help me.
1. Creating a gridview with text and image in each cell
Take a look at the documentation for GridView:
http://developer.android.com/guide/topics/ui/layout/gridview.html
The key method there is the public View getView(int position, View convertView, ViewGroup parent)
That method returns a view for each cell in the grid. You can create an XML layout that includes the TextView and ImageView. In the getView method for the adapter inflate the XML layout, attach an onClickListener so you can start the timer and return it as a view.
2. Updating the TextView from the grid
Keep in mind that a gridview contains contains multiple views, each of which can come from the same resource so this: TextView tvChronometer = (TextView)findViewById(R.id.tvChronometer); is not going to work in a gridview.
What you need to do is to get the specific view from the cell that you need (or iterate through all of them) and update the view. Something like this:
//First get the view for the cell assuming it's a composite view
View cellView = (TextView) gridview.getChildAt(position);
//Then get the actual TextView from that grid cell
TextView tvChronometer = cellView.findViewById(R.id.tvChronometer);
Although the best way in my opinion is to save a reference to the TextView when you a creating/instantiating it in the adapter.