自定义菜单收起展开动画

最近需求sama联合美工娘娘又改了界面,整体的界面是华丽了不少,但是大神何必为难弱智儿童的我呢,下面先看看新界面~

很经典的菜单设计,不过毕竟是版本更迭,不适合在原有基础上大修改改,菜单总共分了4个父菜单和若干个子菜单,点击父菜单会隐藏子菜单,其中还要有收起展开动画,这个首先令我想起了expanedlistview。说动手就动手,花了几分钟先写个demo测试以下吧。

<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"

    android:layout_width="match_parent"
    android:layout_height="match_parent"
    >

    <ExpandableListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/exlistview"
        ></ExpandableListView>

</RelativeLayout>

这个很简单的布局代码,不必多说了~

然后定义一个实体类,包含父子菜单所必需的内容

public class Bean {

    public Bean(String title, List<childBean> childBeans) {
        this.title = title;
        this.childBeans = childBeans;
    }

    public Bean() {
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public List<childBean> getChildBeans() {
        return childBeans;
    }

    public void setChildBeans(List<childBean> childBeans) {
        this.childBeans = childBeans;
    }

    private String title;
    private List<childBean> childBeans;

    public static class childBean{
        private String title;

        public childBean(String title) {
            this.title = title;
        }

        public String getTitle() {
            return title;
        }

        public void setTitle(String title) {
            this.title = title;
        }
    }

}

这几行代码也不必多说,一目了然,如果需要添加其他的 自然也可以自己添加~

最后开始写自定义adapter了,也很简单不是吗?

public class TestAdapter extends BaseExpandableListAdapter {
    private List<Bean> groups;
    private Context context;

    public TestAdapter( Context context, List<Bean> groups){
        this.context=context;
        this.groups=groups;

    }

    @Override
    public int getGroupCount() {
        return groups.size();
    }

    @Override
    public int getChildrenCount(int groupPosition) {
        return  groups.get(groupPosition).getChildBeans().size();
    }

    @Override
    public Object getGroup(int groupPosition) {
        return groups.get(groupPosition);
    }

    @Override
    public Object getChild(int groupPosition, int childPosition) {
        return groups.get(groupPosition).getChildBeans().get(childPosition);
    }

    @Override
    public long getGroupId(int groupPosition) {
        return groupPosition;
    }

    @Override
    public long getChildId(int groupPosition, int childPosition) {
        return childPosition;
    }

    @Override
    public boolean hasStableIds() {
        return false;
    }

    @Override
    public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
        View view= LayoutInflater.from(context).inflate(R.layout.item_a,null);
        TextView tv= (TextView) view.findViewById(R.id.a_text);
        tv.setText(groups.get(groupPosition).getTitle());
        return view;
    }

    @Override
    public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
        View view= LayoutInflater.from(context).inflate(R.layout.item_a,null);
        TextView tv= (TextView) view.findViewById(R.id.a_text);
        tv.setText(groups.get(groupPosition).getChildBeans().get(childPosition).getTitle());
        return view;
    }

    @Override
    public boolean isChildSelectable(int groupPosition, int childPosition) {
        return false;
    }

}

很简单的代码,毕竟只是demo,大家就将就一下看看,最后在activity里调用一下,我们来看效果吧~

public class MainActivity extends AppCompatActivity {
    private ExpandableListView listView;
    private List<Bean> mList=new ArrayList<>();
    private List<Bean.childBean> childList=new ArrayList<>();
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        listView= (ExpandableListView) findViewById(R.id.exlistview);
        Bean.childBean childBean;
        for(int j=0;j<3;j++){
            childBean=new Bean.childBean("aaaa"+j);
            childList.add(childBean);
        }
        for(int i=0;i<3;i++){
            mList.add(new Bean(i+"",childList));
        }
        TestAdapter adapter=new TestAdapter(MainActivity.this,mList);
        listView.setAdapter(adapter);
        listView.setGroupIndicator(null);
    }
}

最后 上动图看效果:

太粗糙了,而且没有收起展开的动画,没办法,继续百度吧,然后找到了一个叫animationExpanedListView的东东,但是我感觉比较久远了,上次更新已经2年之前了,有想实验的小伙伴可以去这里---->https://github.com/idunnololz/AnimatedExpandableListView。

