最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

VerticalBannerView仿淘寶頭條實現(xiàn)垂直輪播廣告

 更新時間:2019年08月22日 09:08:36   作者:ruancoder  
這篇文章主要為大家詳細(xì)介紹了VerticalBannerView仿淘寶頭條實現(xiàn)垂直輪播廣告,具有一定的參考價值,感興趣的小伙伴們可以參考一下

VerticalBannerView是一個仿淘寶APP首頁輪播頭條的自定義控件。

特性:

1.可自由定義展示的內(nèi)容。
2.使用方式類似ListView/RecyclerView。
3.可為當(dāng)前顯示的內(nèi)容添加各種事件,比如點(diǎn)擊打開某個頁面等。

VerticalBannerView開源項目地址

運(yùn)行效果圖:


一、項目使用

(1).添加項目依賴。

dependencies {
  compile 'com.github.Rowandjj:VerticalBannerView:1.0'
}

(2).添加布局。

<LinearLayout
  android:layout_width="match_parent"
  android:layout_height="wrap_content"
  android:gravity="center_vertical"
  android:orientation="horizontal">
 
  <TextView
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:paddingLeft="5dp"
    android:text="淘寶頭條"
    android:textStyle="bold"/>
 
  <View
    android:layout_width="1dp"
    android:layout_height="40dp"
    android:layout_marginLeft="5dp"
    android:layout_marginRight="5dp"
    android:background="#cccccc"/>
 
  <com.taobao.library.VerticalBannerView
    xmlns:app="http://schemas.android.com/apk/res-auto"
    android:id="@+id/banner"
    android:layout_width="wrap_content"
    android:layout_height="36dp"
    app:animDuration="900"
    app:gap="2000"/>
</LinearLayout>

(3).實現(xiàn)Adapter。

public class SampleAdapter extends BaseBannerAdapter<Model> {
  private List<Model> mDatas;
 
  public SampleAdapter01(List<Model> datas) {
    super(datas);
  }
 
  @Override
  public View getView(VerticalBannerView parent) {
    return LayoutInflater.from(parent.getContext()).inflate(R.layout.your_item,null);
  }
 
  @Override
  public void setItem(final View view, final Model data) {
    TextView textView = (TextView) view.findViewById(R.id.text);
    textView.setText(data.title);
    // 你可以增加點(diǎn)擊事件
    view.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        // TODO handle click event
      }
    });
  }
}

(4).為VerticalBannerView設(shè)置Adapter,并啟動動畫。

List<Model> datas = new ArrayList<>();
datas.add(new Model01("Note7發(fā)布了"));
datas.add(new Model01("Note7被召回了"));
SampleAdapter adapter = new SampleAdapter(datas);
VerticalBannerView banner = (VerticalBannerView) findViewById(R.id.banner);
banner.setAdapter(adapter);
banner.start();

二、源碼分析

實現(xiàn)原理:

VerticalBannerView本質(zhì)上是一個垂直的LinearLayout。定義一個Adapter類,向LinearLayout提供子View。初始狀態(tài)下往LinearLayout中添加兩個子View,子View的高度同LinearLayout的高度一致,這樣一來只有第1個子View顯示出來,第2個子View在底部不顯示。然后使用屬性動畫ObjectAnimator同時修改兩個子View的translationY屬性,動畫執(zhí)行過程中translationY從默認(rèn)值0漸變到負(fù)的LinearLayout的高度,顯示出來的效果就是第1個子View逐漸向上退出,第2個子View從底部向上逐漸顯示。動畫執(zhí)行完畢后,移除第1個子View,這樣第2個子View的索引變成0,并完全顯示出來占據(jù)LinearLayout的高度。再將已經(jīng)移除的第1個子View,添加到索引為1的位置,此時該子View超出父視圖之外完全不顯示。一輪動畫執(zhí)行完畢,再調(diào)用postDelay()方法重復(fù)上述動畫,一直循環(huán)下去。

下面進(jìn)入代碼部分,主要是兩個類BaseBannerAdapter和VerticalBannerView。

(1).BaseBannerAdapter類

BaseBannerAdapter類負(fù)責(zé)為廣告欄提供數(shù)據(jù)。我們在使用時,需要寫一個Adapter類繼承BaseBannerAdapter,實現(xiàn)getView()和setItem()方法。在getView()方法中,我們需要把要添加到廣告欄中的item view創(chuàng)建出來并返回,setItem()方法則負(fù)責(zé)為創(chuàng)建的item view綁定數(shù)據(jù)。

