實現(xiàn)一個仿小米日歷控件

項目地址:https://github.com/landscapeside/DragCalendar

先看效果圖:

效果圖

根據(jù)效果,我們可以看到洋满,要實現(xiàn)該控件,需要具備:

  1. 容器以及觸摸事件處理
  2. 周日歷布局以及選擇拧略,切換上下周處理
  3. 月日歷布局以及選擇芦岂,切換上下月處理

首先說說容器

對于其他使用者來說,整個日歷都應該是一個類似于RelativeLayout之類的容器垫蛆,然后里面包含有我們需要的日歷控件禽最,而且因為在滑動日歷的時候也會移動下面的listview或者scrollView部分,所以其實這是一個嵌套滑動控件袱饭,它必須能合理的處理不同的手勢場景(比如在listView的內容滑動到頂部時再下滑會滑出日歷川无,以及上滑收起日歷等),所以它在設計上應該類似于drawerLayout等手勢類容器虑乖,并且需要在滑動時通過滑動進度來動態(tài)設置動畫懦趋。

因此,容器控件需要做到:

  1. 觸摸事件的攔截與處理
  2. 周日歷和月日歷的收起和展開動畫

凡是觸摸事件處理與攔截都是通過復寫onInterceptTouchEventonTouchEvent來實現(xiàn)疹味,一般有如下方式:

  1. 邏輯全部寫在這兩個方法里仅叫,需要開發(fā)者自己去記錄位置坐標,計算方向等等糙捺,并且需要記錄若干狀態(tài)
  2. 用安卓提供的GestureDetector類簡化某些常用手勢場景诫咱,當然依然需要復寫onInterceptTouchEventonTouchEvent,相對來說代碼簡化一些
  3. 用安卓提供的ViewDragHelper類(以下簡稱VDH)來處理洪灯,該類更簡化坎缭,相對GestureDetector來說其增加了容器內孩子視圖的判斷,正是因為這點签钩,可以讓開發(fā)者方便的得知觸摸點由哪個孩子視圖處理掏呼,很大程度上避免處理坐標系信息

這里我們采用的方案主要是VDH,方案1和方案2為輔助铅檩,具體實現(xiàn)方式請參考用viewDragHelper來寫刷新控件一憎夷,用viewDragHelper來寫刷新控件二用viewDragHelper來寫刷新控件三

接下來是代碼的架構方面

更深入的分析需求昧旨,我們會發(fā)現(xiàn)拾给,容器類其實應該負責處理內容區(qū)與周日歷/月日歷的聯(lián)動富拗,而至于日歷內部渲染,邏輯處理應該交由其他類來處理鸣戴,這里我們再次細分:

  • 邏輯部分(Presenter層):向日歷視圖發(fā)出刷新,定位命令粘拾,給調用者暴露關閉窄锅,返回今天方法,并且給調用者提供日期選中缰雇,滾動到某月/某周的消息回調
  • 視圖部分(View層):從效果圖來看入偷,周日歷/月日歷采用viewPager來實現(xiàn)即可,其重點負責日歷的渲染功能械哟,同時當Presenter層被調用返回今天以及更新了數(shù)據(jù)源需要刷新時疏之,最終需要視圖層來刷新頁面以及滑動到正確的周/月

邏輯部分Presenter

根據(jù)使用對象和場景,Presenter提供的能力分為三類:

  • 供控件使用者調用的暇咆,屬于開放API部分
  • 供視圖層調用锋爪,一般是用戶對視圖操作了之后由視圖通知Presenter做某事
  • 消息回傳,視圖通知了Presenter之后爸业,由Presenter來通知使用者來更新UI或者做其他事務
開放API
  • 返回今天(供控件使用者調用)
  • 關閉月日歷(供控件使用者調用)
  • 設置數(shù)據(jù)源
示例圖

開放API由使用者調用其骄,因為根據(jù)效果圖來看,日歷的標題欄實為固定在界面頂部扯旷,正常情況下被toolBar所遮擋拯爽,滑動時逐步顯現(xiàn),因此將標題欄單獨實現(xiàn)為一個控件钧忽,所以需要日歷控件和標題欄控件互動:

  1. 標題欄上有兩個按鈕毯炮,返回今天和收起,點擊后應該通知日歷控件做相應操作
  2. 日歷控件滑動或者選擇后耸黑,可能會導致標題欄上文字顯示改變桃煎,因此需要日歷控件提供回調

