Android用ViewPager实现一个中间大两边小并且带指示器的轮播图,也称画廊效果

项目开发中需实现画廊效果轮播图,且指示器与轮播图分离。作者在GitHub找到ViewPagerIndicator实现指示器功能。介绍了clipChildren属性设置、关键代码实现画廊缩放效果,还说明了xml布局和Java代码实现逻辑,虽未做无限和自动轮播,但称实现不难。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

在项目开发中,遇到了一个新需求,要做一个画廊效果的轮播图,要求指示器跟轮播图分离。效果图如下。以前做轮播图都是直接拿第三方的,现在突然做一个这种小众的效果,发现自己不知道怎么做,尤其是这个指示器,后来在GitHub上搜到了一个很好用 指示器实现方案,特地引用进来,非常感谢LinweiJ,帮我完成了功能实现。老规矩,附上指示器的地址:ViewPagerIndicator

1.ViewPagerIndicator的强大我就不过赘述了,自己前去GitHub看使用方法,我只附上效果图

2.clipChildren,先了解这个属性,因为在只用过程中至关重要。clipChild用来定义他的子控件是否要在他应有的边界内进行绘制。 默认情况下,clipChild被设置为true,不允许进行扩展绘制。所以我们要实现画廊效果就要设置该属性为false。而且,这个属性一定是要添加在viewpager所在的直接父布局的

接下来就是mViewpager.setPageTransformer(true, new CardTransformer());这句关键代码,setPageTransformer设置轮播图两边的缩放效果,才能实现画廊效果,下面附上CardTransformer的代码,可以根据自己的需求设置不同的属性值

/**
 * Created by zzf on 2017/10/27.
 */

public class CardTransformer implements ViewPager.PageTransformer {
    private static final float MAX_SCALE = 1.2f;
    private static final float MIN_SCALE = 1.0f;//0.85f

    @Override
    public void transformPage(View page, float position) {
        if (position <= 1) {
         //   1.2f + (1-1)*(1.2-1.0)
            float scaleFactor = MIN_SCALE + (1 - Math.abs(position)) * (MAX_SCALE - MIN_SCALE);

            page.setScaleX(scaleFactor);  //缩放效果

            if (position > 0) {
                page.setTranslationX(-scaleFactor * 2);
            } else if (position < 0) {
                page.setTranslationX(scaleFactor * 2);
            }
            page.setScaleY(scaleFactor);
        } else {

            page.setScaleX(MIN_SCALE);
            page.setScaleY(MIN_SCALE);
        }
    }
}

3.重要的关键的已经说完,前面说了轮播图和指示器分离,是比较少用的情况,所以我自身没做到自己写一个可以实现的指示器,在引用了ViewPagerIndicator以后,只要在xml布局中设置指示器的任意位置,就可以自己决定指示器是在轮播图内,还是分离开来。这里还是非常感谢LinweiJ提供的这么优秀的开源框架。下面附上xml代码:

<?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"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@drawable/img_home_bg"
    android:clipChildren="false"
    android:orientation="vertical">


    <android.support.v4.view.ViewPager
        android:id="@+id/viewpager"
        android:layout_width="wrap_content"
        android:layout_height="200dp"
        android:layout_gravity="center"
        android:layout_marginLeft="40dp"
        android:layout_marginRight="40dp"
        android:layout_marginTop="30dp"
        android:clipChildren="false"
        android:overScrollMode="never"></android.support.v4.view.ViewPager>

    <com.lwj.widget.viewpagerindicator.ViewPagerIndicator
        android:id="@+id/indicator_line"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginTop="10dp"
        app:vpi_default_color="@color/whilte"
        app:vpi_distance="14dp"
        app:vpi_distanceType="BY_DISTANCE"
        app:vpi_indicatorType="CIRCLE_LINE"
        app:vpi_length="14dp"
        app:vpi_radius="3dp" />

    <RelativeLayout
        android:layout_marginTop="@dimen/dimen_30dp"
        android:clipChildren="false"
        android:layout_width="match_parent"
        android:layout_height="200dp">

        <android.support.v4.view.ViewPager
            android:id="@+id/viewpager2"
            android:layout_width="wrap_content"
            android:layout_height="200dp"
            android:layout_gravity="center"
            android:layout_marginLeft="40dp"
            android:layout_marginRight="40dp"
            android:clipChildren="false"
            android:overScrollMode="never"></android.support.v4.view.ViewPager>

        <com.lwj.widget.viewpagerindicator.ViewPagerIndicator
            android:layout_alignParentBottom="true"
            android:layout_marginBottom="10dp"
            android:id="@+id/indicator_line2"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            app:vpi_default_color="@color/whilte"
            app:vpi_distance="14dp"
            app:vpi_distanceType="BY_DISTANCE"
            app:vpi_indicatorType="CIRCLE_LINE"
            app:vpi_length="14dp"
            app:vpi_radius="3dp" />
    </RelativeLayout>