怎么办呢,看来只有自己想办法了。首先屡清楚思路,模仿listview的效果可以试试,所以首先写一个自定义的adapter出来~

public abstract class SettingsAdapter<T> {
    private Context context;
    private List<T> mList;

    public SettingsAdapter(Context context, List<T> mList){
        this.context=context;
        this.mList=mList;
    }
    public SettingsAdapter(T[] mDatas){
        mList=new ArrayList<T>(Arrays.asList(mDatas));
    }
    public int getCount(){
        return mList==null?0:mList.size();
    }
    public T getItem(int position){
        return  mList.get(position);
    }
    public void notifyDataSetChanged(){
        onDataChanged.changed();
    }

    public abstract View getView(SettingView parent, int position);

    public interface onDataChanged {
        void changed();
    }

    public void setOnDataChanged(onDataChanged onDataChanged) {
        this.onDataChanged = onDataChanged;
    }

    public onDataChanged onDataChanged;

}

这一段代码很容易理解,跟普通的adapter没有啥不一样的~继续向下我们要实现子菜单的布局,首先我们继承linearlayout,因为菜单毕竟是自上向下的嘛~

public  class SettingView extends LinearLayout implements SettingsAdapter.onDataChanged{
    private Context context;
    private SettingsAdapter adapter;

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

    public SettingView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public SettingView(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        this.context=context;
        setOrientation(VERTICAL);
    }

    

接下来我们公开一个adapter调用的方法用法调用adapter;

public void setAdapter(SettingsAdapter adapter){
    this.adapter=adapter;
    changeAdapter();
}

这依然是很简单的代码。我们接下来看changeAdapter里面的内容

private void changeAdapter() {
    removeAllViews();
    SettingsAdapter settingsAdapter = this.adapter;
    for(int i=0;i<settingsAdapter.getCount();i++){
        final View layout = settingsAdapter.getView(this, i);
        layout.setTag(i);
        addView(layout);
        layout.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if(onItemClick!=null){
                    onItemClick.click(v,(Integer) layout.getTag());
                }

            }
        });
    }
}

一目了然是不是,如果看不懂没关系,那就多看几遍,如果多看几遍还是看不懂,也没关系,其实我也不懂。

最后我们通过接口回调将接口公布出去,这是我们的接口以及实现的adapter中接口:

public interface OnItemClick {
    void click(View v,int i);
}

public void setOnItemClick(OnItemClick onItemClick) {
    this.onItemClick = onItemClick;
}

private OnItemClick onItemClick;

@Override
public void changed() {
    changeAdapter();

}

最后在xml中的布局就是这样子的:

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:orientation="vertical"
    >

   <!-- <ExpandableListView
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:id="@+id/exlistview"
        ></ExpandableListView>-->
    <TextView
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:background="@android:color/holo_red_dark"
        android:text="1111111111111111111"
        android:textSize="20sp"
        android:gravity="center"
        android:id="@+id/tv1"
        />
    <com.example.mydemo.SettingView
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:id="@+id/test_layout"
        />

</LinearLayout>

Textview暂时充当的是父菜单,settingview就是子菜单咯~

  tv= (TextView) findViewById(R.id.tv1);
    sv= (SettingView) findViewById(R.id.test_layout);
    for(int i=0;i<5;i++){
        mList.add(i+"");
    }
    SettingsAdapter adapter=new SettingsAdapter(MainActivity.this,mList) {
        @Override
        public View getView(SettingView parent, int position) {
            View view=getLayoutInflater().inflate(R.layout.item_a,null);
           TextView tv= (TextView) view.findViewById(R.id.a_text);
            tv.setText(mList.get(position));
            return view;
        }

    };
    sv.setAdapter(adapter);
    sv.setOnItemClick(new SettingView.OnItemClick() {
        @Override
        public void click(View v,int i) {
            Toast.makeText(MainActivity.this,mList.get(i),Toast.LENGTH_SHORT).show();
        }
    });
}

最后在activity里敲这些很简单的代码,差不多第一步的自定义布局就算实现了。看效果

