I'm just one step to getting this dynamic layout work for me but I just cant figure out what I'm doing wrong.
I have the following string:
AAA__AB/
_AAAAAA/
_AAABBB/
I'm supposed to convert the above strings to a rooms layout whereby:
A - Represents available rooms
B - Represents booked rooms
_ - Represents a space
/ - Represents a new line
I have been following this tutorial Dynamic bus seats layout to try and make an app that can create Apartment rooms layout dynamically in RecyclerView.
I have a horizontally oriented recyclerview as the parent recyclerView. I have an adapter and a data source from a webserver.
Everything is working fine apart from the NEW_LINE feature which is supposed to force the recyclerview add a new line/row so that the rest of the views may align themselves in the new row/line
Please help
The following is what I get
The way my layout looks like as at now
The following is what I want
The intended layout
The Main Activity.java
...rest of the code
//Displaying the rooms and trying to make the layout
private void display_rooms_details(){
ArrayList house_rooms;
house_rooms=new ArrayList<>();
for (int i = 0; i < rooms_list.size(); i++)
{
if(rooms_list.get(i).getType()==0){
house_rooms.add(new Decode_rooms(Decode_rooms.AVAILABLE_ROOMS, rooms_list.get(i).getRoom_type(), rooms_list.get(i).getRoom_number(), rooms_list.get(i).getRoom_name(), rooms_list.get(i).getRoom_symbol(), rooms_list.get(i).getRoom_tenant()));
}
if(rooms_list.get(i).getType()==1){
house_rooms.add(new Decode_rooms(Decode_rooms.BOOKED_ROOMS, rooms_list.get(i).getRoom_type(), rooms_list.get(i).getRoom_number(), rooms_list.get(i).getRoom_name(), rooms_list.get(i).getRoom_symbol(), rooms_list.get(i).getRoom_tenant()));
}
if(rooms_list.get(i).getType()==2){
house_rooms.add(new Decode_rooms(Decode_rooms.NEW_LINE, rooms_list.get(i).getRoom_type(), rooms_list.get(i).getRoom_number(), rooms_list.get(i).getRoom_name(), rooms_list.get(i).getRoom_symbol(), rooms_list.get(i).getRoom_tenant()));
}
if(rooms_list.get(i).getType()==3){
house_rooms.add(new Decode_rooms(Decode_rooms.SPACE, rooms_list.get(i).getRoom_type(), rooms_list.get(i).getRoom_number(), rooms_list.get(i).getRoom_name(), rooms_list.get(i).getRoom_symbol(), rooms_list.get(i).getRoom_tenant()));
}
}
RecyclerView myrv= findViewById(R.id.recycleview_item_list);
Adapter_display_rooms myAdapter= new Adapter_display_rooms(this,house_rooms);
myrv.setLayoutManager(new LinearLayoutManager(this, OrientationHelper.HORIZONTAL,false));
myrv.setItemAnimator(new DefaultItemAnimator());
myrv.setAdapter(myAdapter);
}
Adapter_display_rooms
public class Adapter_display_rooms extends RecyclerView.Adapter<RecyclerView.ViewHolder>{
//Declare context and the class with our list
private static Context mContext;
int total_types;
private static ArrayList<Decode_rooms> rooms_list; //Our data class... this one contains the get and set methods to decode the data for us
//Available rooms layout class
public static class ActualRoomsViewHolder extends RecyclerView.ViewHolder {
CardView cardview;
TextView txtType;
ImageView image;
public ActualRoomsViewHolder(View itemView) {
super(itemView);
this.cardview= itemView.findViewById(R.id.layout_available_rooms);
this.image = itemView.findViewById(R.id.room_icon);
this.txtType = itemView.findViewById(R.id.room_name);
}
}
//New Line layout class
public static class NewLineViewHolder extends RecyclerView.ViewHolder {
LinearLayout linearlayout;
public NewLineViewHolder(View itemView) {
super(itemView);
this.linearlayout = itemView.findViewById(R.id.layout_newline);
}
}
//New Line layout class
public static class AddSpaceViewHolder extends RecyclerView.ViewHolder {
TextView txtType;
public AddSpaceViewHolder(View itemView) {
super(itemView);
this.txtType = itemView.findViewById(R.id.add_space);
}
}
//END OF VIEWHOLDER CLASSES
//The Class Constructor
public Adapter_display_rooms(Context mContext, ArrayList<Decode_rooms> rooms_list) {
this.mContext = mContext;
this.rooms_list = rooms_list;
total_types=rooms_list.size();
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case Decode_rooms.AVAILABLE_ROOMS:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_rooms_layout_listview_rooms, parent, false);
return new ActualRoomsViewHolder(view);
case Decode_rooms.BOOKED_ROOMS:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_rooms_layout_listview_rooms, parent, false);
return new ActualRoomsViewHolder(view);
case Decode_rooms.NEW_LINE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_rooms_layout_listview_newline, parent, false);
return new NewLineViewHolder(view);
case Decode_rooms.SPACE:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.activity_rooms_layout_listview_space, parent, false);
return new AddSpaceViewHolder(view);
}
return null;
}
#Override
public int getItemViewType(int position) {
switch (rooms_list.get(position).type) {
case 0:
return Decode_rooms.AVAILABLE_ROOMS;
case 1:
return Decode_rooms.BOOKED_ROOMS;
case 2:
return Decode_rooms.NEW_LINE;
case 3:
return Decode_rooms.SPACE;
default:
return -1;
}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
final Decode_rooms object = rooms_list.get(position);
if (object != null) {
switch (object.type) {
case Decode_rooms.AVAILABLE_ROOMS:
((ActualRoomsViewHolder) holder).image.setImageResource(R.drawable.ic_seats_book);
((ActualRoomsViewHolder) holder).txtType.setText(object.getRoom_number());
((ActualRoomsViewHolder) holder).cardview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent=new Intent(mContext, Available_room_details.class);
intent.putExtra("room_number",object.getRoom_number());
intent.putExtra("room_name",object.getRoom_name());
intent.putExtra("room_type",object.getRoom_type());
mContext.startActivity(intent);
}
});
((ActualRoomsViewHolder) holder).cardview.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
//Creating the instance of PopupMenu
PopupMenu popup = new PopupMenu(mContext,v);
//Inflating the Popup using xml file
popup.getMenuInflater()
.inflate(R.menu.menu_available_room_options, popup.getMenu());
//registering popup with OnMenuItemClickListener
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(
mContext,
"You Clicked : " + item.getTitle(),
Toast.LENGTH_SHORT
).show();
return true;
}
});
popup.show(); //showing popup menu
return false;
}
});
break;
case Decode_rooms.BOOKED_ROOMS:
((ActualRoomsViewHolder) holder).image.setImageResource(R.drawable.ic_seats_booked);
((ActualRoomsViewHolder) holder).txtType.setText(object.getRoom_number());
((ActualRoomsViewHolder) holder).cardview.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
Intent intent=new Intent(mContext, Booked_room_details.class);
intent.putExtra("room_number",object.getRoom_number());
intent.putExtra("room_name",object.getRoom_name());
intent.putExtra("room_type",object.getRoom_type());
mContext.startActivity(intent);
}
});
((ActualRoomsViewHolder) holder).cardview.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View v) {
//Creating the instance of PopupMenu
PopupMenu popup = new PopupMenu(mContext,v);
//Inflating the Popup using xml file
popup.getMenuInflater()
.inflate(R.menu.menu_booked_room_options, popup.getMenu());
//registering popup with OnMenuItemClickListener
popup.setOnMenuItemClickListener(new PopupMenu.OnMenuItemClickListener() {
public boolean onMenuItemClick(MenuItem item) {
Toast.makeText(
mContext,
"You Clicked : " + item.getTitle(),
Toast.LENGTH_SHORT
).show();
return true;
}
});
popup.show(); //showing popup menu
return false;
}
});
break;
case Decode_rooms.NEW_LINE:
break;
case Decode_rooms.SPACE:
((AddSpaceViewHolder) holder).txtType.setText("Space "+object.getRoom_number());
break;
default:
((ActualRoomsViewHolder) holder).txtType.setText("Default "+object.getRoom_number());
break;
}
}
}
#Override
public int getItemCount() {
return rooms_list.size();
}
Decode rooms java
public class Decode_rooms {
//Aid the custom adapter in inflating different types of layouts
public static final int AVAILABLE_ROOMS = 0;
public static final int BOOKED_ROOMS = 1;
public static final int NEW_LINE = 2;
public static final int SPACE = 3;
String room_type,room_number,room_name,room_symbol,room_tenant;
public int type; //This variable will hold the type of data i.e THE ROOM SYMBOL .. It'll tell the adapter the layout to inflate
public Decode_rooms(int type,String room_type, String room_number, String room_name, String room_symbol, String room_tenant) {
this.type = type;
this.room_type = room_type;
this.room_number = room_number;
this.room_name = room_name;
this.room_symbol = room_symbol;
this.room_tenant = room_tenant;
}
//The setter methods
public void setType(int type) {
this.type = type;
}
public void setRoom_type(String room_type) {
this.room_type = room_type;
}
public void setRoom_number(String room_number) {
this.room_number = room_number;
}
public void setRoom_name(String room_name) {
this.room_name = room_name;
}
public void setRoom_symbol(String room_symbol) {
this.room_symbol = room_symbol;
}
public void setRoom_tenant(String room_tenant) {
this.room_tenant = room_tenant;
}
//The getter methods
public int getType() {
return type;
}
public String getRoom_type() {
return room_type;
}
public String getRoom_number() {
return room_number;
}
public String getRoom_name() {
return room_name;
}
public String getRoom_symbol() {
return room_symbol;
}
public String getRoom_tenant() {
return room_tenant;
}
}
Parent xml
<?xml version="1.0" encoding="utf-8"?>
<android.support.design.widget.CoordinatorLayout 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.support.design.widget.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/AppTheme.AppBarOverlay">
<android.support.v7.widget.Toolbar
android:id="#+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/colorPrimary"
app:popupTheme="#style/AppTheme.PopupOverlay" />
</android.support.design.widget.AppBarLayout>
<include layout="#layout/recyclerview" />
</android.support.design.widget.CoordinatorLayout>
The recyclerView
<?xml version="1.0" encoding="utf-8"?>
<android.support.constraint.ConstraintLayout 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"
app:layout_behavior="#string/appbar_scrolling_view_behavior">
<android.support.v7.widget.RecyclerView
android:id="#+id/recycleview_item_list"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:scrollbars="vertical"
android:background="#color/blue"
android:elevation="2dp"
android:layout_margin="10dp"/>
</android.support.constraint.ConstraintLayout>
The Card view / Individual row item for rooms
<android.support.v7.widget.CardView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_available_rooms"
android:clickable="true"
android:foreground="?android:attr/selectableItemBackground"
android:layout_width="#dimen/room_width"
android:layout_height="#dimen/room_height"
android:layout_margin="#dimen/roomGaping"
android:orientation="horizontal"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<ImageView
android:id="#+id/room_icon"
android:background="#color/black"
android:layout_width="match_parent"
android:layout_height="#dimen/room_width"
android:scaleType="centerCrop"/>
<TextView
android:id="#+id/room_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center"
android:textColor="#2d2d2d"
android:textSize="13sp"
android:text="Room Name"/>
</LinearLayout>
</android.support.v7.widget.CardView>
The new line layout
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="#+id/layout_newline"
android:layout_width="match_parent"
android:layout_height="#dimen/room_height"
android:background="#color/grey_background"
android:orientation="horizontal">
</LinearLayout>
What you want is GridLayoutManager with column count of maximum of length of all rows. (You can set it as lcm of length of all inputs)
Iterate through each input and set column span as required. Let me know if anything is not clear.
Edit: Adding sample code
TestActivity.java
public class TestActivity extends AppCompatActivity {
private RecyclerView mRecyclerView;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_test);
mRecyclerView = findViewById(R.id.recycler_view);
final String[] inputs = new String[3];
inputs[0] = "AAA__AB/";
inputs[1] = "_AAAAAA/";
inputs[2] = "_ABB/";
final StringBuilder sb = new StringBuilder();
for(int i =0; i < inputs.length; i++) {
sb.append(inputs[i]);
}
String[] modifiedInput = sb.toString().split("/");
final int lcm = getLcm(modifiedInput);
GridLayoutManager manager = new GridLayoutManager(getApplicationContext(), lcm);
manager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int i) {
if(sb.toString().charAt(i) == '/') {
return lcm;
}
else {
int index = 0;
for(int j = 0; j< inputs.length; j++) {
if(i < index+inputs[j].length()) {
if(inputs[j].contains("/")) return lcm / (inputs[j].length()-1);
else return lcm / inputs[j].length();
}
else {
index += inputs[j].length();
}
}
throw new IllegalStateException("Wrong index");
}
}
});
List<String> inputDataList = new ArrayList<>();
String flatInputData = sb.toString();
for(int i = 0; i < flatInputData.length(); i++) {
inputDataList.add(String.valueOf(flatInputData.charAt(i)));
}
Adapter adapter = new Adapter(inputDataList);
mRecyclerView.setAdapter(adapter);
mRecyclerView.setLayoutManager(manager);
}
private int getLcm(String[] modifiedInputs) {
int lcm = modifiedInputs[0].length();
for(int i = 1; i< modifiedInputs.length; i++) {
lcm = lcm(lcm, modifiedInputs[i].length());
}
return lcm;
}
public static int lcm(int a, int b) {
return a/gcd(a,b) * b;
}
public static int gcd(int a, int b){
if (a<b) return gcd(b,a);
if (a%b==0) return b;
else return gcd(b, a%b);
}
}
Adapter.java
public class Adapter extends RecyclerView.Adapter<Adapter.ViewHolder> {
public List<String> data;
public Adapter(List<String> data) {
this.data = data;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int i) {
return new ViewHolder(LayoutInflater.from(viewGroup.getContext()).inflate(android.R.layout.simple_list_item_1, viewGroup, false));
}
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int i) {
String text = data.get(i);
if(!text.equals("/"))
viewHolder.textView.setText(text);
}
#Override
public int getItemCount() {
if(data == null) {
return 0;
}
else {
return data.size();
}
}
class ViewHolder extends RecyclerView.ViewHolder {
TextView textView;
public ViewHolder(#NonNull View itemView) {
super(itemView);
textView = itemView.findViewById(android.R.id.text1);
}
}
}
Related
Hi,
I'm making a dynamic Nested RecyclerView.
The Outer RecyclerView (Routine) consists of parts (A, B, C...), add button,
and delete button, and the inner RecyclerView (Detail Routine) consists of text view (set, weight and count).
When i add an outer item, it have one inner RecyclerView item by default.
I thought the implementation was successful, but it wasn't.
The picture is a condition that I have made some items in advance.
This is a picture that creates additional items there.
However, in the picture, when I added the G item, you can see that the F item
and the B item have a change.
And the G item, oddly enough, has two basic items. Originally, it is one.
The second picture is the later item addition.
If i add a new outer recyclerview item, there will be a new change in the whole
item again.
I am not sure why this change is happening. If you add a new item, you want the
existing item to remain.
Please tell me the cause
Here is Code
RoutineAdapter.java ( Outer RecyclerView Adpater )
public class RoutineAdapter extends RecyclerView.Adapter<RoutineAdapter.ViewHolder> {
Context context;
ArrayList<RoutineModel> routineItems = new ArrayList<>();
public RoutineDetailAdapter detailAdapter;
OnRoutineItemClickListener listener;
public void setOnRoutineClickListener(OnRoutineItemClickListener listener) {
this.listener = listener;
}
public void addItem(RoutineModel item) {
routineItems.add(item);
notifyDataSetChanged();
}
public void addDetailItem(RoutineDetailAdapter curDetailAdapter) {
curDetailAdapter.addItem(new RoutineDetailModel());
}
public void deleteDetailItem(RoutineDetailAdapter curDetailAdapter) {
curDetailAdapter.deleteItem();
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.routine_item, parent, false);
ViewHolder holder = new ViewHolder(itemView);
detailAdapter = new RoutineDetailAdapter();
holder.setRoutineDetailRecyClerView();
holder.rv_detail.setAdapter(detailAdapter);
detailAdapter.addItem(new RoutineDetailModel());
return holder;
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
RoutineModel curRoutineItem = routineItems.get(position);
holder.setItems(curRoutineItem);
}
#Override
public int getItemCount() {
return routineItems.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
RecyclerView rv_detail;
TextView routine;
Button addSet;
Button deleteSet;
public ViewHolder(#NonNull View itemView) {
super(itemView);
initViews();
addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RoutineDetailAdapter curDetailAdapter = (RoutineDetailAdapter) rv_detail.getAdapter();
listener.OnAddBtnClick(curDetailAdapter);
}
});
deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
RoutineDetailAdapter curDetailAdapter = (RoutineDetailAdapter) rv_detail.getAdapter();
listener.OnDeleteBtnClick(curDetailAdapter);
}
});
}
private void initViews() {
routine = itemView.findViewById(R.id.routine);
rv_detail = itemView.findViewById(R.id.detail_routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
private void setItems(RoutineModel routineItem) {
routine.setText(routineItem.getRoutine());
}
public void setRoutineDetailRecyClerView() {
rv_detail.setLayoutManager(new LinearLayoutManager(context, RecyclerView.VERTICAL, false));
}
}
public interface OnRoutineItemClickListener {
public void OnAddBtnClick(RoutineDetailAdapter detailAdapter);
public void OnDeleteBtnClick(RoutineDetailAdapter detailAdapter);
}
}
RoutineDetailAdapter.java ( Inner RecyclerView Adapter )
public class RoutineDetailAdapter extends RecyclerView.Adapter<RoutineDetailAdapter.ViewHolder>{
ArrayList<RoutineDetailModel> items = new ArrayList<>();
Context context;
int insertPosition;
int deletePosition;
public void addItem(RoutineDetailModel item) {
items.add(item);
notifyItemInserted(insertPosition);
}
public void deleteItem() {
try {
if(items.size() > 1) { // Leave at least one set
items.remove(deletePosition);
notifyItemRemoved(deletePosition);
deletePosition -= 1;
}
} catch (Exception e) {
// empty
}
}
public ArrayList<RoutineDetailModel> getItem() {
return this.items;
}
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
View itemView = inflater.inflate(R.layout.routine_detail_item, parent, false);
return new ViewHolder(itemView);
}
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
RoutineDetailModel item = items.get(position);
holder.setItem(item, position);
insertPosition = position + 1;
deletePosition = position;
}
#Override
public int getItemCount() {
return items.size();
}
public class ViewHolder extends RecyclerView.ViewHolder {
TextView set;
public ViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
}
private void setItem(RoutineDetailModel item, int setCount) {
set.setText(setCount + 1 + "set");
}
}
}
WriteRoutineActivity.java
public class WriteRoutineActivity extends AppCompatActivity {
Button add_routine_btn;
TextView title;
RecyclerView routine_rv;
RoutineAdapter routineAdapter;
LinearLayoutManager routineLayoutManger;
ArrayList<String> titleData;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_write_routine);
initViews();
setPageTitle(getIntent());
setRoutineRecyclerview();
routineAdapter = new RoutineAdapter();
routine_rv.setAdapter(routineAdapter);
add_routine_btn.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
WorkoutListDialogFragment routineDialog = new WorkoutListDialogFragment();
routineDialog.show(getSupportFragmentManager(), "RoutineListDialog");
}
});
routineAdapter.setOnRoutineClickListener(new RoutineAdapter.OnRoutineItemClickListener() {
#Override
public void OnAddBtnClick(RoutineDetailAdapter curDetailAdapter) {
routineAdapter.addDetailItem(curDetailAdapter);
}
#Override
public void OnDeleteBtnClick(RoutineDetailAdapter curDetailAdapter) {
routineAdapter.deleteDetailItem(curDetailAdapter);
}
});
}
private void initViews() {
title = findViewById(R.id.body_part_detail_title);
routine_rv = findViewById(R.id.routine_recyclerview);
add_routine_btn = findViewById(R.id.add_routine);
}
private void setRoutineRecyclerview() {
routineLayoutManger = new LinearLayoutManager(getApplicationContext(), RecyclerView.VERTICAL, false);
routine_rv.setLayoutManager(routineLayoutManger);
routine_rv.setHasFixedSize(true);
RecyclerView.ItemDecoration divider = new DividerItemDecorator(ContextCompat.getDrawable(getApplicationContext(), R.drawable.divider));
routine_rv.addItemDecoration(divider);
}
public void setPageTitle(Intent intent) {
if(intent != null) {
titleData = intent.getStringArrayListExtra("bodypart");
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
String new_title = String.join(" / ", titleData);
title.setText(new_title);
}
else {
StringBuilder new_title = new StringBuilder();
for (int i = 0; i < titleData.size(); i++) {
if (i == titleData.size() - 1) {
new_title.append(titleData.get(i));
break;
}
new_title.append(titleData.get(i)).append(" / ");
}
title.setText(new_title);
}
}
}
public void addRoutine(String routine) {
routineAdapter.addItem(new RoutineModel(routine));
routine_rv.smoothScrollToPosition(routineAdapter.getItemCount() - 1);
}
}
ADDED
Main.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.coordinatorlayout.widget.CoordinatorLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
xmlns:app="http://schemas.android.com/apk/res-auto"
tools:context=".data.DailyRecordDetailActivity">
<com.google.android.material.appbar.AppBarLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:theme="#style/Theme.AppBarOverlay"
app:elevation="0dp">
<androidx.appcompat.widget.Toolbar
android:layout_width="match_parent"
android:layout_height="wrap_content">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<TextView
android:id="#+id/body_part_detail_title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="PART"
android:textColor="#color/white"
android:textAppearance="#style/TextAppearance.AppCompat.Widget.ActionBar.Title"
android:layout_centerHorizontal="true"/>
</RelativeLayout>
</androidx.appcompat.widget.Toolbar>
</com.google.android.material.appbar.AppBarLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="10dp"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
<Button
android:id="#+id/add_routine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="ROUTINE ADD"
android:backgroundTint="#color/light_green"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintBottom_toTopOf="#+id/routine_recyclerview"
app:layout_constraintRight_toRightOf="parent"/>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/routine_recyclerview"
android:layout_width="match_parent"
android:layout_height="0dp"
android:scrollbars="vertical"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintTop_toBottomOf="#+id/add_routine"/>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>
RoutineItem.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:background="#color/white"
android:layout_marginTop="10dp"
android:layout_marginBottom="10dp">
<LinearLayout
android:id="#+id/linear_routine"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintLeft_toLeftOf="parent">
<TextView
android:id="#+id/routine"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingLeft="10dp"
android:text="ROUTINE"
android:textSize="16dp"
android:textStyle="bold"
android:layout_weight="2"/>
<Button
android:id="#+id/add_set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:text="ADD"
android:textColor="#color/black"
android:gravity="center"
android:layout_weight="1"/>
<Button
android:id="#+id/delete_set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="?android:selectableItemBackground"
android:text="DELETE"
android:textColor="#color/black"
android:layout_weight="1"/>
</LinearLayout>
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/detail_routine"
android:layout_width="match_parent"
android:layout_height="match_parent"
app:layout_constraintTop_toBottomOf="#+id/linear_routine"
app:layout_constraintLeft_toLeftOf="parent"/>
</androidx.constraintlayout.widget.ConstraintLayout>
RoutineDetail.xml
<?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="wrap_content"
android:orientation="horizontal"
android:layout_marginLeft="5dp"
android:padding="10dp"
android:clipToPadding="false">
<TextView
android:id="#+id/set"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="SET"/>
<TextView
android:id="#+id/weight"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0 kg"/>
<TextView
android:id="#+id/reps"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_weight="1"
android:text="0"/>
</LinearLayout>
For every RoutineModel item you recreate detailAdapter but you need new instance. Check TODO in code below:
public class RoutineAdapter extends RecyclerView.Adapter<RoutineAdapter.ViewHolder> {
Context context;
ArrayList<RoutineModel> routineItems = new ArrayList<>();
//public RoutineDetailAdapter detailAdapter; //TODO move it to local variable in method onCreateViewHolder
OnRoutineItemClickListener listener;
like this
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
context = parent.getContext();
LayoutInflater inflater = LayoutInflater.from(context);
View itemView = inflater.inflate(R.layout.routine_item, parent, false);
ViewHolder holder = new ViewHolder(itemView);
RoutineDetailAdapter detailAdapter = new RoutineDetailAdapter(); //TODO Create local variable
detailAdapter.addItem(new RoutineDetailModel());
holder.setRoutineDetailRecyClerView();
holder.rv_detail.setAdapter(detailAdapter);
return holder;
}
But this is not good way to solve this... Its better to use one Adapter with multiple item views. I have no time now to explain, I will post some code later...
EDITED
I had same problem and and solved this on this (better) way:
Create Adapter that support multiple views
public class MultipleViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private Context context;
private List<Object> items; //List<Object or Generic class or abstarct base Model class parent of Routine and RoutineDetails>
private OnItemClickListener listener; //TODO new interface
..............
}
Override onCreateViewHolder like this
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType == 1){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder (itemView);
}/*else{*/
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailsViewHolder (itemView);
//}
}
Override onBindViewHolder like this and create your updateViews methods
#Override
public void onBindViewHolder(#NonNull ViewHolder holder, int position) {
Object object = items.get(position); //Object or Generic class or abstarct base Model class parent of Routine and RoutineDetails>
if(object instanceof RoutineModel) {
//create method
updateRoutineViews((RoutineViewHolder) holder, (RoutineModel) object, position);
}else if(object instanceof RoutineDetailsModel) {
//create method
updateRoutineDetailsViewHolder((RoutineDetailsViewHolder) holder, (RoutineDetailModel) object);
}
}
and override getItemViewType method
#Override
public int getItemViewType(int position) {
Object object = items.get(position);
if(object instanceof RoutineModel){
return 1;
}
//else if instanceOf RoutineDetailModel return 0
return 0;
}
This is so simple and better for performance...
Also in OnItemClickListener get item by position and check instanceOf object like above. You can add new item to the clickedItemPosition + 1 with adapter.notifyDataSetChanges()
In MainActivity you have to create mixed list with Routine and Routine details
List<Object> mixedList = new ArrayList<>();
for(RoutineModel rm: routineList){
mixedList.add(rm);
if(rm has routineDetailModels){ //pseudo code
for(RoutineDetailModels rmdetilas: rm.getRoutineDetailModels()){
mixedList.add(rmdetilas);
}
}
}
adpater.swapData(mixedList); //create your method to swap or set data to adapter
EDITED
handle click
public interface OnItemClickListener{
void onClick(View view, int position);
void onLongClick(View view, int position);
}
and
adapter.setOnItemClickListener(new OnItemClickListener() {
#Override
public void onClick(View view, int position) {
Object item = (Object) adapter.getItem(position);
if(item instanceof Routine) {
if (view.getId() == R.id.delete_id) {
//TODO delete form list, notifyItemRemoved or generate all mixeddata again and swap
} else if (view.getId() == R.id.whateveryouwant) {
//TODO doSomething()
}
}
}
#Override
public void onLongClick(View view, int position) {
}
});
The code above is just an example of how to solve the problem
For more help feel free to comment
EDITED 2
public class RoutineViewHolder extends RecyclerView.ViewHolder {
public RecyclerView rv_detail;
public TextView routine;
public Button addSet;
public Button deleteSet;
public ViewHolder(#NonNull View itemView) {
super(itemView);
//initViews(); in constructor
routine = itemView.findViewById(R.id.routine);
rv_detail = itemView.findViewById(R.id.detail_routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
}
update method called from onBindViewHolder
private void updateRoutineViews(RoutineViewHolder holder, RoutineModel routineItem, position){
holder.routine.setText(routineItem.getRoutine());
holder.rv_detail.setText(routineItem.getWhateverYouWant());
holder.deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
onItemClickListener.onClick(v, position); //or holder.getAdapterPosition()
}
});
}
COMPLETE CODE
Create complete new app to test it and understand (copy/paste this code), then integrate in your app...
There are more improvements but I don't have time, this is a quick fix
Activity
public class RoutineActivity extends AppCompatActivity {
private MultipleViewAdapter adapter;
private List<RoutineModel> routineList;
#Override
protected void onCreate(#Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_routine);
RecyclerView contentList = findViewById(R.id.list);
contentList.setLayoutManager(new LinearLayoutManager(this));
adapter = new MultipleViewAdapter(this, new ArrayList<>());
contentList.setAdapter(adapter);
adapter.setOnItemClickListener(new MultipleViewAdapter.OnItemClickListener() {
#Override
public void onClick(View view, int position) {
Object item = (Object) adapter.getItem(position);
if(item instanceof RoutineModel) {
RoutineModel routineModel = (RoutineModel) item;
if (view.getId() == R.id.add_set) {
int weight = randomInt(99);
Toast.makeText(RoutineActivity.this, "New item with weight: " + weight + " kg", Toast.LENGTH_SHORT).show();
routineModel.addDetails(new RoutineDetailsModel(routineModel.getDetailsSize() + 1, weight));
adapter.swapData(getMixedList()); // OR add item to adapter and notify item inserted
//TODO implement SnappingLinearLayoutManager and set list.smoothScroleto... routineModel
} else if (view.getId() == R.id.delete_set) {
//TODO doSomething()
boolean deleted = routineModel.removeDetails(routineModel.getDetailsSize() - 1); // -1 !!! to delete last item
Toast.makeText(RoutineActivity.this, deleted ? "Last item is deleted": "No more items", Toast.LENGTH_SHORT).show();
adapter.swapData(getMixedList()); // OR remove item from adapter and notify item removed
//TODO implement SnappingLinearLayoutManager and set list.smoothScroleto.... routineModel
}
}else if(item instanceof RoutineDetailsModel) {
RoutineDetailsModel routineModel = (RoutineDetailsModel) item;
Toast.makeText(RoutineActivity.this, "Weight: " + routineModel.getWeight() + " kg", Toast.LENGTH_SHORT).show();
//TODO EDITED 4 (copy/paste this) random new Weight
routineModel.setWeight(randomInt(99));
adapter.notifyItemChanged(position);
Toast.makeText(RoutineActivity.this, "New Weight: " + routineModel.getWeight() + " kg", Toast.LENGTH_SHORT).show();
}
}
#Override
public void onLongClick(View view, int position) {
}
});
initFakeData();
adapter.swapData(getMixedList());
}
private List<Object> getMixedList() {
List<Object> mixedList = new ArrayList<>();
for(RoutineModel rm: routineList){
mixedList.add(rm);
if(rm.getRoutineDetailsModel() != null && rm.getRoutineDetailsModel().size() > 0){
for(RoutineDetailsModel rmdetilas: rm.getRoutineDetailsModel()){
mixedList.add(rmdetilas);
}
}
}
return mixedList;
}
private void initFakeData() {
routineList = new ArrayList<>();
for(int i = 0; i < 5; i++){
RoutineModel routineModel = new RoutineModel(String.valueOf(i + 1));
for(int j = 0; j < 4; j++){
routineModel.addDetails(new RoutineDetailsModel(j+1, randomInt(99)));
}
routineList.add(routineModel);
}
}
private int randomInt(int max) {
return (int) Math.floor(Math.random() * max);
}
}
Activity 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="vertical">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/list"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</LinearLayout>
Adapter
public class MultipleViewAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private final Context context; // TODO only if you need conetxt
private List<Object> items;
private OnItemClickListener onItemClickListener;
public MultipleViewAdapter(Context context, List<Object> items) {
this.context = context;
this.items = items;
}
#NonNull
#Override
public RecyclerView.ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int viewType) {
if(viewType == 1){
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_item, parent, false);
return new RoutineViewHolder(itemView);
}/*else{*/
View itemView = LayoutInflater.from(parent.getContext()).inflate(R.layout.routine_detail_item, parent, false);
return new RoutineDetailsViewHolder(itemView);
//}
}
#Override
public void onBindViewHolder(#NonNull RecyclerView.ViewHolder holder, int position) {
Object object = items.get(position); //Object or Generic class or abstarct base Model class parent of Routine and RoutineDetails>
if(object instanceof RoutineModel) {
updateRoutineViews((RoutineViewHolder) holder, (RoutineModel) object, position);
}else if(object instanceof RoutineDetailsModel) {
updateRoutineDetailsViewHolder((RoutineDetailsViewHolder) holder, (RoutineDetailsModel) object, position);
}
}
private void updateRoutineViews(RoutineViewHolder holder, RoutineModel routineItem, int position){
holder.routine.setText("Routine " + routineItem.getRoutine());
holder.addSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onItemClickListener != null) onItemClickListener.onClick(v, position);
}
});
holder.deleteSet.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onItemClickListener != null) onItemClickListener.onClick(v, position);
}
});
}
private void updateRoutineDetailsViewHolder(RoutineDetailsViewHolder holder, RoutineDetailsModel routineDetailsModel, int position){
holder.set.setText(routineDetailsModel.getSet() + " set");
holder.weight.setText(routineDetailsModel.getWeight() + " kg");
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(onItemClickListener != null) onItemClickListener.onClick(v, position);
}
});
}
#Override
public int getItemViewType(int position) {
Object object = items.get(position);
if(object instanceof RoutineModel){
return 1;
}
//else if instanceOf RoutineDetailModel return 0
return 0;
}
#Override
public int getItemCount() {
if(items == null) return 0;
return items.size();
}
public Object getItem(int position) {
if(this.items == null || position < 0 || position >= this.items.size()) return null;
return this.items.get(position);
}
public void swapData(List<Object> newItems) {
if (newItems != null) {
this.items = newItems;
notifyDataSetChanged();
}
}
public void setOnItemClickListener(OnItemClickListener onItemClickListener) {
this.onItemClickListener = onItemClickListener;
}
public interface OnItemClickListener{
void onClick(View view, int position);
void onLongClick(View view, int position);
}
public static class RoutineViewHolder extends RecyclerView.ViewHolder {
public TextView routine;
public Button addSet;
public Button deleteSet;
public RoutineViewHolder(#NonNull View itemView) {
super(itemView);
//initViews(); in constructor
routine = itemView.findViewById(R.id.routine);
addSet = itemView.findViewById(R.id.add_set);
deleteSet = itemView.findViewById(R.id.delete_set);
}
}
public static class RoutineDetailsViewHolder extends RecyclerView.ViewHolder {
public TextView set;
public TextView weight;
public RoutineDetailsViewHolder(#NonNull View itemView) {
super(itemView);
set = itemView.findViewById(R.id.set);
weight = itemView.findViewById(R.id.weight);
}
}
}
RoutineModel
public class RoutineModel {
private List<RoutineDetailsModel> routineDetailsList;
private String routine;
public RoutineModel(String routine) {
this.routine = routine;
}
public List<RoutineDetailsModel> getRoutineDetailsModel() {
return routineDetailsList;
}
public void setRoutineDetailsModel(List<RoutineDetailsModel> routineDetailsModel) {
this.routineDetailsList = routineDetailsModel;
}
public void addDetails(RoutineDetailsModel item) {
if(routineDetailsList == null) {
routineDetailsList = new ArrayList<>();
}
this.routineDetailsList.add(item);
}
public boolean removeDetails(int index) {
if(routineDetailsList == null || index >= routineDetailsList.size() || index < 0) return false;
this.routineDetailsList.remove(index);
return true;
}
public String getRoutine() {
return routine;
}
public void setRoutine(String routine) {
this.routine = routine;
}
public int getDetailsSize() {
if(routineDetailsList == null) return 0;
return routineDetailsList.size();
}
}
RoutineDetailsModel
public class RoutineDetailsModel {
private int set;
private int weight;
public RoutineDetailsModel(int set, int weight) {
this.set = set;
this.weight = weight;
}
public int getSet() {
return set;
}
public void setSet(int set) {
this.set = set;
}
public int getWeight() {
return weight;
}
public void setWeight(int weight) {
this.weight = weight;
}
}
Item layouts
Find in question: routine_item.xml and routine_detail_item.xml
I am making an app in which I want to set images onto gridview and on itemclick I want to show the description of an image via textview. I have already done the setting of images onto gridview, but now I want to do the second part of it.
Following Images shows the visualization of The layout :
Before :
After :
Basically, I want to Add a new layout in between of 2 rows of gridview below the clicked image. I have done a lot of research and also seen many libraries and StackOverflow answers but can't figure out to do so. I need example code to do so for gridview or any other views. Thanks.
All the stackoverflow Answers and libraries that i have gone through :=
Android expandable GridView like Google images
Add view between 2 rows Gridview
Dynamically adding a whole row in GridView, or splitting the View
Libraries ->
https://github.com/thewaychung/ExpandableGridView
https://github.com/paolorotolo/ExpandableHeightListView
You can try the following approach for your reference :
I am using text view instead of ImageView. But the concept remain same.
It is displayed something like this.
Const.java : Class to keep constants.
public class Const {
public static final int DES_VIEW=0;
public static final int FOOD_VIEW=1;
public static final int FAKE_VIEW=2;
public static final int COLUMN_NUMBER = 2;
}
RowData.java : An Interface which will be used by the adapter class to get view type of each element.
public interface RowData {
int getViewType();
}
FoodItem.java : Just a plain pojo class which implements RowData ( The parent item FoodItem item 0 is populated )
public class FoodItem implements RowData{
public int getViewType() {
return viewType;
}
public void setViewType(int viewType) {
this.viewType = viewType;
}
private int viewType;
public boolean isExpanded() {
return isExpanded;
}
public void setExpanded(boolean expanded) {
isExpanded = expanded;
}
private boolean isExpanded;
public String getFoodName() {
return foodName;
}
public void setFoodName(String foodName) {
this.foodName = foodName;
}
private String foodName;
public String getDescription() {
return description == null ? "DEFAULT" : description;
}
public void setDescription(String description) {
this.description = description;
}
private String description;
}
FakeData.java : Pojo class to populate empty views.
public class FakeData implements RowData {
#Override
public int getViewType() {
return Const.FAKE_VIEW;
}
}
FoodAdapter.java : Adapter class.
public class FoodAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
public void setFoodItems(List<RowData> foodItems) {
this.foodItems = foodItems;
// addBlankViews();
}
private List<RowData> foodItems;
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
switch (viewType) {
case Const.FOOD_VIEW:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout, parent, false);
return new VH(view);
case Const.DES_VIEW:
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.des, parent, false);
return new DesView(view);
case Const.FAKE_VIEW:
Log.e("CALLED", " ---> " + viewType);
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.layout, parent, false);
return new FakeView(view);
default:
return null;
}
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
int view = getItemViewType(position);
switch (view) {
case Const.FAKE_VIEW: {
FakeView fakeView = (FakeView) holder;
Log.e("CALLED", position + " " + view);
}
break;
case Const.DES_VIEW: {
DesView desView = (DesView) holder;
desView.setData((FoodItem) getFoodItem(position));
}
break;
case Const.FOOD_VIEW: {
VH foodView = (VH) holder;
foodView.setData((FoodItem) getFoodItem(position));
}
break;
}
}
RowData getFoodItem(int position) {
if (foodItems != null) {
return foodItems.get(position);
}
return null;
}
#Override
public int getItemViewType(int position) {
RowData foodItem = getFoodItem(position);
if (foodItem != null) {
return foodItem.getViewType();
}
return -1;
}
#Override
public int getItemCount() {
if (foodItems == null) {
return 0;
} else {
return foodItems.size();
}
}
class VH extends RecyclerView.ViewHolder {
TextView foodItemName;
public VH(View itemView) {
super(itemView);
foodItemName = itemView.findViewById(R.id.food_item);
foodItemName.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
FoodAdapter.this.onClick(getAdapterPosition());
}
});
}
void setData(FoodItem data) {
foodItemName.setText(data.getFoodName());
}
}
class DesView extends RecyclerView.ViewHolder {
TextView foodDesView;
public DesView(View itemView) {
super(itemView);
foodDesView = itemView.findViewById(R.id.des_item);
foodDesView.setTextColor(Color.BLUE);
}
void setData(FoodItem data) {
foodDesView.setText(data.getDescription());
}
}
class FakeView extends RecyclerView.ViewHolder {
public FakeView(View itemView) {
super(itemView);
}
}
private int currentExpandedIndex = -1;
private void onClick(int position) {
// calculation to show and hide the child view (with magenta background ).
if (currentExpandedIndex != -1) {
foodItems.remove(currentExpandedIndex);
currentExpandedIndex = -1;
notifyDataSetChanged();
return;
}
int startIndex = position % Const.COLUMN_NUMBER;
if (startIndex == 0) {
startIndex = position;
} else {
startIndex = position - startIndex;
}
startIndex = startIndex + Const.COLUMN_NUMBER;
if (currentExpandedIndex == -1) {
FoodItem des = new FoodItem();
des.setViewType(Const.DES_VIEW);
FoodItem foodItem = (FoodItem) getFoodItem(position);
des.setDescription(foodItem.getDescription());
foodItems.add(startIndex, des);
currentExpandedIndex = startIndex;
}
notifyDataSetChanged();
}
}
MainActivity.java : Activity class.
public class MainActivity extends AppCompatActivity {
private FoodAdapter foodAdapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
RecyclerView recyclerView = findViewById(R.id.reycleView);
((SimpleItemAnimator) recyclerView.getItemAnimator()).setSupportsChangeAnimations(false);
foodAdapter = new FoodAdapter();
foodAdapter.setFoodItems(getFoodItems());
GridLayoutManager mLayoutManager = new GridLayoutManager(this,Const.COLUMN_NUMBER);
mLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() {
#Override
public int getSpanSize(int position) {
switch (foodAdapter.getItemViewType(position)) {
case Const.DES_VIEW:
return Const.COLUMN_NUMBER;
default:
return 1;
}
}
});
recyclerView.setLayoutManager(mLayoutManager);
recyclerView.setHasFixedSize(true);
recyclerView.setAdapter(foodAdapter);
}
private List<RowData> foodItems;
private List<RowData> getFoodItems() {
foodItems = new ArrayList<>();
for (int i = 0; i < 18; i++) {
FoodItem foodItem = new FoodItem();
foodItem.setFoodName(" FoodItem item " + i);
foodItem.setExpanded(false);
foodItem.setDescription("Fake des of " + i);
foodItem.setViewType(Const.FOOD_VIEW);
foodItems.add(foodItem);
}
// calculation to add empty view.
// these view will be added when, there is shortage of parent views to fill row completely
int size = foodItems.size();
int blankView = size % Const.COLUMN_NUMBER;
if (blankView != 0) {
blankView = Const.COLUMN_NUMBER - blankView;
for (int i = 0; i < blankView; i++) {
foodItems.add(new FakeData());
}
}
return foodItems;
}
}
activity_main.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="check.service.com.genericrecylerview.MainActivity">
<android.support.v7.widget.RecyclerView
android:id="#+id/reycleView"
android:layout_width="match_parent"
android:layout_height="wrap_content"/>
</LinearLayout>
des.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:orientation="vertical"
android:background="#color/colorAccent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/des_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
layout.xml
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:orientation="vertical"
android:layout_height="wrap_content">
<TextView
android:id="#+id/food_item"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
newbie here. I'm having difficulty implementing animation on a single list item when the card(list item) is long pressed. When I long press on a single card, all the cards animate. And can someone explain why this problem occurs only when horizontalScroll of the recyclerView has been disabled by me.
Main Activity:
public class MainActivity extends AppCompatActivity {
RecyclerView recyclerView;
TextView score;
ArrayList words = new ArrayList();
wordCardsList wc;
static int scored = 0;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
vibrator = (Vibrator)getSystemService(VIBRATOR_SERVICE);
final int initialDatasetSize = words.size();
score = findViewById(R.id.score);
score.setText("" + 0 + "/" + initialDatasetSize);
recyclerView = findViewById(R.id.recyclerView);
wc = new wordCardsList(this, words);
final LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false){
#Override
public boolean canScrollHorizontally() {
return false;
}
};
final GridLayoutManager gridLayoutManager = new GridLayoutManager(this, 1, GridLayoutManager.HORIZONTAL, false);
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(wc);
new ItemTouchHelper(new ItemTouchHelper.SimpleCallback(0, ItemTouchHelper.UP) {
#Override
public boolean onMove(RecyclerView recyclerView, RecyclerView.ViewHolder viewHolder, RecyclerView.ViewHolder target) {
return false;
}
#Override
public void onSwiped(RecyclerView.ViewHolder viewHolder, int direction) {
mediaPlayer.start();
vibrator.vibrate(6);
if(words.size()!=1) {
int pos = viewHolder.getAdapterPosition();
words.remove(pos);
System.out.println(words.size());
wc.notifyDataSetChanged();
}
score.setText("" + ++scored + "/" + initialDatasetSize);
}
}).attachToRecyclerView(recyclerView);
}
}
Recycler View Adapter:
public class wordCardsList extends RecyclerView.Adapter<wordCardsList.PersonViewHolder>{
private ArrayList words;
private Context context;
Converter converter = new Converter();
int longPressCount = 0;
public wordCardsList(Context context, ArrayList words) {
this.words = words;
this.context = context;
}
#Override
public PersonViewHolder onCreateViewHolder(ViewGroup parent, final int viewType) {
View view = LayoutInflater.from(context).inflate(R.layout.card, parent, false);
final PersonViewHolder personViewHolder = new PersonViewHolder(view);
longPressCount = 1;
return personViewHolder;
}
#Override
public void onBindViewHolder(wordCardsList.PersonViewHolder holder, int position) {
final String word = words.get(position).toString();
holder.words.setText(word);
holder.cardView.setOnLongClickListener(new View.OnLongClickListener() {
#Override
public boolean onLongClick(View view) {
//Working: System.out.println("Long Pressed on" + personViewHolder.getAdapterPosition());
if(longPressCount == 1) {
float pixels = converter.convertDpToPixel(190f, context);
view.findViewById(R.id.cardLayout).animate().translationY(-pixels).setDuration(100).start();
view.findViewById(R.id.exampleHeading).animate().alpha(1f).setDuration(400).start();
view.findViewById(R.id.example1).animate().alpha(1f).setDuration(400).start();
view.findViewById(R.id.example2).animate().alpha(1f).setDuration(400).start();
view.findViewById(R.id.definitionHeading).animate().alpha(1f).setDuration(400).start();
view.findViewById(R.id.definition1).animate().alpha(1f).setDuration(400).start();
vibrator.vibrate(6);
scored--;
longPressCount=0;
}
return true;
}
});
}
#Override
public int getItemCount() {
return words.size();
}
class PersonViewHolder extends RecyclerView.ViewHolder{
public TextView words;
public CardView cardView;
public PersonViewHolder(View view) {
super(view);
words = view.findViewById(R.id.wordName);
cardView = view.findViewById(R.id.card);
}
}
}
You don't want do in java program just do some change in your XML card following like that
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:card_view="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<android.support.v7.widget.CardView
android:id="#+id/card_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:layout_margin="#dimen/size_5"
android:elevation="3dp"
card_view:cardCornerRadius="#dimen/size_5">
<LinearLayout
android:id="#+id/list_header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="#dimen/size_20"
android:layout_marginRight="#dimen/size_20"
android:background="?attr/selectableItemBackgroundBorderless"
android:orientation="vertical"
android:paddingBottom="#dimen/font_10"
android:paddingTop="#dimen/font_10">
</LinearLayout>
</android.support.v7.widget.CardView>
</LinearLayout>
The main changes are inside your LinearLayout add this line that's all
android:background="attr/selectableItemBackgroundBorderless"
I have a CardView which has ExpandableTextView as Child of CardView when i was clicked on CardView the Expandable Text View expand and also in next click it was closed but the Main Problem is that When I click to Expandable Text View it was expanded and also some of other CardView's Expandable TextView in the RecycleView also expanded without clicked on that CardView. i was searched for the reason but not able to get any proper solution and i am new in development of Material's concept Layout So Please help me in My Following code. Thanks in Advance.
The following is my code for RecycleView Adapter.
public class TestRecyclerViewAdapter extends RecyclerView.Adapter<TestRecyclerViewAdapter.PersonViewHolder> {
FloatingActionMenu menubutton;
Context con;
Handler handler;
public ExpandableTextView previous_text_view = null;
public static class PersonViewHolder extends RecyclerView.ViewHolder {
CardView cv;
TextView personName;
TextView personAge;
ImageView personPhoto;
ExpandableTextView expandableTextView;
CustomObject object;
// FloatingActionButton button;
PersonViewHolder(View itemView) {
super(itemView);
cv = (CardView) itemView.findViewById(R.id.cv);
expandableTextView = (ExpandableTextView) itemView.findViewById(R.id.expandingTextView);
// button = (FloatingActionButton)itemView.findViewById(R.id.action_c);
}
public void bindexpand(CustomObject cob){
object = cob;
//expandableTextView.isExpanded(object.isExpanded());
}
}
List<CustomObject> contents;
static final int TYPE_HEADER = 0;
static final int TYPE_CELL = 1;
public TestRecyclerViewAdapter(List<CustomObject> contents, FloatingActionMenu button, Context context) {
this.contents = contents;
menubutton = button;
con = context;
}
#Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return TYPE_HEADER;
default:
return TYPE_CELL;
}
}
#Override
public int getItemCount() {
return contents.size();
}
#Override
public PersonViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view = null;
switch (viewType) {
case TYPE_HEADER: {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_card_big, parent, false);
return new PersonViewHolder(view) {
};
}
case TYPE_CELL: {
view = LayoutInflater.from(parent.getContext())
.inflate(R.layout.list_item_card_small, parent, false);
final PersonViewHolder pvh = new PersonViewHolder(view);
return pvh;
}
}
return null;
}
#Override
public void onBindViewHolder(final PersonViewHolder holder, final int position) {
switch (getItemViewType(position)) {
case TYPE_HEADER:
break;
case TYPE_CELL:
// holder.button.setColorNormalResId(R.color.blue);
final int pos = position;
holder.cv.setTag(new Integer(pos));
final CustomObject c = contents.get(pos);
holder.cv.setOnClickListener(new View.OnClickListener() {
#TargetApi(Build.VERSION_CODES.HONEYCOMB)
#Override
public void onClick(View v) {
holder.expandableTextView.toggleExpansion();
CardView cvdata = (CardView)v;
int clickedPos = ((Integer)cvdata.getTag()).intValue();
if (holder.expandableTextView.isExpanded()) {
for (int i=1;i<contents.size();i++){
if(clickedPos != i && contents.get(i).isExpanded() == true){
c.setIsExpanded(false);
notifyItemChanged(i);
}
}
menubutton.showMenuButton(true);
contents.get(clickedPos).setIsExpanded(true);
} else {
menubutton.hideMenuButton(true);
}
}
});
break;
}
}
}
Now, The following is the code for My Custom Object class:
public class CustomObject {
String data;
boolean isExpanded;
public CustomObject(){
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public boolean isExpanded() {
return isExpanded;
}
public void setIsExpanded(boolean isExpanded) {
this.isExpanded = isExpanded;
}
}
Last One, the code for cardview's xml layout.
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout 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="wrap_content">
<android.support.v7.widget.CardView
android:id="#+id/cv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:cardCornerRadius="2dp"
app:cardElevation="2dp"
android:layout_marginBottom="#dimen/cardMarginVertical"
android:layout_marginLeft="#dimen/cardMarginHorizontal"
android:layout_marginRight="#dimen/cardMarginHorizontal"
android:layout_marginTop="#dimen/cardMarginVertical"
app:cardPreventCornerOverlap="false"
app:contentPadding="0dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<com.github.florent37.materialviewpager.sample.ExpandableTextView
android:id="#+id/expandingTextView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textSize="15dp"
android:layout_marginLeft="10dp"
android:maxLines="3"
android:layout_marginTop="10dp"
android:layout_marginRight="10dp"
android:layout_marginBottom="10dp"
android:text="#string/longText" />
</LinearLayout>
</android.support.v7.widget.CardView>
</FrameLayout>
I'm trying to implement a recyclerview that behaves like my sketch below:
The idea is that there is a parent list, when an list item in the parent list is tapped, that list item reveals a child list that contains it's own data. when a list item is tapped in the child list, the value of that child is reflected and updates the value of the parent in the parent list item.
I've tried to get it working for the past 3 days to no avail. I tried using the AdvancedReyclerview library but to a beginner like me, it was a giant mess of things that didn't make sense especially when passing in the data. I copied and pasted files that I needed to get a minimal working version but I had no idea how to pass my data into the recyclerview and how to update it with the newly selected value.
Is it even possible to do what I'm trying to do or am I way out of my depth here?
If it's still difficult to understand, I can explain it more.
EDIT: someone recommended that I do this with the ExpandableListView rather than the RecyclerView. Any thoughts on that?
1.ExpandableRecyclerAdapter.class
public abstract class ExpandableRecyclerAdapter<T extends ExpandableRecyclerAdapter.ListItem> extends RecyclerView.Adapter<ExpandableRecyclerAdapter.ViewHolder> {
protected Context mContext;
protected List<T> allItems = new ArrayList<>();
protected List<T> visibleItems = new ArrayList<>();
private List<Integer> indexList = new ArrayList<>();
private SparseIntArray expandMap = new SparseIntArray();
private int mode;
protected static final int TYPE_HEADER = 1000;
private static final int ARROW_ROTATION_DURATION = 150;
public static final int MODE_NORMAL = 0;
public static final int MODE_ACCORDION = 1;
public ExpandableRecyclerAdapter(Context context) {
mContext = context;
}
public static class ListItem {
public int ItemType;
public ListItem(int itemType) {
ItemType = itemType;
}
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public int getItemCount() {
return visibleItems == null ? 0 : visibleItems.size();
}
protected View inflate(int resourceID, ViewGroup viewGroup) {
return LayoutInflater.from(mContext).inflate(resourceID, viewGroup, false);
}
public class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(View view) {
super(view);
}
}
public class HeaderViewHolder extends ViewHolder {
ImageView arrow;
public HeaderViewHolder(View view) {
super(view);
view.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
toggleExpandedItems(getLayoutPosition(),false);
/*if(isExpanded(getLayoutPosition())){
collapseItems(getLayoutPosition(),false);
}else {
expandItems(getLayoutPosition(),true);
}*/
}
});
}
public HeaderViewHolder(View view, final ImageView arrow) {
super(view);
this.arrow = arrow;
arrow.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
handleClick();
}
});
}
protected void handleClick() {
if (toggleExpandedItems(getLayoutPosition(), false)) {
openArrow(arrow);
} else {
closeArrow(arrow);
}
}
public void bind(int position) {
arrow.setRotation(isExpanded(position) ? 90 : 0);
}
}
public boolean toggleExpandedItems(int position, boolean notify) {
if (isExpanded(position)) {
collapseItems(position, notify);
return false;
} else {
expandItems(position, notify);
if (mode == MODE_ACCORDION) {
collapseAllExcept(position);
}
return true;
}
}
public void expandItems(int position, boolean notify) {
int count = 0;
int index = indexList.get(position);
int insert = position;
for (int i=index+1; i<allItems.size() && allItems.get(i).ItemType != TYPE_HEADER; i++) {
insert++;
count++;
visibleItems.add(insert, allItems.get(i));
indexList.add(insert, i);
}
notifyItemRangeInserted(position + 1, count);
int allItemsPosition = indexList.get(position);
expandMap.put(allItemsPosition, 1);
if (notify) {
notifyItemChanged(position);
}
}
public void collapseItems(int position, boolean notify) {
int count = 0;
int index = indexList.get(position);
for (int i=index+1; i<allItems.size() && allItems.get(i).ItemType != TYPE_HEADER; i++) {
count++;
visibleItems.remove(position + 1);
indexList.remove(position + 1);
}
notifyItemRangeRemoved(position + 1, count);
int allItemsPosition = indexList.get(position);
expandMap.delete(allItemsPosition);
if (notify) {
notifyItemChanged(position);
}
}
protected boolean isExpanded(int position) {
int allItemsPosition = indexList.get(position);
return expandMap.get(allItemsPosition, -1) >= 0;
}
#Override
public int getItemViewType(int position) {
return visibleItems.get(position).ItemType;
}
public void setItems(List<T> items) {
allItems = items;
List<T> visibleItems = new ArrayList<>();
expandMap.clear();
indexList.clear();
for (int i=0; i<items.size(); i++) {
if (items.get(i).ItemType == TYPE_HEADER) {
indexList.add(i);
visibleItems.add(items.get(i));
}
}
this.visibleItems = visibleItems;
notifyDataSetChanged();
}
protected void removeItemAt(int visiblePosition) {
int allItemsPosition = indexList.get(visiblePosition);
allItems.remove(allItemsPosition);
visibleItems.remove(visiblePosition);
incrementIndexList(allItemsPosition, visiblePosition, -1);
incrementExpandMapAfter(allItemsPosition, -1);
notifyItemRemoved(visiblePosition);
}
private void incrementExpandMapAfter(int position, int direction) {
SparseIntArray newExpandMap = new SparseIntArray();
for (int i=0; i<expandMap.size(); i++) {
int index = expandMap.keyAt(i);
newExpandMap.put(index < position ? index : index + direction, 1);
}
expandMap = newExpandMap;
}
private void incrementIndexList(int allItemsPosition, int visiblePosition, int direction) {
List<Integer> newIndexList = new ArrayList<>();
for (int i=0; i<indexList.size(); i++) {
if (i == visiblePosition) {
if (direction > 0) {
newIndexList.add(allItemsPosition);
}
}
int val = indexList.get(i);
newIndexList.add(val < allItemsPosition ? val : val + direction);
}
indexList = newIndexList;
}
public void collapseAll() {
collapseAllExcept(-1);
}
public void collapseAllExcept(int position) {
for (int i=visibleItems.size()-1; i>=0; i--) {
if (i != position && getItemViewType(i) == TYPE_HEADER) {
if (isExpanded(i)) {
collapseItems(i, true);
}
}
}
}
public void expandAll() {
for (int i=visibleItems.size()-1; i>=0; i--) {
if (getItemViewType(i) == TYPE_HEADER) {
if (!isExpanded(i)) {
expandItems(i, true);
}
}
}
}
public static void openArrow(View view) {
view.animate().setDuration(ARROW_ROTATION_DURATION).rotation(180);
}
public static void closeArrow(View view) {
view.animate().setDuration(ARROW_ROTATION_DURATION).rotation(0);
}
public int getMode() {
return mode;
}
public void setMode(int mode) {
this.mode = mode;
}
}
2.activity_main
<?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="match_parent">
<android.support.v7.widget.RecyclerView android:id="#+id/main_recycler"
android:layout_width="match_parent"
android:layout_height="match_parent" />
3.item_header
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:padding="#dimen/standard_padding">
<LinearLayout
android:id="#+id/lnr_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true">
<TextView
android:id="#+id/txt_header_address"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:drawableLeft="#mipmap/ic_usa"
android:gravity="center"
android:text="Beverly Hills"
android:textStyle="bold" />
</LinearLayout>
<ImageView
android:id="#+id/img_arrow"
android:layout_width="#dimen/arrow_size"
android:layout_height="#dimen/arrow_size"
android:layout_alignParentRight="true"
android:src="#mipmap/arrow" />
<TextView
android:id="#+id/txt_header_name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:text="Home"
android:textStyle="bold" />
4.item_content.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout 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="wrap_content"
android:orientation="vertical">
<RelativeLayout
android:id="#+id/rcl_header_btn"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:orientation="horizontal">
<Button
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/btn_cancle" />
<Button
style="?android:attr/borderlessButtonStyle"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="#string/btn_save" />
</RelativeLayout>
<LinearLayout
android:id="#+id/lnr_parent"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="#+id/rcl_header_btn"
android:gravity="center_vertical"
android:orientation="vertical">
<EditText
android:id="#+id/edt_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="DESCRIPTION" />
<EditText
android:id="#+id/edt_address"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:hint="Address" />
<LinearLayout
android:id="#+id/lnr_child_1"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/edt_city"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="City" />
<EditText
android:id="#+id/edt_state"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="State" />
</LinearLayout>
<LinearLayout
android:id="#+id/lnr_child_2"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<EditText
android:id="#+id/edt_zipcode"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Zip Code" />
<EditText
android:id="#+id/edt_country"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1"
android:hint="Country" />
</LinearLayout>
</LinearLayout>
<RelativeLayout
android:id="#+id/rcl_bottom"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="#+id/lnr_parent">
<CheckBox
android:id="#+id/chk_marked"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/txt_type" />
<TextView
android:id="#+id/txt_type"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/btn_delete"
android:layout_toRightOf="#+id/chk_marked"
android:gravity="center"
android:text="SET AS DEFAULT" />
<Button
android:id="#+id/btn_delete"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentRight="true"
android:text="DELETE" />
</RelativeLayout>
5.Adapter
public class PeopleAdapter extends ExpandableRecyclerAdapter<PeopleAdapter.PeopleListItem> {
public static final int TYPE_PERSON = 1001;
public PeopleAdapter(Context context) {
super(context);
setItems(getSampleItems());
}
public static class PeopleListItem extends ExpandableRecyclerAdapter.ListItem {
public String Text;
public PeopleListItem(String group) {
super(TYPE_HEADER);
Text = group;
}
public PeopleListItem(String first, String last) {
super(TYPE_PERSON);
Text = first + " " + last;
}
}
public class HeaderViewHolder extends ExpandableRecyclerAdapter.HeaderViewHolder {
TextView name;
public HeaderViewHolder(View view) {
super(view, (ImageView) view.findViewById(R.id.img_arrow));
name = (TextView) view.findViewById(R.id.txt_header_name);
}
public void bind(int position) {
super.bind(position);
name.setText(visibleItems.get(position).Text);
}
}
public class PersonViewHolder extends ExpandableRecyclerAdapter.ViewHolder {
EditText name;
public PersonViewHolder(View view) {
super(view);
name = (EditText) view.findViewById(R.id.edt_description);
}
public void bind(int position) {
name.setText(name.getText());
}
}
#Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
switch (viewType) {
case TYPE_HEADER:
return new HeaderViewHolder(inflate(R.layout.item_header, parent));
case TYPE_PERSON:
default:
return new PersonViewHolder(inflate(R.layout.item_content, parent));
}
}
#Override
public void onBindViewHolder(ExpandableRecyclerAdapter.ViewHolder holder, int position) {
switch (getItemViewType(position)) {
case TYPE_HEADER:
((HeaderViewHolder) holder).bind(position);
break;
case TYPE_PERSON:
default:
((PersonViewHolder) holder).bind(position);
break;
}
}
private List<PeopleListItem> getSampleItems() {
List<PeopleListItem> items = new ArrayList<>();
items.add(new PeopleListItem("Friends"));
items.add(new PeopleListItem("", ""));
items.add(new PeopleListItem("Friends"));
items.add(new PeopleListItem("", ""));
return items;
}
}
6.MainActivity.java
public class MainActivity extends AppCompatActivity {
RecyclerView recycler;
PeopleAdapter adapter;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
Toolbar toolbar = (Toolbar) findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
recycler = (RecyclerView) findViewById(R.id.main_recycler);
adapter = new PeopleAdapter(this);
adapter.setMode(ExpandableRecyclerAdapter.MODE_ACCORDION);
recycler.setLayoutManager(new LinearLayoutManager(this));
recycler.setAdapter(adapter);
}
#Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
getMenuInflater().inflate(R.menu.menu_main, menu);
return true;
}
#Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case R.id.action_expand_all:
adapter.expandAll();
return true;
case R.id.action_collapse_all:
adapter.collapseAll();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
}
You can check my library in here
And create something like below code:
public class PurchaseItemRecyclerViewAdapter extends ExpandableRecyclerView.Adapter<PurchaseItemRecyclerViewAdapter.ChildViewHolder,ExpandableRecyclerView.SimpleGroupViewHolder,String,String>
{
List<ItemModel> itemModels;
public PurchaseItemRecyclerViewAdapter() {
this.itemModels = new ArrayList<>();
itemModels.add(new ItemModel("group 0",3,"subitem :"));
itemModels.add(new ItemModel("group 1",3,"subitem :"));
itemModels.add(new ItemModel("group 2",2,"subitem :"));
itemModels.add(new ItemModel("group 3",1,"subitem :"));
itemModels.add(new ItemModel("group 4",3,"subitem :"));
itemModels.add(new ItemModel("group 5",5,"subitem :"));
}
#Override
public int getGroupItemCount() {
return itemModels.size();
}
#Override
public int getChildItemCount(int i) {
return itemModels.get(i).getSubItemCount();
}
#Override
public String getGroupItem(int i) {
return itemModels.get(i).getParentName();
}
#Override
public String getChildItem(int group, int child) {
return itemModels.get(group).getSubItemPrefix() + child;
}
#Override
protected ExpandableRecyclerView.SimpleGroupViewHolder onCreateGroupViewHolder(ViewGroup parent)
{
return new ExpandableRecyclerView.SimpleGroupViewHolder(parent.getContext());
}
#Override
protected ChildViewHolder onCreateChildViewHolder(ViewGroup parent, int viewType)
{
View rootView = LayoutInflater.from(parent.getContext()).inflate(R.layout.purchase_list_content,parent,false);
return new ChildViewHolder(rootView);
}
#Override
public void onBindGroupViewHolder(ExpandableRecyclerView.SimpleGroupViewHolder holder, int group) {
super.onBindGroupViewHolder(holder, group);
holder.setText(getGroupItem(group));
}
#Override
public void onBindChildViewHolder(ChildViewHolder holder, final int group, int position)
{
super.onBindChildViewHolder(holder, group, position);
holder.name.setText(getChildItem(group, position));
holder.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
itemModels.get(group).setParentName("edited Parent");
notifyItemChanged(group);
}
});
}
#Override
public int getChildItemViewType(int i, int i1) {
return 1;
}
public class ChildViewHolder extends RecyclerView.ViewHolder
{
private TextView name;
public ChildViewHolder(View itemView) {
super(itemView);
name = (TextView) itemView.findViewById(R.id.item_name);
}
}
}
and this ItemModel class:
public class ItemModel {
String parentName;
int subItemCount;
String subItemPrefix;
public ItemModel(String parentName, int subItemCount, String subItemPrefix) {
this.parentName = parentName;
this.subItemCount = subItemCount;
this.subItemPrefix = subItemPrefix;
}
public String getParentName() {
return parentName;
}
public void setParentName(String parentName) {
this.parentName = parentName;
}
public int getSubItemCount() {
return subItemCount;
}
public void setSubItemCount(int subItemCount) {
this.subItemCount = subItemCount;
}
public String getSubItemPrefix() {
return subItemPrefix;
}
public void setSubItemPrefix(String subItemPrefix) {
this.subItemPrefix = subItemPrefix;
}
}
This is bit late but take a look on this advanced recyclerview library https://github.com/h6ah4i/android-advancedrecyclerview
In their documentation you can see Expandable item related classes/interfaces
check it out.
You can easily achieve it with this library, there is a full example here.
Basically you group your items into sections:
class MySection extends StatelessSection {
String header;
List<String> list;
boolean expanded = true;
public MySection(String header, List<String> list) {
// call constructor with layout resources for this Section header and items
super(R.layout.section_header, R.layout.section_item);
this.myHeader = header;
this.myList = list;
}
#Override
public int getContentItemsTotal() {
return expanded? list.size() : 0;
}
#Override
public RecyclerView.ViewHolder getHeaderViewHolder(View view) {
return new HeaderViewHolder(view);
}
#Override
public void onBindHeaderViewHolder(RecyclerView.ViewHolder holder) {
final HeaderViewHolder headerHolder = (HeaderViewHolder) holder;
headerHolder.tvTitle.setText(title);
headerHolder.rootView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
expanded = !expanded;
headerHolder.imgArrow.setImageResource(
expanded ? R.drawable.ic_keyboard_arrow_up_black_18dp : R.drawable.ic_keyboard_arrow_down_black_18dp
);
sectionAdapter.notifyDataSetChanged();
}
});
}
#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 create instance of your sections and set up your adapter:
// Create an instance of SectionedRecyclerViewAdapter
SectionedRecyclerViewAdapter sectionAdapter = new SectionedRecyclerViewAdapter();
// Add your Sections
sectionAdapter.addSection(new MySection("A", Arrays.asList(new String[] {"a", "b", "c" })));
sectionAdapter.addSection(new MySection("B", Arrays.asList(new String[] {"d", "e", "f" })));
// Set up your RecyclerView with the SectionedRecyclerViewAdapter
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.recyclerview);
recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
recyclerView.setAdapter(sectionAdapter);