自定义view——属性动画之多条目菜单筛选

这里写图片描述

首先我们可以模仿ListView中的adapter的设计模式,自己搭建一个BaseAdapter类(BaseMenuAdapter),相当于ListView中的BaseAdapter

public  abstract class BaseMenuAdapter {
    //获取总共多少条
    public abstract int getCount();

    //获取Tab的view
    public abstract View getTabView(int position, ViewGroup parent);
    //获取Menu的view
    public abstract View getMenuView(int position, ViewGroup parent);
    //提供接口给外部去更新文字
    public abstract void openMenu(View tabView);

    public abstract void closeMenu(View tabView);
}

ListScreenMenuAdapter自己定义一个适配器,相当于我们ListView中的自己定义的adapter

public class ListScreenMenuAdapter extends BaseMenuAdapter {
    private String[] mItems = {"类型", "品牌", "价格", "更多"};
    private Context mContext;

    public ListScreenMenuAdapter(Context context) {
        this.mContext = context;
    }

    @Override
    public int getCount() {
        return mItems.length;
    }
     //这里设置tabview的布局为TextView所以没有findviewById的形式,后期可以修改
     //这里相当于自己的adapter继承BaseAdapter后实现的getView(),这里图方便也没有添加View contentView
    @Override
    public View getTabView(int position, ViewGroup parent) {
        TextView tabView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_tab, parent, false);
        tabView.setText(mItems[position]);
        tabView.setTextColor(Color.BLACK);
        return tabView;
    }

    @Override
    public View getMenuView(int position, ViewGroup parent) {
        // 真正开发过程中,不同的位置显示的布局不一样
        TextView menuView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu, parent, false);
        menuView.setText(mItems[position]);
        return menuView;
    }

    @Override
    public void openMenu(View tabView) {
        TextView tv = (TextView) tabView;
        tv.setTextColor(Color.parseColor("#FF4081"));
    }

    //关闭菜单,设置颜色为黑色
    @Override
    public void closeMenu(View tabView) {
        TextView tv = (TextView) tabView;
        tv.setTextColor(Color.BLACK);
    }
}

**ListDataScreenView:自己定义View,实现布局,整体是一个线性布局,布局中添加一个Tab的布局+内容的FrameLayout布局,而FrameLayout中是LinearLayout+View(阴影),注意以下几个问题
// 菜单文字挤到一堆去了 ,菜单的 Tab 不见了(解决) ,宽度不是等宽,weight要是为1
// 内容的高度应该不是全部 应该是整个 View的 75%
// 进来的时候阴影不显示 ,内容也是不显示的(把它移上去)
// 内容还没有显示出来,打开的时候显示当前位置的菜单,关闭的时候隐藏,阴影点击应该要关闭菜单
// 动画在执行的情况下就不要在响应动画事件
// 打开和关闭 变化tab的显示 , 肯定不能把代码写到 ListDataScreen 里面来
// 当菜单是打开的状态 不要执行动画只要切换
**

public class ListDataScreenView extends LinearLayout implements View.OnClickListener {
    private Context mContext;
    // 1.1 创建头部用来存放 Tab
    private LinearLayout mMenuTabView;
    // 1.2 创建 FrameLayout 用来存放 = 阴影(View) + 菜单内容布局(FrameLayout)
    private FrameLayout mMenuMiddleView;
    // 阴影
    private View mShadowView;
    // 创建菜单用来存放菜单内容
    private FrameLayout mMenuContainerView;
    // 阴影的颜色
    private int mShadowColor = 0x88888888;
    private BaseMenuAdapter mAdapter;
    private int mMenuContainerHeight;

    private int DURATION_TIME = 350;
    private int mCurrentPosition = -1;//当前位置
    // 动画是否在执行
    private boolean mAnimatorExecute;

    public ListDataScreenView(Context context) {
        this(context, null);
    }

    public ListDataScreenView(Context context, @Nullable AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public ListDataScreenView(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.mContext = context;
        initLayout();
    }

    /**
     * 初始化控件
     */
    private void initLayout() {
        //  1. 先创建一个 xml 布局 ,再加载,findViewById
        //  2. 简单的效果用代码去创建
        setOrientation(VERTICAL);
        //创建头部用来存放Tab
        mMenuTabView = new LinearLayout(mContext);
        mMenuTabView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT
                , LayoutParams.WRAP_CONTENT));
        addView(mMenuTabView);
        // 1.2 创建 FrameLayout 用来存放 = 阴影(View) + 菜单内容布局(FrameLayout)
        mMenuMiddleView = new FrameLayout(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
        params.weight = 1;
        mMenuMiddleView.setLayoutParams(params);
        addView(mMenuMiddleView);
        // 创建阴影 可以不用设置 LayoutParams 默认就是 MATCH_PARENT ,MATCH_PARENT
        mShadowView = new View(mContext);
        mShadowView.setBackgroundColor(mShadowColor);
        mShadowView.setVisibility(GONE);
        mShadowView.setAlpha(0f);//默认是0
        mMenuMiddleView.addView(mShadowView);
        mShadowView.setOnClickListener(this);
        // 创建菜单用来存放菜单内容
        mMenuContainerView = new FrameLayout(mContext);
        mMenuContainerView.setBackgroundColor(Color.WHITE);
        mMenuMiddleView.addView(mMenuContainerView);
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        int heightSize = MeasureSpec.getSize(heightMeasureSpec);
        // 内容的高度应该不是全部  应该是整个 View的 75%
        //只需要测量一次就可以了
        if (mMenuContainerHeight == 0 && heightSize > 0) {
            ViewGroup.LayoutParams params = mMenuContainerView.getLayoutParams();
            mMenuContainerHeight = (int) (heightSize * 75f / 100);
            params.height = mMenuContainerHeight;
            mMenuContainerView.setLayoutParams(params);
            // 进来的时候阴影不显示 ,内容也是不显示的(把它移上去)
            mMenuContainerView.setTranslationY(-mMenuContainerHeight);
        }
    }