public abstract class BaseBannerAdapter<T> {
  private List<T> mDatas;
  private OnDataChangedListener mOnDataChangedListener;
 
  public BaseBannerAdapter(List<T> datas) {
    mDatas = datas;
    if (datas == null || datas.isEmpty()) {
      throw new RuntimeException("nothing to show");
    }
  }
 
  public BaseBannerAdapter(T[] datas) {
    mDatas = new ArrayList<>(Arrays.asList(datas));
  }
 
  // 設(shè)置banner填充的數(shù)據(jù)
  public void setData(List<T> datas) {
    this.mDatas = datas;
    notifyDataChanged();
  }
 
  void setOnDataChangedListener(OnDataChangedListener listener) {
    mOnDataChangedListener = listener;
  }
 
  // 獲取banner總數(shù)
  public int getCount() {
    return mDatas == null ? 0 : mDatas.size();
  }
 
  // 通知數(shù)據(jù)改變
  void notifyDataChanged() {
    mOnDataChangedListener.onChanged();
  }
 
  // 獲取數(shù)據(jù)
  public T getItem(int position) {
    return mDatas.get(position);
  }
 
  // 設(shè)置banner的ItemView
  public abstract View getView(VerticalBannerView parent);
 
  // 設(shè)置banner的數(shù)據(jù)
  public abstract void setItem(View view, T data);
 
  // 數(shù)據(jù)變化的監(jiān)聽
  interface OnDataChangedListener {
    void onChanged();
  }
}

(2).VerticalBannerView類

VerticalBannerView類繼承自LinearLayout,并在構(gòu)造方法中設(shè)定方向為垂直。同時VerticalBannerView類實現(xiàn)了OnDataChangedListener接口,實現(xiàn)onChanged()方法,這樣當(dāng)改變數(shù)據(jù)后調(diào)用BaseBannerAdapter的notifyDataChanged()時,VerticalBannerView的onChanged()方法被回調(diào),執(zhí)行setupAdapter()重新初始化數(shù)據(jù)。

public class VerticalBannerView extends LinearLayout implements BaseBannerAdapter.OnDataChangedListener {
  public VerticalBannerView(Context context, AttributeSet attrs, int defStyleAttr) {
    super(context, attrs, defStyleAttr);
    init(context, attrs, defStyleAttr);
  }
 
  private void init(Context context, AttributeSet attrs, int defStyleAttr) {
    setOrientation(VERTICAL);
 ......
  }
 
  ......
 
  @Override
  public void onChanged() {
    setupAdapter();
  }
 
  ......
}

為VerticalBannerView添加item數(shù)據(jù),需要調(diào)用setAdapter()方法,關(guān)鍵在于其中執(zhí)行的setupAdapter()方法。

public void setAdapter(BaseBannerAdapter adapter) {
  if (adapter == null) {
    throw new RuntimeException("adapter must not be null");
  }
  if (mAdapter != null) {
    throw new RuntimeException("you have already set an Adapter");
  }
  this.mAdapter = adapter;
  mAdapter.setOnDataChangedListener(this);
  setupAdapter();
}

在setupAdapter()方法中,先移除所有的子View,然后調(diào)用Adapter的getView()方法創(chuàng)建兩個子View,分別賦值給成員變量mFirstView和mSecondView,并為這兩個子View綁定數(shù)據(jù),最后再調(diào)用addView()添加進(jìn)來。

// 初始化Child View
private void setupAdapter() {
  // 先移除所有的子View
  removeAllViews();
 
  if (mAdapter.getCount() == 1) {
    mFirstView = mAdapter.getView(this);
    mAdapter.setItem(mFirstView, mAdapter.getItem(0));
    addView(mFirstView);
  } else {
    // 調(diào)用Adapter的getView()方法,創(chuàng)建兩個子View,分別賦值給mFirstView和mSecondView
    mFirstView = mAdapter.getView(this);
    mSecondView = mAdapter.getView(this);
    // 使用索引0和1的數(shù)據(jù),為mFirstView和mSecondView設(shè)置數(shù)據(jù)
    mAdapter.setItem(mFirstView, mAdapter.getItem(0));
    mAdapter.setItem(mSecondView, mAdapter.getItem(1));
    // 將mFirstView和mSecondView添加到當(dāng)前View
    addView(mFirstView);
    addView(mSecondView);
 
    mPosition = 1;
    isStarted = false;
  }
  setBackgroundDrawable(mFirstView.getBackground());
}