從代碼實現(xiàn)角度,由于這些操作實際為邏輯控制部分崎坊,因此應該交由presenter來實現(xiàn)备禀,調用者應該通過presenter作為橋梁來操作控件視圖,且控件視圖回調的消息通過presenter回傳給調用者

根據(jù)需求奈揍,在月日歷下曲尸,會根據(jù)某接口返回的參數(shù)來標識當天是否有數(shù)據(jù)

month_card.png

此數(shù)據(jù)來源于網(wǎng)絡請求,是異步操作男翰,因此只能有調用者在網(wǎng)絡請求返回之后將數(shù)據(jù)傳入控件且刷新另患,與
返回今天和關閉相同,調用者最好不要直接操作控件視圖蛾绎,而通過presenter作為橋接昆箕,間接通知視圖刷新頁面鸦列,
使得調用者與視圖解耦,將日歷視圖具體實現(xiàn)邏輯隱蔽起來鹏倘。幾個主要代碼實現(xiàn)如下:

    // 設置數(shù)據(jù)源
    public <T> void parseData(List<T> sources) {
        if (calendarDotVO == null) {
            throw new IllegalArgumentException("Dot Data must not be null");
        }
        calendarDotVO.parseData(sources);
        viewBuilder().dragCalendarLayout.reDraw();
    }

    // 返回今天
    public void backToday() {
        setSelectTime(todayTime);
        viewBuilder().dragCalendarLayout.backToday();
    }

    // 關閉月日歷
    public void close() {
        viewBuilder().dragCalendarLayout.setExpand(false);
    }

當點擊返回今天時薯嗤,需要做到:

  1. 回滾周/月視圖至今天所在的周/月,后文就討論具體實現(xiàn)
  2. 通知調用者重新選中今天

供日歷視圖調用的presenter接口纤泵,一般為通知調用者進行業(yè)務處理

根據(jù)設計骆姐,日歷視圖有如下幾個會引發(fā)調用者業(yè)務處理的操作:

  1. 周日歷下,左右滑動切換會導致日期的自動切換捏题,比如選中日期為周二且滑至上一周時玻褪,同時日期切換至該周周二
  2. 月日歷下,左右滑動切換日歷標題欄上展示日期
  3. 月日歷下標題欄展示日期或者選擇日期非今日公荧,展示返回今日按鈕

之前說過带射,周日歷/月日歷實際為viewPager實現(xiàn),因此要實現(xiàn)滑動切換邏輯只需監(jiān)聽ViewPager.OnPageChangeListener循狰,因月日歷和周日歷的實際實現(xiàn)不同窟社,這里用枚舉CalendarPagerChangeEnum來區(qū)分:

    MONTH{
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            CalendarPresenter.instance()
                    .setCurrentScrollDate(DateUtils.getTagTimeStr(
                            CalendarType.MONTH.calculateByOffset(position)));
        }

        @Override
        public void onPageScrollStateChanged(int state) {
            if (stateChangeListener != null) {
                stateChangeListener.onStateChange(state);
            }
            if (state == ViewPager.SCROLL_STATE_DRAGGING) {
                ((MonthCalendarAdapter)adapter).showDivider(true);
            } else if (state == ViewPager.SCROLL_STATE_IDLE) {
                ((MonthCalendarAdapter)adapter).showDivider(false);
            }
        }
    },
    WEEK{
        @Override
        public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

        }

        @Override
        public void onPageSelected(int position) {
            Calendar calendar = CalendarPresenter.instance().selectCalendar();
            int week = CalendarType.WEEK.defPosition() - position;
            if (week != CalendarPresenter.instance().getWeekDiff()) {
                calendar.add(Calendar.DATE, -(week - CalendarPresenter.instance().getWeekDiff()) * 7);
                CalendarPresenter.instance().setSelectTime(DateUtils.getTagTimeStr(calendar),true);
            }
        }

        @Override
        public void onPageScrollStateChanged(int state) {

        }
    };

這里有一個小tips,為了提升用戶體驗晤揣,月日歷滑動時需展示邊界線桥爽,因此才有

    @Override
    public void onPageScrollStateChanged(int state) {
        if (state == ViewPager.SCROLL_STATE_DRAGGING) {
            ((MonthCalendarAdapter)adapter).showDivider(true);
        } else if (state == ViewPager.SCROLL_STATE_IDLE) {
            ((MonthCalendarAdapter)adapter).showDivider(false);
        }
    }

