Verticalbannerview imitates Taobao headlines to realize vertical rotation advertising

Verticalbannerview is a custom control that imitates the rotation headlines on the home page of Taobao app.

characteristic:

1. You can freely define the displayed content. 2. The usage is similar to listview / recyclerview. 3. Various events can be added to the currently displayed content, such as clicking to open a page, etc.

Verticalbannerview open source project address

Operation effect diagram:

1、 Project use

(1) . add project dependencies.

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

(2) . add layout.

<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) . implement the 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);
    // 你可以增加点击事件
    view.setOnClickListener(new View.OnClickListener() {
      @Override
      public void onClick(View v) {
        // TODO handle click event
      }
    });
  }
}

(4) . set the adapter for verticalbannerview and start the animation.

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

2、 Source code analysis

Implementation principle:

Verticalbannerview is essentially a vertical LinearLayout. Define an adapter class to provide a child view to LinearLayout. In the initial state, add two sub views to the LinearLayout. The height of the sub view is the same as that of the LinearLayout. In this way, only the first sub view is displayed and the second sub view is not displayed at the bottom. Then use the attribute animation objectanimator to modify the translationy attribute of the two sub views at the same time. During the animation execution, the translationy gradually changes from the default value of 0 to the negative LinearLayout height. The displayed effect is that the first sub view gradually exits upward and the second sub view gradually displays upward from the bottom. After the animation is executed, remove the first sub view, so that the index of the second sub view becomes 0 and is fully displayed, occupying the height of LinearLayout. Then add the removed first child view to the position with index 1. At this time, the child view is not displayed beyond the parent view. After the execution of a round of animation, call the postdelay () method to repeat the above animation and continue the cycle.

Next, enter the code section, mainly including two classes: basebaneradapter and verticalbannerview.

(1) . basebaneradapter class

The basebaneradapter class is responsible for providing data for the ad board. When using, we need to write an adapter class to inherit the basebaneradapter and implement the getview () and setitem () methods. In the getview () method, we need to create and return the item view to be added to the advertisement column, and the setitem () method is responsible for binding data for the created item view.

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));
  }

  // 设置banner填充的数据
  public void setData(List<T> datas) {
    this.mDatas = datas;
    notifyDataChanged();
  }

  void setOnDataChangedListener(OnDataChangedListener listener) {
    mOnDataChangedListener = listener;
  }

  // 获取banner总数
  public int getCount() {
    return mDatas == null ? 0 : mDatas.size();
  }

  // 通知数据改变
  void notifyDataChanged() {
    mOnDataChangedListener.onChanged();
  }

  // 获取数据
  public T getItem(int position) {
    return mDatas.get(position);
  }

  // 设置banner的ItemView
  public abstract View getView(VerticalBannerView parent);

  // 设置banner的数据
  public abstract void setItem(View view,T data);

  // 数据变化的监听
  interface OnDataChangedListener {
    void onChanged();
  }
}

(2) . verticalbannerview class

The verticalbannerview class inherits from LinearLayout and sets the direction to vertical in the construction method. At the same time, the VerticalBannerView class implements the OnDataChangedListener interface and implements the onChanged () method. When the notifyDataChanged () of BaseBannerAdapter is called after changing the data, the onChanged () method of VerticalBannerView is callback, and setupAdapter () is executed to reinitialize the data.

public class VerticalBannerView extends LinearLayout implements BaseBannerAdapter.OnDataChangedListener {
  public VerticalBannerView(Context context,AttributeSet attrs,int defStyleAttr) {
    super(context,attrs,defStyleAttr);
    init(context,defStyleAttr);
  }

  private void init(Context context,int defStyleAttr) {
    setOrientation(VERTICAL);
 ......
  }

  ......

  @Override
  public void onChanged() {
    setupAdapter();
  }

  ......
}

To add item data to verticalbannerview, you need to call the setadapter () method. The key lies in the setupadapter () method executed therein.

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();
}

In the setupAdapter () method, first remove all the sub View, then call the getView () method of Adapter to create two sub View, assign them to member variables mFirstView and mSecondView respectively, and bind the data for these two sub View, and finally add addView ().

