美文网首页
仿美团、58、汽车之家等下拉列表选择效果

仿美团、58、汽车之家等下拉列表选择效果

作者: 刘孙猫咪 | 来源:发表于2018-12-15 20:06 被阅读0次
GIF.gif

这是一个采用adapter设计模式实现的下拉列表选择效果;可以大致分为三步来实现;
1.最上边tab栏
2.阴影背景
3.内容区域
这里自定义容器继承的是LinearLayout,在构造方法中就要去初始化布局,将tab栏、内容区域、阴影背景实例化并添加到布局容器中;

/**
     * 初始化布局
     */
    private void initLayout() {
        //创建tab
        mTabView = new LinearLayout(mContext);
        mTabView.setLayoutParams(new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT));
        mTabView.setBackgroundColor(Color.BLUE);
        addView(mTabView);
        //创建内容容器
        mContentView = new FrameLayout(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, 0);
        params.weight = 1;
        addView(mContentView);
        //创建阴影
        mShadowView = new View(mContext);
        mShadowView.setBackgroundColor(0x88888888);
        mContentView.addView(mShadowView);
        //设置阴影透明
        mShadowView.setAlpha(0f);
        //设置阴影隐藏
        mShadowView.setVisibility(GONE);
        //设置阴影点击事件 点击阴影关闭
        mShadowView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                closeMenu();
            }
        });
        //创建列表内容容器
        listContentView = new FrameLayout(mContext);
        listContentView.setBackgroundColor(Color.WHITE);
        mContentView.addView(listContentView);
    }

初始化完毕后,还需要重新设置内容区域的高度;最好是根据自定义容器高度的百分比来进行设置;

@Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        //重新设置内容布局显示的高度
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (mContentViewHeight == 0 && height > 0) {
            //onMeasure方法会被多次调用
            mContentViewHeight = (int) ((height * 65f) / 100);
            ViewGroup.LayoutParams params = listContentView.getLayoutParams();
            params.height = mContentViewHeight;
            listContentView.setLayoutParams(params);
            //一开始设置内容布局位移 隐藏掉
            listContentView.setTranslationY(-mContentViewHeight);
        }
    }

在自定义容器中提供一个setAdapter方法,并定义一个抽象的BaseMenuAdapter类,提供getCount、getTabView、getContentView等方法;在外部调用setAdapter时根据需求去实例化一个BaseMenuAdapter子类;

/**
     * 设置适配器
     *
     * @param adapter
     */
    public void setAdapter(BaseMenuAdapter adapter) {
        if (adapter == null) {
            return;
        }
        this.menuAdapter = adapter;
        int count = menuAdapter.getCount();
        for (int i = 0; i < count; i++) {
            View tabView = menuAdapter.getTabView(i, mTabView);
            LinearLayout.LayoutParams tabParams = new LinearLayout.LayoutParams(0, LinearLayout.LayoutParams.WRAP_CONTENT);
            tabParams.weight = 1;
            tabView.setLayoutParams(tabParams);
            mTabView.addView(tabView);
            //设置tab点击事件
            tabClick(i);
            View contentView = menuAdapter.getContentView(i, listContentView);
            listContentView.addView(contentView);
            //一开始将内容布局隐藏掉
            contentView.setVisibility(GONE);
        }
    }

在setAdapter方法中根据getCount方法返回的数量通过getTabView和getContentView获取到对应的tab view和内容view并添加到对应的布局容器中,同时为tab view添加对应的点击事件;

/**
     * tab点击事件
     *
     * @param position
     */
    private void tabClick(final int position) {
        View childAt = mTabView.getChildAt(position);
        childAt.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mCurrentPosition == -1) {
                    //打开
                    openMenu(position);
                } else {
                    if (mCurrentPosition == position) {
                        //关闭
                        closeMenu();
                    } else {
                        //切换 隐藏之前的
                        View childAt = listContentView.getChildAt(mCurrentPosition);
                        childAt.setVisibility(GONE);
                        menuAdapter.closeMenu(mTabView.getChildAt(mCurrentPosition));
                        //显示当前选择的
                        mCurrentPosition = position;
                        childAt = listContentView.getChildAt(mCurrentPosition);
                        childAt.setVisibility(VISIBLE);
                        menuAdapter.openMenu(mTabView.getChildAt(mCurrentPosition));
                    }
                }
            }
        });
    }

在点击事件中判断如果当前内容菜单是关闭的那就调用openMenu方法打开,在打开的时候设置相应的位移和透明度动画效果;

/**
     * 打开
     *
     * @param position
     */
    private void openMenu(final int position) {
        if (isAnimatorExcute) {
            return;
        }
        listContentView.setVisibility(VISIBLE);
        View childAt = listContentView.getChildAt(position);
        childAt.setVisibility(VISIBLE);
        //平移动画
        ObjectAnimator translationY = ObjectAnimator.ofFloat(listContentView, "translationY", -mContentViewHeight, 0);
        translationY.setDuration(DURACTION_TIME);
        translationY.start();
        //透明度动画
        mShadowView.setVisibility(VISIBLE);
        ObjectAnimator alpha = ObjectAnimator.ofFloat(mShadowView, "alpha", 0f, 1f);
        alpha.setDuration(DURACTION_TIME);
        alpha.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                isAnimatorExcute = true;
                //改变tab字体颜色
                menuAdapter.openMenu(mTabView.getChildAt(position));
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                mCurrentPosition = position;
                isAnimatorExcute = false;
            }
        });
        alpha.start();
    }