同時Presenter提供選擇日期和設置月日歷滾動日期接口:

    // 設置選中日期并觸發(fā)消息回傳,通知調用者進行業(yè)務處理
    public void setSelectTime(String selectTime, boolean autoReset) {
        if (TextUtils.isEmpty(selectTime)) {
            throw new IllegalArgumentException("selectTime can not be empty");
        }
        if (DateUtils.diff(todayTime, selectTime) < 0) {
            if (autoReset) {
                selectTime = todayTime;
            } else {
                return;
            }
        }
        boolean close  = false;
        this.selectTime = selectTime;
        if (callbk != null) {
            close = callbk.onSelect(selectTime);
        }
        notifyCalendarBar(selectTime);
        viewBuilder().dragCalendarLayout.focusCalendar();
        if (close) {
            close();
        }
    }

    // 月日歷下當前滾動到某月時的日期設置
    public void setCurrentScrollDate(String currentScrollDate) {
        if (TextUtils.isEmpty(currentScrollDate)) {
            throw new IllegalArgumentException("currentScrollDate can not be empty");
        }
        if (!currentDate.equals(currentScrollDate)) {
            currentDate = currentScrollDate;
            currentDateCallbk();
            notifyCalendarBar(currentScrollDate);
        }
    }

    // 當前滾動日期的消息回傳
    private void currentDateCallbk() {
        if (callbk != null) {
            callbk.onScroll(currentDate);
        }
    }

    // 日歷標題欄的消息回傳
    private void notifyCalendarBar(String barDate) {
        if (callbk != null) {
            boolean isToday;
            if (DateUtils.diffMonth(todayTime, barDate) == 0) {
                isToday = TextUtils.equals(todayTime, selectTime);
            } else {
                isToday = false;
            }
            callbk.onCalendarBarChange(barDate,isToday);
        }
    }
消息回傳通知

根據(jù)之前的約定昧识,調用者只與presenter交互钠四,同樣的,presenter接受到日歷視圖的操作后跪楞,由presenter通知調用者進行業(yè)務處理

    // presenter提供的消息通知接口
    public interface ICallbk {
        void onCalendarBarChange(String currentTime, boolean isToday);
        void onScroll(String currentTime);
        boolean onSelect(String selectTime);
    }

    ICallbk callbk = null;

    public void setCallbk(ICallbk callbk) {
        this.callbk = callbk;
        currentDateCallbk();
        notifyCalendarBar(currentDate);
    }

此處在設置消息通知接口時需強制觸發(fā)消息一次缀去,目的是為了在初始階段刷新日歷標題欄

視圖部分(VIEW)

視圖層主要負責:

  1. 周視圖渲染以及用戶操作后對presenter發(fā)起消息通知
  2. 月視圖渲染以及用戶操作后對presenter發(fā)起消息通知

從結構上來說,兩者都是采用viewPager實現(xiàn)甸祭,不同點即其渲染方式不同缕碎,因此這里也可采用枚舉CalendarType加以區(qū)分:

    public enum CalendarType implements IAdapterRefresh,IAdapterConstant {
        MONTH {
            @Override
            public void refresh(ViewGroup view, int position) {
                //給view 填充內容
    
                //設置開始時間為本周日
                Calendar day = calculateByOffset(position);
                view.setTag(day.get(Calendar.MONTH) + "");
                //找到這個月的第一天所在星期的周日
                day.add(Calendar.DAY_OF_MONTH, -(day.get(Calendar.DAY_OF_MONTH) - 1));
                int day_of_week = day.get(Calendar.DAY_OF_WEEK) - 1;
                day.add(Calendar.DATE, -day_of_week);
                ((ICalendarCard)view).render(day);
            }
    
            @Override
            public int getCount() {
                return 1200;
            }
    
            @Override
            public int defPosition() {
                return getCount() - 1;
            }
        },
    
        WEEK {
            @Override
            public void refresh(ViewGroup view, int position) {
                //給view 填充內容
    
                //設置開始時間為本周日
                Calendar day = calculateByOffset(position);
                int day_of_week = day.get(Calendar.DAY_OF_WEEK) - 1;
                day.add(Calendar.DATE, -day_of_week);
                ((ICalendarCard)view).render(day);
            }
    
            @Override
            public int getCount() {
                return 4800;
            }
    
            @Override
            public int defPosition() {
                return getCount() - 1;
            }
        }
    }

    public interface IAdapterRefresh {
        void refresh(ViewGroup view, int position);
    }

    public interface IAdapterConstant {
        int getCount();
        int defPosition();
    }

