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!