</LinearLayout>

实现效果

4.接下来就是Java代码的实现逻辑,实现起来还是非常简单的,我给轮播图添加了点击事件监听,可以直接使用

Activity代码:

/**
 * Created by zzf on 2018/8/29.
 */

public class BannerActivity extends BaseActivity {

    @BindView(R.id.viewpager)
    ViewPager mViewpager;
    @BindView(R.id.indicator_line)
    ViewPagerIndicator mIndicatorLine;
    @BindView(R.id.viewpager2)
    ViewPager mViewpager2;
    @BindView(R.id.indicator_line2)
    ViewPagerIndicator mIndicatorLine2;

    private BannerAdapter mBannerAdapter;
    private BannerAdapter mBannerAdapter2;

    @Override
    protected BasePresenter createPresenter() {
        return null;
    }

    @Override
    public int getLayoutId() {
        return R.layout.activity_banner;
    }

    @Override
    public void initView() {
        initFirstBanner();
        initSecondBanner();
    }

    private void initSecondBanner() {
        mBannerAdapter2 = new BannerAdapter(this, mViewpager2);
        mViewpager2.setAdapter(mBannerAdapter2);
        mViewpager2.setOffscreenPageLimit(2);//预加载2个
        mViewpager2.setPageMargin(30);//设置viewpage之间的间距
        mViewpager2.setClipChildren(false);
        mViewpager2.setPageTransformer(true, new CardTransformer());
        mIndicatorLine2.setViewPager(mViewpager2, 6);
        mBannerAdapter2.setItemClickListener(new BannerAdapter.ItemClickListener() {

            @Override
            public void onItemClick(int index) {
//                ToastUtils.showToast("点击了图片" + index);
            }
        });
        mViewpager2.setCurrentItem(0);
    }

    private void initFirstBanner() {
        mBannerAdapter = new BannerAdapter(this, mViewpager);
        mViewpager.setAdapter(mBannerAdapter);
        mViewpager.setOffscreenPageLimit(2);//预加载2个
        mViewpager.setPageMargin(30);//设置viewpage之间的间距
        mViewpager.setClipChildren(false);
        mViewpager.setPageTransformer(true, new CardTransformer());
        mIndicatorLine.setViewPager(mViewpager, 6);
        mBannerAdapter.setItemClickListener(new BannerAdapter.ItemClickListener() {

            @Override
            public void onItemClick(int index) {
//                ToastUtils.showToast("点击了图片" + index);
            }
        });
        mViewpager.setCurrentItem(0);
    }
}

adapter代码:

/**
 * Created by zzf on 2018/7/29.
 */

public class BannerAdapter extends PagerAdapter implements View.OnClickListener, ViewPager.OnPageChangeListener {

    private Context mContext;
    private ItemClickListener itemClickListener;
    private int currentPosition = 0;


    public BannerAdapter(Context context, ViewPager viewPager) {
        mContext = context;
        viewPager.clearOnPageChangeListeners();
        viewPager.addOnPageChangeListener(this);
    }

    public void setItemClickListener(ItemClickListener itemClickListener) {
        this.itemClickListener = itemClickListener;
    }


    @Override
    public int getCount() {
        return 6;
    }

    @Override
    public boolean isViewFromObject(View view, Object object) {
        return view == object;
    }

    @Override
    public Object instantiateItem(ViewGroup container, int position) {
        View view = LayoutInflater.from(mContext).inflate(R.layout.item_viewpager, null);
        ImageView imageView = view.findViewById(R.id.iv_icon);
        imageView.setOnClickListener(this);
        container.addView(view);
        return view;
    }