枚舉CalendarType中只需處理邏輯部分,這里為計算出每周/月上起始時間(這里的起始時間并非每一周/月的第一天池户,而應該是每一張周卡片/月卡片第一行第一列開始的那個日期咏雌,因日歷橫向是從周日開始,所以只需算出第一行的周日即可)校焦,并調用相應的周/月視圖進行渲染赊抖。而周/月視圖來源于不同的PagerAdapter(因為周/月為兩個不想干的viewpager),以下以周日歷適配器為例:

    public class WeekCalendarAdapter extends CalendarBaseAdapter {
        private List<View> views = new ArrayList<>();
        WeekCard currentCard;
    
        public WeekCalendarAdapter(Context context) {
            views.clear();
            for (int i = 0; i < 4; i++) {
                views.add(new WeekCard(context));
            }
        }
    
        @Override
        public int getCount() {
            return CalendarType.WEEK.getCount();
        }
    
        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }
    
        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
        }
    
        public WeekCard currentCard() {
            return currentCard;
        }
    
        @Override
        public void setPrimaryItem(ViewGroup container, int position, Object object) {
            currentCard = (WeekCard) object;
            super.setPrimaryItem(container, position, object);
        }
    
        @Override
        public Object instantiateItem(ViewGroup container, final int position) {
            ViewGroup view = (ViewGroup) views.get(position % views.size());
            int index = container.indexOfChild(view);
            if (index != -1) {
                container.removeView(view);
            }
            try {
                container.addView(view);
            } catch (Exception e) {
    
            }
            CalendarType.WEEK.refresh(view, position);
            return view;
        }
    }

其中寨典,適配器用4個視圖循環(huán)使用達到節(jié)省資源的目的氛雪,WeekCard實現(xiàn)了ICalendarCard接口:

    public interface ICalendarCard {
        void render(final Calendar today);
    }