如果当前内容菜单是打开的,点击的位置就只之前的位置就调用closeMenu方法关闭当前菜单,如果点击的位置不是当前的位置就切换菜单内容;

/**
     * 关闭
     */
    public void closeMenu() {
        if (isAnimatorExcute) {
            return;
        }
        //平移动画
        ObjectAnimator translationY = ObjectAnimator.ofFloat(listContentView, "translationY", 0, -mContentViewHeight);
        translationY.setDuration(DURACTION_TIME);
        translationY.start();
        //透明度动画
        ObjectAnimator alpha = ObjectAnimator.ofFloat(mShadowView, "alpha", 1f, 0f);
        alpha.setDuration(DURACTION_TIME);
        alpha.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationStart(Animator animation) {
                super.onAnimationStart(animation);
                isAnimatorExcute = true;
                //改变tab字体颜色
                menuAdapter.closeMenu(mTabView.getChildAt(mCurrentPosition));
            }

            @Override
            public void onAnimationEnd(Animator animation) {
                super.onAnimationEnd(animation);
                View childAt = listContentView.getChildAt(mCurrentPosition);
                childAt.setVisibility(GONE);
                listContentView.setVisibility(GONE);
                mShadowView.setVisibility(GONE);
                mCurrentPosition = -1;
                isAnimatorExcute = false;
            }
        });
        alpha.start();
    }
public abstract class BaseMenuAdapter {
    /**
     * 获取总条数
     * @return
     */
    public abstract int getCount();

    /**
     * 获取tabview
     * @return
     */
    public abstract View getTabView(int position,ViewGroup parent);

    /**
     * 获取内容view
     * @return
     */
    public abstract View getContentView(int position,ViewGroup parent);

    /**
     * 打开内容
     * @param childAt
     */
    public void openMenu(View childAt) {
    }

    /**
     * 关闭内容
     * @param childAt
     */
    public void closeMenu(View childAt) {
    }
    public interface MenuClickListener{
        void menuClickListener(int position,String value);
    }
    protected MenuClickListener listener;
    public void setOnMenuClickListener(MenuClickListener listener){
        this.listener=listener;
    }
}

对于打开或者关闭时,tab字体或者箭头的处理,可以在BaseMenuAdapter 子类的openMenu和closeMenu方法中进行处理;

public class ListScreenMenuAdapter extends BaseMenuAdapter {
    private Context mContext;

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

    private String[] items = {"类型", "品牌", "价格", "更多"};

    @Override
    public int getCount() {
        return items.length;
    }

    @Override
    public View getTabView(int position, ViewGroup parent) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.tab_item_view, parent, false);
        TextView tabTxt = (TextView) view.findViewById(R.id.tab_txt);
        tabTxt.setText(items[position]);
        return view;
    }

    @Override
    public View getContentView(int position, ViewGroup parent) {
        //可以根据不同类型实例化不同的view
        View view = null;
        if (items[position].equals("类型")) {
            view = createListView(position, parent);
        } else {
            view = createView(position, parent);
        }
        return view;
    }

    /**
     * 创建列表布局view
     * @param position
     * @param parent
     * @return
     */
    private View createListView(int position, ViewGroup parent) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.leixi_layout, parent, false);
        RecyclerView recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
        recyclerView.setLayoutManager(new LinearLayoutManager(mContext));
        LexingAdapter adapter = new LexingAdapter(mContext);
        recyclerView.setAdapter(adapter);
        //条目点击事件
        adapter.setOnItemClickListener(new LexingAdapter.ItemClickListener() {
            @Override
            public void itemClickListener(int position, String value) {
                if(listener!=null){
                    listener.menuClickListener(position,value);
                }
            }
        });
        return view;
    }

    /**
     * 创建布局
     *
     * @param position
     * @param parent
     * @return
     */
    private View createView(final int position, ViewGroup parent) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.content_item_view, parent, false);
        TextView contentTxt = (TextView) view.findViewById(R.id.content_txt);
        contentTxt.setText(items[position]);
        contentTxt.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(listener!=null){
                    listener.menuClickListener(-1,items[position]);
                }
            }
        });
        LinearLayout conentLayout = (LinearLayout) view.findViewById(R.id.conent_layout);
        conentLayout.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {

            }
        });
        return view;
    }
    @Override
    public void openMenu(View childAt) {
        super.openMenu(childAt);
        setTabColor(childAt, Color.RED);
    }

    @Override
    public void closeMenu(View childAt) {
        super.closeMenu(childAt);
        setTabColor(childAt, Color.BLACK);
    }

    /**
     * 设置选择颜色
     *
     * @param childAt
     * @param color
     */
    private void setTabColor(View childAt, int color) {
        if (childAt instanceof LinearLayout) {
            LinearLayout layout = (LinearLayout) childAt;
            int childCount = layout.getChildCount();
            if (childCount > 0) {
                View childAt1 = layout.getChildAt(0);
                if (childAt1 instanceof TextView) {
                    TextView tv = (TextView) childAt1;
                    tv.setTextColor(color);
                }
            }
        }
    }
}

点击内容区域选择的内容通过BaseMenuAdapter中的MenuClickListener接口进行回调;

/**
     * 内容菜单点击事件
     * @param listener
     */
    public void menuClick(BaseMenuAdapter.MenuClickListener listener){
        menuAdapter.setOnMenuClickListener(listener);
    }

源码地址

相关文章

网友评论

      本文标题:仿美团、58、汽车之家等下拉列表选择效果

      本文链接:https://www.haomeiwen.com/subject/cnnahqtx.html