仿大眾點(diǎn)評多條目下拉菜單篩選

分析:

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

GIF.gif

結(jié)構(gòu)分析:

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

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

實(shí)現(xiàn)

分析完了宿亡,我們就可以用代碼來實(shí)現(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) + 菜單內(nèi)容布局(FrameLayout)
    private FrameLayout mMenuMiddleView;
    // 陰影
    private View mShadowView;
    // 創(chuàng)建菜單用來存放菜單內(nèi)容
    private FrameLayout mMenuContainerView;
    // 陰影的顏色
    private int mShadowColor = 0x88888888;
    // 篩選菜單的 Adapter
    private BaseMenuAdapter mAdapter;
    // 內(nèi)容菜單的高度
    private int mMenuContainerHeight;
    // 當(dāng)前打開的位置
    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.布局實(shí)例化好 (組合控件)
     */
    private void initLayout() {
        //  1. 先創(chuàng)建一個(gè) 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) + 菜單內(nèi)容布局(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)建陰影 可以不用設(shè)置 LayoutParams 默認(rèn)就是 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)建菜單用來存放菜單內(nèi)容
        mMenuContainerView = new FrameLayout(mContext);
        mMenuContainerView.setBackgroundColor(Color.WHITE);
        mMenuMiddleView.addView(mMenuContainerView);//菜單內(nèi)容在上面
    }

    @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) {
            // 內(nèi)容的高度應(yīng)該不是全部  應(yīng)該是整個(gè) View的 75%
            mMenuContainerHeight = (int) (height * 75f / 100);
            //獲取菜單內(nèi)容View的LayoutParams
            ViewGroup.LayoutParams params = mMenuContainerView.getLayoutParams();
            //設(shè)置菜單內(nèi)容的高度
            params.height = mMenuContainerHeight;
            mMenuContainerView.setLayoutParams(params);
            // 進(jìn)來的時(shí)候陰影不顯示 坤按,內(nèi)容也是不顯示的(把它移上去)
            //放菜單內(nèi)容
            mMenuContainerView.setTranslationY(-mMenuContainerHeight);
        }
    }

}

2毯欣、設(shè)置點(diǎn)擊事件馒过,以及過程中的動畫

 /**
     * 設(shè)置tab的點(diǎn)擊
     *
     * @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) {
                        // 打開了,關(guān)閉
                        closeMenu();
                    } else {
                        // 切換一下顯示
                        //拿到菜單容器里的子view(TextView)
                        View currentMenu = mMenuContainerView.getChildAt(mCurrentPosition);
                        currentMenu.setVisibility(View.GONE);//將子view(TextView)設(shè)置無不可見
                        mAdapter.menuClose(mMenuTabView.getChildAt(mCurrentPosition));

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

    /**
     * 關(guān)閉菜單
     */
    private void closeMenu() {
        //動畫正在執(zhí)行,點(diǎn)擊無效
        if (mAnimatorExecute) {
            return;
        }
        // 關(guān)閉動畫  位移動畫  透明度動畫
        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);
        // 要等關(guān)閉動畫執(zhí)行完才能去隱藏當(dāng)前菜單
        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;
            }

            //關(guān)閉菜單動畫開始
            @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í)行酗钞,點(diǎn)擊無效
        if (mAnimatorExecute) {
            return;
        }
        //設(shè)置陰影可見
        mShadowView.setVisibility(View.VISIBLE);
        // 獲取當(dāng)前位置顯示當(dāng)前菜單腹忽,菜單是加到了菜單容器
        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;
                // 把當(dāng)前的 tab 傳到外面
                mAdapter.menuOpen(tabView);
            }
        });
        alphaAnimator.start();
    }

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

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

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

public abstract class BaseMenuAdapter {
    // 獲取總共有多少條
    public abstract int getCount();
    // 獲取當(dāng)前的TabView
    public abstract View getTabView(int position, ViewGroup parent);
    // 獲取當(dāng)前的菜單內(nèi)容
    public abstract View getMenuView(int position, ViewGroup parent);

    /**
     * 菜單打開
     * @param tabView
     */
    public void menuOpen(View tabView) {

    }

    /**
     * 菜單關(guān)閉
     * @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很像骇扇。

 /**
     * 設(shè)置 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);//加一個(gè)TextView
            LinearLayout.LayoutParams params = (LayoutParams) tabView.getLayoutParams();
            params.weight = 1;
            tabView.setLayoutParams(params);

            // 設(shè)置tab點(diǎn)擊事件
            setTabClick(tabView, i);

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

   

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

GI88F.gif

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

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末继低,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子稍走,更是在濱河造成了極大的恐慌袁翁,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件婿脸,死亡現(xiàn)場離奇詭異粱胜,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)狐树,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進(jìn)店門年柠,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人褪迟,你說我怎么就攤上這事冗恨。” “怎么了味赃?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵掀抹,是天一觀的道長。 經(jīng)常有香客問我心俗,道長傲武,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任城榛,我火速辦了婚禮揪利,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘狠持。我一直安慰自己疟位,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布喘垂。 她就那樣靜靜地躺著甜刻,像睡著了一般。 火紅的嫁衣襯著肌膚如雪正勒。 梳的紋絲不亂的頭發(fā)上得院,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機(jī)與錄音章贞,去河邊找鬼祥绞。 笑死,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蜕径。 我是一名探鬼主播怪蔑,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼丧荐!你這毒婦竟也來了缆瓣?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤虹统,失蹤者是張志新(化名)和其女友劉穎弓坞,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體车荔,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡渡冻,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了忧便。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片族吻。...
    茶點(diǎn)故事閱讀 39,779評論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖珠增,靈堂內(nèi)的尸體忽然破棺而出超歌,到底是詐尸還是另有隱情,我是刑警寧澤蒂教,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布巍举,位于F島的核電站,受9級特大地震影響凝垛,放射性物質(zhì)發(fā)生泄漏懊悯。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一梦皮、第九天 我趴在偏房一處隱蔽的房頂上張望炭分。 院中可真熱鬧,春花似錦剑肯、人聲如沸捧毛。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽岖妄。三九已至,卻和暖如春寂祥,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背七兜。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工丸凭, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓惜犀,卻偏偏與公主長得像铛碑,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個(gè)殘疾皇子虽界,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容