仿大眾點評多條目下拉菜單篩選

分析:

首先來看看我們要實現(xiàn)的效果,下面這張圖是大眾點評APP里面的一個多條目下拉菜單篩選的一個效果,這是很多App里面都比較常見的一種多條目篩選菜單。

GIF.gif

結構分析:

Screenshot_2017-08-01-20-44-12-15.png

整個控件我么用一個LinearLayout 實現(xiàn),所以我們要繼承LinearLayout .然后是上面紅色長方形部分的Tab欄,下面最外層黑色框框是一個FrameLayout用來存放陰影(綠色框框部分)和菜單布局(最里面紅色框框部分)

實現(xiàn)

分析完了,我們就可以用代碼來實現(xiàn)了,代碼如下:

1、基本布局:

public class ListDataScreenView extends LinearLayout  {
    private Context mContext;
    // 1.1 創(chuàng)建頭部用來存放 Tab
    private LinearLayout mMenuTabView;
    // 1.2 創(chuàng)建 FrameLayout 用來存放 = 陰影(View) + 菜單內容布局(FrameLayout)
    private FrameLayout mMenuMiddleView;
    // 陰影
    private View mShadowView;
    // 創(chuàng)建菜單用來存放菜單內容
    private FrameLayout mMenuContainerView;
    // 陰影的顏色
    private int mShadowColor = 0x88888888;
    // 篩選菜單的 Adapter
    private BaseMenuAdapter mAdapter;
    // 內容菜單的高度
    private int mMenuContainerHeight;
    // 當前打開的位置
    private int mCurrentPosition = -1;
    private long DURATION_TIME = 350;
    // 動畫是否在執(zhí)行
    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);
        mContext = context;
        initLayout();
    }

    /**
     * 1.布局實例化好 (組合控件)
     */
    private void initLayout() {
        //  1. 先創(chuàng)建一個 xml 布局 ,再加載,findViewById
        //  2. 簡單的效果用代碼去創(chuàng)建  早期IOS 用代碼創(chuàng)建布局
        setOrientation(VERTICAL);

        // 1.1 創(chuàng)建頭部用來存放 Tab
        mMenuTabView = new LinearLayout(mContext);
        mMenuTabView.setLayoutParams(new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
        addView(mMenuTabView);

        // 1.2 創(chuàng)建 FrameLayout 用來存放 = 陰影(View) + 菜單內容布局(FrameLayout)
        mMenuMiddleView = new FrameLayout(mContext);
        LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT, 0);
        params.weight = 1;
        mMenuMiddleView.setLayoutParams(params);
        addView(mMenuMiddleView);

        // 創(chuàng)建陰影 可以不用設置 LayoutParams 默認就是 MATCH_PARENT ,MATCH_PARENT
        mShadowView = new View(mContext);
        mShadowView.setBackgroundColor(mShadowColor);
        mShadowView.setAlpha(0f);
        mShadowView.setOnClickListener(this);
        mShadowView.setVisibility(GONE);
        mMenuMiddleView.addView(mShadowView);//陰影View在下面

        // 創(chuàng)建菜單用來存放菜單內容
        mMenuContainerView = new FrameLayout(mContext);
        mMenuContainerView.setBackgroundColor(Color.WHITE);
        mMenuMiddleView.addView(mMenuContainerView);//菜單內容在上面
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        Log.e("TAG", "onMeasure");
        int height = MeasureSpec.getSize(heightMeasureSpec);
        if (mMenuContainerHeight == 0 && height > 0) {
            // 內容的高度應該不是全部  應該是整個 View的 75%
            mMenuContainerHeight = (int) (height * 75f / 100);
            //獲取菜單內容View的LayoutParams
            ViewGroup.LayoutParams params = mMenuContainerView.getLayoutParams();
            //設置菜單內容的高度
            params.height = mMenuContainerHeight;
            mMenuContainerView.setLayoutParams(params);
            // 進來的時候陰影不顯示 ,內容也是不顯示的(把它移上去)
            //放菜單內容
            mMenuContainerView.setTranslationY(-mMenuContainerHeight);
        }
    }

}

