Android ViewPager + Fragment + ViewPager + Fragment

布局比較復(fù)雜的時候,可能會嵌套兩層或者三層ViewPager舟肉,這時整個布局的高度就會有問題,需要自定義控件去適配
先提供兩個類

public class ScrollableLayout extends LinearLayout {

    private final String TAG = "scrollableLayout";
    private float mDownX;
    private float mDownY;
    private float mLastY;

    private int minY = 0;
    private int maxY = 0;
    private int maxGap = 0;//滑動距離差
    private int mHeadHeight;
    private int mExpandHeight;
    private int mTouchSlop;
    private int mMinimumVelocity;
    private int mMaximumVelocity;
    // 方向
    private DIRECTION mDirection;
    private int mCurY;
    private int mLastScrollerY;
    private boolean needCheckUpdown;
    private boolean updown;
    private boolean mDisallowIntercept;
    private boolean isClickHead;
    private boolean isClickHeadExpand;

    private View mHeadView;
    private ViewPager childViewPager;

    private Scroller mScroller;
    private VelocityTracker mVelocityTracker;
    public boolean isScroll = false;

    /**
     * 滑動方向 *
     */
    public enum DIRECTION {
        UP,// 向上劃
        DOWN// 向下劃
    }

    public interface OnScrollListener {

        void onScroll(int currentY, int maxY);

    }

    private OnScrollListener onScrollListener;

    public void setOnScrollListener(OnScrollListener onScrollListener) {
        this.onScrollListener = onScrollListener;
    }

    //滾動狀態(tài)監(jiān)聽,V1.1.3新增
//    public interface OnScrollStatusListener {
//        void onScrollStatus(boolean isScroll);
//    }
//
//    private OnScrollStatusListener onScrollStatusListener;
//
//    public void setOnScrollStatusListener(OnScrollStatusListener l) {
//        this.onScrollStatusListener = l;
//    }

    public interface OnScrollableLayoutTouchListener {

        void onDispatchEvent(boolean isDown, float rawy);

    }

    private OnScrollableLayoutTouchListener onScrollableLayoutTouchListener;

    public void setOnScrollableLayoutTouchListener(OnScrollableLayoutTouchListener listener) {
        this.onScrollableLayoutTouchListener = listener;
    }

    private ScrollableHelper mHelper;

    public ScrollableHelper getHelper() {
        return mHelper;
    }

    public ScrollableLayout(Context context) {
        super(context);
        init(context);
    }

