今天我们结合属性动画实现下面的效果:
需求分析:
这个需求是一个自定义View,而且也是组合控件,和58同城中自定义View是一样的,那么我们来分析下它这个效果的布局是怎样的?我们打算采用 LinearLayout。
布局 = 整体是垂直的LinearLayout :
1 最上边用 LinearLayout包裹,放置多个Tab,并且是可点击的View;
2 中间是菜单内容(FrameLayout);
3 下边是阴影 (直接使用View,让View透明度变化即可);
package com.example.michael.view_07;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.pm.FeatureGroupInfo;
import android.graphics.Color;
import android.support.annotation.ColorInt;
import android.support.annotation.Nullable;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
/**
* Created by Michael on 2019/11/11.
*/
public class FilterDataView extends LinearLayout {
private LinearLayout mMenuTabView;
private FrameLayout mMidView;
private View mShadeView;
private FrameLayout mContentView;
private BaseMenuAdapter mAdapter;
public FilterDataView(Context context) {
this(context,null);
}
public FilterDataView(Context context,
@Nullable AttributeSet attrs) {
this(context, attrs,0);
}
public FilterDataView(Context context,
@Nullable AttributeSet attrs,
int defStyleAttr) {
super(context, attrs, defStyleAttr);
initLayout();
}
private void initLayout() {
setOrientation(VERTICAL);
mMenuTabView = new LinearLayout(getContext());
mMenuTabView.setLayoutParams(new ViewGroup
.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.WRAP_CONTENT));
addView(mMenuTabView);
mMidView = new FrameLayout(getContext());
mMidView.setLayoutParams(new ViewGroup
.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
ViewGroup.LayoutParams.MATCH_PARENT));
addView(mMidView);
mShadeView = new View(getContext());
mShadeView.setBackgroundColor(Color.parseColor("#999999"));
mShadeView.setAlpha(0f);
mMidView.addView(mShadeView);
mContentView = new FrameLayout(getContext());
mContentView.setLayoutParams(new ViewGroup
.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,600));
mContentView.setBackgroundColor(Color.RED);
mContentView.setTranslationY(-600);
mMidView.addView(mContentView);
}
public void setAdapter(BaseMenuAdapter adapter){
mAdapter = adapter;
int count = adapter.getCount();
for (int i = 0; i < count; i++) {
View tabView = mAdapter.getTabView(i,mMenuTabView);
mMenuTabView.addView(tabView);
LinearLayout.LayoutParams params = (LayoutParams)
tabView.getLayoutParams();
params.weight = 1;
tabView.setLayoutParams(params);
tabClick(tabView,i);
View menuView = mAdapter.getMenuView(i,mContentView);
menuView.setVisibility(View.GONE);
mContentView.addView(menuView);
}
}
private int mCurrentTag = -1;
private boolean mAnimatorExecute = false;
public static final int DURATION_TIME = 1500;
private void tabClick(final View tabView, final int i) {
tabView.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
if (mCurrentTag == -1){
openMenu(tabView,i);
}else{
closeMenu(tabView,i);
}
}
});
}
private void closeMenu(View tabView, final int i) {
if (mAnimatorExecute){
return;
}
// 关闭动画 此处涉及到 translationY位移动画 竖直的Y方向 透明度动画
// 此处直接使用属性动画
// translationY位移动画 竖直的Y方向
ObjectAnimator translationAnimator = ObjectAnimator
.ofFloat(mContentView, "translationY", 0, -600);
translationAnimator.setDuration(DURATION_TIME) ;
translationAnimator.start();
mShadeView.setVisibility(VISIBLE);
ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadeView, "alpha", 1f, 0f);
alphaAnimator.setDuration(DURATION_TIME) ;
// 等关闭动画执行完才能去隐藏当前菜单
alphaAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
// 根据位置获取当前具体菜单的内容,然后隐藏,并且把当前位置置为-1
int count = mContentView.getChildCount();
for (int j = 0; j < count; j++) {
View v = mContentView.getChildAt(j);
v.setVisibility(View.GONE);
}
mCurrentTag = -1 ;
mShadeView.setVisibility(View.GONE);
mAnimatorExecute = false ;
}
@Override
public void onAnimationStart(Animator animation) {
// 根据当前位置获取对应Tab
mAdapter.menuClose(mMenuTabView.getChildAt(mCurrentTag));
mAnimatorExecute = true ;
}
});
alphaAnimator.start();
}
private void openMenu(final View tabView, final int i) {
if (mAnimatorExecute){
return;
}
mShadeView.setVisibility(View.VISIBLE);
// 根据当前位置显示当前菜单,菜单加到了菜单容器
View menuView = mContentView.getChildAt(i);
menuView.setVisibility(VISIBLE);
// 打开菜单 开启动画 位移动画 透明度动画
ObjectAnimator translationAnimator = ObjectAnimator
.ofFloat(mContentView, "translationY", -600, 0);
translationAnimator.setDuration(DURATION_TIME) ;
translationAnimator.start();
ObjectAnimator alphaAnimator = ObjectAnimator
.ofFloat(mShadeView, "alpha", 0f, 1f);
alphaAnimator.setDuration(DURATION_TIME) ;
// 透明动画执行完毕 记录当前位置
alphaAnimator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mAnimatorExecute = false ;
mCurrentTag = i ;
}
@Override
public void onAnimationStart(Animator animation) {
// 把当前 Tab 传递到 外面
mAdapter.menuOpen(tabView);
mAnimatorExecute = true ;
}
});
alphaAnimator.start();
}
}
Adapter设计模式使用,将方法放到基类中,让子类继承然后实现方法即可
package com.example.michael.view_07;
import android.view.View;
import android.view.ViewGroup;
public abstract class BaseMenuAdapter {
// 获取总共有多少条 就是上边的筛选菜单
public abstract int getCount() ;
// 获取当前的TabView
public abstract View getTabView(int position , ViewGroup parent) ;
// 获取当前的菜单内容
public abstract View getMenuView(int position , ViewGroup parent) ;
/**
* 菜单打开
* @param tabView
*/
public void menuOpen(View tabView){
}
/**
* 菜单关闭
* @param tabView
*/
public void menuClose(View tabView){
}
}
子类Adapter:
package com.example.michael.view_07;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
public class ListAdapter extends BaseMenuAdapter {
private String [] items = {"类型","品牌","价格","更多"};
@Override
public int getCount() {
return items.length;
}
@Override
public View getTabView(int position, ViewGroup parent) {
TextView textView = (TextView) LayoutInflater
.from(parent.getContext())
.inflate(R.layout.tab_view,parent,false);
textView.setText(items[position]);
return textView;
}
@Override
public View getMenuView(int position, ViewGroup parent) {
TextView textView = (TextView) LayoutInflater
.from(parent.getContext())
.inflate(R.layout.content_view,parent,false);
textView.setText(items[position]);
return textView;
}
@Override
public void menuClose(View tabView) {
super.menuClose(tabView);
TextView tabTv = (TextView) tabView;
tabTv.setTextColor(Color.BLACK);
}
@Override
public void menuOpen(View tabView) {
super.menuOpen(tabView);
TextView tabTv = (TextView) tabView;
tabTv.setTextColor(Color.RED);
}
}
在MainActivity中使用:
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
FilterDataView fl_ = findViewById(R.id.fl_);
fl_.setAdapter(new ListAdapter());
}
几个布局文件:
activity_main:
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.example.michael.view_07.MainActivity">
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Hello World!"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent"
android:layout_centerInParent="true"
/>
<com.example.michael.view_07.FilterDataView
android:id="@+id/fl_"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</RelativeLayout>
content_view:
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="@+id/view_menu"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
>
</TextView>
tab_view:
<?xml version="1.0" encoding="utf-8"?>
<TextView
android:id="@+id/tv_tab"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:gravity="center"
android:padding="10dp"
>
</TextView>