然后是周日歷viewPager:

    public class WeekView extends LinearLayout implements ICalendarView {

        ViewPager weekPager;
        WeekCalendarAdapter adapter;
    
        public WeekView(Context context) {
            super(context);
            setLayoutParams(new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
            setOrientation(VERTICAL);
    
            View.inflate(getContext(), R.layout.calendar_pager, this);
            weekPager = (ViewPager) findViewById(R.id.cal_pager);
            ViewGroup.LayoutParams layoutParams = weekPager.getLayoutParams();
            layoutParams.height = dp2px(getContext(), WEEK_HEIGHT);
            weekPager.setLayoutParams(layoutParams);
    
            adapter = new WeekCalendarAdapter(context);
            weekPager.setAdapter(adapter);
            weekPager.setCurrentItem(CalendarType.WEEK.defPosition());
            weekPager.setOnPageChangeListener(CalendarPagerChangeEnum.WEEK.setAdapter(adapter));
        }
    
        @Override
        public void backToday() {
            weekPager.setCurrentItem(CalendarType.WEEK.defPosition(), true);
        }
    
        @Override
        public int currentIdx() {
            return weekPager.getCurrentItem();
        }
    
        @Override
        public void focusCalendar() {
            weekPager.setCurrentItem(CalendarType.WEEK.defPosition() - CalendarPresenter.instance().getWeekDiff(), true);
            reDraw();
        }
    
        @Override
        public void reDraw() {
            adapter.notifyDataSetChanged();
        }
    }

我們會發(fā)現(xiàn),因為ViewPager以及包含它的容器為動態(tài)實例化耸成,因此需要手動的設置高度而無法用系統(tǒng)的wrap_content屬性报亩,因此這里需要開發(fā)者自我計算一個合理的高度WEEK_HEIGHT浴鸿,此處作者設置每周高45dp,一個月最高為305dp(6行的周加上上下邊距總共305dp弦追,詳細設置見Range類)

    public class Range {
        public static final int MONTH_HEIGHT = 305;
        public static final int WEEK_HEIGHT = 45;
        public static final int DAY_HEIGHT = 45;
        public static final int MONTH_PADDING_TOP = 25;
        public static final int MONTH_PADDING_BOTTOM = 10;
    }

月日歷實現(xiàn)與之類似岳链,就不贅述
另外劲件,在前述的presenter實現(xiàn)中宠页,提到返回今日時需要通時回滾周/月日歷視圖到當前周/月,其實際為相應的ViewPager重設當前頁寇仓,因此在前述的presenter的backToday實現(xiàn)中調用的viewBuilder().dragCalendarLayout.backToday();實際上是調用周視圖WeekView的weekPager.setCurrentItem(CalendarType.WEEK.defPosition(), true);以及月視圖MonthView的monthPager.setCurrentItem(CalendarType.MONTH.defPosition(), true);

周,月視圖渲染實現(xiàn)

周卡片的渲染烤宙,實際上只需要7個橫向排列的日期遍烦,而月卡片實際上是縱向排6個周卡片,這里給出主要的渲染代碼:

    // 周卡片
    @Override
    public void render(Calendar today) {
        for (int a = 0; a < 7; a++) {
            final int dayOfMonth = today.get(Calendar.DAY_OF_MONTH);
            final ViewGroup dayOfWeek = (ViewGroup) getChildAt(a);
            dayOfWeek.setTag(DateUtils.getTagTimeStr(today));
            dayOfWeek.setOnClickListener(v -> CalendarPresenter.instance().setSelectTime(dayOfWeek.getTag().toString()));

            //如果是選中天的話顯示為藍色
            if (CalendarPresenter.instance().getSelectTime().equals(DateUtils.getTagTimeStr(today))) {
                ((TextView) dayOfWeek.findViewById(R.id.gongli)).setText(DateUtils.getTagTimeStrByMouthandDay(today));
                renderSelect(dayOfWeek, DateUtils.getTagTimeStr(today));
            } else {
                ((TextView) dayOfWeek.findViewById(R.id.gongli)).setText(dayOfMonth + "");
                if (DateUtils.diff(CalendarPresenter.instance().today(), DateUtils.getTagTimeStr(today)) >= 0) {
                    renderNormal(dayOfWeek, DateUtils.getTagTimeStr(today));
                } else {
                    renderGray(dayOfWeek, DateUtils.getTagTimeStr(today));
                }
            }
            today.add(Calendar.DATE, 1);
        }
    }

    // 月卡片
    @Override
    public void render(Calendar today) {
        int pageMonth = (Integer.parseInt((String) getTag()));
        //一頁顯示一個月+7天躺枕,為42服猪;
        for (int b = 0; b < 6; b++) {
            final ViewGroup view = (ViewGroup) monthContent.getChildAt(b);
            int currentMonth = today.get(Calendar.MONTH);
            if (pageMonth != currentMonth && b != 0) {
                view.setVisibility(INVISIBLE);
                today.add(Calendar.DATE, 7);
            } else {
                view.setVisibility(VISIBLE);
                for (int a = 0; a < 7; a++) {
                    final int dayOfMonth = today.get(Calendar.DAY_OF_MONTH);
                    final ViewGroup dayOfWeek = (ViewGroup) view.getChildAt(a);
                    ((TextView) dayOfWeek.findViewById(R.id.gongli)).setText(dayOfMonth + "");
                    dayOfWeek.setTag(DateUtils.getTagTimeStr(today));

                    dayOfWeek.setOnClickListener(v -> CalendarPresenter.instance().setSelectTime(dayOfWeek.getTag().toString()));

                    //不是當前月淺色顯示
                    currentMonth = today.get(Calendar.MONTH);
                    if (pageMonth != currentMonth) {
                        renderInvisible(dayOfWeek);
//                        renderGray(dayOfWeek,DateUtils.getTagTimeStr(today));
                        today.add(Calendar.DATE, 1);
                    } else {
                        //如果是選中天的話顯示為藍色
                        if (CalendarPresenter.instance().getSelectTime().equals(DateUtils.getTagTimeStr(today))) {
                            selectPos = calculatePos(b);
                            renderSelect(dayOfWeek, DateUtils.getTagTimeStr(today));
                        } else {
                            if (DateUtils.diff(CalendarPresenter.instance().today(), DateUtils.getTagTimeStr(today)) >= 0) {
                                renderNormal(dayOfWeek, DateUtils.getTagTimeStr(today));
                            } else {
                                renderGray(dayOfWeek, DateUtils.getTagTimeStr(today));
                            }
                        }
                        today.add(Calendar.DATE, 1);
                    }
                }
            }
        }
    }

關于仿小米日歷的實現(xiàn)到此結束,祝各位天天開心拐云,生活愉快罢猪!

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市叉瘩,隨后出現(xiàn)的幾起案子膳帕,更是在濱河造成了極大的恐慌,老刑警劉巖薇缅,帶你破解...
    沈念sama閱讀 211,348評論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件危彩,死亡現(xiàn)場離奇詭異,居然都是意外死亡泳桦,警方通過查閱死者的電腦和手機汤徽,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,122評論 2 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來灸撰,“玉大人谒府,你說我怎么就攤上這事「√海” “怎么了完疫?”我有些...
    開封第一講書人閱讀 156,936評論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長亲轨。 經(jīng)常有香客問我趋惨,道長,這世上最難降的妖魔是什么惦蚊? 我笑而不...
    開封第一講書人閱讀 56,427評論 1 283
  • 正文 為了忘掉前任器虾,我火速辦了婚禮讯嫂,結果婚禮上,老公的妹妹穿的比我還像新娘兆沙。我一直安慰自己欧芽,他們只是感情好,可當我...
    茶點故事閱讀 65,467評論 6 385
  • 文/花漫 我一把揭開白布葛圃。 她就那樣靜靜地躺著千扔,像睡著了一般。 火紅的嫁衣襯著肌膚如雪库正。 梳的紋絲不亂的頭發(fā)上曲楚,一...
    開封第一講書人閱讀 49,785評論 1 290
  • 那天,我揣著相機與錄音褥符,去河邊找鬼龙誊。 笑死,一個胖子當著我的面吹牛喷楣,可吹牛的內容都是我干的趟大。 我是一名探鬼主播,決...
    沈念sama閱讀 38,931評論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼铣焊,長吁一口氣:“原來是場噩夢啊……” “哼逊朽!你這毒婦竟也來了?” 一聲冷哼從身側響起曲伊,我...
    開封第一講書人閱讀 37,696評論 0 266
  • 序言:老撾萬榮一對情侶失蹤叽讳,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后坟募,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體绽榛,經(jīng)...
    沈念sama閱讀 44,141評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 36,483評論 2 327
  • 正文 我和宋清朗相戀三年婿屹,在試婚紗的時候發(fā)現(xiàn)自己被綠了灭美。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,625評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡昂利,死狀恐怖届腐,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情蜂奸,我是刑警寧澤犁苏,帶...
    沈念sama閱讀 34,291評論 4 329
  • 正文 年R本政府宣布,位于F島的核電站扩所,受9級特大地震影響围详,放射性物質發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,892評論 3 312
  • 文/蒙蒙 一助赞、第九天 我趴在偏房一處隱蔽的房頂上張望买羞。 院中可真熱鬧,春花似錦雹食、人聲如沸畜普。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,741評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽吃挑。三九已至,卻和暖如春街立,著一層夾襖步出監(jiān)牢的瞬間舶衬,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評論 1 265
  • 我被黑心中介騙來泰國打工赎离, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留约炎,地道東北人。 一個月前我還...
    沈念sama閱讀 46,324評論 2 360
  • 正文 我出身青樓蟹瘾,卻偏偏與公主長得像,于是被迫代替她去往敵國和親掠手。 傳聞我的和親對象是個殘疾皇子憾朴,可洞房花燭夜當晚...
    茶點故事閱讀 43,492評論 2 348

推薦閱讀更多精彩內容

  • Android 自定義View的各種姿勢1 Activity的顯示之ViewRootImpl詳解 Activity...
    passiontim閱讀 171,757評論 25 707
  • 內容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標下拉刷新...
    皇小弟閱讀 46,727評論 22 665
  • Android UI相關開源項目庫匯總OpenDigg 抽屜菜單MaterialDrawer ★7337 - 安卓...
    黃海佳閱讀 8,699評論 3 77
  • 平平淡淡的幸福,總是讓人留戀喷鸽。 我什么時候也可以有呢众雷。真想就此時光不再流淌,我不再老去做祝,不用去面對許許多多的人間雜...
    蜜呢閱讀 249評論 0 0
  • 目錄 上一章 緋雪轉眼間就來到了無惘海底砾省。她所在的地方似乎是一個密閉的空間,但是四周還能看到海里的魚兒混槐,而這個空間...
    遲安北閱讀 173評論 0 3