布局是写好了,但是暂时还没有任何的点击效果和动画效果,这时候想到了什么呢,属性动画,当当当~不过objectAnimator里并没有对View高度的动画,不过我们知道valueAnimator是可以实现这个的,不多说看代码~

public static ValueAnimator DropAnim(final View view,int start,int end){
    ValueAnimator animator=ValueAnimator.ofInt(start,end);
    animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            int value= (Integer) animation.getAnimatedValue();
            ViewGroup.LayoutParams lp = view.getLayoutParams();
            lp.height=value;
            view.setLayoutParams(lp);
        }
    });
    return  animator;
}

首先,我们需要把target的view 和view的起始高度传起来,最后通过动画的listener实现对view高度的动态变化,看到这里是不是有些思路了~~~

public static void animatorOpen(final View view, int height){
    view.setVisibility(View.VISIBLE);
    AnimatorSet set=new AnimatorSet();
    set.setDuration(1000);
    ValueAnimator animator = DropAnim(view, 0, height);
    ObjectAnimator oa=ObjectAnimator.ofFloat(view, View.ALPHA, 0.0f, 1.0f);
    set.playTogether(animator,oa);
    set.setDuration(1000);
    set.start();
}
public static void animatorClose(final View view, int height){
    AnimatorSet set=new AnimatorSet();
    set.setDuration(1000);
    ValueAnimator animator = DropAnim(view, height,0);
    ObjectAnimator oa=ObjectAnimator.ofFloat(view,View.ALPHA,1.0f,0.0f);
    set.playTogether(animator,oa);
    set.addListener(new AnimatorListenerAdapter() {
        @Override
        public void onAnimationEnd(Animator animation) {
            super.onAnimationEnd(animation);
            view.setVisibility(View.GONE);
        }
    });
    set.start();
}

这是动画开关的两个方法,方法大致相同,我们不仅实现了高度的变化,而且加入了透明度的变化,当然你也可以加入一些其他的动画,这里就不多做介绍了。

最后在在点击事件中通过判断子菜单的vissable来判断动画的开合,是不是很简单呢?最后的一个难点是获取view的高度,我们并没有在自定义的布局里直接获取到他的高度,当然你也可以在自定义view的时候直接通过   getMeasuredHeight()
的方法获得出来,当然也可以通过post方法进行获取

sv.post(new Runnable() {
    @Override
    public void run() {
        height=sv.getHeight();
    }
});
tv.setOnClickListener(new View.OnClickListener() {
    @Override
    public void onClick(View v) {
        if(sv.getVisibility()==View.VISIBLE){
            AnimUtils.animatorClose(sv,height);
        }else{
            AnimUtils.animatorOpen(sv,height);
        }
    }
});

最后大功告成~来看一下动画效果吧~

是不是很炫呢?最后有一个小问题,就是如何在fragment中获取控件的高度呢,起初我也是傻傻的post方法,然并卵的是然并卵啊~最后还是找到了答案~通过

ViewTreeObserver来获取,然后在点击或者初始化完成之后进行移除监听,不然他会一直监听控件的高度,切记,移除这个监听,必须放在动画之前,否则会出现意想不到的彩蛋呢~

好了,第一篇博客大功告成,我要去水群了~~~~大家再见·~~~~~

时间: 2024-10-12 09:23:10

自定义菜单收起展开动画的相关文章

jQuery鼠标悬停3d菜单展开动画

效果体验:http://hovertree.com/texiao/jquery/93/ 竖直的主菜单贴着页面左侧,当光标移入菜单项时,以3D动画的方式弹出对应的二级菜单.采用jQuery和CSS3实现.支持Chrome,火狐,Edge等浏览器. 效果图: 代码如下: <!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta name="viewport"

C# WPF 简单自定义菜单切换动画

微信公众号:Dotnet9,网站:Dotnet9,问题或建议,请网站留言: 如果您觉得Dotnet9对您有帮助,欢迎赞赏 C# WPF 简单自定义菜单切换动画 内容目录 实现效果 业务场景 编码实现 本文参考 源码下载 1.实现效果 自定义菜单切换动画 2.业务场景 菜单切换动画 3.编码实现 3.1 添加Nuget库 使用 .Net Core 3.1 创建名为"CustomMenu"的WPF解决方案,添加两个Nuget库:MaterialDesignThemes和MaterialDe