2、設置點擊事件,以及過程中的動畫

 /**
     * 設置tab的點擊
     *
     * @param tabView
     * @param position
     */
    private void setTabClick(final View tabView, final int position) {
        tabView.setOnClickListener(new OnClickListener() {
            @Override
            public void onClick(View v) {
                if (mCurrentPosition == -1) {
                    // 沒打開
                    openMenu(position, tabView);
                } else {
                    if (mCurrentPosition == position) {
                        // 打開了,關閉
                        closeMenu();
                    } else {
                        // 切換一下顯示
                        //拿到菜單容器里的子view(TextView)
                        View currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);
                        currentMenu.setVisibility(View.GONE);//將子view(TextView)設置無不可見
                        mAdapter.menuClose(mMenuTabView.getChildAt(mCurrentPosition));

                        //拿到當前點擊的位置
                        mCurrentPosition = position;
                        currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);
                        currentMenu.setVisibility(View.VISIBLE);
                        mAdapter.menuOpen(mMenuTabView.getChildAt(mCurrentPosition));
                    }
                }
            }
        });
    }

    /**
     * 關閉菜單
     */
    private void closeMenu() {
        //動畫正在執(zhí)行,點擊無效
        if (mAnimatorExecute) {
            return;
        }
        // 關閉動畫  位移動畫  透明度動畫
        ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContainerView, "translationY", 0, -mMenuContainerHeight);
        translationAnimator.setDuration(DURATION_TIME);
        translationAnimator.start();
        mShadowView.setVisibility(View.VISIBLE);
        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 1f, 0f);
        alphaAnimator.setDuration(DURATION_TIME);
        // 要等關閉動畫執(zhí)行完才能去隱藏當前菜單
        alphaAnimator.addListener(new AnimatorListenerAdapter() {
            //動畫執(zhí)行完畢
            @Override
            public void onAnimationEnd(Animator animation) {
                View menuView = mMenuContainerView.getChildAt(mCurrentPosition);
                menuView.setVisibility(View.GONE);
                mCurrentPosition = -1;
                mShadowView.setVisibility(GONE);
                mAnimatorExecute = false;
            }

            //關閉菜單動畫開始
            @Override
            public void onAnimationStart(Animator animation) {
                mAnimatorExecute = true;
                mAdapter.menuClose(mMenuTabView.getChildAt(mCurrentPosition));
            }
        });
        alphaAnimator.start();
    }

    /**
     * 打開菜單
     *
     * @param position
     * @param tabView
     */
    private void openMenu(final int position, final View tabView) {
        //動畫正在執(zhí)行,點擊無效
        if (mAnimatorExecute) {
            return;
        }
        //設置陰影可見
        mShadowView.setVisibility(View.VISIBLE);
        // 獲取當前位置顯示當前菜單,菜單是加到了菜單容器
        View menuView = mMenuContainerView.getChildAt(position);
        menuView.setVisibility(View.VISIBLE);

        // 打開開啟動畫  位移動畫  透明度動畫
        //位移動畫
        ObjectAnimator translationAnimator = ObjectAnimator.ofFloat(mMenuContainerView, "translationY", -mMenuContainerHeight, 0);
        translationAnimator.setDuration(DURATION_TIME);
        translationAnimator.start();

        //透明度動畫
        ObjectAnimator alphaAnimator = ObjectAnimator.ofFloat(mShadowView, "alpha", 0f, 1f);
        alphaAnimator.setDuration(DURATION_TIME);
        alphaAnimator.addListener(new AnimatorListenerAdapter() {
            @Override
            public void onAnimationEnd(Animator animation) {
                mAnimatorExecute = false;
                mCurrentPosition = position;
            }

            @Override
            public void onAnimationStart(Animator animation) {
                mAnimatorExecute = true;
                // 把當前的 tab 傳到外面
                mAdapter.menuOpen(tabView);
            }
        });
        alphaAnimator.start();
    }

