recyclerview-animators,讓你的RecyclerView與眾不同

RecyclerView已經(jīng)普及使用,其各式各樣的布局格式娄徊,以及眾多的優(yōu)越特性趟据,使得RecyclerView具有很大的靈活性。其中之一便是ItemAnimator埠对,通過自定義ItemAnimator可以實現(xiàn)各種各樣的Item增加络断,刪除,改變项玛,移動等動畫效果貌笨。這也是本篇文章的主要內(nèi)容。

一襟沮、recyclerview-animators

本篇文章將圍繞recyclerview-animators展開锥惋,先介紹其簡單使用,然后介紹其實現(xiàn)原理臣嚣。

首先净刮,介紹recyclerview-animators的效果圖。GitHub地址:recyclerview-animators

這個庫主要實現(xiàn)兩部分內(nèi)容硅则。

一:自定義ItemAnimator淹父,實現(xiàn)Item增加和刪除的動畫效果。

二:對Adapter進行封裝怎虫,在onBindViewHolder中暑认,綁定過程中設(shè)置顯示動畫困介。

效果圖

自定義ItemAnimator效果
Adapter動畫效果

二、recyclerview-animators的使用

ItemAnimator的使用

1.引入依賴
 compile 'jp.wasabeef:recyclerview-animators:2.2.6'
2.使用
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
recyclerView.setItemAnimator(new SlideInLeftAnimator());
3.高級功能

動畫時長

recyclerView.getItemAnimator().setAddDuration(1000);
recyclerView.getItemAnimator().setRemoveDuration(1000);
recyclerView.getItemAnimator().setMoveDuration(1000);
recyclerView.getItemAnimator().setChangeDuration(1000);

插值器

SlideInLeftAnimator animator = new SlideInLeftAnimator();
animator.setInterpolator(new OvershootInterpolator());
recyclerView.setItemAnimator(animator);

另外自定義動畫實現(xiàn)

static class MyViewHolder extends RecyclerView.ViewHolder implements AnimateViewHolder {
  public MyViewHolder(View itemView) {
    super(itemView);
  }

  @Override
  public void preAnimateRemoveImpl(RecyclerView.ViewHolder holder) {

  }

  @Override
  public void animateRemoveImpl(RecyclerView.ViewHolder holder, ViewPropertyAnimatorListener listener) {
    ViewCompat.animate(itemView)
          .translationY(-itemView.getHeight() * 0.3f)
          .alpha(0)
          .setDuration(300)
          .setListener(listener)
          .start();
  }

  @Override
  public void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
    ViewCompat.setTranslationY(itemView, -itemView.getHeight() * 0.3f);
    ViewCompat.setAlpha(itemView, 0);
  }

  @Override
  public void animateAddImpl(RecyclerView.ViewHolder holder, ViewPropertyAnimatorListener listener) {
    ViewCompat.animate(itemView)
          .translationY(0)
          .alpha(1)
          .setDuration(300)
          .setListener(listener)
          .start();
  }
}
4.注意

使用以下方式蘸际,才會觸發(fā)動畫效果座哩,具體下面分析:

notifyItemInserted(int)
notifyItemRemoved(int)
notifyItemRangeInserted(int, int)
notifyItemRangeRemoved(int, int)

例如:

public void remove(int position) {
  mDataSet.remove(position);
  notifyItemRemoved(position);
}

public void add(String text, int position) {
  mDataSet.add(position, text);
  notifyItemInserted(position);
}

Adapter的使用

1.使用
RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
MyAdapter adapter = new MyAdapter();
recyclerView.setAdapter(new AlphaInAnimationAdapter(adapter));
2.高級功能

動畫時長

MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
alphaAdapter.setDuration(1000);
recyclerView.setAdapter(alphaAdapter);

插值器

MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
alphaAdapter.setInterpolator(new OvershootInterpolator());
recyclerView.setAdapter(alphaAdapter);

是否僅顯示一次動畫效果

MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
scaleAdapter.setFirstOnly(false);
recyclerView.setAdapter(alphaAdapter);

復(fù)合動畫