為了實現(xiàn)子View之間的切換,需要把上面添加進(jìn)來的子View的高度修改為與VerticalBannerView高度一致。這個操作在onMeasure()方法中完成。

@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec, heightMeasureSpec);
  // 成員變量mBannerHeight有一個默認(rèn)值
  if (LayoutParams.WRAP_CONTENT == getLayoutParams().height) {
    // 如果當(dāng)前View的高度設(shè)置為wrap_content,使用默認(rèn)值
    getLayoutParams().height = (int) mBannerHeight;
  } else {
    // 如果當(dāng)前View設(shè)定了固定高度,則使用設(shè)定的高度
    mBannerHeight = getHeight();
  }
  // 修改mFirstView和mSecondView的高度為其父視圖的高度
  if (mFirstView != null) {
    mFirstView.getLayoutParams().height = (int) mBannerHeight;
  }
  if (mSecondView != null) {
    mSecondView.getLayoutParams().height = (int) mBannerHeight;
  }
}

上面的準(zhǔn)備工作完成后,就可以進(jìn)入動畫的執(zhí)行了。VerticalBannerView通過調(diào)用start()方法啟動切換動畫。在start()方法中,調(diào)用postDelayed()執(zhí)行AnimRunnable任務(wù),在AnimRunnable的run()方法中,先調(diào)用performSwitch(),然后再次調(diào)用postDelayed()使AnimRunnable任務(wù)一直循環(huán)執(zhí)行下去。兩個子View之間的切換工作由performSwitch()負(fù)責(zé)執(zhí)行。

public void start() {
  if (mAdapter == null) {
    throw new RuntimeException("you must call setAdapter() before start");
  }
 
  if (!isStarted && mAdapter.getCount() > 1) {
    isStarted = true;
    postDelayed(mRunnable, mGap);
  }
}
 
private AnimRunnable mRunnable = new AnimRunnable();
 
private class AnimRunnable implements Runnable {
  @Override
  public void run() {
    performSwitch();
    // 調(diào)用postDelayed()延時再執(zhí)行,一直循環(huán)下去
    postDelayed(this, mGap);
  }
}

下面再進(jìn)入performSwitch()方法,該方法是Item View之間產(chǎn)生切換效果的核心。

// 執(zhí)行切換
private void performSwitch() {
  // 動畫在執(zhí)行過程中,View的translationY屬性從0一直減小到-mBannerHeight
  // 因為translationY屬性一直是負(fù)值,所以View向上移動
  ObjectAnimator animator1 = ObjectAnimator.ofFloat(mFirstView, "translationY", -mBannerHeight);
  ObjectAnimator animator2 = ObjectAnimator.ofFloat(mSecondView, "translationY", -mBannerHeight);
  AnimatorSet set = new AnimatorSet();
  set.playTogether(animator1, animator2);
  set.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
      // 動畫執(zhí)行完成,把mFirstView和mSecondView的translationY恢復(fù)到默認(rèn)狀態(tài)
      mFirstView.setTranslationY(0);
      mSecondView.setTranslationY(0);
      // 使用下一條數(shù)據(jù),設(shè)置到第一個子View
      View removedView = getChildAt(0);
      mPosition++;
      mAdapter.setItem(removedView, mAdapter.getItem(mPosition % mAdapter.getCount()));
      // 移除第一個子View,此時當(dāng)前LinearLayout的childCount==1
      removeView(removedView);
      // 移除的View,再添加到第2個位置
      addView(removedView, 1);
    }
  });
  set.setDuration(mAnimDuration);
  set.start();
}