3、寫Adapter提供數(shù)據(jù)源

這個時候我們的效果還是死的,我么得給他設置寫一個Adapter,現(xiàn)實情況下,我們的數(shù)據(jù)也是通常要去網(wǎng)絡獲取,所以不能寫死。

>BaseMenuAdapter 
/**
 * 篩選菜單的 Adapter
 */

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:

public class ListScreenMenuAdapter extends BaseMenuAdapter{
    private Context mContext;

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

    private String[] mItems ={"附近","美食","智能排序","篩選"};

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

    @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) {
        // 真正開發(fā)過程中,不同的位置顯示的布局不一樣
        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 menuClose(View tabView) {
        TextView tabTv = (TextView) tabView;
        tabTv.setTextColor(Color.BLACK);
    }

    @Override
    public void menuOpen(View tabView) {
        TextView tabTv = (TextView) tabView;
        tabTv.setTextColor(Color.RED);
    }
}


有了Adapter之后,我們就可以給ListDataScreenView 以下方法,是不是感覺跟ListView很像。

 /**
     * 設置 Adapter
     *
     * @param adapter
     */
    public void setAdapter(BaseMenuAdapter adapter) {
        this.mAdapter = adapter;
        // 獲取有多少條
        int count = mAdapter.getCount();
        for (int i = 0; i < count; i++) {
            // 獲取菜單的Tab
            View tabView = mAdapter.getTabView(i, mMenuTabView);
            mMenuTabView.addView(tabView);//加一個TextView
            LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams();
            params.weight = 1;
            tabView.setLayoutParams(params);

            // 設置tab點擊事件
            setTabClick(tabView, i);

            // 獲取菜單的內容(一個TextView)
            View menuView = mAdapter.getMenuView(i, mMenuContainerView);
            menuView.setVisibility(GONE);
            mMenuContainerView.addView(menuView);
        }
        // 內容還沒有顯示出來,打開的時候顯示當前位置的菜單,關閉的時候隱藏,陰影點擊應該要關閉菜單
        // 動畫在執(zhí)行的情況下就不要在響應動畫事件
        // 打開和關閉 變化tab的顯示 , 肯定不能把代碼寫到 ListDataScreen 里面來
        // 當菜單是打開的狀態(tài) 不要執(zhí)行動畫只要切換
    }

   

最后看下我們實現(xiàn)的效果:

GI88F.gif

全部代碼下載地址:https://github.com/zkxok/MultipleItemSelection

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
【社區(qū)內容提示】社區(qū)部分內容疑似由AI輔助生成,瀏覽時請結合常識與多方信息審慎甄別。
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發(fā)布,文章內容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務。

相關閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 179,057評論 25 709
  • 發(fā)現(xiàn) 關注 消息 iOS 第三方庫、插件、知名博客總結 作者大灰狼的小綿羊哥哥關注 2017.06.26 09:4...
    肇東周閱讀 15,355評論 4 61
  • 【引子】幸福的苦惱 春曉在一家高科技公司從事研發(fā)工作。由于他工作非常努力,深受上司賞識,于是,在不久前被公司提拔為...
    郭致星閱讀 1,857評論 0 27
  • 現(xiàn)在基本上每個應用的頭部,都會是一個無限滾動顯示圖片的scrollview,然后點擊圖片可以跳轉到不同的頁面。今天...
    西木柚子閱讀 2,970評論 15 69
  • 阿里文娛智能營銷平臺 kun?jing?shi?shi?shen?me 1.開發(fā)者:得不到有效分發(fā) 2.用戶:根本...
    鵬城穆羽閱讀 920評論 0 0

友情鏈接更多精彩內容