MyAdapter adapter = new MyAdapter();
AlphaInAnimationAdapter alphaAdapter = new AlphaInAnimationAdapter(adapter);
recyclerView.setAdapter(new ScaleInAnimationAdapter(alphaAdapter));

三、自定義ItemAnimator實現(xiàn)原理

上面我們分析了recyclerview-animators的實現(xiàn)粮彤,那么我們?nèi)绾螌崿F(xiàn)自己想要的更炫酷的動畫方式呢根穷,以及上述注意中為什么只能使用那幾種方式才能實現(xiàn)動畫效果呢?這一切都需要理解其原理才行导坟,下面我們主要分析自定義動畫的實現(xiàn)屿良。

1.類結(jié)構(gòu)

首先,所有的自定義ItemAnimator都是繼承BaseItemAnimator實現(xiàn)的惫周。

public abstract class BaseItemAnimator extends SimpleItemAnimator 

而SimpleItemAnimator 又是什么尘惧? 是RecyclerView中根據(jù)ItemAnimator進行的一些簡單的封裝。

abstract public class SimpleItemAnimator extends RecyclerView.ItemAnimator

RecyclerView有著默認(rèn)的動畫效果實現(xiàn)DefaultItemAnimator递递,也繼承了SimpleItemAnimator 喷橙,后面具體比較分析。

一切的源頭都是ItemAnimator登舞,所以先來了解下它的主要方法贰逾。

2.ItemAnimator

當(dāng)RecyclerView中的item在屏幕上由可見變?yōu)椴豢梢姇r調(diào)用此方法

 public abstract boolean animateDisappearance(@NonNull ViewHolder viewHolder,
                @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo);

當(dāng)RecyclerView中的item顯示到屏幕上時調(diào)用此方法

public abstract boolean animateAppearance(@NonNull ViewHolder viewHolder,
                @Nullable ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);

當(dāng)RecyclerView中的item狀態(tài)發(fā)生改變時調(diào)用此方法(notifyItemChanged(position))

public abstract boolean animateChange(@NonNull ViewHolder oldHolder,
                @NonNull ViewHolder newHolder,
                @NonNull ItemHolderInfo preLayoutInfo, @NonNull ItemHolderInfo postLayoutInfo);

統(tǒng)籌RecyclerView中所有的動畫,統(tǒng)一啟動執(zhí)行

abstract public void runPendingAnimations();

這幾個方法是自定義ItemAnimator的關(guān)鍵逊躁,實現(xiàn)不同的動畫效果似踱。

SimpleItemAnimator 在ItemAnimator的基礎(chǔ)上,針對上述幾個方法稽煤,進行了一定的封裝核芽,使得可以更專注于動畫的實現(xiàn)。

3.SimpleItemAnimator

SimpleItemAnimator對這幾個方法是如何封裝的呢酵熙?
以添加一個Item舉例轧简,最后調(diào)用的是animateAppearance方法,SimpleItemAnimator的animateAppearance方法代碼如下:

public boolean animateDisappearance(@NonNull ViewHolder viewHolder,
            @NonNull ItemHolderInfo preLayoutInfo, @Nullable ItemHolderInfo postLayoutInfo) {
        int oldLeft = preLayoutInfo.left;
        int oldTop = preLayoutInfo.top;
        View disappearingItemView = viewHolder.itemView;
        int newLeft = postLayoutInfo == null ? disappearingItemView.getLeft() : postLayoutInfo.left;
        int newTop = postLayoutInfo == null ? disappearingItemView.getTop() : postLayoutInfo.top;
        if (!viewHolder.isRemoved() && (oldLeft != newLeft || oldTop != newTop)) {
            disappearingItemView.layout(newLeft, newTop,
                    newLeft + disappearingItemView.getWidth(),
                    newTop + disappearingItemView.getHeight());
            if (DEBUG) {
                Log.d(TAG, "DISAPPEARING: " + viewHolder + " with view " + disappearingItemView);
            }
            return animateMove(viewHolder, oldLeft, oldTop, newLeft, newTop);
        } else {
            if (DEBUG) {
                Log.d(TAG, "REMOVED: " + viewHolder + " with view " + disappearingItemView);
            }
            return animateRemove(viewHolder);
        }
    }

