Past few days I've been working on fixed footer for my recyclerview but so far I didn't get the result that I expected. I've already taken a look existing questions but got same result as before.It seems I 'm still missing a point but I can't figure out... Therefore I'm waiting for your suggestions. Thanks!
Here is the current view of layout, I want that FOOTER part to be sticky at bottom of recyclerview.
DrawerAdapter.java
public class DrawerAdapter extends RecyclerView.Adapter<DrawerAdapter.DrawerViewHolder> {
// Class for view types.
private class VIEW_TYPES {
public static final int Header = 0;
public static final int Normal = 1;
public static final int Footer = 2;
}
private ArrayList<DrawerItem> drawerMenuList;
private String userProfileName;
private String userProfileMail;
private Drawable userProfilePic;
private OnItemSelecteListener mListener;
public DrawerAdapter(ArrayList<DrawerItem> drawerMenuList, String name, String mail, Drawable profileImage) {
this.drawerMenuList = drawerMenuList;
this.userProfileMail = mail;
this.userProfileName = name;
this.userProfilePic = profileImage;
}
#Override
public DrawerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View view;
if (viewType == VIEW_TYPES.Header) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.header, parent, false);
} else if (viewType == VIEW_TYPES.Footer) {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.footer, parent, false);
} else {
view = LayoutInflater.from(parent.getContext()).inflate(R.layout.item_row, parent, false);
}
return new DrawerViewHolder(view, viewType);
}
#Override
public void onBindViewHolder(DrawerViewHolder holder, int position) {
// If item's position 0 it's a header.
if (position == 0) {
holder.headerName.setText(userProfileName);
holder.headerMail.setText(userProfileMail);
holder.headerProfile.setImageDrawable(userProfilePic);
} else if (position == 5) { // If position is 5 then it's footer.
holder.headerName.setText("FOOTER");
} else { // it's a menu item.
holder.title.setText(drawerMenuList.get(position - 1).getTitle());
holder.icon.setImageResource(drawerMenuList.get(position - 1).getIcon());
}
}
#Override
public int getItemCount() {
return drawerMenuList.size() + 1;
}
#Override
public int getItemViewType(int position) {
if (position == 0) {
return VIEW_TYPES.Header;
} else if (position == 5) {
return VIEW_TYPES.Footer;
} else {
return VIEW_TYPES.Normal;
}
}
class DrawerViewHolder extends RecyclerView.ViewHolder {
TextView title;
TextView headerName;
ImageView icon;
TextView headerMail;
ImageView headerProfile;
public DrawerViewHolder(View itemView, int viewType) {
super(itemView);
if (viewType == VIEW_TYPES.Header) {
headerName = (TextView) itemView.findViewById(R.id.header_name);
headerMail = (TextView) itemView.findViewById(R.id.header_email);
headerProfile = (ImageView) itemView.findViewById(R.id.circleView);
} else if (viewType == VIEW_TYPES.Footer) {
headerName = (TextView) itemView.findViewById(R.id.textView3);
} else {
title = (TextView) itemView.findViewById(R.id.title);
icon = (ImageView) itemView.findViewById(R.id.icon);
}
itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
mListener.onItemSelected(view, getAdapterPosition());
}
});
}
}
// To set click listener.
public interface OnItemSelecteListener {
void onItemSelected(View v, int position);
}
public void setOnItemClickLister(OnItemSelecteListener mListener) {
this.mListener = mListener;
}
}
MainActivity.java
for (int i = 0; i < navMenuTitles.length; i++) {
Log.e("I", String.valueOf(i));
mDrawerItemList.add(new DrawerItem(navMenuTitles[i], navMenuIcons.getResourceId(i, -1)));
}
mDrawerItemList.add(new DrawerItem());
mRecyclerView = (RecyclerView) findViewById(R.id.drawerRecyclerView);
mDrawerAdapter = new DrawerAdapter(mDrawerItemList, logged_user.name, logged_user.email, draw_userImage);
mLinearLayoutManager = new CustomGridLayoutManager(HomeScreenActivity.this){
#Override
public boolean canScrollVertically() {
return false;
}
};
mRecyclerView.setLayoutManager(mLinearLayoutManager);
mRecyclerView.setAdapter(mDrawerAdapter);
mDrawerLayout = (DrawerLayout) findViewById(R.id.DrawerLayout);
mDrawerToggle = new ActionBarDrawerToggle(HomeScreenActivity.this, mDrawerLayout, mToolbar, R.string.openDrawer, R.string.closeDrawer) {
#Override
public void onDrawerOpened(View drawerView) {
super.onDrawerOpened(drawerView);
// code here will execute once the drawer is opened( As I dont want anything happened whe drawer is
// open I am not going to put anything here)
}
#Override
public void onDrawerClosed(View drawerView) {
super.onDrawerClosed(drawerView);
// Code here will execute once drawer is closed
}
}; // Drawer Toggle Object Made
mDrawerLayout.addDrawerListener(mDrawerToggle); // Drawer Listener set to the Drawer toggle
mDrawerToggle.syncState(); // Finally we set the drawer toggle sync State
mDrawerAdapter.setOnItemClickLister(new DrawerAdapter.OnItemSelecteListener() {
#Override
public void onItemSelected(View v, int position) {
Log.e("CLICK", "You clicked at position: " + position);
}
});
drawer_layout.xml
<android.support.v4.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/DrawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:elevation="7dp">
<FrameLayout
android:id="#+id/containerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
<android.support.v7.widget.RecyclerView
android:id="#+id/drawerRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:layout_gravity="left"
android:background="#ffffff"
app:reverseLayout="false"
app:stackFromEnd="false">
</android.support.v7.widget.RecyclerView>
footer.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:orientation="vertical" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="bottom"
android:layout_alignParentBottom="true"
android:background="#ffffff"
android:layout_alignBottom="#id/drawerRecyclerView"
android:foregroundGravity="bottom">
<LinearLayout
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentStart="false"
android:layout_alignParentEnd="true"
android:background="#f16666"
android:foregroundGravity="bottom"
android:gravity="center_horizontal|bottom"
android:paddingTop="#dimen/_15sdp"
android:paddingBottom="#dimen/_15sdp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textAppearance="?android:attr/textAppearanceLarge"
android:text="TEST"
android:id="#+id/textView3"
android:layout_gravity="center_horizontal"
android:layout_alignParentBottom="true"
android:gravity="center_horizontal|bottom" />
</LinearLayout>
</RelativeLayout>
In your drawer_layout.xml put RecyclerView inside a RelativeLayout and set alignParentTop to true. Then remove your footer view from your RecyclerView and put that inside the RelativeLayout below RecyclerView like this:
<android.support.v4.widget.DrawerLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="#+id/DrawerLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentBottom="true"
android:elevation="7dp">
<FrameLayout
android:id="#+id/containerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" />
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="#+id/drawerRecyclerView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_alignParentTop="true"
android:layout_gravity="left"
android:background="#ffffff"
app:reverseLayout="false"
app:stackFromEnd="false" />
<include layout="#layout/footer" />
</RelativeLayout>
</android.support.v4.widget.DrawerLayout>
You could also take a look at NavigationView so you don't have to customize your drawer list. Here is a tutorial on how to use this. It is pretty easy to use and requires less code. You could then take a look at this answer on how to put a footer in NavigationView.
I am using LinearLayout with weight-ing (created multiple dimens files with different values). For me it works as excepted:
<?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"
android:background="#color/white"
android:orientation="vertical"
<include layout="#layout/header" />
<android.support.v7.widget.RecyclerView
android:id="#+id/recycleView"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="0.5"
tools:layout_height="0dp"
tools:listitem="#layout/row" />
<TextView
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_weight="#dimen/footer_weight"
android:padding="#dimen/extra_padding"
android:paddingEnd="#dimen/small_padding"
android:paddingLeft="#dimen/small_padding"
android:paddingRight="#dimen/small_padding"
android:paddingStart="#dimen/small_padding"
android:text="#string/contact"
android:textColor="#color/grey" />
</LinearLayout>
Related
I have an AlertDialog in it there used to be a ListView in which the contents of the ArrayList were displayed. For beauty, I decided to replace it with Recyclerview, Card and the problems started.
Items are added to ArrayList <String> mainListWord during the work and they should be included in the Recyclerview cards. The problem is that either the entire list falls into one card at once, or each next card contains the previous elements + 1. For example: the first card: 1 element, the second card: 2 elements, etc.
How to implement it so that when a new element is added to the array, it will be placed in its own card?
activity_main.xml
<?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="match_parent"
tools:context=".MainActivity">
<EditText
android:id="#+id/innerText"
android:layout_width="wrap_content"
android:layout_height="50dp"
android:layout_centerInParent="true"/>
<Button
android:id="#+id/press"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:text="Press"
android:layout_below="#id/innerText"
android:layout_centerHorizontal="true"
android:onClick="openDialog"/>
</RelativeLayout>
recycler_item.xml
<?xml version="1.0" encoding="utf-8"?>
<androidx.cardview.widget.CardView
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"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:cardCornerRadius="4dp"
android:layout_margin="4dp"
app:cardBackgroundColor="#color/colorCardBack">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_margin="5dp"
android:background="#color/colorCard">
<ImageView
android:id="#+id/imageView_1"
android:layout_width="50dp"
android:layout_height="50dp"
android:padding="4dp" />
<TextView
android:id="#+id/textview_1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Text_1"
android:textStyle="bold"
android:textColor="#android:color/black"
android:layout_centerInParent="true"
android:textSize="16sp"
android:layout_marginStart="5dp"
android:layout_toEndOf="#id/imageView_1"/>
</RelativeLayout>
</androidx.cardview.widget.CardView>
stats_fragment.xml
<?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="match_parent"
tools:context=".MainActivity">
<androidx.recyclerview.widget.RecyclerView
android:id="#+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="50dp"
android:layout_margin="10dp"
android:background="#color/colorAccent"
android:scrollbars="vertical"
android:layout_above="#id/butClose"/>
<Button
android:id="#+id/butClose"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="5dp"
android:layout_centerHorizontal="true"
android:layout_alignParentBottom="true"/>
</RelativeLayout>
MainActivity.java
public class MainActivity extends AppCompatActivity {
private EditText innerText;
private Button press, butClose;
private ArrayList<RecyclerViewItem> recyclerViewItem;
private ArrayList<String> mainListWord;
private AlertDialog OptionDialog;
private RecyclerView recyclerView;
private RecyclerView.Adapter adapter;
private RecyclerView.LayoutManager layoutManager;
#RequiresApi(api = Build.VERSION_CODES.O)
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerViewItem = new ArrayList<>();
mainListWord = new ArrayList<>();
innerText = findViewById(R.id.innerText);
press = findViewById(R.id.butClose);
}
#RequiresApi(api = Build.VERSION_CODES.O)
public void makeRecyclerList(ArrayList<String> income){
String[] listWord_lenght = income.toArray(new String[0]);
String keyWord = (String.join("", listWord_lenght));
recyclerViewItem.add(new RecyclerViewItem(R.drawable.star, keyWord));
}
#RequiresApi(api = Build.VERSION_CODES.O)
public void openDialog(View v){
String word = innerText.getText().toString();
mainListWord.add(word);
makeRecyclerList(mainListWord);
Dialogus();
innerText.setText("");
}
public void Dialogus(){
LayoutInflater li = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
View v = li.inflate(R.layout.stats_fragment, null, false);
OptionDialog = new AlertDialog.Builder(this).create();
OptionDialog.setTitle("TestInfo");
recyclerView = v.findViewById(R.id.recycler_view);
recyclerView.setHasFixedSize(true);
adapter = new RecyclerViewAdapter(recyclerViewItem);
layoutManager = new LinearLayoutManager(this);
recyclerView.setAdapter(adapter);
recyclerView.setLayoutManager(layoutManager);
butClose = v.findViewById(R.id.butClose);
OptionDialog.setView(v);
OptionDialog.setCancelable(true);
butClose.setBackgroundColor(Color.CYAN);
butClose.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
OptionDialog.dismiss();
}
});
OptionDialog.show();
}
}
RecyclerViewAdapter.java
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.RecyclerViewViewHolder> {
private ArrayList<RecyclerViewItem> arrayList;
public static class RecyclerViewViewHolder extends RecyclerView.ViewHolder {
public ImageView imageView_1;
public TextView textview_1;
public RecyclerViewViewHolder(#NonNull View itemView) {
super(itemView);
imageView_1 = itemView.findViewById(R.id.imageView_1);
textview_1 = itemView.findViewById(R.id.textview_1);
}
}
public RecyclerViewAdapter(ArrayList<RecyclerViewItem> arrayList){
this.arrayList = arrayList;
}
#NonNull
#Override
public RecyclerViewViewHolder onCreateViewHolder(#NonNull ViewGroup viewGroup, int viewType) {
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.recycler_item, viewGroup, false);
RecyclerViewViewHolder recyclerViewViewHolder= new RecyclerViewViewHolder(view);
return recyclerViewViewHolder;
}
#Override
public void onBindViewHolder(#NonNull RecyclerViewViewHolder recyclerViewViewHolder, int position) {
RecyclerViewItem recyclerViewItem = arrayList.get(position);
recyclerViewViewHolder.imageView_1.setImageResource(recyclerViewItem.getImageResource());
recyclerViewViewHolder.textview_1.setText(recyclerViewItem.getText_1());
}
#Override
public int getItemCount() {
return arrayList.size();
}
}
RecyclerViewItem.java
package freijer.app.one;
public class RecyclerViewItem {
private int imageResource;
private String text_1;
public int getImageResource() {
return imageResource;
}
public void setImageResource(int imageResource) {
this.imageResource = imageResource;
}
public String getText_1() {
return text_1;
}
public void setText_1(String text_1) {
this.text_1 = text_1;
}
public RecyclerViewItem(int imageResource, String text_1) {
this.imageResource = imageResource;
this.text_1 = text_1;
}
}
I want to implement a ListView with internet images. I use Glide to load images in list items, but I found sizes of images always be confusing. I wrote some test code to reappear this problem.
At first it is normal.
However when I slide the ListView fastly, the sizes of images become confusing.
How can I fix the problem without modify the source code of glide?
MainActivity.java
public class MainActivity extends AppCompatActivity {
private String[] urls = new String[]{"https:......"};
//urls of images.
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
ListView listView = findViewById(R.id.list_view);
final SparseArray<String> sparseArray = new SparseArray<>();
listView.setAdapter(new BaseAdapter() {
#Override
public int getCount() {
return 100000;
}
#Override
public Object getItem(int i) {
String url = sparseArray.get(i);
if(url == null){
url = urls[(int)(Math.random() * urls.length)];
sparseArray.put(i,url);
}
return url;
}
#Override
public long getItemId(int i) {
return i;
}
#Override
public View getView(int i, View view, ViewGroup viewGroup) {
if(view == null){
view = View.inflate(getBaseContext(),R.layout.list_view_item_image,null);
}
ImageView imageView = view.findViewById(R.id.image_view);
Glide.with(getBaseContext())
.load((String)getItem(i))
.into(imageView);
return view;
}
});
}
}
activity_main.xml
<?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"
tools:context=".MainActivity">
<ListView
android:id="#+id/list_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
/>
</android.support.constraint.ConstraintLayout>
list_view_item_image.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">
<ImageView
android:id="#+id/image_view"
android:adjustViewBounds="true"
android:maxHeight="200dp"
android:maxWidth="200dp"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>
I'm trying to show an additional CardView that contains a different layout but have got lost with my code. I'm also unsure of how to to show the array of strings within a GridView for the CardView itself. Does anyone know where I may have gone wrong & what can be done so that the following can be achieved?:
Place the CardView containing the GridView wherever I want within the RecyclerView.
Show the array of strings in a GridView within a CardView
What I want to add to the RecyclerView (above the Item A CardView)
RecyclerView current contents
Fragment class
public class MyFragment extends android.support.v4.app.Fragment {
private MonRecyclerAdapterWithGrid adapterG;
static final String[] frenchVowels = new String[]{
"a", "e", "i", "o", "u", "y"
};
public MyFragment() {}
#Override
public View onCreateView(LayoutInflater inflater, #Nullable ViewGroup container, #Nullable Bundle savedInstanceState) {
return inflater.inflate(R.layout.fragment_rv, container, false);
}
#Override
public void onActivityCreated(#Nullable Bundle savedInstanceState) {
View v = getView();
assert v != null;
recyclerView = v.findViewById(R.id.my_recyclerview);
linearLayoutManager = new LinearLayoutManager(getActivity());
MyRecyclerAdapter adapter = new MyRecyclerAdapter(getContext(), getHeader(), getListItemsG(), getListItemsT());
recyclerView.setLayoutManager(linearLayoutManager);
recyclerView.setAdapter(adapter);
super.onActivityCreated(savedInstanceState);
}
RecyclerView recyclerView;
LinearLayoutManager linearLayoutManager;
public RecyclerViewHeader getHeader()
{
return new RecyclerViewHeader();
}
public List<RecyclerViewItemGV> getListItemsG() {
List<RecyclerViewItemGV> rvItemsG = new ArrayList<>();
RecyclerViewItemGV itemG = new RecyclerViewItemGV();
itemG.setTitleGV("Item A");
itemG.setVowelsGV(adapterG);
for (String fVowels : frenchVowels) {
// ?????? Still not working :-(
adapterG.addAdapterItem(new MyFragment.AdapterItem(frenchVowels));
}
rvItemsG.add(itemG);
return rvItemsG;
}
public List<RecyclerViewItemTV> getListItemsT()
{
List<RecyclerViewItemTV> rvItemsT = new ArrayList<>();
RecyclerViewItemTV itemA = new RecyclerViewItemTV();
itemA.setTitleTV("Item A");
itemA.setDescriptionTV("Feature A1");
rvItemsT.add(itemA);
RecyclerViewItemTV itemB = new RecyclerViewItemTV();
itemB.setTitleTV("Item B");
itemB.setDescriptionTV("Feature B1\nFeature B2");
rvItemsT.add(itemB);
RecyclerViewItemTV itemC = new RecyclerViewItemTV();
itemC.setTitleTV("Item C");
itemC.setDescriptionTV("Feature C1\nFeature C2\nFeature C3");
rvItemsT.add(itemC);
return rvItemsT;
}
}
RecyclerView adapter class
public class MyRecyclerAdapter extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int TYPE_HEADER = 0;
private static final int TYPE_ITEMG = 1;
private static final int TYPE_ITEMT = 2;
private Context mContext;
RecyclerViewHeader header;
List<RecyclerViewItemGV> listItemsG;
List<RecyclerViewItemTV> listItemsT;
ValueAnimator mAnimator;
public MyRecyclerAdapter(Context context, RecyclerViewHeader header, List<RecyclerViewItemGV> listItemsG, List<RecyclerViewItemTV> listItemsT)
{
this.mContext = context;
this.header = header;
this.listItemsG = listItemsG;
this.listItemsT = listItemsT;
}
#Override
public RecyclerView.ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
if(viewType == TYPE_HEADER)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_header_expandcollapsebuttons, parent, false);
return new MyRecyclerAdapter.VHHeader(v);
}
else if(viewType == TYPE_ITEMG)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_gv, parent, false);
return new MyRecyclerAdapter.VHItemG(v);
}
else if(viewType == TYPE_ITEMT)
{
View v = LayoutInflater.from(parent.getContext()).inflate(R.layout.recyclerview_item_tv, parent, false);
return new MyRecyclerAdapter.VHItemT(v);
}
throw new RuntimeException("there is no type that matches the type " + viewType + " + make sure your using types correctly");
}
private RecyclerViewItemGV getItemG(int position)
{
return listItemsG.get(position);
}
private RecyclerViewItemTV getItemT(int position)
{
return listItemsT.get(position);
}
#Override
public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
final Typeface iconFont = FontManager.getTypeface(mContext, FontManager.FONTAWESOME);
if (holder instanceof MyRecyclerAdapter.VHHeader)
{
final MyRecyclerAdapter.VHHeader vhHeader = (MyRecyclerAdapter.VHHeader)holder;
}
else if (holder instanceof MyRecyclerAdapter.VHItemG){
RecyclerViewItemGV currentItemG = getItemG(position-1);
final MonRecyclerAdapterWithGrid.VHItemG vhItemG = (MyRecyclerAdapter.VHItemG)holder;
vhItemG.txtAG.setText(currentItemG.getTitleGV());
vhItemG.mGridViewG.setVisibility(View.GONE);
vhItemG.txtExpandCollapseG.setText(R.string.fa_icon_chevron_down);
vhItemG.txtExpandCollapseG.setTypeface(iconFont);
//Add onPreDrawListener
vhItemG.mGridViewG.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
vhItemG.mGridViewG.getViewTreeObserver().removeOnPreDrawListener(this);
vhItemG.mGridViewG.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
vhItemG.mGridViewG.measure(widthSpec, heightSpec);
vhItemG.mGridViewHeight = vhItemG.mGridViewG.getMeasuredHeight();
return true;
}
});
vhItemG.mCardViewG.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(vhItemG.mGridViewG.getVisibility() == View.GONE){
vhItemG.expandG();
} else {
vhItemG.collapseG();
}
}
});
}
else if (holder instanceof MyRecyclerAdapter.VHItemT)
{
RecyclerViewItemTV currentItem = getItemT(position-2);
final MyRecyclerAdapter.VHItemT vhItemT = (MyRecyclerAdapter.VHItemT)holder;
vhItemT.txtA.setText(currentItem.getTitleTV());
vhItemT.txtB.setText(currentItem.getDescriptionTV());
vhItemT.txtB.setVisibility(View.GONE);
vhItemT.txtExpandCollapse.setText(R.string.fa_icon_chevron_down);
vhItemT.txtExpandCollapse.setTypeface(iconFont);
//Add onPreDrawListener
vhItemT.txtB.getViewTreeObserver().addOnPreDrawListener(
new ViewTreeObserver.OnPreDrawListener() {
#Override
public boolean onPreDraw() {
vhItemT.txtB.getViewTreeObserver().removeOnPreDrawListener(this);
vhItemT.txtB.setVisibility(View.GONE);
final int widthSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
final int heightSpec = View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED);
vhItemT.txtB.measure(widthSpec, heightSpec);
vhItemT.textBHeight = vhItemT.txtB.getMeasuredHeight();
return true;
}
});
vhItemT.cardView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View v) {
if(vhItemT.txtB.getVisibility() == View.GONE){
vhItemT.expandT();
} else {
vhItemT.collapseT();
}
}
});
}
}
// need to override this method
#Override
public int getItemViewType(int position) {
if(isPositionHeader(position))
return TYPE_HEADER;
return TYPE_ITEMG;
return TYPE_ITEMT;
}
private boolean isPositionHeader(int position)
{
return position == 0;
}
#Override
public int getItemCount() {
return listItemsG.size()+1;
return listItemsT.size()+1;
}
class VHHeader extends RecyclerView.ViewHolder{
Button btnCollapseAll, btnExpandAll;
public VHHeader(View headerView) {
super(headerView);
this.btnCollapseAll = headerView.findViewById(R.id.btn_collapseall);
this.btnExpandAll = headerView.findViewById(R.id.btn_expandall);
}
}
public class VHItemG extends RecyclerView.ViewHolder{
CardView mCardViewG;
LinearLayout mLinearLayoutG;
RelativeLayout mRelativeLayoutG;
RecyclerView mRecyclerViewG;
GridView mGridViewG;
TextView txtExpandCollapseG, txtAG;
public int mGridViewHeight;
public VHItemG(View itemView) {
super(itemView);
this.mCardViewG = itemView.findViewById(R.id.cv_gv);
this.mLinearLayoutG = itemView.findViewById(R.id.linearlayout_gv_titlerow);
this.mRelativeLayoutG = itemView.findViewById(R.id.relativelayout_gv);
this.mRecyclerViewG = itemView.findViewById(R.id.my_recyclerview);
this.txtAG = itemView.findViewById(R.id.tv_gv_A);
this.txtExpandCollapseG = itemView.findViewById(R.id.tv_gv_expandcollapse);
this.mGridViewG = itemView.findViewById(R.id.gv_a);
}
private void expandG() {
// change visibility to 'VISIBLE'
mGridViewG.setVisibility(View.VISIBLE);
// change direction of chevron to 'up'
txtExpandCollapseG.setText(R.string.fa_icon_chevron_up);
// apply animation to the height of 'txtB'
mAnimator = slideAnimator(0, mGridViewHeight);
// start the animation
mAnimator.start();
}
private void collapseG() {
// change direction of chevron to 'down'
txtExpandCollapseG.setText(R.string.fa_icon_chevron_down);
int finalHeight = mGridViewG.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
// Height will be 0, but set visibility to 'GONE'
mGridViewG.setVisibility(View.GONE);
}
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimator.start();
}
public ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// update height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = mGridViewG.getLayoutParams();
layoutParams.height = value;
mGridViewG.setLayoutParams(layoutParams);
}
});
return animator;
}
}
public class VHItemT extends RecyclerView.ViewHolder{
CardView cardView;
LinearLayout mLinearLayout;
RecyclerView mRecyclerView;
RelativeLayout mRelativeLayout;
TextView txtExpandCollapse, txtA, txtB;
public int textBHeight;
public VHItemT(View itemView) {
super(itemView);
this.cardView = itemView.findViewById(R.id.linearlayout_tv_main);
this.mLinearLayout = itemView.findViewById(R.id.linearlayout_tv_titlerow);
this.mRelativeLayout = itemView.findViewById(R.id.relativelayout_tv);
this.mRecyclerView = itemView.findViewById(R.id.my_recyclerview);
this.txtExpandCollapse = itemView.findViewById(R.id.tv_tv_expandcollapse);
this.txtA = itemView.findViewById(R.id.tv_tv_A);
this.txtB = itemView.findViewById(R.id.tv_tv_B);
}
private void expandT() {
// change visibility to 'VISIBLE'
txtB.setVisibility(View.VISIBLE);
// change direction of chevron to 'up'
txtExpandCollapse.setText(R.string.fa_icon_chevron_up);
// apply animation to the height of 'txtB'
mAnimator = slideAnimator(0, textBHeight);
// start the animation
mAnimator.start();
}
private void collapseT() {
// change direction of chevron to 'down'
txtExpandCollapse.setText(R.string.fa_icon_chevron_down);
int finalHeight = txtB.getHeight();
ValueAnimator mAnimator = slideAnimator(finalHeight, 0);
mAnimator.addListener(new Animator.AnimatorListener() {
#Override
public void onAnimationEnd(Animator animator) {
// Height will be 0, but set visibility to 'GONE'
txtB.setVisibility(View.GONE);
}
#Override
public void onAnimationStart(Animator animator) {
}
#Override
public void onAnimationCancel(Animator animator) {
}
#Override
public void onAnimationRepeat(Animator animator) {
}
});
mAnimator.start();
}
public ValueAnimator slideAnimator(int start, int end) {
ValueAnimator animator = ValueAnimator.ofInt(start, end);
animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
#Override
public void onAnimationUpdate(ValueAnimator valueAnimator) {
// update height
int value = (Integer) valueAnimator.getAnimatedValue();
ViewGroup.LayoutParams layoutParams = txtB.getLayoutParams();
layoutParams.height = value;
txtB.setLayoutParams(layoutParams);
}
});
return animator;
}
}
}
GridView adapter (currently excluded from project)
private class MyGVAdapter extends ArrayAdapter<AdapterItem> {
private List<AdapterItem> items = new ArrayList<>();
MyGVAdapter(Context context, int textviewid) {
super(context, textviewid);
}
void addAdapterItem(MyGVFragment.AdapterItem item) {
items.add(item);
}
#Override
public int getCount() {
return items.size();
}
#Override
public MyGVFragment.AdapterItem getItem(int position) {
return ((null != items) ? items.get(position) : null);
}
#Override
public long getItemId(int position) {
return position;
}
#NonNull
#Override
public View getView(final int position, View convertView, #NonNull final ViewGroup parent) {
View rowView;
if (convertView == null) {
rowView = getActivity().getLayoutInflater().inflate(R.layout.gridview_item, parent, false);
} else {
rowView = convertView;
}
TextView tv = rowView.findViewById(R.id.item_gridview);
tv.setText(items.get(position).first);
return rowView;
}
}
class AdapterItem {
String first;
AdapterItem(String first) {
this.first = first;
}
}
}
gridview_item.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="match_parent" >
<TextView
android:id="#+id/item_gridview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:paddingStart="0dp"
android:paddingEnd="10dp"
android:paddingTop="10dp"
android:paddingBottom="10dp"
android:textAppearance="?android:attr/textAppearanceLarge"
android:textColor="?android:attr/textColorPrimary"
/>
</LinearLayout>
CardView with GridView (recyclerview_item_gv)
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:id="#+id/cv_gv"
android:layout_marginBottom="20dp"
>
<LinearLayout
android:id="#+id/lineralayout_gv_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/linearlayout_gv_titlerow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="2dp"
android:weightSum="100">
<TextView
android:id="#+id/tv_gv_A"
android:layout_weight="90"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
style="#android:style/TextAppearance.Medium" />
<TextView
android:id="#+id/tv_gv_expandcollapse"
android:importantForAccessibility="no"
android:clickable="true"
android:focusable="true"
android:layout_weight="10"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="?android:attr/textColorPrimary"
style="#android:style/TextAppearance.Large" />
</LinearLayout>
<RelativeLayout
android:id="#+id/relativelayout_gv"
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<GridView
android:id="#+id/gv_a"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:columnWidth="100dp"
android:numColumns="auto_fit"
android:layout_marginBottom="20dp"
android:stretchMode="columnWidth" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
CardView with TextView (recyclerview_item_tv.xml)
<?xml version="1.0" encoding="utf-8"?>
<android.support.v7.widget.CardView
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:clickable="true"
android:focusable="true"
android:id="#+id/cv_tv"
android:layout_marginBottom="20dp">
<LinearLayout
android:id="#+id/linearlayout_gv_main"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="10dp"
android:animateLayoutChanges="true">
<LinearLayout
android:id="#+id/linearlayout_tv_titlerow"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginBottom="2dp"
android:weightSum="100">
<TextView
android:id="#+id/tv_tv_A"
android:layout_weight="90"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
style="#android:style/TextAppearance.Medium" />
<TextView
android:id="#+id/tv_tv_expandcollapse"
android:importantForAccessibility="no"
android:clickable="true"
android:focusable="true"
android:layout_weight="10"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="10dp"
android:textColor="?android:attr/textColorPrimary"
style="#android:style/TextAppearance.Large" />
</LinearLayout>
<RelativeLayout
android:id="#+id/relativelayout_tv"
android:animateLayoutChanges="true"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:id="#+id/tv_B"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:textColor="?android:attr/textColorPrimary"
style="#android:style/TextAppearance.Large" />
</RelativeLayout>
</LinearLayout>
</android.support.v7.widget.CardView>
Ok so I made a quick example of how to do it. This is how the Activity class I'm posting looks like, all items are in the same RecyclerView:
Bear in mind, it might not look like your code because I am trying to use (At least for me) best practices, and also shorten the amount of code and classes by containing a lot of things in the same class.
Here is the Activity:
public class RecyclerActivity extends AppCompatActivity {
RecyclerView recycler;
ArrayList<String> data;
RecyclerView.Adapter<ViewHolder> adapter;
private static final int ITEM_TYPE = 100;
private static final int HEADER_TYPE = 101;
private static final int HEADER_TYPE_2 = 102;
private static final int GRID_TYPE = 103;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_recycler);
// find recycler,
recycler = findViewById(R.id.recycler);
// set the layout
recycler.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
// init data,
data = new ArrayList<>();
data.add("Item A");
data.add("Item B");
data.add("Item C");
// create the adapter
adapter = createAdapter();
// set the adapter
recycler.setAdapter(adapter);
}
// creates the adapter,
private RecyclerView.Adapter<ViewHolder> createAdapter() {
return new RecyclerView.Adapter<ViewHolder>() {
#NonNull
#Override
public ViewHolder onCreateViewHolder(#NonNull ViewGroup parent, int type) {
switch (type) {
case HEADER_TYPE:
// inflate the layout,
ViewHolder holderHeader1 = new ViewHolder(inflateHelper(R.layout.header, parent));
// set an on click to the view here to create only one object,
holderHeader1.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do something
}
});
return holderHeader1;
case HEADER_TYPE_2:
// inflate the layout,
ViewHolder holderHeader2 = new ViewHolder(inflateHelper(R.layout.header, parent));
// set an on click to the view here to create only one object,
holderHeader2.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do something
}
});
return holderHeader2;
case ITEM_TYPE:
// inflate the layout,
ViewHolder holderItem = new ViewHolder(inflateHelper(R.layout.item, parent));
// set an on click to the view here to create only one object,
holderItem.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do something
}
});
return holderItem;
case GRID_TYPE:
// inflate the layout,
ViewHolder holderGrid = new ViewHolder(inflateHelper(R.layout.grid, parent));
// set an on click to the view here to create only one object,
holderGrid.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do something
}
});
return holderGrid;
default:
// inflate the layout,
ViewHolder holderItemDefault = new ViewHolder(inflateHelper(R.layout.item, parent));
// set an on click to the view here to create only one object,
holderItemDefault.itemView.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
// do something
}
});
return holderItemDefault;
}
}
/**
* Keep the viewholder simple and the all the view finding here. This way you
* only have one viewholder.
*/
#Override
public void onBindViewHolder(#NonNull ViewHolder viewHolder, int position) {
// go through the positions
switch (getItemViewType(position)) {
case HEADER_TYPE:
Button expandButton = viewHolder.itemView.findViewById(R.id.button);
expandButton.setText("Expand");
break;
case HEADER_TYPE_2:
Button collapseButton = viewHolder.itemView.findViewById(R.id.button);
collapseButton.setText("Collapse");
break;
case ITEM_TYPE:
// get the current item
String item = data.get(position - 3);
TextView title = viewHolder.itemView.findViewById(R.id.title);
title.setText(item);
break;
case GRID_TYPE:
break;
}
}
#Override
public int getItemCount() {
return data.size() + 3;
}
#Override
public int getItemViewType(int position) {
switch (position) {
case 0:
return HEADER_TYPE;
case 1:
return HEADER_TYPE_2;
case 2:
return GRID_TYPE;
default: return ITEM_TYPE;
}
}
};
}
private View inflateHelper(int resId, ViewGroup parent) {
return LayoutInflater.from(this).inflate(resId, parent, false);
}
// inner class for viewholder to use,
class ViewHolder extends RecyclerView.ViewHolder {
public ViewHolder(#NonNull View itemView) {
super(itemView);
}
}
}
You might notice that I have used one ViewHolder. This is a pattern I believe we should adopt because it simplifies code so much more. You can find the views you need in the onBind method and thus get away with using one for all types of views.
Also I add the on click listener in the onCreateViewHolder because it is much more efficient to set it there, and it allows for the viewholder to not be onClick specific.
As I said, you can use as many types as you want, you just need to check for it, and set it at the right position.
Here are the layout files if you are interested:
Header
<?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:padding="4dp"
android:gravity="center">
<Button
android:id="#+id/button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Expand" />
</LinearLayout>
Item
<?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:padding="16dp"
android:elevation="2dp"
android:layout_margin="16dp"
android:background="#drawable/rounded">
<TextView
android:id="#+id/title"
android:layout_width="0dp"
android:layout_weight="1"
android:layout_height="wrap_content"
android:text="Item A"
android:textColor="#fff"/>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="#drawable/ic_chevron_right"
android:tint="#fff"/>
</LinearLayout>
Grid Layout (I cheated a bit here because I didn't see a reason to not hardcode vowels, although you might need to do it dynamically)
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="32dp"
android:layout_marginEnd="32dp"
android:layout_marginTop="16dp"
android:layout_marginBottom="16dp"
android:padding="16dp"
android:background="#drawable/rounded"
android:backgroundTint="#fff"
android:elevation="4dp">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical">
<TextView
android:id="#+id/title"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:paddingBottom="16dp"
android:text="French Vowels"
android:textStyle="bold"
style="#style/Base.TextAppearance.AppCompat.Medium"/>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="#+id/a"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="wrap_content"
android:text="a"
style="#style/Base.TextAppearance.AppCompat.Medium"/>
<TextView
android:id="#+id/e"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="wrap_content"
android:text="e"
style="#style/Base.TextAppearance.AppCompat.Medium"/>
<TextView
android:id="#+id/i"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="wrap_content"
android:text="i"
style="#style/Base.TextAppearance.AppCompat.Medium"/>
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp">
<TextView
android:id="#+id/o"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="wrap_content"
android:text="o"
style="#style/Base.TextAppearance.AppCompat.Medium"/>
<TextView
android:id="#+id/u"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="wrap_content"
android:text="u"
style="#style/Base.TextAppearance.AppCompat.Medium"/>
<TextView
android:id="#+id/y"
android:layout_width="0dp"
android:layout_weight="1"
android:gravity="center"
android:layout_height="wrap_content"
android:text="y"
style="#style/Base.TextAppearance.AppCompat.Medium"/>
</LinearLayout>
</LinearLayout>
</FrameLayout>
Rounded Drawable
<?xml version="1.0" encoding="utf-8"?>
<shape android:shape="rectangle" xmlns:android="http://schemas.android.com/apk/res/android">
<solid android:color="#222"/>
<corners android:radius="6dp"/>
</shape>
For anyone reading, if you happen to use Kotlin, consider using my small library which eliminates this recycler view boilerplate to a simple chain of functions:
https://github.com/Pfuster12/BoilerCycle
Okay, so I've been looking far and wide, I've been able to add the same child item to every group, but I'm unable to get it to respond to clicks. Right now it's acting like a child header, and I need it to act like the other child items.
Currently my lists are being populated from a HashMap that is located in another file. Because you cannot add to the middle of a HashMap without completely rebuilding it, I figured the best option was to add the child somewhere when the list is being accessed within the adapter. I'm just not sure where or how.
As you can see from the getChildView() I add my repeating child layout (expandedListItemChildHeader), if the position of child is 0. I also added 1 value to the size of the getChildrenCount().
Here's my adapter code:
public class CustomExpandableListAdapter extends BaseExpandableListAdapter {
private Context context;
private List<String> expandableListTitle;
private HashMap<String, List<String>> expandableListDetail;
public CustomExpandableListAdapter(Context context, List<String> expandableListTitle,
HashMap<String, List<String>> expandableListDetail) {
this.context = context;
this.expandableListTitle = expandableListTitle;
this.expandableListDetail = expandableListDetail;
}
#Override
public Object getChild(int listPosition, int expandedListPosition) {
return this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
.get(expandedListPosition);
}
#Override
public long getChildId(int listPosition, int expandedListPosition) {
return expandedListPosition;
}
#Override
public View getChildView(int listPosition, final int expandedListPosition,
boolean isLastChild, View convertView, ViewGroup parent) {
LayoutInflater layoutInflater = (LayoutInflater) this.context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
//if we're in the first position then add the list_item_header
if(expandedListPosition == 0)
{
convertView = layoutInflater.inflate(R.layout.list_item_header, null);
TextView expandedListTextView = (TextView) convertView.findViewById(R.id.expandedListItemChildHeader);
expandedListTextView.setText("All Devices");
}
//otherwise add the list_item
if (expandedListPosition > 0 && expandedListPosition < getChildrenCount(listPosition)) {
final String expandedListText = (String) getChild(listPosition, expandedListPosition - 1);
convertView = layoutInflater.inflate(R.layout.list_item, null);
TextView expandedListTextView = (TextView) convertView.findViewById(R.id.expandedListItem);
expandedListTextView.setText(expandedListText);
}
return convertView;
}
#Override
public int getChildrenCount(int listPosition) {
//add a value of one to the size of the count of children per group to make
//room for the child header
return (this.expandableListDetail.get(this.expandableListTitle.get(listPosition))
.size() + 1);
}
#Override
public Object getGroup(int listPosition) {
return this.expandableListTitle.get(listPosition);
}
#Override
public int getGroupCount() {
return this.expandableListTitle.size();
}
#Override
public long getGroupId(int listPosition) {
return listPosition;
}
#Override
public View getGroupView(int listPosition, boolean isExpanded,
View convertView, ViewGroup parent) {
String listTitle = (String) getGroup(listPosition);
if (convertView == null) {
LayoutInflater layoutInflater = (LayoutInflater) this.context.
getSystemService(Context.LAYOUT_INFLATER_SERVICE);
convertView = layoutInflater.inflate(R.layout.list_group, null);
}
TextView listTitleTextView = (TextView) convertView
.findViewById(R.id.listTitle);
listTitleTextView.setTypeface(Typeface.create("sans-serif-medium", Typeface.NORMAL));
listTitleTextView.setText(listTitle);
//Use this conditional to change direction of group indicator icons
//the ImageView in the xml layout is invisible by default.
ImageView groupIndicatorView = (ImageView) convertView.findViewById(R.id.expand_GroupIndicator);
if (getChildrenCount(listPosition) == 0 ) {
groupIndicatorView.setVisibility( View.INVISIBLE );
}
else {
groupIndicatorView.setVisibility( View.VISIBLE );
groupIndicatorView.setImageResource( isExpanded ? R.drawable.ic_expand_less_black_24dp : R.drawable.ic_expand_more_black_24dp );
}
return convertView;
}
#Override
public boolean hasStableIds() {
return false;
}
#Override
public boolean isChildSelectable(int listPosition, int expandedListPosition) {
return true;
}
Following is my expandable list code in my MainActivity which is located in the onCreate() method: (note: the variables listed at the top of the below sample code is outside of the onCreate() method, I just included it for reference.
ExpandableListView expandableListView;
ExpandableListAdapter expandableListAdapter;
List<String> expandableListTitle;
HashMap<String, List<String>> expandableListDetail;
expandableListView = (ExpandableListView) findViewById(R.id.navDrawer_userListView);
expandableListDetail = ExpandableListDataPump.getData();
expandableListTitle = new ArrayList<String>(expandableListDetail.keySet());
expandableListAdapter = new CustomExpandableListAdapter(this, expandableListTitle, expandableListDetail);
expandableListView.setAdapter(expandableListAdapter);
expandableListView.setOnGroupExpandListener(new ExpandableListView.OnGroupExpandListener() {
//initialize int var
int previousGroup = 0;
#Override
public void onGroupExpand(int groupPosition) {
//this conditional enables only one drop down group to open at a time
if(groupPosition != previousGroup)
expandableListView.collapseGroup(previousGroup);
previousGroup = groupPosition;
Toast.makeText(
getApplicationContext(),
expandableListTitle.get(groupPosition)
+ " List Expanded.",
Toast.LENGTH_SHORT
).show();
}
});
expandableListView.setOnGroupCollapseListener(new ExpandableListView.OnGroupCollapseListener() {
#Override
public void onGroupCollapse(int groupPosition) {
Toast.makeText(getApplicationContext(),
expandableListTitle.get(groupPosition) + " List Collapsed.",
Toast.LENGTH_SHORT
).show();
}
});
expandableListView.setOnChildClickListener(new ExpandableListView.OnChildClickListener() {
#Override
public boolean onChildClick(ExpandableListView parent, View v,
int groupPosition, int childPosition, long id) {
v.setSelected(true);
//Change title on toolbar to match group selection
getSupportActionBar().setTitle(expandableListTitle.get(groupPosition) + "'s Devices");
//Change subtitle on toolbar to match item selection
//must offset child position by 1 to have room for child header
getSupportActionBar().setSubtitle((String)expandableListAdapter.getChild(groupPosition, childPosition - 1));
Toast.makeText(
getApplicationContext(),
expandableListTitle.get(groupPosition)
+ " -> "
+ expandableListDetail.get(
expandableListTitle.get(groupPosition)).get(
childPosition - 1), Toast.LENGTH_SHORT
).show();
return false;
}
});
Here are my layouts
starting with the list_item_header.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">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="7dp">
<Switch
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#null"
android:id="#+id/toggle_allDevices"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:paddingLeft="2dp"
android:tint="#color/drawerContent"/>
<TextView
android:id="#+id/expandedListItemChildHeader"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#drawable/expandable_text_selector"
android:paddingLeft="65dp"
android:paddingTop="16dp"
android:textSize="16dp"
android:paddingBottom="16dp" />
</RelativeLayout>
</LinearLayout>
Here is my list_item.xml layout
<?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">
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="7dp">
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#null"
android:id="#+id/deviceTypeIcon"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:paddingLeft="14dp"
android:tint="#color/drawerContent"/>
<TextView
android:id="#+id/expandedListItem"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:textColor="#drawable/expandable_text_selector"
android:paddingLeft="65dp"
android:paddingTop="16dp"
android:textSize="16dp"
android:paddingBottom="16dp" />
</RelativeLayout>
</LinearLayout>
The list_group.xml layout
<?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">
<View
android:layout_width="match_parent"
android:layout_height="0.25dp"
android:layout_alignParentTop="true"
android:background="#color/drawerContent" />
<RelativeLayout
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_marginTop="7dp">
<TextView
android:id="#+id/listTitle"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:paddingLeft="10dp"
android:textColor="#color/drawerContent"
android:background="#color/drawerBg"
android:paddingTop="16dp"
android:textSize="16dp"
android:paddingBottom="16dp" />
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="#+id/expand_GroupIndicator"
android:layout_alignParentRight="true"
android:layout_centerVertical="true"
android:layout_gravity="center"
android:src="#drawable/ic_expand_more_black_24dp"
android:paddingRight="15dp"
android:tint="#color/drawerContent"
android:visibility="invisible"/>
</RelativeLayout>
</LinearLayout>
And finally the snippet of my NavigationView from
the main activity:
<android.support.design.widget.NavigationView
android:background="#color/drawerBg"
app:itemTextColor="#color/drawerContent"
app:itemIconTint="#color/drawerContent"
android:id="#+id/nav_view"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="start"
android:fitsSystemWindows="true">
<ExpandableListView
android:id="#+id/navDrawer_userListView"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:divider="#color/drawerContent"
android:textColor="#color/drawerContent"
android:background="#color/drawerBg"
android:dividerHeight="#null"
android:paddingTop="?attr/actionBarSize"
android:layoutDirection="rtl"
android:choiceMode="singleChoice"
android:groupIndicator="#null">
</ExpandableListView>
</android.support.design.widget.NavigationView>
Any help would be greatly appreciated! Thanks!
Okay, I was able to solve the last part of my issue. it turns out that all I had to do is put a conditional in my onChildClick() method to adjust for which index I was accessing using the following code:
//this conditional corrects for our new child header that is added in getChildView()
String newChildSelect = "";
if (childPosition == 0) {
newChildSelect = "All Devices";
getSupportActionBar().setSubtitle(newChildSelect);
}
else
{
newChildSelect = expandableListDetail.get(expandableListTitle.get(groupPosition)).get(childPosition - 1);
//Change subtitle on toolbar to match item selection
getSupportActionBar().setSubtitle((String)expandableListAdapter.getChild(groupPosition, childPosition - 1));
}
I have a listview with multiple items and one button to start a new activity. I'm getting Null Pointer Exception when I run my app. How and where shoud I set the OnClickListener to run properly?
Also, how can I pass with Intent the arraylist named listaIngrediente?
Here is my code
main_activity.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"
android:paddingLeft="#dimen/activity_horizontal_margin"
android:paddingRight="#dimen/activity_horizontal_margin"
android:paddingTop="#dimen/activity_vertical_margin"
android:paddingBottom="#dimen/activity_vertical_margin"
tools:context="com.example.radu.fridgecheck.MainActivity"
android:orientation="vertical">
<ListView
android:id="#+id/listView1"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_weight="1">
</ListView>
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/find_recipies"
android:id="#+id/find"
android:layout_weight="0"/>
</LinearLayout>
list_item.xml
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:padding="6dip" >
<CheckBox
android:id="#+id/checkBox"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentLeft="true"
android:focusable="false"
android:focusableInTouchMode="false"/>
<TextView
android:id="#+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignBaseline="#+id/checkBox"
android:layout_alignBottom="#+id/checkBox"
android:layout_toRightOf="#+id/checkBox"
android:text="TextView" />
</RelativeLayout>
Adapter
public class Adapter extends ArrayAdapter<Ingredient> {
public LayoutInflater inflater;
public ArrayList<Ingredient> listaIngrediente;
ArrayList<String> ingredienteDisponibile;
private Activity activity;
public Adapter(Activity activity, int textResourceId, ArrayList<Ingredient> ingrediente) {
super(activity, textResourceId, ingrediente);
this.activity=activity;
this.listaIngrediente=ingrediente;
try {
inflater = (LayoutInflater) activity.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
} catch (Exception e) {
e.printStackTrace();
}
}
public int getCount() {
return listaIngrediente.size();
}
public long getItemId(int position) {
return position;
}
public View getView(int position, View convertView, ViewGroup parent) {
View v = convertView;
if (convertView == null) {
v = inflater.inflate(R.layout.list_item, null);
}
final Ingredient ingredients = getItem(position);
final TextView display_ingredients = (TextView) v.findViewById(R.id.name);
display_ingredients.setText(ingredients.getNameI());
final CheckBox checkBox = (CheckBox) v.findViewById(R.id.checkBox);
ListView listView = (ListView) v.findViewById(R.id.listView1);
Button button = (Button) v.findViewById(R.id.button);
listView.addFooterView(button);
Button find = (Button) v.findViewById(R.id.find);
find.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
Intent i = new Intent(getContext(), ShowRecipes.class);
Bundle args = new Bundle(); /// nu reusesc sa transfer obiectele
args.putSerializable("ARRAYLIST",listaIngrediente);
i.putExtra("BUNDLE",args);
activity.startActivity(i);
}
});
v.setOnClickListener(new View.OnClickListener() {
#Override
public void onClick(View view) {
if(!ingredients.isSelected()) {
checkBox.setChecked(true);
ingredients.setSelected(true);
}
else{
checkBox.setChecked(false);
ingredients.setSelected(false);
}
}
});
return v;
}
}
You have to write that in MainActivity class, because your button is in main_activity.xml
In MainActivity, write this method
public void showRecipes(View view) {
Intent i = new Intent(getContext(), ShowRecipes.class);
Bundle args = new Bundle(); /// nu reusesc sa transfer obiectele
args.putSerializable("ARRAYLIST",listaIngrediente);
i.putExtra("BUNDLE",args);
startActivity(i);
}
In Xml add onClick
<Button
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="#string/find_recipies"
android:id="#+id/find"
android:onClick="showRecipes"
/>`
More Information on click Events
http://developer.android.com/guide/topics/ui/controls/button.html#HandlingEvents