    public ScrollableLayout(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.HONEYCOMB)
    public ScrollableLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context);
    }

    @TargetApi(Build.VERSION_CODES.LOLLIPOP)
    public ScrollableLayout(Context context, AttributeSet attrs, int defStyleAttr, int
            defStyleRes) {
        super(context, attrs, defStyleAttr, defStyleRes);
        init(context);
    }

    private void init(Context context) {
        mHelper = new ScrollableHelper();
        mScroller = new Scroller(context);
        final ViewConfiguration configuration = ViewConfiguration.get(context);
        mTouchSlop = configuration.getScaledTouchSlop();
        mMinimumVelocity = configuration.getScaledMinimumFlingVelocity();
        mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
    }

    public boolean isSticked() {
        return mCurY == maxY;
    }

    /**
     * 擴大頭部點擊滑動范圍
     *
     * @param expandHeight
     */
    public void setClickHeadExpand(int expandHeight) {
        mExpandHeight = expandHeight;
    }

    public int getMaxY() {
        return maxY;
    }

    public boolean isHeadTop() {
        return mCurY == minY;
    }

    public boolean canPtr() {
        return updown && mCurY == minY && mHelper.isTop();
    }

    public int getMaxGap() {
        return maxGap;
    }

    public void setMaxGap(int maxGap) {
        this.maxGap = maxGap;
    }

    public void requestScrollableLayoutDisallowInterceptTouchEvent(boolean disallowIntercept) {
        super.requestDisallowInterceptTouchEvent(disallowIntercept);
        mDisallowIntercept = disallowIntercept;
    }

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {

        float currentX = ev.getX();
        float currentY = ev.getY();
        float deltaY;
        int shiftX = (int) Math.abs(currentX - mDownX);
        int shiftY = (int) Math.abs(currentY - mDownY);
        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                if (null != onScrollableLayoutTouchListener) {
                    onScrollableLayoutTouchListener.onDispatchEvent(true, currentY);
                }
                mDisallowIntercept = false;
                needCheckUpdown = true;
                updown = true;
                mDownX = currentX;
                mDownY = currentY;
                mLastY = currentY;
                checkIsClickHead((int) currentY, mHeadHeight, getScrollY());
                checkIsClickHeadExpand((int) currentY, mHeadHeight, getScrollY());
                initOrResetVelocityTracker();
                mVelocityTracker.addMovement(ev);
                mScroller.forceFinished(true);

                break;
            case MotionEvent.ACTION_MOVE:
                if (mDisallowIntercept) {
                    break;
                }
                initVelocityTrackerIfNotExists();
                mVelocityTracker.addMovement(ev);
                deltaY = mLastY - currentY;
                if (needCheckUpdown) {
                    if (shiftX > mTouchSlop && shiftX > shiftY) {
                        needCheckUpdown = false;
                        updown = false;
                    } else if (shiftY > mTouchSlop && shiftY > shiftX) {
                        needCheckUpdown = false;
                        updown = true;
                    }
                }

                if (updown && shiftY > mTouchSlop && shiftY > shiftX &&
                        (!isSticked() || mHelper.isTop() || isClickHeadExpand)) {

                    if (childViewPager != null) {
                        childViewPager.requestDisallowInterceptTouchEvent(true);
                    }
                    scrollBy(0, (int) (deltaY + 0.5));
                }
                mLastY = currentY;
                break;
            case MotionEvent.ACTION_UP:
                if (null != onScrollableLayoutTouchListener) {
                    onScrollableLayoutTouchListener.onDispatchEvent(false, currentY);
                }

                if (updown && shiftY > shiftX && shiftY > mTouchSlop) {
                    mVelocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
                    float yVelocity = -mVelocityTracker.getYVelocity();
                    boolean dislowChild = false;
                    if (Math.abs(yVelocity) > mMinimumVelocity) {
                        mDirection = yVelocity > 0 ? DIRECTION.UP : DIRECTION.DOWN;
                        if ((mDirection == DIRECTION.UP && isSticked()) || (!isSticked() &&
                                getScrollY() == 0 && mDirection == DIRECTION.DOWN)) {
                            dislowChild = true;
                        } else {
                            mScroller.fling(0, getScrollY(), 0, (int) yVelocity, 0, 0, -Integer
                                    .MAX_VALUE, Integer.MAX_VALUE);
                            mScroller.computeScrollOffset();
                            mLastScrollerY = getScrollY();
                            invalidate();
                        }
                    }
                    if (!dislowChild && (isClickHead || !isSticked())) {
                        int action = ev.getAction();
                        ev.setAction(MotionEvent.ACTION_CANCEL);
                        boolean dispathResult = super.dispatchTouchEvent(ev);
                        ev.setAction(action);
                        return dispathResult;
                    }
                }
                break;
            default:
                break;
        }
        super.dispatchTouchEvent(ev);
        return true;
    }

    @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH)
    private int getScrollerVelocity(int distance, int duration) {
        if (mScroller == null) {
            return 0;
        } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            return (int) mScroller.getCurrVelocity();
        } else {
            return distance / duration;
        }
    }

    @Override
    public void computeScroll() {
        if (mScroller.computeScrollOffset()) {
//            if(null!=onScrollStatusListener){
//                onScrollStatusListener.onScrollStatus(true);
//            }
            final int currY = mScroller.getCurrY();
            if (mDirection == DIRECTION.UP) {
                // 手勢向上劃
                if (isSticked()) {
                    int distance = mScroller.getFinalY() - currY;
                    int duration = calcDuration(mScroller.getDuration(), mScroller.timePassed());
                    mHelper.smoothScrollBy(getScrollerVelocity(distance, duration), distance,
                            duration);
                    mScroller.forceFinished(true);
                    return;
                } else {
                    scrollTo(0, currY);
                }
            } else {
                // 手勢向下劃
                if (mHelper.isTop() || isClickHeadExpand) {
                    int deltaY = (currY - mLastScrollerY);
                    int toY = getScrollY() + deltaY;
                    scrollTo(0, toY);
                    if (mCurY <= minY) {
                        mScroller.forceFinished(true);
                        return;
                    }
                }
                invalidate();
            }
            mLastScrollerY = currY;
        }
//        else {
//            if(null!=onScrollStatusListener){
//                onScrollStatusListener.onScrollStatus(false);
//            }
//
//        }
    }

    @Override
    public void scrollBy(int x, int y) {
        int scrollY = getScrollY();
//        LogUtils.i("scrollBy===y===" + y + ";;;;scrollY==" + scrollY);
        int toY = scrollY + y;
        if (toY >= maxY) {
            toY = maxY;
        } else if (toY <= minY) {
            toY = minY;
        }
        y = toY - scrollY;
        super.scrollBy(x, y);
    }

    @Override
    public void scrollTo(int x, int y) {
//        LogUtils.i("scrollTo===maxY==" + maxY + ";;;;" + y);
        if (y >= maxY) {
            y = maxY;
        } else if (y <= minY) {
            y = minY;
        }
        mCurY = y;
        if (onScrollListener != null) {
            onScrollListener.onScroll(y, maxY);
        }
        super.scrollTo(x, y);
    }

    private void initOrResetVelocityTracker() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        } else {
            mVelocityTracker.clear();
        }
    }

    private void initVelocityTrackerIfNotExists() {
        if (mVelocityTracker == null) {
            mVelocityTracker = VelocityTracker.obtain();
        }
    }

    private void recycleVelocityTracker() {
        if (mVelocityTracker != null) {
            mVelocityTracker.recycle();
            mVelocityTracker = null;
        }
    }

    private void checkIsClickHead(int downY, int headHeight, int scrollY) {
        isClickHead = downY + scrollY <= headHeight;
    }

    private void checkIsClickHeadExpand(int downY, int headHeight, int scrollY) {
        if (mExpandHeight <= 0) {
            isClickHeadExpand = false;
        }
        isClickHeadExpand = downY + scrollY <= headHeight + mExpandHeight;
    }

    private int calcDuration(int duration, int timepass) {
        return duration - timepass;
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        mHeadView = getChildAt(0);
        measureChildWithMargins(mHeadView, widthMeasureSpec, 0, MeasureSpec.UNSPECIFIED, 0);
        maxY = mHeadView.getMeasuredHeight() - maxGap;
        mHeadHeight = mHeadView.getMeasuredHeight();
        super.onMeasure(widthMeasureSpec, MeasureSpec.makeMeasureSpec(MeasureSpec.getSize
                (heightMeasureSpec) + maxY, MeasureSpec.EXACTLY));
    }

    @Override
    protected void onFinishInflate() {
        if (mHeadView != null && !mHeadView.isClickable()) {
            mHeadView.setClickable(true);
        }
        int childCount = getChildCount();
        for (int i = 0; i < childCount; i++) {
            View childAt = getChildAt(i);
            if (childAt != null && childAt instanceof ViewPager) {
                childViewPager = (ViewPager) childAt;
            }
        }
        super.onFinishInflate();
    }

    public DIRECTION getmDirection() {
        return mDirection;
    }

    public void setmDirection(DIRECTION mDirection) {
        this.mDirection = mDirection;
    }