其實封裝的很簡單匾二,只是判斷了一下布局前后兩個ViewHolder的left/top坐標(biāo)是否相等 如果不相等則調(diào)用animateMove哮独,否則調(diào)用animateAdd(viewHolder)方法。

其他方法類似察藐,最后我們只需要實現(xiàn)animateRemove, animateAdd, animateMove, animateChange 這4個方法皮璧。

下面我們先看看RecyclerView中DefaultItemAnimator的實現(xiàn),再來分析BaseItemAnimator分飞。

4.DefaultItemAnimator

首先是對animateRemove, animateAdd, animateMove, animateChange 這4個方法的實現(xiàn)悴务。
還是以animateAdd為例:

 public boolean animateAdd(final ViewHolder holder) {
        resetAnimation(holder);
        ViewCompat.setAlpha(holder.itemView, 0);
        mPendingAdditions.add(holder);
        return true;
    }

首先調(diào)用resetAnimation(holder)停止此holder中itemView的動畫效果,然后將holder.itemView的透明度設(shè)置為0不可見狀態(tài)(也可以設(shè)置其他屬性,相當(dāng)于動畫開始的初始效果)讯檐。
將holder添加到mPendingAdditions集合中羡疗。

注意:此集合一會兒會在runPendingAnimations方法中進行遍歷執(zhí)行動畫。

然后是對runPendingAnimations的實現(xiàn)别洪。
下面上源碼叨恨,很長,不過別害怕挖垛,很簡單痒钝。

public void runPendingAnimations() {
        boolean removalsPending = !mPendingRemovals.isEmpty();
        boolean movesPending = !mPendingMoves.isEmpty();
        boolean changesPending = !mPendingChanges.isEmpty();
        boolean additionsPending = !mPendingAdditions.isEmpty();
        if (!removalsPending && !movesPending && !additionsPending && !changesPending) {
            // nothing to animate
            return;
        }
        // First, remove stuff
        for (ViewHolder holder : mPendingRemovals) {
            animateRemoveImpl(holder);
        }
        mPendingRemovals.clear();
        // Next, move stuff
        if (movesPending) {
            final ArrayList<MoveInfo> moves = new ArrayList<>();
            moves.addAll(mPendingMoves);
            mMovesList.add(moves);
            mPendingMoves.clear();
            Runnable mover = new Runnable() {
                @Override
                public void run() {
                    for (MoveInfo moveInfo : moves) {
                        animateMoveImpl(moveInfo.holder, moveInfo.fromX, moveInfo.fromY,
                                moveInfo.toX, moveInfo.toY);
                    }
                    moves.clear();
                    mMovesList.remove(moves);
                }
            };
            if (removalsPending) {
                View view = moves.get(0).holder.itemView;
                ViewCompat.postOnAnimationDelayed(view, mover, getRemoveDuration());
            } else {
                mover.run();
            }
        }
        // Next, change stuff, to run in parallel with move animations
        if (changesPending) {
            final ArrayList<ChangeInfo> changes = new ArrayList<>();
            changes.addAll(mPendingChanges);
            mChangesList.add(changes);
            mPendingChanges.clear();
            Runnable changer = new Runnable() {
                @Override
                public void run() {
                    for (ChangeInfo change : changes) {
                        animateChangeImpl(change);
                    }
                    changes.clear();
                    mChangesList.remove(changes);
                }
            };
            if (removalsPending) {
                ViewHolder holder = changes.get(0).oldHolder;
                ViewCompat.postOnAnimationDelayed(holder.itemView, changer, getRemoveDuration());
            } else {
                changer.run();
            }
        }
        // Next, add stuff
        if (additionsPending) {
            final ArrayList<ViewHolder> additions = new ArrayList<>();
            additions.addAll(mPendingAdditions);
            mAdditionsList.add(additions);
            mPendingAdditions.clear();
            Runnable adder = new Runnable() {
                @Override
                public void run() {
                    for (ViewHolder holder : additions) {
                        animateAddImpl(holder);
                    }
                    additions.clear();
                    mAdditionsList.remove(additions);
                }
            };
            if (removalsPending || movesPending || changesPending) {
                long removeDuration = removalsPending ? getRemoveDuration() : 0;
                long moveDuration = movesPending ? getMoveDuration() : 0;
                long changeDuration = changesPending ? getChangeDuration() : 0;
                long totalDelay = removeDuration + Math.max(moveDuration, changeDuration);
                View view = additions.get(0).itemView;
                ViewCompat.postOnAnimationDelayed(view, adder, totalDelay);
            } else {
                adder.run();
            }
        }
    }