// 初始化Child View
private void setupAdapter() {
  // 先移除所有的子View
  removeAllViews();

  if (mAdapter.getCount() == 1) {
    mFirstView = mAdapter.getView(this);
    mAdapter.setItem(mFirstView,mAdapter.getItem(0));
    addView(mFirstView);
  } else {
    // 调用Adapter的getView()方法,创建两个子View,分别赋值给mFirstView和mSecondView
    mFirstView = mAdapter.getView(this);
    mSecondView = mAdapter.getView(this);
    // 使用索引0和1的数据,为mFirstView和mSecondView设置数据
    mAdapter.setItem(mFirstView,mAdapter.getItem(0));
    mAdapter.setItem(mSecondView,mAdapter.getItem(1));
    // 将mFirstView和mSecondView添加到当前View
    addView(mFirstView);
    addView(mSecondView);

    mPosition = 1;
    isStarted = false;
  }
  setBackgroundDrawable(mFirstView.getBackground());
}

In order to switch between sub views, you need to modify the height of the sub view added above to be consistent with the height of verticalbannerview. This is done in the onmeasure () method.

@Override
protected void onMeasure(int widthMeasureSpec,int heightMeasureSpec) {
  super.onMeasure(widthMeasureSpec,heightMeasureSpec);
  // 成员变量mBannerHeight有一个认值
  if (LayoutParams.WRAP_CONTENT == getLayoutParams().height) {
    // 如果当前View的高度设置为wrap_content,使用认值
    getLayoutParams().height = (int) mBannerHeight;
  } else {
    // 如果当前View设定了固定高度,则使用设定的高度
    mBannerHeight = getHeight();
  }
  // 修改mFirstView和mSecondView的高度为其父视图的高度
  if (mFirstView != null) {
    mFirstView.getLayoutParams().height = (int) mBannerHeight;
  }
  if (mSecondView != null) {
    mSecondView.getLayoutParams().height = (int) mBannerHeight;
  }
}

After the above preparations are completed, you can enter the execution of the animation. Verticalbannerview starts the toggle animation by calling the start () method. In the start () method, calling postDelayed () performs the AnimRunnable task. In the run () method of AnimRunnable, we call performSwitch () first, and then call postDelayed () again to make the AnimRunnable task circulate continuously. The switching between two child views is performed by performswitch ().

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();
    // 调用postDelayed()延时再执行,一直循环下去
    postDelayed(this,mGap);
  }
}

Next, enter the performswitch () method, which is the core of the switching effect between item views.

// 执行切换
private void performSwitch() {
  // 动画在执行过程中,View的translationY属性从0一直减小到-mBannerHeight
  // 因为translationY属性一直是负值,所以View向上移动
  ObjectAnimator animator1 = ObjectAnimator.ofFloat(mFirstView,"translationY",-mBannerHeight);
  ObjectAnimator animator2 = ObjectAnimator.ofFloat(mSecondView,-mBannerHeight);
  AnimatorSet set = new AnimatorSet();
  set.playTogether(animator1,animator2);
  set.addListener(new AnimatorListenerAdapter() {
    @Override
    public void onAnimationEnd(Animator animation) {
      // 动画执行完成,把mFirstView和mSecondView的translationY恢复到认状态
      mFirstView.setTranslationY(0);
      mSecondView.setTranslationY(0);
      // 使用下一条数据,设置到第一个子View
      View removedView = getChildAt(0);
      mPosition++;
      mAdapter.setItem(removedView,mAdapter.getItem(mPosition % mAdapter.getCount()));
      // 移除第一个子View,此时当前LinearLayout的childCount==1
      removeView(removedView);
      // 移除的View,再添加到第2个位置
      addView(removedView,1);
    }
  });
  set.setDuration(mAnimDuration);
  set.start();
}

In the performswitch() method, use the attribute animation objectanimator to modify the translationy attribute of two mfirstview and msecondview at the same time. During the animation execution, the translationy decreases from the default value of 0 to - mbannerheight. During this process, mfirstview gradually exits upward and msecondview gradually appears from the bottom. After the animation is executed, remove mfirstview, and msecondview becomes the first child view and is fully displayed to fill the height of the parent view. Then add the removed mfirstview to the second position. At this time, mfirstview is not displayed. Since the performswitch () method is called all the time, mfirstview and msecondview are switched all the time.

The above is the whole content of this article. I hope it will help you in your study, and I hope you will support us a lot.

The content of this article comes from the network collection of netizens. It is used as a learning reference. The copyright belongs to the original author.
THE END
分享
二维码
< <上一篇
下一篇>>