public class ScrollableHelper {
    private static final String TAG = "ScrollableHelper";

    private ScrollableContainer mCurrentScrollableCainer;
//    public int viewTopMargin = 0;

    /**
     * a viewgroup whitch contains ScrollView/ListView/RecycelerView..
     */
    public interface ScrollableContainer {
        public int viewTopMargin = 0;

        /**
         * @return ScrollView/ListView/RecycelerView..'s instance
         */
        View getScrollableView();

    }

    public void setCurrentScrollableContainer(ScrollableContainer scrollableContainer) {
        this.mCurrentScrollableCainer = scrollableContainer;
    }

    private View getScrollableView() {
        if (mCurrentScrollableCainer == null) {
            return null;
        }
        return mCurrentScrollableCainer.getScrollableView();
    }

    /**
     * 判斷是否滑動到頂部方法,ScrollAbleLayout根據(jù)此方法來做一些邏輯判斷
     * 目前只實現(xiàn)了AdapterView,ScrollView,RecyclerView
     * 需要支持其他view可以自行補充實現(xiàn)
     *
     * @return
     */
    public boolean isTop() {
        View scrollableView = getScrollableView();
        if (scrollableView == null) {
//            throw new NullPointerException("You should call ScrollableHelper
// .setCurrentScrollableContainer() to set ScrollableContainer.");
            return true;
        }
        if (scrollableView instanceof AdapterView) {
            return isAdapterViewTop((AdapterView) scrollableView);
        }
        if (scrollableView instanceof ScrollView) {
            return isScrollViewTop((ScrollView) scrollableView);
        }
        if (scrollableView instanceof RecyclerView) {
            return isRecyclerViewTop((RecyclerView) scrollableView);
        }
        if (scrollableView instanceof WebView) {
            return isWebViewTop((WebView) scrollableView);
        }
        throw new IllegalStateException("scrollableView must be a instance of " +
                "AdapterView|ScrollView|RecyclerView");
    }