(三)实现菜单点击动画

在上一篇中,我们已经处理好了菜单的折叠和展开.如果你还没读过,可以点击下面的链接:http://www.cnblogs.com/fuly550871915/p/4930654.html 贴出上一篇文章的效果图吧,如下: 折叠和展开还不错.所写的代码也越来越简单,主要就是动画的添加而已.下面我们为每一个菜单添加点击动画.即,点击的时候,,让被点击的菜单放大消失,其他菜单缩小消失. 我们还是直接看代码,然后在做解释吧.还是修改ArcMenu中的代码,如下: 1 package com.example

自定义菜单创建接口

自定义菜单接口可实现多种类型按钮,如下: 1.click:点击推事件用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互: 2.view:跳转URL用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息. 3.scancode_push:扫码推事件用户点击按钮后,微信客户端将调起

微信自定义菜单

自定义菜单接口可实现多种类型按钮,如下: 1.click:点击推事件 用户点击click类型按钮后,微信服务器会通过消息接口推送消息类型为event 的结构给开发者(参考消息接口指南),并且带上按钮中开发者填写的key值,开发者可以通过自定义的key值与用户进行交互: 2.view:跳转URL 用户点击view类型按钮后,微信客户端将会打开开发者在按钮中填写的网页URL,可与网页授权获取用户基本信息接口结合,获得用户基本信息. 3.scancode_push:扫码推事件 用户点击按钮后,微信客户

微信公众平台自定义菜单新增扫一扫、发图片、发位置 LBS运作更便捷

今天微信公众平台发布更新,自定义菜单新增扫一扫.发图片.发送位置等功能,这对于有意挖掘微信LBS服务的运营者来说更便捷了,订阅号不用返回微信界面就能扫图.发送图片.调用地理位置,用户体验更友好,自然也提高了黏度,对涨粉也好一些.详细能力如下: 公众号自定义菜单新增扫一扫.发图片.发位置功能 1. 扫码推送事件 用户点击按钮后,微信客户端将调起扫一扫工具,完成扫码操作后显示扫描结果(如果是URL,将进入URL),且会将扫码的结果传给开发者,开发者可以下发消息. 2. 扫码推送事件,且弹出“消息接收

微信公众号开发系列-开发模式创建自定义菜单

通过程序方式实现自定义菜单,通过http请求封装类交互微信自定义菜单接口 1.得到AccessToken access_token是公众号的全局唯一票据,公众号调用各接口时都需使用access_token.正常情况下access_token有效期为7200秒,重复获取将导致上次获取的access_token失效.由于获取access_token的api调用次数非常有限,建议开发者全局存储与更新access_token,频繁刷新access_token会导致api调用受限,影响自身业务. 请开发者

微信公众平台开发(58)自定义菜单

一.自定义菜单概述 自定义菜单能够帮助公众号丰富界面,让用户更好更快地理解公众号的功能.开启自定义菜单后,公众号界面如图所示: 二.申请自定义菜单 个人订阅号只能编辑生成菜单,无法开发.企业订阅号通过微信认证:可以申请到自定义菜单资格 服务号默认有菜单权限. 三.获得AppId 和AppSecert AppId和AppSecret在开发者中心-开发者ID中,可以找到. 四.获得Access Token 用appid和appsecert获得access token,接口为 https://api.

微信公众平台开发之自定义菜单的创建和删除

写在前: 在做这一块时,先看一下 公众平台开发文档(点击进入): 在创建菜单时,都是基于JSON传输数据,所以要用到JSON,下载相关包 点击下载: 公众平台开发文档上有说明: 请注意: 1.自定义菜单最多包括3个一级菜单,每个一级菜单最多包含5个二级菜单.2.一级菜单最多4个汉字,二级菜单最多7个汉字,多出来的部分将会以"..."代替.3.创建自定义菜单后,由于微信客户端缓存,需要24小时微信客户端才会展现出来.测试时可以尝试取消关注公众账号后再次关注,则可以看到创建后的效果. 自定