软件编程
位置:首页>> 软件编程>> Android编程>> Android实现视图轮播效果

Android实现视图轮播效果

作者:lhj_android  发布时间:2023-04-14 08:52:10 

标签:Android,视图轮播

最近接手了一个需求,要求实现,叮咚买菜。

Android实现视图轮播效果

秒杀位置的轮播

拆解

通过观察发现其实还是挺简单,大致分为

1、商品图片的上下轮播
2、价格布局渐隐渐现
在android上实现布局轮播,其实官方已经提供了实现
ViewFlipper
AdapterViewFlipper

Android实现视图轮播效果

由于后端传递的是一组商品,不确定个数。那么选取AdapterViewFlipper是最好的选择

布局复用,用adpter的方式填充数据

而且不论是ViewFlipper还是AdapterViewFlipper 系统都帮助实现了自动轮播的功能,我们只需要设置它的进入和退出动画就可以。
但上面的效果有一个和AdapterViewFlipper联动的效果,在布局移动到屏幕外面的过程中需要执行一个渐隐渐现的联动效果。
查看了上面两个布局,在调用showNext方法时没有提供改变时的监听,那没办法只能自己去实现。其实也简单,继承AdapterViewFlipper重写它的showNext方法就行了。

MFAdapterViewFlipper


/**
* File description.
* 自定义AdapterViewFlipper 在它执行下一个动画时回调给也无妨
* @author lihongjun
* @date 9/24/21
*/
public class MFAdapterViewFlipper extends AdapterViewFlipper {

// view切换时的回调
   private AdapterViewFlipperChangeListener adapterViewFlipperChangeListener;

public MFAdapterViewFlipper(Context context) {
       super(context);
   }

public MFAdapterViewFlipper(Context context, AttributeSet attrs) {
       super(context, attrs);
   }

public MFAdapterViewFlipper(Context context, AttributeSet attrs, int defStyleAttr) {
       super(context, attrs, defStyleAttr);
   }

public MFAdapterViewFlipper(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
       super(context, attrs, defStyleAttr, defStyleRes);
   }

public void setAdapterViewFlipperChangeListener(AdapterViewFlipperChangeListener adapterViewFlipperChangeListener) {
       this.adapterViewFlipperChangeListener = adapterViewFlipperChangeListener;
   }

@Override
   public void showNext() {
       super.showNext();
       if (adapterViewFlipperChangeListener != null) {
           adapterViewFlipperChangeListener.showNext();
       }
   }

@Override
   public void showPrevious() {
       super.showPrevious();
       if (adapterViewFlipperChangeListener != null) {
           adapterViewFlipperChangeListener.showPrevious();
       }
   }

/**
    * 布局切换时的回调
    */
   public interface AdapterViewFlipperChangeListener {

/**
        * 显示后一个
        */
       void showNext();

/**
        * 显示前一个
        */
       void showPrevious();
   }
}

动起来

接下来就是填充数据让他动起来了
为了让外面使用这个布局简单点,那自定义一下view吧

布局


<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
   xmlns:app="http://schemas.android.com/apk/res-auto"
   android:layout_width="match_parent"
   android:layout_height="match_parent">

<MFAdapterViewFlipper
       android:id="@+id/home_avf"
       android:layout_width="match_parent"
       android:layout_height="wrap_content"
       android:flipInterval="3000"
       android:loopViews="true" />

<LinearLayout
       android:id="@+id/ll_price"
       android:layout_width="match_parent"
       android:layout_height="78dp"
       android:gravity="bottom"
       android:orientation="horizontal">

<PriceViewB
           android:id="@+id/layout_left_price"
           android:layout_width="0dp"
           android:layout_height="wrap_content"
           android:layout_weight="1"
           android:gravity="center_horizontal"
           app:price_type="2" />

<PriceViewB
           android:id="@+id/layout_right_price"
           android:layout_width="0dp"
           android:layout_height="wrap_content"
           android:layout_weight="1"
           android:gravity="center_horizontal"
           app:price_type="2" />

</LinearLayout>

</RelativeLayout>

android:flipInterval=“3000”
android:loopViews=“true”
轮播间隔3秒
开启轮播布局

自定义view


