Hi,
I'm trying to make an expandable drawer item that when I click on the arrow it expands and when I click on the item, it executes the normal onclick. As this is not a default behavior, I tried implementing my own ExpandableDrawerItem. However, I couldn't grasp on how to do it properly as I couldn't find a way to set the expand state and redraw the item from the item itself. Any tips?
There's also a minor thing that I found. When creating the SecondaryDrawerItem's, there is no level that matches the parent text. Here's a screenshot to explain myself better.
The first subitem has level 4 and the second has level 5. None matches the parent. I think I can fix this by using my own layout for the subitems.
Also on the screenshot, and regarding my first issue, I want to click on Profile and launch an intent and when I click on the arrow to expand the subitems.
Thanks!
@kakai248 hi. sorry for the late answer.
The default ExpandableDrawerItem works as following: It hooks into the default click behavior by overwriting the getDrawerItemClickListener
https://github.com/mikepenz/MaterialDrawer/blob/develop/library/src/main/java/com/mikepenz/materialdrawer/model/ExpandableDrawerItem.java#L59
So for your item you will have to change this as you just want to expand on the arrow click. The problem is that the item by default does not know about the adapter it is used in. So for your requirement you will need to pass the reference to the adapter to the item, and expand it in the onClick of the arrow.
Yeah that happens because of the specification of the Material Design Guidelines that the item starts at the left when no icon is passed. You might can work around this by providing a transparent Drawable you can create one really simple via the IconicsDrawable. Just create one with a ' ' (space) as text: https://github.com/mikepenz/Android-Iconics/blob/develop/library-core/src/main/java/com/mikepenz/iconics/IconicsDrawable.java#L104
Afterwards the levels will work as expected.
Regarding the icon on the secondary items, great solution, it works perfectly!
Doing what you said I managed to get the arrow in my custom ExpandableDrawerItem working. It now expands on click of the arrow and has animation. However, I can't make the item itself _not_ expand on click. Even overriding the click listener, it still expands and collapses on click. Upon searching, this is something the FastAdapter does by itself (here). So there's no chance of overriding this behavior without changing the adapter?
Okay I managed to do it. But I'm not really sure if this solution brings any disadvantages despite working. I made the CustomExpandableDrawerItem implement IClickable and its respective functions and then played with the consumed event to make the adapter ignore the expandable part.
Here's the code in case anyone ever needs it:
/**
* Created by mikepenz on 03.02.15.
* NOTE: The arrow will just animate (and rotate) on APIs higher than 11 as the ViewCompat will skip this on API 10
*/
public class CustomExpandableDrawerItem extends BasePrimaryDrawerItem<CustomExpandableDrawerItem, CustomExpandableDrawerItem.ViewHolder> implements IClickable {
private Drawer.OnDrawerItemClickListener mOnDrawerItemClickListener;
protected ColorHolder arrowColor;
private FastAdapter<IDrawerItem> adapter;
public CustomExpandableDrawerItem(FastAdapter<IDrawerItem> adapter) {
super();
this.adapter = adapter;
}
public CustomExpandableDrawerItem withArrowColor(@ColorInt int arrowColor) {
this.arrowColor = ColorHolder.fromColor(arrowColor);
return this;
}
public CustomExpandableDrawerItem withArrowColorRes(@ColorRes int arrowColorRes) {
this.arrowColor = ColorHolder.fromColorRes(arrowColorRes);
return this;
}
@Override
public int getType() {
return R.id.material_drawer_item_expandable;
}
@Override
@LayoutRes
public int getLayoutRes() {
return R.layout.material_drawer_item_custom_expandable;
}
@Override
public CustomExpandableDrawerItem withOnDrawerItemClickListener(Drawer.OnDrawerItemClickListener onDrawerItemClickListener) {
mOnDrawerItemClickListener = onDrawerItemClickListener;
return this;
}
@Override
public Drawer.OnDrawerItemClickListener getOnDrawerItemClickListener() {
return mOnDrawerItemClickListener;
}
@Override
public void bindView(ViewHolder viewHolder) {
Context ctx = viewHolder.itemView.getContext();
//bind the basic view parts
bindViewHelper(viewHolder);
//make sure all animations are stopped
viewHolder.arrow.setColor(this.arrowColor != null ? this.arrowColor.color(ctx) : getIconColor(ctx));
viewHolder.arrow.clearAnimation();
if (!isExpanded()) {
ViewCompat.setRotation(viewHolder.arrow, 0);
} else {
ViewCompat.setRotation(viewHolder.arrow, 180);
}
viewHolder.itemView.findViewById(R.id.material_drawer_arrow_container).setOnClickListener(v -> {
if (this.isEnabled()) {
if (this.getSubItems() != null) {
if (!this.isExpanded()) {
ViewCompat.animate(viewHolder.arrow).rotation(180).start();
} else {
ViewCompat.animate(viewHolder.arrow).rotation(0).start();
}
}
}
int pos = adapter.getPosition(this);
adapter.toggleExpandable(pos);
});
//call the onPostBindView method to trigger post bind view actions (like the listener to modify the item if required)
onPostBindView(this, viewHolder.itemView);
}
@Override
public ViewHolderFactory<ViewHolder> getFactory() {
return new ItemFactory();
}
public static class ItemFactory implements ViewHolderFactory<ViewHolder> {
public ViewHolder create(View v) {
return new ViewHolder(v);
}
}
protected static class ViewHolder extends BaseViewHolder {
public IconicsImageView arrow;
public ViewHolder(View view) {
super(view);
arrow = (IconicsImageView) view.findViewById(R.id.material_drawer_arrow);
arrow.setIcon(new IconicsDrawable(view.getContext(), MaterialDrawerFont.Icon.mdf_expand_more).sizeDp(16).paddingDp(2).color(Color.BLACK));
}
}
@Override
public IItem withOnItemPreClickListener(FastAdapter.OnClickListener onItemPreClickListener) {
return null;
}
@Override
public FastAdapter.OnClickListener getOnPreItemClickListener() {
return (v, adapter, item, position) -> true;
}
@Override
public IItem withOnItemClickListener(FastAdapter.OnClickListener onItemClickListener) {
return null;
}
@Override
public FastAdapter.OnClickListener getOnItemClickListener() {
return (v, adapter, item, position) -> mOnDrawerItemClickListener != null && mOnDrawerItemClickListener.onItemClick(v, position, this);
}
}
I also changed the layout a bit to make the arrow bigger, easier to click and add a click background to it (the changes kinda override material guidelines).
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout 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="@dimen/material_drawer_item_primary"
android:orientation="horizontal"
android:paddingLeft="@dimen/material_drawer_vertical_padding"
android:paddingStart="@dimen/material_drawer_vertical_padding">
<ImageView
android:id="@+id/material_drawer_icon"
android:layout_width="@dimen/material_drawer_item_primary_icon"
android:layout_height="@dimen/material_drawer_item_primary"
android:layout_gravity="center_vertical"
android:paddingBottom="@dimen/material_drawer_item_primary_icon_padding"
android:paddingEnd="@dimen/material_drawer_item_primary_icon_padding_right"
android:paddingLeft="0dp"
android:paddingRight="@dimen/material_drawer_item_primary_icon_padding_right"
android:paddingStart="0dp"
android:paddingTop="@dimen/material_drawer_item_primary_icon_padding" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:gravity="center_vertical|start"
android:orientation="vertical">
<TextView
android:id="@+id/material_drawer_name"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif-medium"
android:gravity="center_vertical|start"
android:lines="1"
android:singleLine="true"
android:textDirection="anyRtl"
android:textSize="@dimen/material_drawer_item_primary_text"
tools:text="Some drawer text" />
<TextView
android:id="@+id/material_drawer_description"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="sans-serif"
android:gravity="center_vertical|start"
android:lines="1"
android:singleLine="true"
android:textDirection="anyRtl"
android:textSize="@dimen/material_drawer_item_primary_description"
tools:text="Some drawer text" />
</LinearLayout>
<LinearLayout
android:id="@+id/material_drawer_arrow_container"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:gravity="center"
android:clickable="true"
android:background="?attr/selectableItemBackgroundBorderless"
android:paddingEnd="@dimen/material_drawer_padding"
android:paddingLeft="@dimen/material_drawer_padding"
android:paddingRight="@dimen/material_drawer_padding"
android:paddingStart="@dimen/material_drawer_padding"
android:layout_marginEnd="-8dp"
android:layout_marginRight="-8dp">
<com.mikepenz.iconics.view.IconicsImageView
android:id="@+id/material_drawer_arrow"
android:layout_width="16dp"
android:layout_height="16dp"
app:ico_color="@color/md_black_1000"
app:ico_icon="gmd-keyboard-arrow-down"
app:ico_size="16dp" />
</LinearLayout>
</LinearLayout>
@kakai248 looks ok. But there is one thing you should change. Never define a onClick listener in the onBind as it will be bound each time the item is set with the new value. And this happens many many times when you scroll. You should define the listener in the ViewHolder, which makes the thing more complex as you will have to get the item from the Tag set on the itemView
Hm... thanks for the tip. Managed to do it by passing the drawer to the item instead of the adapter. And then used the tag, like you said, to get the item in the ViewHolder.
Thank you very much!
great :)
@kakai248 great solution, but can you please let me know how to pass FastAdapter in CustomExpandableDrawerItem class's constructor when preparing for DrawerItems early?
I had to change that. I build the drawer without items and add them later.
leftDrawer = new DrawerBuilder()
.withActivity(this)
.withAccountHeader(accountHeader)
.withCloseOnClick(true)
.withSelectedItem(-1)
.build();
leftDrawer.addItems(...);
Thanx @kakai248
Wouldn't it make sense to add a withOnArrowClickListener, withExpandOnArrowClickOnly or similar to the ExpandableDrawerItem? Seems like a valid feature that may be used more often...
Most helpful comment
Okay I managed to do it. But I'm not really sure if this solution brings any disadvantages despite working. I made the
CustomExpandableDrawerItemimplementIClickableand its respective functions and then played with the consumed event to make the adapter ignore the expandable part.Here's the code in case anyone ever needs it:
I also changed the layout a bit to make the arrow bigger, easier to click and add a click background to it (the changes kinda override material guidelines).