    private static boolean isRecyclerViewTop(RecyclerView recyclerView) {
        if (recyclerView != null) {
            RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager();
            if (layoutManager instanceof LinearLayoutManager) {
                int firstVisibleItemPosition = ((LinearLayoutManager) layoutManager)
                        .findFirstVisibleItemPosition();
                View childAt = recyclerView.getChildAt(0);
                if (null != childAt) {
//                    LogUtils.d("childAt.getTop()==" + childAt.getTop());
                }
                if (childAt == null || (firstVisibleItemPosition == 0 && childAt.getTop() > -1)) {
                    return true;
                }
            } else if (layoutManager instanceof StaggeredGridLayoutManager) {
                int[] firstVisibleItemPositions = ((StaggeredGridLayoutManager) layoutManager)
                        .findFirstVisibleItemPositions(new int[]{0, 1});
                int margin = 0;
//                try {
//                    BaseStaggeredGridAdapter adapter = (BaseStaggeredGridAdapter) recyclerView
//                            .getAdapter();
//                    if (null == adapter || 0 >= adapter.getItemCount()) {
//                        return true;
//                    }
//                    margin = adapter.getViewTop();
//                } catch (Exception e) {
//                    e.printStackTrace();
//                    LogUtil.e("StaggeredGridLayoutManager", e.getMessage());
//                    margin = 0;
//                }
                View childAt = recyclerView.getChildAt(firstVisibleItemPositions[0]);
                if (childAt != null) {
//                    LogUtils.d("StaggeredGridLayoutManager", "childAt.getTop()==" + childAt.getTop
//                            ());
                }

                if (childAt != null && (firstVisibleItemPositions[0] == 0 && childAt.getTop() ==
                        margin)) {
                    return true;
                }
            }
        }
        return false;
    }