在performSwitch()方法中,使用屬性動畫ObjectAnimator同時修改兩個mFirstView和mSecondView的translationY屬性,動畫執(zhí)行中translationY從默認(rèn)值0一直減小到-mBannerHeight,在這個過程中mFirstView逐漸向上退出,mSecondView從底部逐漸顯現(xiàn)。動畫執(zhí)行完畢后,移除mFirstView,mSecondView變成第1個子View并完全顯示出來填充父視圖的高度。再將移除的mFirstView添加到第2個位置,此時mFirstView未顯示出來。由于performSwitch()方法一直循環(huán)被調(diào)用,mFirstView和mSecondView就這樣一直循環(huán)切換。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • Flutter 滾動監(jiān)聽及實戰(zhàn)appBar滾動漸變的實現(xiàn)

    Flutter 滾動監(jiān)聽及實戰(zhàn)appBar滾動漸變的實現(xiàn)

    這篇文章主要介紹了Flutter 滾動監(jiān)聽及實戰(zhàn)appBar滾動漸變,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-09-09
  • Android?自定義?Dialog?實現(xiàn)列表?單選、多選、搜索功能

    Android?自定義?Dialog?實現(xiàn)列表?單選、多選、搜索功能

    Android開發(fā)經(jīng)常需要用到對話框來進(jìn)行信息的篩選和搜索,本文詳細(xì)介紹了如何使用自定義Dialog結(jié)合RecyclerView和搜索框?qū)崿F(xiàn)這一功能,通過Builder模式構(gòu)建復(fù)雜的Dialog對象,使得代碼更加靈活和易于維護(hù),文中提供了詳細(xì)的步驟和代碼注釋
    2024-10-10
  • Android自定義漸變式炫酷ListView下拉刷新動畫

    Android自定義漸變式炫酷ListView下拉刷新動畫

    這篇文章主要為大家詳細(xì)介紹了Android自定義漸變式炫酷ListView下拉刷新動畫,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2017-02-02
  • Android 后臺調(diào)度任務(wù)與省電詳解

    Android 后臺調(diào)度任務(wù)與省電詳解

    本文主要介紹 Android 后臺調(diào)度任務(wù)與省電,這里整理了詳細(xì)的知識資料供大家學(xué)習(xí)參考,希望能幫助有需要的小伙伴
    2016-08-08
  • 詳解Flutter如何在單個屏幕上實現(xiàn)多個列表

    詳解Flutter如何在單個屏幕上實現(xiàn)多個列表

    這篇文章主要為大家詳細(xì)介紹了Flutter如何在單個屏幕上實現(xiàn)多個列表,這些列表可以水平排列、網(wǎng)格格式、垂直排列,甚至是這些常用布局的組合,感興趣的小伙伴可以了解下
    2023-11-11
  • Android實現(xiàn)快遞物流跟蹤布局效果

    Android實現(xiàn)快遞物流跟蹤布局效果

    本篇文章主要介紹了Android實現(xiàn)快遞跟蹤布局效果,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-05-05
  • Android自定義圓點(diǎn)指示器

    Android自定義圓點(diǎn)指示器

    這篇文章主要為大家詳細(xì)介紹了Android自定義圓點(diǎn)指示器,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2021-06-06
  • Android將Xamarin For VS升級為4.1.0.530版教程

    Android將Xamarin For VS升級為4.1.0.530版教程

    這篇文章主要介紹了Android將Xamarin For VS升級為4.1.0.530版的圖文教程,感興趣的小伙伴們可以參考一下
    2016-06-06
  • Android pdf viewer在android studio應(yīng)用問題說明詳解

    Android pdf viewer在android studio應(yīng)用問題說明詳解

    這篇文章主要介紹了Android pdf viewer在android studio應(yīng)用問題說明的相關(guān)資料,本文介紹的非常詳細(xì),具有參考借鑒價值,需要的朋友可以參考下
    2016-09-09
  • Android實現(xiàn)井字游戲

    Android實現(xiàn)井字游戲

    這篇文章主要為大家詳細(xì)介紹了Android實現(xiàn)井字游戲,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2022-01-01

最新評論

彭山县| 永平县| 车险| 云南省| 聂拉木县| 虞城县| 河南省| 肃宁县| 喀什市| 长岛县| 原阳县| 同仁县| 离岛区| 敖汉旗| 盐城市| 黑山县| 杭锦旗| 凉城县| 巩留县| 兴和县| 曲沃县| 彭阳县| 桃园市| 泊头市| 贵州省| 南通市| 宝山区| 安图县| 筠连县| 高碑店市| 崇州市| 东乌珠穆沁旗| 景东| 恩平市| 松阳县| 离岛区| 贵溪市| 临泽县| 烟台市| 惠来县| 中超|