布局
效果图
1.自定义垂直滚动控件VerticalBannerView
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.widget.LinearLayout;
/**
* @author: wuxifan
* @date on 2019-08-19 16:42
* @email 1095838464@qq.com
**/
@SuppressWarnings("unused")
public class VerticalBannerView extends LinearLayout implements BaseBannerAdapter.OnDataChangedListener {
private float mBannerHeight = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,40,getResources().getDisplayMetrics());
private int mGap = 4000;
private int mAnimDuration = 1000;
private BaseBannerAdapter mAdapter;
private View mFirstView;
private View mSecondView;
private int mPosition;
private boolean isStarted;
private Paint mDebugPaint;
public VerticalBannerView(Context context) {
this(context,null);
}
public VerticalBannerView(Context context, AttributeSet attrs) {
this(context, attrs,0);
}
public VerticalBannerView(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
init(context,attrs,defStyleAttr);
}
/**
* bannerHeight banner的高度
* animDuration 每次切换动画时间
* gap banner切换时间
*
* */
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);
mGap = array.getInteger(R.styleable.VerticalBannerView_gap,mGap);
mAnimDuration = array.getInteger(R.styleable.VerticalBannerView_animDuration,mAnimDuration);
if(mGap <= mAnimDuration){
mGap = 4000;
mAnimDuration = 1000;
}
array.recycle();
}
/**
* 设置banner的数据
* */
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();
}
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);
}
}
public void stop(){
removeCallbacks(mRunnable);
isStarted = false;
}
private void setupAdapter() {
removeAllViews();
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());
}
@Override
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;
}
}
@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);
}
}
@Override
public void onChanged() {
setupAdapter();
}
private void performSwitch(){
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();
}
private AnimRunnable mRunnable = new AnimRunnable();
private class AnimRunnable implements Runnable{
@Override
public void run() {
performSwitch();
postDelayed(this,mGap);
}
}
@Override
protected void onDetachedFromWindow() {
super.onDetachedFromWindow();
stop();
}
@Override
protected void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
}
public int getmPosition(){
return (mPosition-1)%mAdapter.getCount();
}
}
2.所用到的styeable(在values/atrs.xml里定义一个styleable)
<declare-styleable name="VerticalBannerView">
<attr name="gap" format="integer" />
<attr name="animDuration" format="integer"/>
</declare-styleable>
3.自定义BaseBannerAdapter
import android.view.View;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* @author: wuxifan
* @date on 2019-08-19 16:43
* @email 1095838464@qq.com
* 作用:
**/
@SuppressWarnings("unused")
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 notifyDataChanged() {
mOnDataChangedListener.onChanged();
}
void setOnDataChangedListener(OnDataChangedListener listener) {
mOnDataChangedListener = listener;
}
public int getCount() {
return mDatas == null ? 0 : mDatas.size();
}
public T getItem(int position) {
return mDatas.get(position);
}
/**
* 设置banner的样式
*/
public abstract View getView(VerticalBannerView parent);
/**
* 设置banner的数据
*/
public abstract void setItem(View view, T data);
interface OnDataChangedListener {
void onChanged();
}
}
4.实现自己的VerticaBannerSampleAdapter
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import android.widget.Toast;
import java.util.List;
/**
* @author: wuxifan
* @date on 2019-08-19 16:46
* @email 1095838464@qq.com
* 作用:
**/
public class VerticaBannerSampleAdapter extends BaseBannerAdapter<BannerList> {
private List<BannerList> mDatas;
public VerticaBannerSampleAdapter(List<BannerList> datas) {
super(datas);
}
@Override
public View getView(VerticalBannerView parent) {
return LayoutInflater.from(parent.getContext()).inflate(R.layout.item_vertical_banner_tv, null);
}
@Override
public void setItem(final View view, final BannerList data) {
TextView tv = (TextView) view.findViewById(R.id.tv_title);
tv.setText(data.getTitle());
//你可以增加点击事件
view.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//比如打开url
Toast.makeText(view.getContext(),data.getUrl(),Toast.LENGTH_SHORT).show();
}
});
}
}
5.用来垂直展示的布局文件 这里简单的就一个TextView
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
<TextView
android:id="@+id/tv_title"
android:layout_width="match_parent"
android:gravity="center_vertical"
android:layout_height="wrap_content"
android:layout_marginRight="@dimen/dp_10"
android:ellipsize="marquee"
android:singleLine="true"
android:text="hahahhahahahahhahahahahahhah" />
</LinearLayout>
6.正式布局引用
<!--切换时长2000ms 动画间隔900ms-->
<com.wuxifan.demo.module.main.other.VerticalBannerView
android:id="@+id/banner_01"
android:layout_width="match_parent"
android:layout_height="@dimen/dp_18"
android:layout_marginTop="@dimen/dp_5"
android:layout_marginBottom="@dimen/dp_10"
app:animDuration="900"
app:gap="5500" />
7.代码中设置
@BindView(R.id.banner_01)
VerticalBannerView banner01;
private void setHeaderArticle(List<BannerList> bannerLists) {
final VerticaBannerSampleAdapter adapter = new VerticaBannerSampleAdapter(bannerLists);
banner01.setAdapter(adapter);
banner01.start();
btnLookDetail.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
//banner01.getmPostion()为当前显示的滚动item的下标
Toast.makeText(view.getContext(),bannerLists.get(banner01.getmPosition()).getUrl(),Toast.LENGTH_SHORT).show();
}
});
}
8.BannerList这个是自定义的javeBean 自行替换成所需的咯
import android.text.TextUtils;
import java.io.Serializable;
public class BannerList implements Serializable {
private String title;
private String url;
private String id;
private String sub_title;
private String shortCutName;
private String iconUrl;
private String targetUrl;
public String getShortCutName() {
return shortCutName == null ? "" : shortCutName;
}
public void setShortCutName(String shortCutName) {
this.shortCutName = shortCutName;
}
public String getIconUrl() {
return iconUrl == null ? "" : iconUrl;
}
public void setIconUrl(String iconUrl) {
this.iconUrl = iconUrl;
}
public String getTargetUrl() {
return targetUrl == null ? "" : targetUrl;
}
public void setTargetUrl(String targetUrl) {
this.targetUrl = targetUrl;
}
public String getId() {
return id == null ? "" : id;
}
public void setId(String id) {
this.id = id;
}
public String getSub_title() {
return sub_title == null ? "" : sub_title;
}
public void setSub_title(String sub_title) {
this.sub_title = sub_title;
}
public String getTitle() {
return title == null ? "" : title;
}
public void setTitle(String title) {
this.title = title;
}
public String getUrl() {
return url == null ? "" : url;
}
public void setUrl(String url) {
this.url = url;
}
}
站在网上各位大神的肩膀上总结的 特此感谢