    public void setAdapter(BaseMenuAdapter adapter) {
        if (adapter != null) {
            this.mAdapter = adapter;
        }
        //获取一共多少条
        int count = mAdapter.getCount();
        for (int i = 0; i < count; i++) {
            // 获取菜单的Tab
            View tabView = mAdapter.getTabView(i, mMenuTabView);
            LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams();
            params.weight = 1;
            tabView.setLayoutParams(params);
            mMenuTabView.addView(tabView);
            //设置点击事件
            setTabClick(tabView, i);
            //获取内容的view
            View menuView = mAdapter.getMenuView(i, mMenuContainerView);
            menuView.setVisibility(GONE);//设置默认不可见

            mMenuContainerView.addView(menuView);
        }
        // 挤到一堆去了 ,菜单的 Tab 不见了(解决) ,宽度不是等宽,weight要是为1
        // 内容的高度应该不是全部  应该是整个 View的 75%
        // 进来的时候阴影不显示 ,内容也是不显示的(把它移上去)
        // 内容还没有显示出来,打开的时候显示当前位置的菜单,关闭的时候隐藏,阴影点击应该要关闭菜单
        // 动画在执行的情况下就不要在响应动画事件
        // 打开和关闭 变化tab的显示 , 肯定不能把代码写到 ListDataScreen 里面来
        // 当菜单是打开的状态 不要执行动画只要切换
    }

    /**
     * tabView的点击事件
     */
    private void setTabClick(final View tabView, final int postition) {
        tabView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mCurrentPosition == -1) {//没有打开
                    openMenu(tabView, postition);
                } else {//已经打开
                    if (mCurrentPosition == postition) {//如果点击的是同一个位置则关闭
                        closeMenu();
                    } else {//切换显示yixia
                        View currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);
                        currentMenu.setVisibility(GONE);
                        mAdapter.closeMenu(mMenuTabView.getChildAt(mCurrentPosition));
                        mCurrentPosition = postition;
                        currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);
                        currentMenu.setVisibility(VISIBLE);
                        mAdapter.openMenu(mMenuTabView.getChildAt(mCurrentPosition));
                    }

                }
            }
        });
    }

    private void closeMenu() {
        if (mAnimatorExecute) {
            return;
        }
        //设置阴影为可见
        mShadowView.setVisibility(GONE);
        // 获取当前位置显示当前菜单,菜单是加到了菜单容器
        final View menuView = mMenuContainerView.getChildAt(mCurrentPosition);
        menuView.setVisibility(GONE);
        //位移动画
        ObjectAnimator translationAnimation =
                ObjectAnimator.ofFloat(mMenuContainerView, "translationY", 0, -mMenuContainerHeight);
        translationAnimation.setDuration(DURATION_TIME);
        translationAnimation.start();
        //阴影的透明度变化
        ObjectAnimator rotationAniamtion = ObjectAnimator.ofFloat(mShadowView, "alpha", 1f, 0f);
        rotationAniamtion.setDuration(DURATION_TIME);
        rotationAniamtion.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mCurrentPosition = -1;
                mAnimatorExecute = false;
            }

            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                mAnimatorExecute = true;
                mAdapter.closeMenu(mMenuTabView.getChildAt(mCurrentPosition));
            }
        });
        rotationAniamtion.start();

    }

    /**
     * 打开菜单
     */
    private void openMenu(final View tabView, final int postition) {
        if (mAnimatorExecute) {
            return;
        }
        //设置阴影为可见
        mShadowView.setVisibility(VISIBLE);

        // 获取当前位置显示当前菜单,菜单是加到了菜单容器
        final View menuView = mMenuContainerView.getChildAt(postition);
        menuView.setVisibility(VISIBLE);
        //位移动画
        ObjectAnimator translationAnimation =
                ObjectAnimator.ofFloat(mMenuContainerView, "translationY", -mMenuContainerHeight, 0);
        translationAnimation.setDuration(DURATION_TIME);
        translationAnimation.start();
        //阴影的透明度变化
        ObjectAnimator rotationAniamtion = ObjectAnimator.ofFloat(mShadowView, "alpha", 0f, 1f);
        rotationAniamtion.setDuration(DURATION_TIME);
        rotationAniamtion.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mCurrentPosition = postition;
                mAnimatorExecute = false;
            }

            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                mAnimatorExecute = true;
                mAdapter.openMenu(tabView);
            }
        });
        rotationAniamtion.start();

    }

    //点击阴影部分,关闭菜单
    @Override
    public void onClick(View v) {
        closeMenu();
    }
}