remove最先執(zhí)行,for循環(huán)遍歷mPendingRemovals集合中所有的ViewHolder晕换。remove完成后午乓,再同時開始move和change動畫,最后執(zhí)行add動畫闸准。

其中,需要注意幾個重要的方法:
animateRemoveImpl(holder);
animateMoveImpl(moveInfo.holder,moveInfo.fromX, moveInfo.fromY,moveInfo.toX, moveInfo.toY);
animateChangeImpl(change);
animateAddImpl(holder);

這幾個方法執(zhí)行具體的動畫效果梢灭,具體來看源碼夷家。

下面來分析下BaseItemAnimator的實現(xiàn)。

5.BaseItemAnimator

了解了DefaultItemAnimator敏释,那么對BaseItemAnimator的實現(xiàn)就清楚了库快。

首先BaseItemAnimator使用了DefaultItemAnimator中的runPendingAnimations()方法的實現(xiàn)。
其次BaseItemAnimator使用了DefaultItemAnimator中的animateMove, animateChange钥顽,以及animateRemoveImpl义屏,animateChangeImpl等方法的實現(xiàn)。

所以BaseItemAnimator只留下了animateRemove, animateAdd方法蜂大,方便我們自定義實現(xiàn)闽铐。

以animateAdd方法為例:

 @Override public boolean animateAdd(final ViewHolder holder) {
    endAnimation(holder);
    preAnimateAdd(holder);
    mPendingAdditions.add(holder);
    return true;
  }

第一步:停止此holder中itemView的動畫效果,與DefaultItemAnimator一致奶浦。

第二步:初始化itemView的屬性

第三步:將holder添加到mPendingAdditions集合中兄墅。

與DefaultItemAnimator的不同主要是第二步,詳細看看澳叉。

private void preAnimateAdd(final ViewHolder holder) {
    ViewHelper.clear(holder.itemView);

    if (holder instanceof AnimateViewHolder) {
      ((AnimateViewHolder) holder).preAnimateAddImpl(holder);
    } else {
      preAnimateAddImpl(holder);
    }
  }

如果holder屬于AnimateViewHolder類或子類隙咸,那么就調(diào)用其preAnimateAddImpl()方法,否則調(diào)用內(nèi)部的preAnimateAddImpl()方法成洗。

protected void preAnimateAddImpl(final ViewHolder holder) {
  }

該方法空實現(xiàn)五督,由子類負(fù)責(zé)實現(xiàn),初始化定義各種動畫初始屬性瓶殃。

關(guān)于AnimateViewHolder類充包,前面說過,可以通過自定義實現(xiàn)該接口碌燕,實現(xiàn)各種動畫效果來代替具體的ItemAnimator误证,其內(nèi)部需要實現(xiàn)的方法继薛,與繼承BaseItemAnimator的子類需要實現(xiàn)的方法一樣。

除此之外愈捅,針對add遏考,在runPendingAnimations()方法中,需要指定具體的動畫效果蓝谨。

截取runPendingAnimations()方法中的add部分灌具。