/**
* File description.
* 首页秒杀位 图片轮播
*
* @author lihongjun
* @date 9/24/21
*/
public class HomeCountDownProductSwitch extends RelativeLayout implements MFAdapterViewFlipper.AdapterViewFlipperChangeListener {

// 一排固定展示2个
   private static final int MAX_PRODUCT_SIZE = 2;

// 轮播布局
   private MFAdapterViewFlipper mAdapterViewFlipper;
   private HomeCountDownProductSwitchAdapter adapter;
   // 价格整体布局
   private View mVPrice;
   private MFPriceViewB leftMFPriceView;
   private MFPriceViewB rightMFPriceView;

// 当前轮播的位置
   private int currentPosition = 0;
   // 轮播的屏数
   private int mFlipCount;

// 商品集合数据
   List<TileBean.Product> mProductList;

public HomeCountDownProductSwitch(Context context) {
       this(context, null);
   }

public HomeCountDownProductSwitch(Context context, AttributeSet attrs) {
       super(context, attrs);
       LayoutInflater.from(context).inflate(R.layout.home_count_down_product_switch, this);
       mAdapterViewFlipper = findViewById(R.id.home_avf);
       leftMFPriceView = findViewById(R.id.layout_left_price);
       rightMFPriceView = findViewById(R.id.layout_right_price);
       mVPrice = findViewById(R.id.ll_price);

mAdapterViewFlipper.setAdapterViewFlipperChangeListener(this);

adapter = new HomeCountDownProductSwitchAdapter(context);
       mAdapterViewFlipper.setAdapter(adapter);
   }

/**
    * 设置展示数据
    *
    * @param productList
    */
   public void setProductList(List<TileBean.Product> productList) {
       mAdapterViewFlipper.stopFlipping();
       this.mProductList = productList;
       this.currentPosition = 0;
       int productSize = CommonUtils.isEmpty(productList) ? 0 : productList.size();
       // 每行展示2个 所以一共有多少行 等于2的整除加余数
       mFlipCount = (productSize / MAX_PRODUCT_SIZE) + (productSize % MAX_PRODUCT_SIZE);
       changeCurrentPrice();
       adapter.setData(productList);
       postDelayed(new Runnable() {
           @Override
           public void run() {
               startFlipping();
           }
       },1000);
   }

/**
    * 开始轮播
    */
   private void startFlipping() {
       mAdapterViewFlipper.setInAnimation(getContext(),R.animator.anim_count_down_product_in);
       mAdapterViewFlipper.setOutAnimation(getContext(),R.animator.anim_count_down_product_out);
       Animation priceOutAnimation = AnimationUtils.loadAnimation(getContext(),R.anim.home_anim_price_out);
       priceOutAnimation.setDuration(500);
       mAdapterViewFlipper.getOutAnimation().addListener(new Animator.AnimatorListener() {
           @Override
           public void onAnimationStart(Animator animation) {
               mVPrice.startAnimation(priceOutAnimation);
           }

@Override
           public void onAnimationEnd(Animator animation) {
               changeCurrentPrice();
           }

@Override
           public void onAnimationCancel(Animator animation) {

}

@Override
           public void onAnimationRepeat(Animator animation) {

}
       });
       mAdapterViewFlipper.startFlipping();
   }

/**
    * 更改当前价格模块
    */
   private void changeCurrentPrice() {
       int productSize = MFCommonUtils.isEmpty(mProductList) ? 0 : mProductList.size();
       // 数据不合法不显示价格
       if (MFCommonUtils.isEmpty(mProductList) || productSize <= 0) {
           mVPrice.setVisibility(GONE);
           return;
       }

// 每排展示两个商品数据
       int start = currentPosition * MAX_PRODUCT_SIZE;
       int end = start + 1;

TileBean.Product leftProduct = null;
       TileBean.Product rightProduct = null;
       // 左边的商品
       if (productSize > start) {
           leftProduct = mProductList.get(start);
       }
       // 右边的商品
       if (productSize > end) {
           rightProduct = mProductList.get(end);
       }
       leftMFPriceView.initPriceUI(leftProduct != null ? leftProduct.getPriceInfo() : null);
       rightMFPriceView.initPriceUI(rightProduct != null ? rightProduct.getPriceInfo() : null);
   }

/**
    * 显示后一个
    */
   @Override
   public void showNext() {
       currentPosition ++;
       // 如果已经循环了1轮 那从头开始
       if (currentPosition == mFlipCount) {
           currentPosition = 0;
       }
   }

/**
    * 显示前一个
    */
   @Override
   public void showPrevious() {

}

/**
    * 布局适配器
    */
   private static final class HomeCountDownProductSwitchAdapter extends BaseAdapter {

private Context mContext;
       // 商品列表
       private List<TileBean.Product> productList;

public HomeCountDownProductSwitchAdapter(Context context) {
           this.mContext = context;
       }

/**
        * 更新数据
        *
        * @param
        */
       public void setData(List<TileBean.Product> productList) {
           this.productList = productList;
           notifyDataSetChanged();
       }

@Override
       public int getCount() {
           int count = 0;
           if (MFCommonUtils.isEmpty(productList)) {
               return count;
           }
           // 每行展示2个 所以一共有多少行 等于2的整除加余数
           count = (productList.size() / MAX_PRODUCT_SIZE) + (productList.size() % MAX_PRODUCT_SIZE);
           return count;
       }

@Override
       public Object getItem(int position) {
           return productList == null ? null : productList.get(position);
       }

@Override
       public long getItemId(int position) {
           return 0;
       }

@Override
       public View getView(int position, View convertView, ViewGroup parent) {
           ViewHolder holder;
           if (convertView == null) {
               convertView = LayoutInflater.from(mContext).inflate(R.layout.home_page_tile_time_down_holder_flipper, null);
               holder = new ViewHolder(convertView);
               convertView.setTag(holder);
           } else {
               holder = (ViewHolder) convertView.getTag();
           }
           // 设置数据
           // 每排展示两个商品数据
           int start = position * MAX_PRODUCT_SIZE;
           int end = start + 1;
           int productSize = MFCommonUtils.isEmpty(productList) ? 0 : productList.size();
           TileBean.Product leftProduct = null;
           TileBean.Product rightProduct = null;
           // 左边的商品
           if (productSize > start) {
               leftProduct = productList.get(start);
           }
           // 右边的商品
           if (productSize > end) {
               rightProduct = productList.get(end);
           }
           holder.bindData(leftProduct, rightProduct, position);
           return convertView;
       }
   }

// holder
   private static final class ViewHolder {

private View itemView;

// 左边和有点两个布局控件
       private ImageView mIvLeft;
       private ImageView mIvRight;

private int imageRadio;

public ViewHolder(View itemView) {
           this.itemView = itemView;

mIvLeft = itemView.findViewById(R.id.iv_left_img);
           mIvRight = itemView.findViewById(R.id.iv_right_img);

imageRadio = itemView.getResources().getDimensionPixelSize(R.dimen.margin_8);
       }

/**
        * 设置数据
        *
        * @param leftProduct  左边的商品
        * @param rightProduct 右边的商品
        */
       public void bindData(TileBean.Product leftProduct, TileBean.Product rightProduct, int position) {
           // 如果这一排都没商品则隐藏
           if (leftProduct == null && rightProduct == null) {
               itemView.setVisibility(View.GONE);
               return;
           }
           itemView.setVisibility(View.VISIBLE);
           if (leftProduct != null) {
               GlideHelper.loadRoundAndGifImage(mIvLeft, leftProduct.getImage(), imageRadio, R.drawable.ic_default_50);
           }
           if (rightProduct != null) {
               GlideHelper.loadRoundAndGifImage(mIvRight, rightProduct.getImage(), imageRadio, R.drawable.ic_default_50);
           }

}
   }

}

注意点在于

1、进入和退出动画必须是属性动画
2、当前滚动的屏数,根据它可以算出对应的postion


* 显示后一个
*/
@Override
public void showNext() {
   currentPosition ++;
   // 如果已经循环了1轮 那从头开始
   if (currentPosition == mFlipCount) {
       currentPosition = 0;
   }
}

在执行out动画时,执行价格布局的渐隐渐现动画


mAdapterViewFlipper.getOutAnimation().addListener(new Animator.AnimatorListener() {
           @Override
           public void onAnimationStart(Animator animation) {
               mVPrice.startAnimation(priceOutAnimation);
           }

@Override
           public void onAnimationEnd(Animator animation) {
               changeCurrentPrice();
           }

@Override
           public void onAnimationCancel(Animator animation) {

}

@Override
           public void onAnimationRepeat(Animator animation) {

}
       });

执行渐隐渐现动画时显示的是上一个价格,在动画执行完毕后设置当前应该展示的价格

来源:https://blog.csdn.net/lhj_android/article/details/120671866

0
投稿

猜你喜欢

手机版 软件编程 asp之家 www.aspxhome.com