TabView和Menu的布局

<?xml version="1.0" encoding="utf-8"?>
<TextView xmlns:android="http://schemas.android.com/apk/res/android"
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/tab_view_tv"
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="wrap_content"
    android:gravity="center"
    android:padding="10dp"
    android:layout_height="wrap_content"
    tools:context=".MainActivity"/>

<?xml version="1.0" encoding="utf-8"?>
<TextView 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:gravity="center"
    android:layout_height="match_parent"
    tools:context=".MainActivity"/>

主布局

<?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=".MainActivity">

    <TextView
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:text="列表 内容"
        android:layout_centerInParent="true"
        app:layout_constraintBottom_toBottomOf="parent"
        app:layout_constraintLeft_toLeftOf="parent"
        app:layout_constraintRight_toRightOf="parent"
        app:layout_constraintTop_toTopOf="parent" />

    <TextView
        android:layout_width="wrap_content"
        android:text="test"
        android:onClick="click"
        android:layout_alignParentBottom="true"
        android:layout_height="wrap_content" />

    <com.hbwj.a15_.ListDataScreenView
        android:id="@+id/list_data_screen_view"
        android:layout_width="match_parent"
        android:layout_height="match_parent"/>



</RelativeLayout>

此时想点击menu中的item然后点击关闭,这时可有三种方式:

  • 1.1 方式一:可以写一个监听通过Activity去掉用 ListDataScreenView 的关闭方法
  • 1.2 方式二:可以让Adapter去持有我们 ListDataScreenView 的对象
  • 1.3 方式三:利用观察者设计模式(观察者 和 被观察者,注册订阅的概念) 场景 (今天这个,ListView的notifyDataChanged , RxJava等等)

今天主要讲第三种方式,参考的是listview中notifyChanged

第一步:MenuObserver观察者

public abstract class MenuObserver {
    //关闭菜单
    public abstract void closeMenu();
}

修改BaseMenuAdapter

 //这里图方便就不用List方式
    private MenuObserver mMenuObserver;
    //模仿ListView中的源码
    public void registerDataSetObserver(MenuObserver observer) {
        mMenuObserver=observer;
    }

    public void unregisterDataSetObserver(MenuObserver observer) {
        mMenuObserver=null;
    }
    public void closeMenu(){
        mMenuObserver.closeMenu();
    }

修改ListDataScreenView中的setAdapter

    private class AdapterMenuObserver extends MenuObserver{

        @Override
        public void closeMenu() {
            //关闭菜单
            ListDataScreenView.this.closeMenu();
        }
    }
    private AdapterMenuObserver mMenuObserver;


    //模仿ListView中的adapter
    public void setAdapter(BaseMenuAdapter adapter) {
        if (mAdapter != null && mMenuObserver != null) {
            mAdapter.unregisterDataSetObserver(mMenuObserver);
        }
        if (adapter != null) {
            this.mAdapter = adapter;
        }

        mMenuObserver = new AdapterMenuObserver();
        mAdapter.registerDataSetObserver(mMenuObserver);
        //获取一共多少条
        int count = mAdapter.getCount();
        for (int i = 0; i < count; i++) {
            // 获取菜单的Tab
            View tabView = mAdapter.getTabView(i, mMenuTabView);
            LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams();
            params.weight = 1;
            tabView.setLayoutParams(params);
            mMenuTabView.addView(tabView);
            //设置点击事件
            setTabClick(tabView, i);
            //获取内容的view
            View menuView = mAdapter.getMenuView(i, mMenuContainerView);
            menuView.setVisibility(GONE);//设置默认不可见

            mMenuContainerView.addView(menuView);
        }
    }

最后修改具体的ListScreenMenuAdapter

  @Override
    public View getMenuView(int position, ViewGroup parent) {
        // 真正开发过程中,不同的位置显示的布局不一样
        TextView menuView = (TextView) LayoutInflater.from(mContext).inflate(R.layout.ui_list_data_screen_menu, parent, false);
        menuView.setText(mItems[position]);
        menuView.setOnClickListener(new View.OnClickListener() {//点击关闭菜单
            @Override
            public void onClick(View v) {
                //1.1 方式一:可以写一个监听通过Activity去掉用 ListDataScreenView 的关闭方法
                //1.2 方式二:可以让Adapter去持有我们 ListDataScreenView 的对象
                //1.3 方式三:利用观察者设计模式(观察者 和 被观察者,注册订阅的概念) 场景 (今天这个,ListView的notifyDataChanged , RxJava等等)
                closeMenu();
                //Toast.makeText(mContext, "关闭菜单", Toast.LENGTH_SHORT).show();
            }
        });
        return menuView;
    }
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值