    private static boolean isAdapterViewTop(AdapterView adapterView) {
        if (adapterView != null) {
            int firstVisiblePosition = adapterView.getFirstVisiblePosition();
            View childAt = adapterView.getChildAt(0);
            int margin = 0;
            if (null != childAt) {
//                if (adapterView instanceof StaggeredGridView) {
//                    margin = ((StaggeredGridView) adapterView).getmItemMargin();
//
//                } else {
//                    margin = childAt.getTop();
//                }
//                LogUtils.d("isAdapterViewTop", "childAt.getTop()==" + childAt.getTop() + "," +
//                        "margin==" + margin);

            }

            if (childAt == null || (firstVisiblePosition == 0 && childAt.getTop() == margin)) {
                return true;
            }
        }
        return false;
    }

    private static boolean isScrollViewTop(ScrollView scrollView) {
        if (scrollView != null) {
            int scrollViewY = scrollView.getScrollY();
//            LogUtils.d("isScrollViewTop", "scrollViewY===" + scrollViewY);
            return scrollViewY <= 0;
        }
        return false;
    }

    private static boolean isWebViewTop(WebView scrollView) {
        if (scrollView != null) {
            int scrollViewY = scrollView.getScrollY();
            return scrollViewY <= 0;
        }
        return false;
    }

    @SuppressLint("NewApi")
    public void smoothScrollBy(int velocityY, int distance, int duration) {
        View scrollableView = getScrollableView();
        if (scrollableView instanceof AbsListView) {
            AbsListView absListView = (AbsListView) scrollableView;
            if (Build.VERSION.SDK_INT >= 21) {
                absListView.fling(velocityY);
            } else {
                absListView.smoothScrollBy(distance, duration);
            }
        } else if (scrollableView instanceof ScrollView) {
            ((ScrollView) scrollableView).fling(velocityY);
        } else if (scrollableView instanceof RecyclerView) {
            ((RecyclerView) scrollableView).fling(0, velocityY);
        } else if (scrollableView instanceof WebView) {
            ((WebView) scrollableView).flingScroll(0, velocityY);
        }
    }

在第一層的ViewPager布局里面使用ScrollableLayout,這里不需要進行任何配置

1591954543(1).jpg

下部分ViewPager+Fragment的布局免猾,正常的一個列表疆瑰,其實實際情況需要放一個上拉加載的控件


1591954639(1).jpg

到這里基本就可以實現(xiàn)正常的顯示下部分Fragment的內(nèi)容了眉反,但是這個時候會發(fā)生上下滑動事件的沖突,需要在底部的Fragment里面繼承ScrollableHelper.ScrollableContainer穆役,實現(xiàn)getScrollableView方法寸五,返回當(dāng)前布局的RecyclerView

1591954912(1).jpg

在上一層的Fragment里面設(shè)置ScrollableLayout.getHelper().setCurrentScrollableContainer(fagment),此時的fragment為當(dāng)前顯示的fragment耿币,可以監(jiān)聽ViewPager的切換事件

viewPager.setOnPageChangeListener(new ViewPager.OnPageChangeListener() {
            @Override
            public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

            }

            @Override
            public void onPageSelected(int position) {
                OneFragment oneFragment1 = (OneFragment) mFragments.get(position);
                scrollableLayout.getHelper().setCurrentScrollableContainer(oneFragment1);
            }

            @Override
            public void onPageScrollStateChanged(int state) {

            }
        });

當(dāng)然給ViewPager 設(shè)置adapter的時候梳杏,需要設(shè)置默認(rèn)第一個Fragment

到此基本完成了適配,但是實際情況可能需要置頂中間部分的導(dǎo)航欄,實現(xiàn)下拉刷新和上拉加載十性,所以可以去監(jiān)聽scrollableLayout的滑動事件去判斷

