Reality of Visible/Gone View
setVisibility()
method of View
, by setting the parameter is View.VISIBLE
or View.GONE
, the layout will be shown or hidden! If this process has an animation, we will see expanding/collapsing view effect. The fact that, adding animation when show/hide views is not easy as we imagine, you can read this discussion on StackOverflow.So, fortunately, a lot of experienced programmers has developed many libraries to help us making this effect. In this post, I would like to present one of them: ExpandableLayout, which very useful to expanding/collapsing view when user click on it!
DEMO VIDEO:
Importing library
build.gradle
, but the smarter way is adding 2 files: ExpandableLayout.java
and attr.xml
to your Android Studio Project (put ExpandableLayout.java
to your src/main/java/[your_package]
folder and attr.xml
to res/values
folder respectively).Expandable layout usage
ExpandableLayout
has two children views, the first is visible layout and the seconds one is hidden layout. It's structure in xml file:
<com.silencedut.expandablelayout.ExpandableLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
app:expWithParentScroll="true"
app:expDuration = "300"
app:expExpandScrollTogether = "false"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<!-- The visible layout -->
<layout1
...
/>
<!-- The hidden layout -->
<layout2
...
/>
</com.silencedut.expandablelayout.ExpandableLayout>
In my sample project, I will make a list view using RecyclerView
and each list row is an ExpandableLayout
, so I have these xml files for odd and even row:
item_odd.xml
<?xml version="1.0" encoding="utf-8"?>
<info.devexchanges.smoothexpandcollapserecyclerview.ExpandableLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/expandable_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:expWithParentScroll="true">
<!-- The visible layout -->
<RelativeLayout
android:id="@+id/first_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#00bcd4">
<ImageView
android:id="@+id/image_view"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="5dp"
android:contentDescription="@null"
android:scaleType="fitXY"
android:src="@drawable/one" />
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/image_view"
android:text="@string/ezreal"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:id="@+id/show_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_view"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:drawablePadding="5dp"
android:layout_marginTop="10dp"
android:drawableRight="@drawable/arrow_down"
android:layout_toRightOf="@+id/image_view"
android:text="Show description"
android:textColor="@android:color/white" />
</RelativeLayout>
<!-- The hidden layout -->
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@android:color/holo_green_dark"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/ezreal_story"
android:textColor="@android:color/white" />
</LinearLayout>
</info.devexchanges.smoothexpandcollapserecyclerview.ExpandableLayout>
item_even.xml
In Java code, we can handle expand/collapse event by call <?xml version="1.0" encoding="utf-8"?>
<info.devexchanges.smoothexpandcollapserecyclerview.ExpandableLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/expandable_layout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="center"
app:expWithParentScroll="true">
<RelativeLayout
android:id="@+id/firstLayer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="#6889ff">
<ImageView
android:id="@+id/image_view"
android:layout_width="80dp"
android:layout_height="80dp"
android:layout_margin="5dp"
android:scaleType="fitXY"
android:src="@drawable/two" />
<TextView
android:id="@+id/text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginLeft="5dp"
android:layout_toRightOf="@+id/image_view"
android:text="@string/title"
android:textColor="@android:color/white"
android:textSize="18sp" />
<TextView
android:id="@+id/show_info"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_below="@+id/text_view"
android:layout_centerVertical="true"
android:layout_marginLeft="5dp"
android:layout_marginTop="10dp"
android:layout_toRightOf="@+id/image_view"
android:drawablePadding="5dp"
android:drawableRight="@drawable/arrow_down"
android:text="Show description"
android:textColor="@android:color/white" />
</RelativeLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
android:orientation="vertical"
android:padding="10dp">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/jinx_story"
android:textColor="@android:color/white"
android:textSize="12sp" />
</LinearLayout>
</info.devexchanges.smoothexpandcollapserecyclerview.ExpandableLayout>
setOnExpandListener(OnExpandListener listener)
. We'll put this code in RecyclerView
adapter class, the most important file of the project:
RecyclerViewAdapter.java
In main activity code, setting package info.devexchanges.smoothexpandcollapserecyclerview;
import android.content.Context;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import java.util.HashSet;
public class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.ReyclerViewHolder> {
private LayoutInflater layoutInflater;
private HashSet<Integer> expandedPositionSet;
private Context context;
public RecyclerViewAdapter(Context context) {
this.layoutInflater = LayoutInflater.from(context);
expandedPositionSet = new HashSet<>();
this.context = context;
}
@Override
public ReyclerViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
View item = layoutInflater.inflate(viewType == 0 ? R.layout.item_odd : R.layout.item_even, parent, false);
return new ReyclerViewHolder(item);
}
@Override
public void onBindViewHolder(ReyclerViewHolder holder, int position) {
holder.updateItem(position);
}
@Override
public int getItemViewType(int position) {
return position % 2;
}
@Override
public int getItemCount() {
return 20;
}
class ReyclerViewHolder extends RecyclerView.ViewHolder {
private ExpandableLayout expandableLayout;
private TextView showInfo;
private ReyclerViewHolder(final View view) {
super(view);
expandableLayout = (ExpandableLayout) view.findViewById(R.id.expandable_layout);
showInfo = (TextView) view.findViewById(R.id.show_info);
}
private void updateItem(final int position) {
expandableLayout.setOnExpandListener(new ExpandableLayout.OnExpandListener() {
@Override
public void onExpand(boolean expanded) {
registerExpand(position, showInfo);
}
});
expandableLayout.setExpand(expandedPositionSet.contains(position));
}
}
private void registerExpand(int position, TextView textView) {
if (expandedPositionSet.contains(position)) {
removeExpand(position);
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.arrow_down, 0);
textView.setText("Show description");
Toast.makeText(context, "Position: " + position + " collapsed!", Toast.LENGTH_SHORT).show();
} else {
addExpand(position);
textView.setCompoundDrawablesWithIntrinsicBounds(0, 0, R.drawable.arrow_up, 0);
textView.setText("Hide description");
Toast.makeText(context, "Position: " + position + " expanded!", Toast.LENGTH_SHORT).show();
}
}
private void removeExpand(int position) {
expandedPositionSet.remove(position);
}
private void addExpand(int position) {
expandedPositionSet.add(position);
}
}
LayoutManager
(vertical LinearLayout
) for RecyclerView
:
MainActivity.java
package info.devexchanges.smoothexpandcollapserecyclerview;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
public class MainActivity extends AppCompatActivity {
private RecyclerView recyclerView;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
recyclerView = (RecyclerView)findViewById(R.id.recycler_view);
LinearLayoutManager linearLayoutManager = new LinearLayoutManager(this);
recyclerView.setLayoutManager(linearLayoutManager);
RecyclerViewAdapter recyclerViewAdapter = new RecyclerViewAdapter(this);
recyclerView.setAdapter(recyclerViewAdapter);
}
}
Note
: Make sure that you added RecyclerView
dependency to your application level build.gradle
:
dependencies {
compile 'com.android.support:recyclerview-v7:24.1.1'
compile 'com.android.support:appcompat-v7:24.1.1'
}
Running application, we'll have this output:
Conclusions
Animation
from xml to make this effect, I will present this solution at next post. References:
- Library page: https://github.com/SilenceDut/ExpandableLayout
- StackOverflow discussion: http://stackoverflow.com/q/4043398/3962895
- Update: read my new post for this topic HERE!