首先先展示一下具体的效果
实现了一个自定义item的轮换
下面给出一下具体的使用方法
首先是item的Model
public class Model {
private ImageView picture;//item的图片
public String title; //item的标题
public String content; //item的内容
//构造方法
public Model(String title, String content){
this.content=content;
this.title=title;
}
}
public class MyAdapter extends BaseBannerAdapter<Model> //继承的是BaseBannerAdapter泛型为Model{
private List<Model> myData; //Model的数据list
//构造方法
public MyAdapter(List<Model> datas) {
super(datas);
}
//重写getView方法
@Override
public View getView(VerticalBannerView parent) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.item,null);//绑定item布局
}
//设置item
@Override
public void setItem(View view, Model data) {
TextView title,content;
//绑定id
title= (TextView) view.findViewById(R.id.title);
content= (TextView) view.findViewById(R.id.content);
//赋值
title.setText(data.title);
content.setText(data.content);
}
}
接下去给出MainActivity
public class MainActivity extends AppCompatActivity { private VerticalBannerView vbv; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); vbv= (VerticalBannerView) findViewById(R.id.vbv); List<Model> list=new ArrayList<Model>(); for(int i=0;i<10;i++){ list.add(new Model("title"+i,"saber"+i)); } MyAdapter myAdapter=new MyAdapter(list); vbv.setAdapter(myAdapter); vbv.start();//注意要start } }
layout的代码很简单就不给出了
这样就实现了类似淘宝头条的功能
我们来进一步地看一下VerticalBannerView的源代码
public class VerticalBannerView extends LinearLayout implements BaseBannerAdapter.OnDataChangedListener
从一开始看出来这其实是一个布局
继承于Layout 实现了数据变化的一个监听器
然后是一些变量
private float mBannerHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,40,getResources().getDisplayMetrics());//高度
private int mGap = 4000; //切换时间
private int mAnimDuration = 1000;//每次切换动画的时间
private BaseBannerAdapter mAdapter; //适配器
//两个先后View
private View mFirstView;
private View mSecondView;
//位置
private int mPosition;
//判别是否启动的bool值
private boolean isStarted;
private Paint mDebugPaint;
首先是第一个方法,初始化方法
private void init(Context context, AttributeSet attrs, int defStyleAttr) {
setOrientation(VERTICAL);
mDebugPaint = new Paint(Paint.ANTI_ALIAS_FLAG);//抗锯齿
TypedArray array = context.obtainStyledAttributes(attrs, R.styleable.VerticalBannerView);//适应gap和animDuration
//获取gap和animDuration
mGap = array.getInteger(R.styleable.VerticalBannerView_gap,mGap);
mAnimDuration = array.getInteger(R.styleable.VerticalBannerView_animDuration,mAnimDuration);
//如果切换时间小于动画时间,即动画还没结束就又得切换,会出错,因此重置gap和animDuration
if(mGap <= mAnimDuration){
mGap = 4000;
mAnimDuration = 1000;
}
array.recycle();//缓存的作用,实现重复调用此对象的功能
}
然后是setAdapter方法
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方法
private void setupAdapter() {
removeAllViews();//移除View
if(mAdapter.getCount() == 1){
mFirstView = mAdapter.getView(this);
mAdapter.setItem(mFirstView,mAdapter.getItem(0));
addView(mFirstView);
}else{
mFirstView = mAdapter.getView(this);
mSecondView = mAdapter.getView(this);
mAdapter.setItem(mFirstView,mAdapter.getItem(0));
mAdapter.setItem(mSecondView,mAdapter.getItem(1));
addView(mFirstView);
addView(mSecondView);
mPosition = 1;
isStarted = false;
}
setBackgroundDrawable(mFirstView.getBackground());
}
这个方法主要是设置数据
Measure方法
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
super.onMeasure(widthMeasureSpec, heightMeasureSpec);
if(LayoutParams.WRAP_CONTENT == getLayoutParams().height){
getLayoutParams().height = (int) mBannerHeight;
}else{
mBannerHeight = getHeight();
}
if(isInEditMode()){
setBackgroundColor(Color.GRAY);
return;
}
if(mFirstView != null){
mFirstView.getLayoutParams().height = (int) mBannerHeight;
}
if(mSecondView != null){
mSecondView.getLayoutParams().height = (int) mBannerHeight;
}
}
这个方法主要是适应宽高,不多加赘述
看一下onDraw方法
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
if(isInEditMode()){
mDebugPaint.setColor(Color.WHITE);
mDebugPaint.setTextSize(TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_SP,16,getResources().getDisplayMetrics()));
mDebugPaint.setStyle(Paint.Style.STROKE);
canvas.drawText("banner is here",20,getHeight()*2/3,mDebugPaint);
}
}
主要是初始的绘制
最为重要的是这一个方法
private void performSwitch(){
//两个ObjectAnimator对应两个View
ObjectAnimator animator1 = ObjectAnimator.ofFloat(mFirstView,"translationY",mFirstView.getTranslationY()-mBannerHeight);
ObjectAnimator animator2 = ObjectAnimator.ofFloat(mSecondView,"translationY",mSecondView.getTranslationY()-mBannerHeight);
//设置动画同时进行
AnimatorSet set = new AnimatorSet();
set.playTogether(animator1,animator2);
set.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
mFirstView.setTranslationY(0);
mSecondView.setTranslationY(0);
View removedView = getChildAt(0);
mPosition++;
mAdapter.setItem(removedView,mAdapter.getItem(mPosition%mAdapter.getCount()));
removeView(removedView);
addView(removedView,1);
}
});
set.setDuration(mAnimDuration);
set.start();
}
Adapter的部分源码较为简单,基本是数据的填充和设置样式,不过多赘述