if (additionsPending) {
      final ArrayList<ViewHolder> additions = new ArrayList<ViewHolder>();
      additions.addAll(mPendingAdditions);
      mAdditionsList.add(additions);
      mPendingAdditions.clear();
      Runnable adder = new Runnable() {
        public void run() {
          boolean removed = mAdditionsList.remove(additions);
          if (!removed) {
            // already canceled
            return;
          }
          for (ViewHolder holder : additions) {
            doAnimateAdd(holder);
          }
          additions.clear();
        }
      };

其中重要的一行就是:doAnimateAdd(holder);

private void doAnimateAdd(final ViewHolder holder) {
    if (holder instanceof AnimateViewHolder) {
      ((AnimateViewHolder) holder).animateAddImpl(holder, new DefaultAddVpaListener(holder));
    } else {
      animateAddImpl(holder);
    }

    mAddAnimations.add(holder);
  }

和上面差不多譬巫,其中animateAddImpl()方法空實現(xiàn)咖楣,需要子類實現(xiàn)。

那么子類需要實現(xiàn)四個方法芦昔,就能完成自定義動畫了诱贿,是不是很簡單?

6.具體實例

下面舉一個實現(xiàn)具體動畫的例子:

public class FadeInAnimator extends BaseItemAnimator {

  public FadeInAnimator() {
  }

  public FadeInAnimator(Interpolator interpolator) {
    mInterpolator = interpolator;
  }

  @Override protected void animateRemoveImpl(final RecyclerView.ViewHolder holder) {
    ViewCompat.animate(holder.itemView)
        .alpha(0)
        .setDuration(getRemoveDuration())
        .setInterpolator(mInterpolator)
        .setListener(new DefaultRemoveVpaListener(holder))
        .setStartDelay(getRemoveDelay(holder))
        .start();
  }

  @Override protected void preAnimateAddImpl(RecyclerView.ViewHolder holder) {
    ViewCompat.setAlpha(holder.itemView, 0);
  }

  @Override protected void animateAddImpl(final RecyclerView.ViewHolder holder) {
    ViewCompat.animate(holder.itemView)
        .alpha(1)
        .setDuration(getAddDuration())
        .setInterpolator(mInterpolator)
        .setListener(new DefaultAddVpaListener(holder))
        .setStartDelay(getAddDelay(holder))
        .start();
  }
}

很簡單吧咕缎,這樣我們就可以任意自定義各種增刪item的效果了珠十。

四、Adapter動畫效果實現(xiàn)原理

主要是用了裝飾者模式凭豪,對原來的adapter進行了一層封裝焙蹭,增加了額外的功能。

RecyclerView recyclerView = (RecyclerView) findViewById(R.id.list);
MyAdapter adapter = new MyAdapter();
recyclerView.setAdapter(new AlphaInAnimationAdapter(adapter));

把原始的adapter傳入了具有動畫效果的新的Adpter中嫂伞,那么新的Adpter內(nèi)部是怎么實現(xiàn)的孔厉?
以AlphaInAnimationAdapter為例:

public class AlphaInAnimationAdapter extends AnimationAdapter {

  private static final float DEFAULT_ALPHA_FROM = 0f;
  private final float mFrom;

  public AlphaInAnimationAdapter(RecyclerView.Adapter adapter) {
    this(adapter, DEFAULT_ALPHA_FROM);
  }

  public AlphaInAnimationAdapter(RecyclerView.Adapter adapter, float from) {
    super(adapter);
    mFrom = from;
  }

  @Override protected Animator[] getAnimators(View view) {
    return new Animator[] { ObjectAnimator.ofFloat(view, "alpha", mFrom, 1f) };
  }
}

可見其內(nèi)部主要提供了getAnimators方法,定義動畫效果帖努,其他方法來自于AnimationAdapter撰豺。

AnimationAdapter中主要是通過onBindViewHolder來增加動畫效果

 @Override public void onBindViewHolder(RecyclerView.ViewHolder holder, int position) {
    mAdapter.onBindViewHolder(holder, position);

    int adapterPosition = holder.getAdapterPosition();
    if (!isFirstOnly || adapterPosition > mLastPosition) {
      for (Animator anim : getAnimators(holder.itemView)) {
        anim.setDuration(mDuration).start();
        anim.setInterpolator(mInterpolator);
      }
      mLastPosition = adapterPosition;
    } else {
      ViewHelper.clear(holder.itemView);
    }
  }

循環(huán)遍歷每個item,增加動畫效果然磷。

五郑趁、總結(jié)

通過上面的分析,對recyclerview-animators庫有了一個深刻的了解姿搜,基于此基礎(chǔ)可以實現(xiàn)各種炫酷的動畫效果寡润。

參考文章:

深入理解 RecyclerView 系列之二:ItemAnimator
RecyclerView.ItemAnimator終極解讀(一)--RecyclerView源碼解析
RecyclerView.ItemAnimator終極解讀(二)--SimpleItemAnimator和DefaultItemAnimator源碼解析
RecyclerView.ItemAnimator終極解讀(三)--繼承DefaultItemAnimator實現(xiàn)自定義動畫

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市舅柜,隨后出現(xiàn)的幾起案子梭纹,更是在濱河造成了極大的恐慌,老刑警劉巖致份,帶你破解...
    沈念sama閱讀 216,402評論 6 499
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件变抽,死亡現(xiàn)場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機绍载,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,377評論 3 392
  • 文/潘曉璐 我一進店門诡宗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人击儡,你說我怎么就攤上這事塔沃。” “怎么了阳谍?”我有些...
    開封第一講書人閱讀 162,483評論 0 353
  • 文/不壞的土叔 我叫張陵蛀柴,是天一觀的道長。 經(jīng)常有香客問我矫夯,道長鸽疾,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,165評論 1 292
  • 正文 為了忘掉前任训貌,我火速辦了婚禮制肮,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘递沪。我一直安慰自己弄企,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,176評論 6 388
  • 文/花漫 我一把揭開白布区拳。 她就那樣靜靜地躺著,像睡著了一般意乓。 火紅的嫁衣襯著肌膚如雪樱调。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,146評論 1 297
  • 那天届良,我揣著相機與錄音笆凌,去河邊找鬼。 笑死士葫,一個胖子當(dāng)著我的面吹牛乞而,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播慢显,決...
    沈念sama閱讀 40,032評論 3 417
  • 文/蒼蘭香墨 我猛地睜開眼爪模,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了荚藻?” 一聲冷哼從身側(cè)響起屋灌,我...
    開封第一講書人閱讀 38,896評論 0 274
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎应狱,沒想到半個月后共郭,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,311評論 1 310
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,536評論 2 332
  • 正文 我和宋清朗相戀三年除嘹,在試婚紗的時候發(fā)現(xiàn)自己被綠了写半。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,696評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡尉咕,死狀恐怖叠蝇,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情龙考,我是刑警寧澤蟆肆,帶...
    沈念sama閱讀 35,413評論 5 343
  • 正文 年R本政府宣布,位于F島的核電站晦款,受9級特大地震影響炎功,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜缓溅,卻給世界環(huán)境...
    茶點故事閱讀 41,008評論 3 325
  • 文/蒙蒙 一蛇损、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧坛怪,春花似錦淤齐、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,659評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至居灯,卻和暖如春祭务,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背怪嫌。 一陣腳步聲響...
    開封第一講書人閱讀 32,815評論 1 269
  • 我被黑心中介騙來泰國打工义锥, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人岩灭。 一個月前我還...
    沈念sama閱讀 47,698評論 2 368
  • 正文 我出身青樓拌倍,卻偏偏與公主長得像,于是被迫代替她去往敵國和親噪径。 傳聞我的和親對象是個殘疾皇子柱恤,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,592評論 2 353

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

  • 內(nèi)容抽屜菜單ListViewWebViewSwitchButton按鈕點贊按鈕進度條TabLayout圖標(biāo)下拉刷新...
    皇小弟閱讀 46,754評論 22 665
  • 這篇文章分三個部分,簡單跟大家講一下 RecyclerView 的常用方法與奇葩用法熄云;工作原理與ListView比...
    LucasAdam閱讀 4,388評論 0 27
  • 簡介: 提供一個讓有限的窗口變成一個大數(shù)據(jù)集的靈活視圖膨更。 術(shù)語表: Adapter:RecyclerView的子類...
    酷泡泡閱讀 5,163評論 0 16
  • 經(jīng)常會聽到有人說,沒辦法我喝水都胖缴允!盡管吃的很少荚守,體重也不會下降珍德,穿不了漂亮的衣服,沒有自信矗漾,氣質(zhì)不佳锈候,節(jié)食、運動...
    卓雅養(yǎng)生閱讀 400評論 0 0
  • 人生充滿了太多的可能性敞贡,每一秒發(fā)生的事情都是不可預(yù)測的泵琳,生活教會了我有所收獲,理解不了就不要亂說誊役,你可以關(guān)閉自己的...
    我心寫閱讀 174評論 0 0