  /*  @Nullable
    @Override
    public CharSequence getPageTitle(int position) {
        return tabList.get(position);
    }*/

    @Override
    public void destroyItem(ViewGroup container, int position, Object object) {
        container.removeView((View) object);
    }

    @Override
    public void onClick(View v) {
        if (null != itemClickListener) {
            itemClickListener.onItemClick(currentPosition);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        currentPosition = position;
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    public interface ItemClickListener {
        void onItemClick(int index);
    }
}

由于当时的项目没要求做无限轮播和自动轮播的要求,所以我也就没加这些功能,但是实现起来也是非常简单的,如果有需要可以提出来,我有时间可以再去实现该功能

Android中,要实现一个中间两边小的ViewPager滚动效果,通常会使用`ViewPager`配合`PagerAdapter`来管理Fragment,并通过自定义布局或者适配器来设计卡片式的外观。以下是一个简化的步骤: 1. **创建项目结构**: - 在`activity_main.xml`布局文件中设置一个`CoordinatorLayout`作为根容器。 - 在`CoordinatorLayout`内添加一个`LinearLayout`作为水平布局,将`android:orientation="horizontal"`属性设为水平方向。 ```xml <androidx.coordinatorlayout.widget.CoordinatorLayout> <LinearLayout android:id="@+id/content_layout" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="horizontal"> <!-- 这里将会放ViewPager --> </LinearLayout> </androidx.coordinatorlayout.widget.CoordinatorLayout> ``` 2. **创建`PagerAdapter`**: 创建一个自定义的PagerAdapter,比如`MiddleCardPagerAdapter`,它可以根据指定的页面索引来调整显示的fragment小。 ```java public class MiddleCardPagerAdapter extends FragmentStatePagerAdapter { private int[] fragmentWidths; // 存储每个Fragment的宽度 public MiddleCardPagerAdapter(FragmentManager fm, int[] fragmentWidths) { super(fm); this.fragmentWidths = fragmentWidths; } @Override public Fragment getItem(int position) { return createFragment(position); } @Override public int getCount() { // 根据需求计算fragment的数量 return fragmentWidths.length; } private Fragment createFragment(int position) { // 实现Fragment并设置其宽度为数组中的值 MyFragment myFragment = new MyFragment(); myFragment.setRetainInstance(true); // 保留实例以便于动态修改宽度 View itemView = LayoutInflater.from(context).inflate(R.layout.fragment_card, null); ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) itemView.getLayoutParams(); layoutParams.width = fragmentWidths[position]; itemView.setLayoutParams(layoutParams); return myFragment; } } ``` 3. **设置ViewPager**: 在MainActivity中初始化`ViewPager`, 设置Adapter,并监听滑动事件。 ```java ViewPager viewPager = findViewById(R.id.view_pager); MiddleCardPagerAdapter adapter = new MiddleCardPagerAdapter(getSupportFragmentManager(), createFragmentWidthArray()); viewPager.setAdapter(adapter); // 监听滑动,改变中间Fragment的宽度来模拟小变化 viewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { @Override public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { if (position == 0 || position == adapter.getCount() - 1) { // 边缘位置不变 return; } MyFragment middleFragment = (MyFragment) viewPager.findFragmentByTag("middle_fragment"); if (middleFragment != null) { middleFragment.updateWidth(positionOffset); } } // 其他方法... }); ``` 4. **自定义Fragment** (`MyFragment`): `MyFragment`需要包含一个用于更新宽度的方法,并根据传入的值调整视图尺寸。 ```java public class MyFragment extends Fragment { private View mView; @Nullable @Override public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { mView = inflater.inflate(R.layout.fragment_card, container, false); // 初始化并添加宽度变化的相关逻辑 return mView; } public void updateWidth(float offset) { // 根据offset百分比调整中间部分的宽度 ViewGroup.LayoutParams layoutParams = (ViewGroup.LayoutParams) mView.getLayoutParams(); layoutParams.width = Math.max(minWidth, (int) (mOriginalWidth * (1 + offset))); mView.setLayoutParams(layoutParams); } }
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值