scrollableLayout.setOnScrollListener(new ScrollableLayout.OnScrollListener() {
            @Override
            public void onScroll(int currentY, int maxY) {
                //1481
                int top = layoutTabCls.getTop();
                LogUtils.I("onScroll", "top:" + top + "---currentY:" + currentY + "---maxY:" + maxY);
//設(shè)置中部導(dǎo)航欄的顯示和隱藏叛溢,如果不理解這里,可以去另外搜索或者留言
                if (currentY >= top) {
                    layoutTabClsTop.setVisibility(View.VISIBLE);
                } else {
                    layoutTabClsTop.setVisibility(View.GONE);
                }
//當(dāng)scrollableLayout向上滑動時劲适,屏蔽刷新控件的下拉刷新楷掉,回到頂部是再打開,記得這個界面需要屏蔽上拉加載的事件
                if (currentY >0){
                    refreshLayout.setEnableRefresh(false);
                }else {
                    refreshLayout.setEnableRefresh(true);
                }
            }
        });

這樣基本適配了下拉刷新的操作减响,至于上拉加載靖诗,可以到具體的那個Fragment里面去實現(xiàn),記得屏蔽掉下拉刷新的事件支示,跟上一步配合使用刊橘,應(yīng)該能解決問題

333.gif
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市颂鸿,隨后出現(xiàn)的幾起案子促绵,更是在濱河造成了極大的恐慌,老刑警劉巖嘴纺,帶你破解...
    沈念sama閱讀 216,692評論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件败晴,死亡現(xiàn)場離奇詭異,居然都是意外死亡栽渴,警方通過查閱死者的電腦和手機尖坤,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,482評論 3 392
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來闲擦,“玉大人慢味,你說我怎么就攤上這事∈洌” “怎么了纯路?”我有些...
    開封第一講書人閱讀 162,995評論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀的道長寞忿。 經(jīng)常有香客問我驰唬,道長,這世上最難降的妖魔是什么腔彰? 我笑而不...
    開封第一講書人閱讀 58,223評論 1 292
  • 正文 為了忘掉前任叫编,我火速辦了婚禮,結(jié)果婚禮上霹抛,老公的妹妹穿的比我還像新娘宵溅。我一直安慰自己,他們只是感情好上炎,可當(dāng)我...
    茶點故事閱讀 67,245評論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般藕施。 火紅的嫁衣襯著肌膚如雪寇损。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,208評論 1 299
  • 那天裳食,我揣著相機與錄音矛市,去河邊找鬼。 笑死诲祸,一個胖子當(dāng)著我的面吹牛浊吏,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播救氯,決...
    沈念sama閱讀 40,091評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼找田,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了着憨?” 一聲冷哼從身側(cè)響起墩衙,我...
    開封第一講書人閱讀 38,929評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎甲抖,沒想到半個月后漆改,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,346評論 1 311
  • 正文 獨居荒郊野嶺守林人離奇死亡准谚,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,570評論 2 333
  • 正文 我和宋清朗相戀三年挫剑,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片柱衔。...
    茶點故事閱讀 39,739評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡樊破,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出秀存,到底是詐尸還是另有隱情捶码,我是刑警寧澤,帶...
    沈念sama閱讀 35,437評論 5 344
  • 正文 年R本政府宣布或链,位于F島的核電站惫恼,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏澳盐。R本人自食惡果不足惜祈纯,卻給世界環(huán)境...
    茶點故事閱讀 41,037評論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望叼耙。 院中可真熱鬧腕窥,春花似錦、人聲如沸筛婉。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,677評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至入蛆,卻和暖如春响蓉,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背哨毁。 一陣腳步聲響...
    開封第一講書人閱讀 32,833評論 1 269
  • 我被黑心中介騙來泰國打工枫甲, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人扼褪。 一個月前我還...
    沈念sama閱讀 47,760評論 2 369
  • 正文 我出身青樓想幻,卻偏偏與公主長得像,于是被迫代替她去往敵國和親话浇。 傳聞我的和親對象是個殘疾皇子脏毯,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,647評論 2 354